From 7614a866ca6044b99c77588c0b984c2372569ac5 Mon Sep 17 00:00:00 2001 From: Gautham Date: Tue, 9 Jul 2024 09:52:42 +0530 Subject: [PATCH 1/4] Make thea sign rotation from outgoing set --- pallets/thea-message-handler/src/lib.rs | 9 +- pallets/thea/src/lib.rs | 182 +++++++++++++----------- pallets/thea/src/validation.rs | 62 ++++++-- primitives/thea/src/types.rs | 2 +- runtimes/mainnet/src/lib.rs | 2 +- runtimes/parachain/src/lib.rs | 2 +- 6 files changed, 155 insertions(+), 104 deletions(-) diff --git a/pallets/thea-message-handler/src/lib.rs b/pallets/thea-message-handler/src/lib.rs index 71d13d864..3889ebbe4 100644 --- a/pallets/thea-message-handler/src/lib.rs +++ b/pallets/thea-message-handler/src/lib.rs @@ -205,7 +205,8 @@ pub mod pallet { let current_set_id = >::get(); match payload.message.payload_type { - PayloadType::ScheduledRotateValidators => { + PayloadType::ScheduledRotateValidators => {}, // Deprecated + PayloadType::ValidatorsRotated => { // Thea message related to key change match ValidatorSet::decode(&mut payload.message.data.as_ref()) { Err(_err) => return Err(Error::::ErrorDecodingValidatorSet.into()), @@ -218,13 +219,11 @@ pub mod pallet { validator_set.set_id, BoundedVec::truncate_from(validator_set.validators), ); + // We are checking if the validator set is changed, then we update it here too + >::put(current_set_id.saturating_add(1)); }, } }, - PayloadType::ValidatorsRotated => { - // We are checking if the validator set is changed, then we update it here too - >::put(current_set_id.saturating_add(1)); - }, PayloadType::L1Deposit => { // Normal Thea message T::Executor::execute_deposits( diff --git a/pallets/thea/src/lib.rs b/pallets/thea/src/lib.rs index 974a59710..09850fea1 100644 --- a/pallets/thea/src/lib.rs +++ b/pallets/thea/src/lib.rs @@ -40,7 +40,7 @@ use sp_runtime::{ use sp_std::collections::btree_set::BTreeSet; use sp_std::prelude::*; use thea_primitives::{ - types::{Message, NetworkType, PayloadType}, + types::{Message, PayloadType}, Network, ValidatorSet, GENESIS_AUTHORITY_SET_ID, }; @@ -758,7 +758,7 @@ impl Pallet { fn change_authorities( incoming: BoundedVec, // n+1th set - queued: BoundedVec, // n+ 2th set + _queued: BoundedVec, // n+ 2th set ) { // ( outgoing) -> (validators/incoming) -> (queued) // nth epoch -> n+1th epoch -> n+2nd epoch @@ -766,95 +766,105 @@ impl Pallet { let outgoing = >::get(id); // nth set ( active ,current ) let new_id = id + 1u64; let active_networks = >::get(); - // We need to issue a new message if the validator set is changing, - // that is, the incoming set is has different session keys from outgoing set. - // This last message should be signed by the outgoing set - // Similar to how Grandpa's session change works. + // // We need to issue a new message if the validator set is changing, + // // that is, the incoming set is has different session keys from outgoing set. + // // This last message should be signed by the outgoing set + // // Similar to how Grandpa's session change works. let incoming_set = BTreeSet::from_iter(incoming.to_vec()); - if incoming_set != BTreeSet::from_iter(queued.to_vec()) { - let uncompressed_keys: Vec<[u8; 20]> = vec![]; - // TODO: Uncomment the following when parsing is fixed for ethereum keys. - // for public_key in queued.clone().into_iter() { - // let public_key: sp_core::ecdsa::Public = public_key.into(); - // if public_key.0 == [0u8; 33] { - // uncompressed_keys.push([0u8; 20]); - // continue; - // } - // if let Ok(compressed_key) = libsecp256k1::PublicKey::parse_compressed(&public_key.0) - // { - // let uncompressed_key = compressed_key.serialize(); - // let uncompressed_key: [u8; 64] = - // if let Ok(uncompressed_key) = uncompressed_key[1..65].try_into() { - // uncompressed_key - // } else { - // log::error!(target: "thea", "Unable to slice last 64 bytes of uncompressed_key for Evm"); - // Self::deposit_event(Event::::UnableToSlicePublicKeyHash( - // public_key.into(), - // )); - // return; - // }; - // let hash: [u8; 32] = sp_io::hashing::keccak_256(&uncompressed_key); - // if let Ok(address) = hash[12..32].try_into() { - // uncompressed_keys.push(address); - // } else { - // log::error!(target: "thea", "Unable to slice last 20 bytes of hash for Evm"); - // Self::deposit_event(Event::::UnableToSlicePublicKeyHash( - // public_key.into(), - // )); - // return; - // } - // } else { - // log::error!(target: "thea", "Unable to parse compressed key"); - // Self::deposit_event(Event::::UnableToParsePublicKey(public_key.into())); - // return; - // } - // } - for network in &active_networks { - let network_config = >::get(*network); - let message = match network_config.network_type { - NetworkType::Evm => { - if let Some(payload) = ValidatorSet::new(uncompressed_keys.clone(), new_id) - { - Self::generate_payload( - PayloadType::ScheduledRotateValidators, - *network, - payload.encode(), - ) - } else { - log::error!(target: "thea", "Unable to generate rotate validators payload"); - Self::deposit_event(Event::::UnableToGenerateValidatorSet(*network)); - continue; - } - }, - NetworkType::Parachain => { - if let Some(payload) = ValidatorSet::new(queued.clone(), new_id) { - Self::generate_payload( - PayloadType::ScheduledRotateValidators, - *network, - payload.encode(), - ) - } else { - log::error!(target: "thea", "Unable to generate rotate validators payload"); - Self::deposit_event(Event::::UnableToGenerateValidatorSet(*network)); - continue; - } - }, - }; - >::insert(message.network, message.nonce); - >::insert(message.network, message.nonce, message); - } - >::put(queued); - } + // if incoming_set != BTreeSet::from_iter(queued.to_vec()) { + // let uncompressed_keys: Vec<[u8; 20]> = vec![]; + // // TODO: Uncomment the following when parsing is fixed for ethereum keys. + // // for public_key in queued.clone().into_iter() { + // // let public_key: sp_core::ecdsa::Public = public_key.into(); + // // if public_key.0 == [0u8; 33] { + // // uncompressed_keys.push([0u8; 20]); + // // continue; + // // } + // // if let Ok(compressed_key) = libsecp256k1::PublicKey::parse_compressed(&public_key.0) + // // { + // // let uncompressed_key = compressed_key.serialize(); + // // let uncompressed_key: [u8; 64] = + // // if let Ok(uncompressed_key) = uncompressed_key[1..65].try_into() { + // // uncompressed_key + // // } else { + // // log::error!(target: "thea", "Unable to slice last 64 bytes of uncompressed_key for Evm"); + // // Self::deposit_event(Event::::UnableToSlicePublicKeyHash( + // // public_key.into(), + // // )); + // // return; + // // }; + // // let hash: [u8; 32] = sp_io::hashing::keccak_256(&uncompressed_key); + // // if let Ok(address) = hash[12..32].try_into() { + // // uncompressed_keys.push(address); + // // } else { + // // log::error!(target: "thea", "Unable to slice last 20 bytes of hash for Evm"); + // // Self::deposit_event(Event::::UnableToSlicePublicKeyHash( + // // public_key.into(), + // // )); + // // return; + // // } + // // } else { + // // log::error!(target: "thea", "Unable to parse compressed key"); + // // Self::deposit_event(Event::::UnableToParsePublicKey(public_key.into())); + // // return; + // // } + // // } + // for network in &active_networks { + // let network_config = >::get(*network); + // let message = match network_config.network_type { + // NetworkType::Evm => { + // if let Some(payload) = ValidatorSet::new(uncompressed_keys.clone(), new_id) + // { + // Self::generate_payload( + // PayloadType::ScheduledRotateValidators, + // *network, + // payload.encode(), + // ) + // } else { + // log::error!(target: "thea", "Unable to generate rotate validators payload"); + // Self::deposit_event(Event::::UnableToGenerateValidatorSet(*network)); + // continue; + // } + // }, + // NetworkType::Parachain => { + // if let Some(payload) = ValidatorSet::new(queued.clone(), new_id) { + // Self::generate_payload( + // PayloadType::ScheduledRotateValidators, + // *network, + // payload.encode(), + // ) + // } else { + // log::error!(target: "thea", "Unable to generate rotate validators payload"); + // Self::deposit_event(Event::::UnableToGenerateValidatorSet(*network)); + // continue; + // } + // }, + // }; + // >::insert(message.network, message.nonce); + // >::insert(message.network, message.nonce, message); + // } + // >::put(queued); + // } if incoming_set != BTreeSet::from_iter(outgoing.to_vec()) { // This will happen when new era starts, or end of the last epoch - >::insert(new_id, incoming); - >::put(new_id); for network in active_networks { - let message = - Self::generate_payload(PayloadType::ValidatorsRotated, network, Vec::new()); //Empty data means activate the next set_id - >::insert(network, message.nonce); - >::insert(network, message.nonce, message); + if let Some(payload) = ValidatorSet::new(incoming.clone(), new_id) { + let message = Self::generate_payload( + PayloadType::ValidatorsRotated, + network, + payload.encode(), + ); + >::insert(network, message.nonce); + >::insert(network, message.nonce, message); + } else { + log::error!(target: "thea", "Unable to generate rotate validators payload"); + Self::deposit_event(Event::::UnableToGenerateValidatorSet(network)); + continue; + } + } + >::insert(new_id, incoming); + >::put(new_id); } } diff --git a/pallets/thea/src/validation.rs b/pallets/thea/src/validation.rs index 40a204c7d..301081c7d 100644 --- a/pallets/thea/src/validation.rs +++ b/pallets/thea/src/validation.rs @@ -28,6 +28,7 @@ use parity_scale_codec::Encode; use sp_application_crypto::RuntimeAppPublic; use sp_std::vec::Vec; use thea_primitives::Network; +use thea_primitives::types::PayloadType; impl Pallet { /// Starts the offchain worker instance that checks for finalized next incoming messages @@ -37,8 +38,11 @@ impl Pallet { return Ok(()); } - let id = >::get(); + let mut id = >::get(); + let id_prev = id.saturating_sub(1); + let authorities = >::get(id).to_vec(); + let prev_authorities = >::get(id_prev).to_vec(); let local_keys = T::TheaId::all(); @@ -54,9 +58,24 @@ impl Pallet { .collect::>(); available_keys.sort(); - let (auth_index, signer) = available_keys.first().ok_or("No active keys available")?; + + let (mut auth_index, signer) = available_keys.first().ok_or("No active keys available")?; log::info!(target: "thea", "Auth Index {:?} signer {:?}", auth_index, signer.clone()); + let local_keys = T::TheaId::all(); + // Fetching the available keys from previous set + let mut prev_available_keys = prev_authorities + .iter() + .enumerate() + .filter_map(move |(auth_index, authority)| { + local_keys + .binary_search(authority) + .ok() + .map(|location| (auth_index, local_keys[location].clone())) + }) + .collect::>(); + prev_available_keys.sort(); + let active_networks = >::get(); log::info!(target:"thea","List of active networks: {:?}",active_networks); @@ -71,7 +90,7 @@ impl Pallet { None => {}, Some(signed_msg) => { // Don't sign again if we already signed it - if signed_msg.contains_signature(&(*auth_index as u32)) { + if signed_msg.contains_signature(&(auth_index as u32)) { log::warn!(target:"thea","Next outgoing nonce for network {:?} is: {:?} is already signed ",network, next_outgoing_nonce); continue; } @@ -82,19 +101,42 @@ impl Pallet { Some(msg) => msg, }; - let msg_hash = sp_io::hashing::sha2_256(message.encode().as_slice()); - // Note: this is a double hash signing - let signature = - sp_io::crypto::ecdsa_sign_prehashed(THEA, &signer.clone().into(), &msg_hash) - .ok_or("Expected signature to be returned")?; - signed_messages.push((network, next_outgoing_nonce, signature.into())); + match message.payload_type { + PayloadType::ScheduledRotateValidators => { + log::warn!(target: "thea", "Ignoring ScheduledRotateValidators message for thea"); + } + PayloadType::ValidatorsRotated => { + // if its validator rotated, then only the previous set should sign it. + let (prev_auth_index, prev_signer) = prev_available_keys.first() + .ok_or("No active keys available from previous set to sign rotation message")?; + log::info!(target: "thea", "Previous Auth Index {:?} previous signer {:?}", prev_auth_index, prev_signer.clone()); + + let msg_hash = sp_io::hashing::sha2_256(message.encode().as_slice()); + // Note: this is a double hash signing + let signature = + sp_io::crypto::ecdsa_sign_prehashed(THEA, &prev_signer.clone().into(), &msg_hash) + .ok_or("Expected signature to be returned")?; + signed_messages.push((network, next_outgoing_nonce, signature.into())); + id = id_prev; // We need to set the id to prev for unsigned validation to pass + auth_index = *prev_auth_index; // We need to set the id to prev for unsigned validation to pass + } + PayloadType::L1Deposit => { + let msg_hash = sp_io::hashing::sha2_256(message.encode().as_slice()); + // Note: this is a double hash signing + let signature = + sp_io::crypto::ecdsa_sign_prehashed(THEA, &signer.clone().into(), &msg_hash) + .ok_or("Expected signature to be returned")?; + signed_messages.push((network, next_outgoing_nonce, signature.into())); + } + } + } if !signed_messages.is_empty() { // we batch these signatures into a single extrinsic and submit on-chain if let Err(()) = SubmitTransaction::>::submit_unsigned_transaction( Call::::submit_signed_outgoing_messages { - auth_index: *auth_index as u32, + auth_index: auth_index as u32, id, signatures: signed_messages, } diff --git a/primitives/thea/src/types.rs b/primitives/thea/src/types.rs index 5defeb958..900c934ec 100644 --- a/primitives/thea/src/types.rs +++ b/primitives/thea/src/types.rs @@ -181,7 +181,7 @@ pub struct IncomingMessage { Clone, Encode, Decode, TypeInfo, Debug, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize, )] pub enum PayloadType { - ScheduledRotateValidators, + ScheduledRotateValidators, // Deprecated ValidatorsRotated, L1Deposit, } diff --git a/runtimes/mainnet/src/lib.rs b/runtimes/mainnet/src/lib.rs index 1f1fa930c..a0cbbe9ea 100644 --- a/runtimes/mainnet/src/lib.rs +++ b/runtimes/mainnet/src/lib.rs @@ -122,7 +122,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: 369, + spec_version: 371, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, diff --git a/runtimes/parachain/src/lib.rs b/runtimes/parachain/src/lib.rs index 1fb5ee714..94d3bf097 100644 --- a/runtimes/parachain/src/lib.rs +++ b/runtimes/parachain/src/lib.rs @@ -179,7 +179,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("polkadex-parachain"), impl_name: create_runtime_str!("polkadex-parachain"), authoring_version: 1, - spec_version: 16, + spec_version: 17, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From e71304de2d2e6e8805e31f740290b3517a69840b Mon Sep 17 00:00:00 2001 From: Gautham Date: Tue, 9 Jul 2024 09:52:52 +0530 Subject: [PATCH 2/4] cargo fmt --- pallets/thea/src/lib.rs | 3 +-- pallets/thea/src/validation.rs | 35 +++++++++++++++++++--------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/pallets/thea/src/lib.rs b/pallets/thea/src/lib.rs index 09850fea1..95942755d 100644 --- a/pallets/thea/src/lib.rs +++ b/pallets/thea/src/lib.rs @@ -758,7 +758,7 @@ impl Pallet { fn change_authorities( incoming: BoundedVec, // n+1th set - _queued: BoundedVec, // n+ 2th set + _queued: BoundedVec, // n+ 2th set ) { // ( outgoing) -> (validators/incoming) -> (queued) // nth epoch -> n+1th epoch -> n+2nd epoch @@ -861,7 +861,6 @@ impl Pallet { Self::deposit_event(Event::::UnableToGenerateValidatorSet(network)); continue; } - } >::insert(new_id, incoming); >::put(new_id); diff --git a/pallets/thea/src/validation.rs b/pallets/thea/src/validation.rs index 301081c7d..b1a93c8fc 100644 --- a/pallets/thea/src/validation.rs +++ b/pallets/thea/src/validation.rs @@ -27,8 +27,8 @@ use frame_system::{offchain::SubmitTransaction, pallet_prelude::BlockNumberFor}; use parity_scale_codec::Encode; use sp_application_crypto::RuntimeAppPublic; use sp_std::vec::Vec; -use thea_primitives::Network; use thea_primitives::types::PayloadType; +use thea_primitives::Network; impl Pallet { /// Starts the offchain worker instance that checks for finalized next incoming messages @@ -42,7 +42,7 @@ impl Pallet { let id_prev = id.saturating_sub(1); let authorities = >::get(id).to_vec(); - let prev_authorities = >::get(id_prev).to_vec(); + let prev_authorities = >::get(id_prev).to_vec(); let local_keys = T::TheaId::all(); @@ -58,7 +58,6 @@ impl Pallet { .collect::>(); available_keys.sort(); - let (mut auth_index, signer) = available_keys.first().ok_or("No active keys available")?; log::info!(target: "thea", "Auth Index {:?} signer {:?}", auth_index, signer.clone()); @@ -104,32 +103,38 @@ impl Pallet { match message.payload_type { PayloadType::ScheduledRotateValidators => { log::warn!(target: "thea", "Ignoring ScheduledRotateValidators message for thea"); - } + }, PayloadType::ValidatorsRotated => { // if its validator rotated, then only the previous set should sign it. - let (prev_auth_index, prev_signer) = prev_available_keys.first() - .ok_or("No active keys available from previous set to sign rotation message")?; + let (prev_auth_index, prev_signer) = prev_available_keys.first().ok_or( + "No active keys available from previous set to sign rotation message", + )?; log::info!(target: "thea", "Previous Auth Index {:?} previous signer {:?}", prev_auth_index, prev_signer.clone()); let msg_hash = sp_io::hashing::sha2_256(message.encode().as_slice()); // Note: this is a double hash signing - let signature = - sp_io::crypto::ecdsa_sign_prehashed(THEA, &prev_signer.clone().into(), &msg_hash) - .ok_or("Expected signature to be returned")?; + let signature = sp_io::crypto::ecdsa_sign_prehashed( + THEA, + &prev_signer.clone().into(), + &msg_hash, + ) + .ok_or("Expected signature to be returned")?; signed_messages.push((network, next_outgoing_nonce, signature.into())); id = id_prev; // We need to set the id to prev for unsigned validation to pass auth_index = *prev_auth_index; // We need to set the id to prev for unsigned validation to pass - } + }, PayloadType::L1Deposit => { let msg_hash = sp_io::hashing::sha2_256(message.encode().as_slice()); // Note: this is a double hash signing - let signature = - sp_io::crypto::ecdsa_sign_prehashed(THEA, &signer.clone().into(), &msg_hash) - .ok_or("Expected signature to be returned")?; + let signature = sp_io::crypto::ecdsa_sign_prehashed( + THEA, + &signer.clone().into(), + &msg_hash, + ) + .ok_or("Expected signature to be returned")?; signed_messages.push((network, next_outgoing_nonce, signature.into())); - } + }, } - } if !signed_messages.is_empty() { From 9c10a72169a252613440a1cbbe366e563bd782d4 Mon Sep 17 00:00:00 2001 From: gautham Date: Tue, 9 Jul 2024 10:49:34 +0530 Subject: [PATCH 3/4] fix thea-message-handler --- pallets/thea-message-handler/src/test.rs | 22 +++++++--------------- pallets/thea/src/tests.rs | 17 +++++++---------- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/pallets/thea-message-handler/src/test.rs b/pallets/thea-message-handler/src/test.rs index f9b26fd2b..619aa3830 100644 --- a/pallets/thea-message-handler/src/test.rs +++ b/pallets/thea-message-handler/src/test.rs @@ -201,12 +201,11 @@ fn test_incoming_message_validator_change_payload() { SignedMessage { validator_set_id: 0, message, signatures: signature_map }; assert_ok!(TheaHandler::incoming_message(RuntimeOrigin::none(), signed_message_sv.clone())); let authorities = >::get(1); - assert_eq!(authorities.len(), 1); - assert_eq!(authorities[0], sp_core::ecdsa::Public::from_raw([1; 33]).into()); + assert_eq!(authorities.len(), 0); let validator_rotated_message = Message { block_no: 0, nonce: 1, - data: vec![1, 2, 3, 4, 5], + data: validator_set.encode(), network: network_id, payload_type: PayloadType::ValidatorsRotated, }; @@ -218,12 +217,12 @@ fn test_incoming_message_validator_change_payload() { message: validator_rotated_message, signatures: signature_map, }; + assert_eq!(>::get(), 0); assert_ok!(TheaHandler::incoming_message(RuntimeOrigin::none(), signed_message.clone())); assert_eq!(>::get(), 1); - assert_noop!( - TheaHandler::incoming_message(RuntimeOrigin::none(), signed_message_sv.clone()), - Error::::InvalidValidatorSetId - ); + // Doesn't do any thing + assert_ok!( + TheaHandler::incoming_message(RuntimeOrigin::none(), signed_message_sv.clone()),); }) } @@ -244,16 +243,9 @@ fn test_rotate_validators_fixture() { TheaHandler::validate_incoming_message(&signed_message).unwrap(); assert_ok!(TheaHandler::incoming_message(RuntimeOrigin::none(), signed_message.clone())); let new_authorities = >::get(238); - assert!(!new_authorities.is_empty()); + assert!(new_authorities.is_empty()); - // Fixture is taken from Polkadex mainnet for network 1 and outgoing nonce 681 - let encoded_payload = hex::decode("ee0000000000000058155e0000000000a902000000000000010100190200000000bbf8f6cfe1504ee7ec55c41f4008f605f9a0312ff8988cae9110fcae2597a0061d9b6a7618eac53158ec6d511274569cd1a03cfd4ff69a200f6d005a98ba75bf01010000000dbee2291288697c5d0819e8fd55f64d417f5ce3293038096abab1cdf67b829424c001e5c4f91d3ea4a3dd8f53f53d2feb81c3981d692c2bdb371bb6997cee8700020000000aae385a81cc02d4ab5f018eb7b0d35763905f6ce42ed2b177a3497021d6128852c509223d118875db424d894aee90fb15e0a6aefd26ad1a3ed67fb704f3c24e01030000008f184fa4ce36b4f2b7a456989b4eadfe52d8ab0a5f202ae111f7f59473c11b201ec44ab2b4ef68723eed7194e0afb86a881ea676d2401bbe5933c616e41998c400040000006b51fc005cd8d875f5b9e3360969c252a727460583cd0a410cd53562f37386bb6ad4f34663b06d11d47cf321e6b6575f22ad0d78c8409e774b4462f2654109cb0006000000b712883ce63de556d339e3bc0eafcc17ecdd8cadb90097d79be4c36d3b98105f1e1a876099806ee6edf5e9eb933becff77d34ad3810116d03e8dcd2bd069852601070000002a73f353c824006cd6227fc1a2b8577017f72345e618704c053ec00337b581a80f68ef01ea7a5a932b9cbb72cfdc9ebf94dca582a87b664454148cc086c59cf0010a000000a758f30dce161e024a2d29ec113bba7c8e649f8f51df87677c3c81e10c623283395962b3419fe6623ad9dfe5d89d6bc66293cab47047481eed183ecfe54cfb8a010b0000000ebebe198a59f22335cee024ff33c8e88ac702325946b1d8ffb9bb12720e4b355c70d66b43886b5261ef488869ea2b316493d10468a93212472b2f8ee7a3ed7d010d000000f1fd4c9b90aadf1b1561207ce44fb9930e0c261c3c422cdf78614b1e27da129f49b5722ebd2875fde561f0376c91537115cddb53e3413cc0dbc088599fbbd6ce000e0000008b325c177e90a4ba58285694fc084dd8fcc0068396142ea57f6b4f7590f57c0c04332f6f7d2ebecc8a3cc031122169e24d18f7f55fdaea00b1dcadd6316fc69a010f0000003d34677af9cedabd6de8ec37f68f4ad3ea81e3b4bbbb6a6a8d6a1483855e4e142c0d9e5b38f98a98b6f8842c13dcd4213ab0f914eed2a270d42af22a23f31ddf01100000002fdf39edb1c523f5c708167af98d92d712dec084415f761db0715700b1040eca7adca5db36b41e69f99d2f6495a52a14c4c805ca7b956e4bbbfd50a07941e1bb001300000077af6d00e333e62670dfed381df3b28da2cd7409bdce8a7c5fc3922aa4455357080449cf285cfa38824a13caff1e1ddc05eae835ee7839ece17f4f9202e3982b00150000009b64d350fb8ec8a874be2d3a48256ba3a85787d1cb9f0907a6422387927a285177bf7575f02148c31449fc452b8431b894b5f8afa4bf7fa3898f2f1581589ba200160000005d1085c1b26080df34d53a43374ac43badbd761a00f8181d78c64646e547922f262f17e407c784f4f45388a8c9831d9893cbd5b436aa7a70d570c4aa6745be8b0117000000df6251cc4c52457fdbfe17e29696498a44b7dcc46fc40203f80d6858e6e814dc1502d00f0735ac3847b9f202c7ee62026501e7d444f05d34e608a55f7900b3860019000000bc3d3575a30a060936d668883e3ffaee719453199a31a6c41a8302373f4c994b3fab3db4cad52081b8584a264ce4fe3167a6e4ed5e7ae6301bc8007f9e084e46001a0000000decc7ac19ea2aaaf036051cda05f64539c0497b8191cbc4d010d43cdcfdc8f2672031c3eb836931526d6938938cb9e7bacbb2da973d05873f673fa8f45412f4011f0000003f63277d0a8e27dfc1a6e84be135ac6eec0b418c7065f81aa30ec6cfca18398b0e822aac5536f5a547119a9ef80131dba42edf993b1efb35aedd249ad623d2cb0121000000571915f239f6f19ab63d0d177fc0d5f0dc4e7e0f261ce63957e43f4520b2786f124f83108b24873ad5e115fa54dc30f6bd98af0ffdab8a538c5366d4074673460022000000f1715aec59c00e5df0d73b94eb367b6525def2539a4d6056fcc2a996ecb94e14615d20058d38cea1c66b51d6c3c718baabd104a836cf7ee29e0cfdf27df7988d002300000037e7883487b670805bacf678c11dc35cf72f3963f3d3811d432dcb5cdc91baee5b4a191c276012884f95ac830d29a979399ba284f44bf42583fbb81eac6874ba0124000000ff90b332fa392d37045d094f3bc82a5aacad093efd221d8b0970824ba328dacc4d7f830fcbcaa9b0e04a580246dc8172edccd2fe4c0c6fc915000dfab75bd86a01270000007d51fcaeb4078fe40052065da79ab1fada151a643b1af1f9ddd28a282be29d5f78c2fa07c7165781879bd579356b2988cdee0aca66ae04863349028d9196cac80028000000b2a99937f3c10d4c39ddf102df4588524f28a97fd36985ee21ec8d565e25fa5074c7b3e48a48b00f9c67b017a7ab5dbafaa76ae661940667fb95de1f0790da300129000000049693139f6ef2a13b8321112d9f0b6670271b0d4d17f35b0391e64c8c85d69e262890b8a774249e486ed5e4a110ec1a400abfe42eddbe3dbe35fba0c5ad49de002a000000b12d2bda763ee755c0e2c2659a2e96df0911be4f1a7b21b297cbfceffe6321ef0b00e2a863f5634954eda7fa6432079f2a36b2655a5dccb708c83e2b45ddfb25002c0000001a04fde2000970572e54372d6f35aa11e7fb5aa6450acbd6c5920823f596398e6a664453b5126da7b84dc0a268b0dc822f1388043b9b0d526b676cfca17b6151002e0000000aa6e444411128a386eb9d22850c931aff50adf50615ee78b2df027a35e3d8b30aa230e5172b16da1e9f97dd25e9d72299069ce4433e26fb28bd060d835d7505002f000000ba34291dfc21f0b7c17d5e637de8d1f403ad879bc852d231643a4d25500171ef703b103567500b04423120819788a57c08e10c638490b56072efd59d2aeee65b0030000000edbef2650ee6af31fdd444e4b8fc6c30f52f01e9b97ce95f35ee4e0d46f088bf2046413bd760574b2e89625dff265537989112b7de03ee7b11858e748668db32003100000039f0ebcc81cc4b624a165cca03adfcc0e103930a9dcaa708ffe296efb4bdd1450c3d9626ce557fb5e6bfd84da9bd6860180ebb91e2b56e2c8c9935071b6807680033000000b613fd86f57f281ca98c4cecec8abee19165635e63d68803dd06b3d11d58ce4358d02f40fb2f6cdb09946fc30e90c49a46ba2ad3eaf39e3b38beab05f2278a010034000000d9833ee0deeb4a16281bf9f2a7bf018393407d0d5633a8429118e1e7fed4ff1313b0f3a9edd5e1f58a78cb2a78e06d4fc90ed03966393d0e727037794335488f003500000014141c3e558a805270652cde3273648491272242fc7f94b01843ee489963ef0f79792f6b5c447d0922307b6822002233d057034ef68d213fee5953fa115f4ae30136000000db6062f434f835c7d80a749f3f530727c3c659309fcf0f312cee3d98d90c542e3a5404e4bde51ae6fe17dcdeb8432fee5f6911f7fb8537533c74559a90daa60b01380000008f38444b91d32386ebbce797068216a280e733b6ba7c0b877050a558cf02ae4f6cec51b7af2e23c5b1e308cf30d2b24dae628ef62591fda88597143344b2a96a013900000082edcf2d908a8d86a1064d88ebccc10ef600a341d1df3e8fd353caf2c373afa111e7c6c737f42c4bf133a6dc7b9d08fa9a3d5d9a4c9f7f74ad4f28512db60ff2013a0000009d0d4ef66c6aab1c09b26c16ef93c151a4806d21197193b523955c0c6ac31c22268438e1f67cfb931831c78d61b9647e2a4665fd7f10b2fc340bacac583f38fd013b0000007d8f9a9158ee98e6002238ddb4f5a72dd65250c4cb2933423cf7d0601b96c10228ce1bcb20c2676c897c4750c0888d85093b550130929c398bd48207aa859475013c0000007e5fa339815383fc19d728a8fcc71416866c4c6492f96116e0608b8656ed034005a077d5d3c1786ca691cc057749d41e4945a6d44dbcf6c4ecaf1cacf046ad2a003e000000754515678f4ea628fa7aa8726e5a8075fad1a2c59301df466ac7d9028ab2527a6d6d6eb1491640c9e83983c792d1a67098df87036e379c71d1be7ec24bdd86b1013f000000648be11178424c4f7fb1b40243e2da04cc1a249f15c7dcd1ca761598b9898f725878b32af90528a07f64eccdf61b614fb00951d765c2febf161c75342bdf3d4301400000005d39e4d8ba1f192777d09f48eb18f884d844618820446e0f017a56bc0f26fd932b76009c9965b0550c7fbf47937850da853d0ec481b705d712493713b89afac1014200000012e2590100150be90f30508cdbaa98e154b5fe22748253f31059e3799fce08ad0d141d7bf33aa9b3a627772ef52db618383d7f888578055c488d88b9bfa5cd7d0043000000c0b3ede0f08974f71f8e469ec55e7c1c9a3d2f762c5002c53893eea75b1b21467353680cc98f0d2d71200e7e69c87c57aa3d42ba0e5377631e9c827b988a306f004400000060e0fce2f2a96c1be7c6907efc3593159035e4e1e9de59fc791243eb21f338c00d0598239cace83f3dc4421509d2a803686e997532ae18f98f2d340639268a420045000000d41469a6cea565c7fe29474768c149ba33726770889648bd7d125612e351db8b7c047dc1d7ce770ff8a0a5548fc570de1b11f9b3c75a22099cda9cb9d591730900460000000eec1756fb59dfa4363f7c3e9bbbea76ecd570c2151adbca58583bb2699b6da538976968d76bc052ae179990ae52e9cab890dd82716649ce0d617f13fe177f53014900000031dbad268ecca63445a444a714e0909c3252f07dcee3046be3c16dc70900da754a2e142b34374ab3f5b6d3ab022283e42ca5238a8c7ec46071cf3b0cfed6c7de004a0000005b4e3acb3e92bdc32df92e29e23d6af8981d3a866b833213e139f0c9c6b824931c4ce2fb05e7c88f0f7a15218a61b5db40c5123fda914f55a74bc3380f1c548e004b0000003e40f92b27242554af23391c45f66fe8724165a0670bc260ab9c7c8c92d7996464a025f2bd987040b7a3bcf80f28a3b4f69a45f01b6c2ca8434541b64209bd41004c000000ec35bf3848dbbb07d283426d4e70ac55dda42970510f32d59546db9ea0c8c6bc6462e1c44deeb5081b2908bb48549c645441583ffad6ec7338dd487f3ab14f25014e0000008776bde0138c363fd10b697fde9b22f4ccc7f4a0d60ea2834dd462d2fd3d47266c4f052ed3d762fd273b4c179ef1c7bc8ff57953af69eadaecf674af3501927c004f000000b599c01ae619ff14a25aca4a1ae70cce7d8869e5ce90c729aea5fc80216aac6162626a04c283633c51264b938db97890acef1852c46d5aaaf2bd79c0061d43960150000000128fad4733838f9e315afa3ab9d86f3b8006bf9c2123f0290fc729b3064a2a8004ad1f9c404c8fc45f4a332041dff3659fee4d15fd72ef97ab8bc4092f3fc8990152000000a64c61fdfd7cfd2ff72f22fe0639afdbae709524b9212626efaf72c88a801a22367d4eb30a457f28c6be262c2c69eba79fce6390999998bd0f0cd9f3df55180d015300000092733e11029d73d6c69bbbc08e934aaf43a4ee6d013997f49f7b4f59646796833c01fb9b79a61b0530f88232818a325c5705fc59f0487ebd1abe147cc44b932801540000005ba5f22f6fec5621382d180aed86bb4d4c33a8491792c9a9f7821f9c2b7a2d1809a6ebd4c897f7d205767a0089d3555a463d22fa0fa24a64e142a1b17b9b198b015500000083dd1a467bf7ea90d3ebb972a6e40fcf8a566600096f088d980cdcf37bda6cba20c0d5fbbcef6ce8fb293417cf356bece0d5978a95ff744c458ac0ea8ccf47c600570000004794c0fc680930b9c60e1773ff0311b6ddb88d2ef3f8f6142a1b058d67ea2a2c759f3be9c4765e1764c01995053e9ee36083b80981f65dda1f08738c6cd84e580159000000483c0dbf3ed4f70ba53b68a0cba2ac12450fd9026137af0474d6f941c30bd7fc47305d840a5ffd562bcb1819f4ea4bdd2c01a8a3ad3d586fe370f24346900cf9015b000000b080589605124ecf70c34946b146ec038901c6403a0effaf93a7d841172c0ada003c6620f0878f6af0a493696f1e6fb658b5faa7f3e3135fc85badead1f7505b015d0000000604a1e71bcff84e8164bc12d117fa5abb7570a6c7c95e6ef926f70ec5f6850c2b05c7a58af64c25fb832d0f7c7a0ca613e542dc2f01c2d9118368e9f412ca0e01600000008bfc6007fde4b9112d734e659928303ae9e40cbbea211f88b8e7acb97d416d7f59589838e73802e0ee935c8c1c1f97a52f1e3ac0b1424638e087002c8c4d7dc40061000000f0afc4f4f9db96994aa2d29a127fb60e3e2398e22171100b4fdc8055122591336311c0954158789293bd94e41019fb2ad35a674953ca698afcd2490e48da78ae016200000058a4168c15d4e401d09852466d6d0087873ef33d7739289463b35a27475884a47fa4309fc5e458c302fb0bfe360c9739e6644711eae873fcb7ea88c31ff5a7d80163000000a6e3ac2352f4d2b7faaf7c1d4e6bf7d5dc893665eb42a359149a037f6e165ad5374f1994c56fe8a6070202f84b4a1535c1c00406c1b6c4b61be55aa71501512000660000002ffa9d5e2f19b537486cac4ea08574b6ba765f2c5a874de073d5c8448e8ce9c72dde526c71f1e4ce4549e4511d0acac1a3ebdd40d3daeca1461602b4eb2c47360067000000ba9186b157690e1439cd96eb51cb3874aed58565541f145c8df6d0a4ecb017aa53a4c0d08b996a14bb84c8f0cbc495681e2b52f03f8abc703d035d25be6da4530069000000e387603e21f6687965486baa7809c95aa089b74f8133147166f0f2b84a54424170079ee15a81dce84948b28afb2c4e8b8aaed1fdee150e9e33bca1eda8f960aa006a000000eb96f2a2328b22b388639462ada66214f719363ac8b228cc178f32e269f4beee0ef61132f3982d7783d9cc558e7e4c16c481fbfc6992526425a12c2348399734016d000000895befb2502d9c0b0682338d57e549f828133e6c059d839ae19a04ae188e77b17f86c3faa4edb54dc552b63b35a09d5ca0a371da3aa176ada4e8f272c889f740016e00000081819b8f3057d15e068e22adc93c7648011c22825a22e2060b2b0ee5a3b20bcf78751229cf1db5d38208bd70e8ae6f5172c6140f0733b106f2eb0af58c264c0f0170000000e12891b9a169ef91242e9304aa0193e09be712a828760d4409385d352a8984616d023f965e44cddbcd276661b52036ee96c46769c1b421691bb0da7ea05811510171000000aaabe6a9daeab76d1f574b15d2af06e307e53bbd6538ad5a53beb1f8997244b95f3ebdff0ed20f62358a2839aed093f91a58648594b47026b714e49e1cea59410172000000416304d1abb91a8ca54bafdc3444888fc796b7ea4fe578b56072521761a02fd658d845ad0dbd54ba8115a43700024ec8371717e6360ed388f10101dd361b26c100730000006c81b41d58857cb31fe0511b1b572a8ff549b3cfcd286681eb71c05aa9d2fe1a42f8059e29767c6629a08eea8490081d0a8496aac5d1ddf30993be826428473c017400000059b66169dc96cc853de6f40ad8eb2b842629af455718c736eeab9ad295c098aa41cba39f05229ffe6652320ae8948428ac7e66beea97980b3c67a27a0673099201750000006a65c633134e0ca316eadd9def7aea91c6f00622f537049d94f3696f516544a34be507a976eeb709d82ebf7802d520c0c003a3749bf0fb2b71c5acbc055300e90076000000c0d39c93b704d9b753202c621f579c0cdb75038000c57b36d127e839b4d155530e44b0df1f57b1a3acdabf8d0ed4c5995633f031939ab5b95c1baa8e6be661eb017700000098bf5550d685e0c4b268b2ab1e218f8ea4803ab43a960027f077d633525210b30b61cd1e4a92f7fd5ee6cf754f5aa0c0a03d6fe5039f8b9f298127c3e5e515bd007800000048d17dd9aae6842084b6fcda34d0cc2cb0a01fb27090be31be0137818f567aac005324e35b24d1411477eefda3206e954486de6d1e911e7965f1cd155f50c670007a000000edeb4d7c5cceaf1c3a4129f27d52d736d3e9809498aa75ed824db6a21b029b922441d67fce6b8082d595efe7f34a59685c042e87b475e91f2d0e272fb7723a57007b000000c2e5b5d57391d45c4da5815d30fbace65d9bb4f2a34e176ebba1a7211b134c9e258e6dde37458d904500e0d2f5dd196373c07309113b8cceb3e798bdd961c32b017c000000d3fb0fa667ab8db11015515e61280fbc729c76eb3e9b6cf5f399a4b1aba302d45aabf2e3a1ffae0394655000f9417bdbf180449f8c80f387972fb39dad47a804007e00000064a1c31c7b68f834fa402120bbfc82dcff7b452e6b62ac49df9b6a9bbd4b520a660fa36a74dabe9fe19a8b50a39a417e35d3e845d79264e42614c1e25c8309f5007f0000004aee095d0049c43ced3b38f93a865d8be25873f26b0643bab1b03385c62745fa1a58f6b61915dbcc9f715717bc7877e7165ee28669fe1e948abad7287a609e930181000000151f0146607a0b2cab319956de69ac2700ae506ce83aceec18b32edeed3073c447f51cf06959046813f78c13b6fdaf7cbad653878d200add9cab31edb2cbd7a1018400000020f538f867cc5077be6848e062d20fc79db939b3de95a71a05172ad9dd8cdf390698e4b3ede3665e684fee7363824ce3c473e2a1c41a7e548e233424680097c2018500000065c93da8e0921852f80986cd9d34ffbf0ab8432059d44c3696c0d4c7b04b526a7d155b247f29710ea9c7a83f30864e642a1b707352e1d504b450bb91d562223400860000000ba082050560577310ecf2cd00f2072036a2bc5dbea08df3b23b2edf56131555664427cb4e3e14256160165d01cc4e3377f3ce6fcb10b1c8fb842296efafedc50187000000fc38ea98a96bedc8d6dab0db6e609c1c709cd31851c7d6881980171d3991475a2c3c4699f4df5925e934079679d2897169780c773e28c3af94ff2a7428e5ca6800880000005c734ff855c588023b3b7ca7e023f258cb365e37840b31d4ab0afac50445d325252406d6d757a84739fd6753424152645e2424cc1a6bee213b46a3f6a4c769d10189000000c7291dda37a0f1c6af2a0d0044d1c3125de9bcc4933838d2c25ea31bc33805e5344377de809c45430b656a6561f5f85ca8f274a5352142e878a71e446df72c4f008a0000000b08bfc1dccf90fa1212a10cadbf07738602be449b9ec1e1f63cf24843b9c20a1f4299bf6f444d59616bcf8dfece6e0ab072ace8be518cebfd6519564a20e7ad008b000000cd5a93b6f8f82a2ff4198139c1e1ea5d4db0dbb8278db173390285abe38951407dc5630c014c9dd12b758af98768530ffd116ce1d7dfaa0169c8f402c2784fd1018c0000009e761d56003667c5df42a543080534b526ff7f525faf74f65cc3bdbd8a6a04415f45e492d5433a47b2ed490d1e1051db8ed70171fa529bec832adddbbf02ab74018d000000b1eb87acf58dd8d819a81d31a5999c1d841d50ccacedfcb902a25cad32d5aa6a7133b0b93d4baa1607cc867c680cf6c5ee9af60fb0cb3263b598b102ad6e300f018f0000007c05742ffc98b5e6d588a3672b3dad4b3fee5b4b2205182e9b2042c85b7873351d2caff4019b8fa94a69624b3e9ddbe7e972c8f5d2f7042c222a7183338102a40191000000237767892469c3fbe8de241896c8a9fb3bd65a75d42f2ca21e25c07a3519b3327c986774ca711ca851042bcbcca6bc555c46a18f5a420412fa4205f519a2fb110092000000c3b245ebcdae9d48003207bb85b76eeb0f7c4934b4f4b2e334f5888f0ef073980dd12d40d222f93ef3acb0ee1a6b3644028eab0f5e6805a20369130f7ed9b94801930000005ba9369277118b84db9382acc646f47c72a686ac3f9542cf7d1f69b98c7aa4a500d84929087f50411b25f6cc4d163f9af03254be63e49389af2fcc11213d700601940000002c4a0f0f12d918c63cac8481515044fb03b9c3fe3ae36d1c86fdbd486a83974a406768aa06c184efd7bfeb9497e23e67b5720530c90a902ddbfd85274d98eb0401980000009d830b30011e8d4c9be08b140cd67dbf203bfd3aa86ccfd0235c27caf56e0d637802e1ced26b521dfd3cf2fc3a4a9afe029082dc8e7f9136c6a7da10e6db38d3009a0000009899606b4fd00cd25510396de38a8b9fe40b094fcc0b9d903a07e1939b1c6f956d5736d49c5918f6437f51f1b596763d06323cc4a93a7d9b2d1439f3c5106c43009c0000007fda5af787aa67b86cf7009c77ce969778d03b678af8be516ce9c1aeb5c0eea34946be82ab8c5816b72f6fda8c40a0eed258cff6390059043f12bdfd46b7fbea009d000000895ee2fa2784b8f3198c9b456c5c784b36a30b121fb3d987374651d38d2899ba17d3517996f7704372ef11dba1ffa23599422724d74481e238721aed0ab3c0ea019e00000002a26ca1562a353bdcc7ce3148f50ab70b84f6857388cd56ee3189e0cedd27b86078c552f19205d65fa243821e7bba4f4eefcddc4fc9593d2919a2f57fb6c032019f000000e52e611c2e0bd82d161923c072d561f73c992d933ac7ae6c0d2f04382f32caf0146c7790b21693f3889d78c82c4f5da21b0225abc8b7933442f62e682db3d26300a1000000220cdb108711387910d7742df50c6c95743604fe0d7d8e7e558d13379a8a35a41394dfe77bd55798a75791a2fd1841343dbd95e7c437dc1987d04c820028ad4f00a2000000e9a6d28311b5e5239aea201ec94342dd9cd0b6466d31f553664c726a4b3688ab3ee7fc2b165e77dcb5868b9204e292c3176b20c0a6644864217525fd5211a79900a30000005a5d213022a9e48ed68c8f4872bd3c465900e67d3d4caca04dbdfdd799ccf6f45c41af8968c5444176732cadab07a07eb9e2953426639491758a97b58fc8905901a50000009b0667c5e60549281020f2020b0282ab70cdcd365c7c299fbd6fc2164eeed7890265cdcde3600c6e86f3f949fee7c263565b4f1327d4c66da1748d5bead8ed0100a6000000129a3b044f42c90cfc0a6c2fe5094e15c094893c3ab260e785f25cfbe16dba8f6248830e95f55c889013e964e72056c2e5dead04986e532115c845ac1b63586901a90000002086348384c06869c7cbedc5ed9f6995acee1356ccd17d61d7471b5cab1d3c986b6dcc2304e25958597981d93e5cd3909cbd64f69ef8b7d0524b7297d2c5ae4800ab0000007b90790299103552a6736a997969b424ccf9115c9babbee3dc6a2fb19515de00538380cf7c68a48a28b586686f38ae43a0982a9c840a09ce7bc07ab3652b232301ae000000018270adaab02ae7e0086e8cd98c46b8d3cd34f353555c6d38bdbf37c55e51874bdccdd843a53406d51d985956cfca04744b5bda89f9a24a6ec8ea0286dd48bc00b3000000cf9bf255231fdafddb5d293a6c829628eb5e28f45907ad830524155fef361b3d07ef733ed9d2df8090922881c73e4f2728176dddb38296e4daf1e2d38841f58101b4000000df81ec93262b110db5ee180c00c7bf90d7c07d3c4dcc4768991aa0f08709d01b7249ed56080f76d1e4c51c0732c8120b1e4a69d41430059ffbb636475e7fc9ec01b60000004e546d0a310a95a150056d5c9133e0e9a9ef0f063564dbc967981d0f08cb3b1a31dbcbbf907912faaed4d0da835452a4e2c74d3563670669ce324dd8ad0abe3c00b70000005db08494fc40c122c21157bc50a1c498176d7af04ca8be9160afa51656db63df11681d558e19b745acacf372a61f10d1bed1768da3258c424d326f2d5c02bb6b01b80000008bef977885b0f536c02f57ef2e4f7a46b6110091e74e79768df882142c7e595554593f50aa1de610128bb61689183e253b3d74ed2f0677dd5032df9d70b995e900b9000000bbd6d47014f2811b0910bd488cb338b5d20a466c9d5de493eee40f49ba919e053e50bed334f5b2184738564ce6059a1f476ccf1e5250a2249a45a80d18472ef801be0000005f6993aee4a31e452c2de13ef057eeeac58ba12932865eadc55d9ef4bf68247044d850c58eb596b87ef50260f2d2fa95819f9c203899ce857ba4618e09cbd58401bf0000000d759e79974cd0a389dd02c8e794a6a4e0dcc78375ecb8a15067db74f60d9d4e088d24622add1546e8db8751b7e0b70febdf00fa4301823913ebe94a58f9a07b00c000000017ab241a36e885a134df66a9f43c608893da4f105770023c1d1f1b15b76b4c220e28cb77cdc75c0b96ff501a2bd7349dcb8c686bcd8de311b9d63e77bea9c6a200c1000000a881620bc2e6b577e1da54598f5d36676404cb55b9cf1e405f74299c2fcad91f7320d56f00399037e579cce17a185c59c565563c838c90e134247a65901ed5c500c20000005500b061eba6a6920a3647f37f0f001cccb9439654e3197b2dae2b94da63795e03bc841ec1d211ba898599dc80c1cc8eda6f6bbd81f86ab453297e34ebb70f4c00c3000000f294ef1ae971cf643dcbb4051c72574a9c28f530376bf81c760a11814a7b48d53cfb7348692feb9d75f79387053150d8ac1acbb487a9d08cb04a31c096f2c5c100c500000097153f39e9c2d14cb5c1cd1282e091ba24c16f8134d1d44459dcf322982729fb57b026e1d680432c5282818b5351841003d602333c27455bdb69e3fec68984f801c60000004898708b21e1915ba8b3ed053aebc5fe13a5f4f10f85a340851c122c5ecbf183234fb136b78531c0dfbac70ea11eece23a60e293fdc8c05180336b218728f63e01c70000005ae97ee3651d5376d96d2cad0138fdc77fb983c43e1e71da5a1d70dedc0019897e4a8624212c2cb5a6f574e24aa30cc2352ae4917c63b7f561e57e98b11ca1ad00").unwrap(); - let signed_message: SignedMessage<::Signature> = Decode::decode(&mut &encoded_payload[..]).unwrap(); - >::put(680); - TheaHandler::validate_incoming_message(&signed_message).unwrap(); - assert_ok!(TheaHandler::incoming_message(RuntimeOrigin::none(), signed_message.clone())); - assert_eq!(>::get(),238); }); } diff --git a/pallets/thea/src/tests.rs b/pallets/thea/src/tests.rs index 26224b404..1594dda49 100644 --- a/pallets/thea/src/tests.rs +++ b/pallets/thea/src/tests.rs @@ -83,8 +83,13 @@ fn test_session_change() { // Simulating the on_new_session to last epoch of an era. Thea::on_new_session(false, authorities.into_iter(), queued.clone().into_iter()); assert!(Thea::validator_set_id() == 0); - assert!(Thea::outgoing_nonce(1) == 1); // Thea validator session change message is generated here + assert!(Thea::outgoing_nonce(1) == 0); // Thea validator session change message is not generated here on new change only when session actually changes + + // Simulating the on_new_session to the first epoch of the next era. + Thea::on_new_session(false, queued.clone().into_iter(), queued.clone().into_iter()); + assert!(Thea::validator_set_id() == 1); + assert!(Thea::outgoing_nonce(1) == 1); let message = Thea::get_outgoing_messages(1, 1).unwrap(); assert_eq!(message.nonce, 1); let validator_set: ValidatorSet<::TheaId> = @@ -93,14 +98,6 @@ fn test_session_change() { queued.iter().map(|(_, public)| public.clone()).collect(); assert_eq!(validator_set.set_id, 1); assert_eq!(validator_set.validators, queued_validators); - - // Simulating the on_new_session to the first epoch of the next era. - Thea::on_new_session(false, queued.clone().into_iter(), queued.clone().into_iter()); - assert!(Thea::validator_set_id() == 1); - assert!(Thea::outgoing_nonce(1) == 2); - let message = Thea::get_outgoing_messages(1, 2).unwrap(); - assert_eq!(message.nonce, 2); - assert!(message.data.is_empty()); }) } @@ -320,7 +317,7 @@ fn test_report_misbehaviour_happy_path() { assert_ok!(Thea::report_misbehaviour(RuntimeOrigin::signed(fisherman), network, 1)); }) } - +use thea_primitives::types::NetworkType; use frame_support::{ assert_noop, traits::{fungible::MutateHold, tokens::Precision}, From 4011469219f339c0c1f9fb82031af62776c2da21 Mon Sep 17 00:00:00 2001 From: gautham Date: Tue, 9 Jul 2024 10:51:08 +0530 Subject: [PATCH 4/4] cargo fmt --- pallets/thea-message-handler/src/test.rs | 3 +-- pallets/thea/src/tests.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pallets/thea-message-handler/src/test.rs b/pallets/thea-message-handler/src/test.rs index 619aa3830..931688295 100644 --- a/pallets/thea-message-handler/src/test.rs +++ b/pallets/thea-message-handler/src/test.rs @@ -221,8 +221,7 @@ fn test_incoming_message_validator_change_payload() { assert_ok!(TheaHandler::incoming_message(RuntimeOrigin::none(), signed_message.clone())); assert_eq!(>::get(), 1); // Doesn't do any thing - assert_ok!( - TheaHandler::incoming_message(RuntimeOrigin::none(), signed_message_sv.clone()),); + assert_ok!(TheaHandler::incoming_message(RuntimeOrigin::none(), signed_message_sv.clone()),); }) } diff --git a/pallets/thea/src/tests.rs b/pallets/thea/src/tests.rs index 1594dda49..bc73c26fc 100644 --- a/pallets/thea/src/tests.rs +++ b/pallets/thea/src/tests.rs @@ -85,7 +85,6 @@ fn test_session_change() { assert!(Thea::validator_set_id() == 0); assert!(Thea::outgoing_nonce(1) == 0); // Thea validator session change message is not generated here on new change only when session actually changes - // Simulating the on_new_session to the first epoch of the next era. Thea::on_new_session(false, queued.clone().into_iter(), queued.clone().into_iter()); assert!(Thea::validator_set_id() == 1); @@ -317,11 +316,11 @@ fn test_report_misbehaviour_happy_path() { assert_ok!(Thea::report_misbehaviour(RuntimeOrigin::signed(fisherman), network, 1)); }) } -use thea_primitives::types::NetworkType; use frame_support::{ assert_noop, traits::{fungible::MutateHold, tokens::Precision}, }; +use thea_primitives::types::NetworkType; use thea_primitives::types::{AssetMetadata, IncomingMessage, SignedMessage, THEA_HOLD_REASON}; #[test]