diff --git a/pallets/exodus/Cargo.toml b/pallets/exodus/Cargo.toml index a512406..16613d6 100644 --- a/pallets/exodus/Cargo.toml +++ b/pallets/exodus/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghost-exodus" -version = "0.0.26" +version = "0.1.0" description = "Threshold signature generation with DKG included" license.workspace = true authors.workspace = true diff --git a/pallets/exodus/runtime-api/Cargo.toml b/pallets/exodus/runtime-api/Cargo.toml index 81f5832..f32117c 100644 --- a/pallets/exodus/runtime-api/Cargo.toml +++ b/pallets/exodus/runtime-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghost-exodus-runtime-api" -version = "0.0.0" +version = "0.0.1" description = "RPC runtime API for ghost EXODUS pallet" license.workspace = true authors.workspace = true @@ -17,4 +17,4 @@ sp-api = { workspace = true } [features] default = ["std"] -std = ["codec/std", "sp-api/std"] \ No newline at end of file +std = ["codec/std", "sp-api/std"] diff --git a/pallets/exodus/runtime-api/src/lib.rs b/pallets/exodus/runtime-api/src/lib.rs index d16110c..2941fe9 100644 --- a/pallets/exodus/runtime-api/src/lib.rs +++ b/pallets/exodus/runtime-api/src/lib.rs @@ -2,6 +2,27 @@ sp_api::decl_runtime_apis! { pub trait ExodusApi { - fn verifying_key(network_id: NetworkId) -> Vec; + fn get_signed_rotation_message( + session: ExodusSession, + network_id: NetworkId, + dkg_index: DkgIndex + ) -> Vec; + + fn get_signed_bridge_message( + session: ExodusSession, + network_id: NetworkId, + dkg_index: DkgIndex + ) -> Vec; + + fn get_signed_governance_message( + session: ExodusSession, + network_id: NetworkId, + dkg_index: DkgIndex + ) -> Vec; + + fn active_verifying_key(network_curve: NetworkCurve) -> Vec; + fn pending_exodus_request_count(network_curve: NetworkCurve) -> u32; + fn is_qualification_onging(network_curve: NetworkCurve) -> bool; + } } diff --git a/pallets/exodus/src/benchmarking.rs b/pallets/exodus/src/benchmarking.rs index 437bd1a..cb8909d 100644 --- a/pallets/exodus/src/benchmarking.rs +++ b/pallets/exodus/src/benchmarking.rs @@ -7,7 +7,7 @@ use frame_system::RawOrigin; use ghost_helpers::networks::NetworkDataBuilder; fn prepare_pallet(authorities_len: usize) -> ( - PendingDkgAuthorities, BlockNumberFor>, + PendingDkgAuthorities, NetworkCurve, T::AuthorityId, AuthIndex @@ -99,7 +99,9 @@ mod benchmarks { let package_hash = ExodusHash::from(blake2_256(&public_package)); Round0Packages::::insert(network_curve, authority_index, package_hash); + let current_block = ::BlockNumberProvider::current_block_number(); let round1_package = PackageContext::default() + .with_block_number(current_block) .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(0) .with_dkg_index(dkg_index) @@ -167,7 +169,9 @@ mod benchmarks { let encryption_bundle = BundledPackages { bitmask, blob }; + let current_block = ::BlockNumberProvider::current_block_number(); let round2_package = PackageContext::default() + .with_block_number(current_block) .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(authority_index) .with_dkg_index(dkg_index) @@ -222,7 +226,9 @@ mod benchmarks { } }); + let current_block = ::BlockNumberProvider::current_block_number(); let round3_package = PackageContext::default() + .with_block_number(current_block) .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(authority_index) .with_dkg_index(dkg_index) @@ -295,7 +301,9 @@ mod benchmarks { let justification_bundle = BundledPackages { bitmask, blob }; + let current_block = ::BlockNumberProvider::current_block_number(); let round4_package = PackageContext::default() + .with_block_number(current_block) .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(authority_index) .with_dkg_index(dkg_index) @@ -346,7 +354,9 @@ mod benchmarks { let dummy_verifying_key = vec![69u8; network_curve.element_bytes_len()]; let dummy_key_members = vec![15]; + let current_block = ::BlockNumberProvider::current_block_number(); let round5_package = PackageContext::default() + .with_block_number(current_block) .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(authority_index) .with_dkg_index(dkg_index) @@ -458,7 +468,9 @@ mod benchmarks { consensus.state.merkle_root = dummy_merkle_root; }); + let current_block = ::BlockNumberProvider::current_block_number(); let round6_package = PackageContext::default() + .with_block_number(current_block) .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(authority_index) .with_dkg_index(dkg_index) @@ -503,8 +515,13 @@ mod benchmarks { } dkg_state.insert_index(authority_index); - let exodus_session = CurrentExodus::::get(); + let all_exodus_sessions = CurrentExodusSessions::::get(); + let exodus_session = all_exodus_sessions + .get(&network_id) + .copied() + .unwrap_or(0); + let dummy_context = ExodusContext::new(network_id, exodus_session); let dummy_address = EvmAddress::repeat_byte(69); // generator point for secp256k1 @@ -522,28 +539,28 @@ mod benchmarks { intervals: [34, 35], booleans: [true, true], }; - let request = ExodusRequest::, BalanceOf>::evm_governance( - exodus_session, - network_id, + let request = ExodusRequest::>::evm_governance( dummy_address, create_bond, ); + let current_block = ::BlockNumberProvider::current_block_number(); let authorities = QualificationAuthorities::::get(&network_curve); - let active_dkg: ActivatedState = (&dkg_state).into(); + let active_dkg: ReadyDkgAuthorities = (&dkg_state).into(); let active_dkg_index = active_dkg.get_dkg_index(); QualificationDkgState::::insert(&network_curve, dkg_state); ActiveDkgAuthorities::::insert(&network_curve, active_dkg); ActiveAuthorities::::insert(&network_curve, authorities); - ExodusRequests::::insert((network_curve, exodus_session), request); + ExodusRequests::::insert((&network_curve, &dummy_context), request); let nonce_package = PackageContext::default() + .with_block_number(current_block) .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(authority_index) .with_dkg_index(active_dkg_index) .with_exodus_nonce_commitment( - exodus_session, + dummy_context, dummy_nonce.to_vec(), dummy_nonce.to_vec(), ) @@ -558,16 +575,16 @@ mod benchmarks { #[extrinsic_call] _(RawOrigin::None, exodus_package, signature); - let roast_session = RoastSessions::::get(&exodus_session, authority_index) + let roast_session = RoastSessions::::get(&dummy_context, authority_index) .expect("ROAST session should exist after nonce commitment"); - let roast_state = RoastSessionStates::::get(&exodus_session, roast_session); + let roast_state = RoastSessionStates::::get(&dummy_context, roast_session); assert!(roast_state.nonce_committers.contains(authority_index)); assert!(!roast_state.group_committers.contains(authority_index)); assert!(!roast_state.partial_signers.contains(authority_index)); let registered_nonces = NonceCommitments::::get( - (exodus_session, roast_session), + (dummy_context, roast_session), authority_index, ).expect("Nonces should be registered during nonce commitment"); @@ -589,8 +606,13 @@ mod benchmarks { } dkg_state.insert_index(authority_index); - let exodus_session = CurrentExodus::::get(); + let all_exodus_sessions = CurrentExodusSessions::::get(); + let exodus_session = all_exodus_sessions + .get(&network_id) + .copied() + .unwrap_or(0); + let dummy_context = ExodusContext::new(network_id, exodus_session); let dummy_bytes = EvmBytes32::repeat_byte(69); let dummy_address = EvmAddress::repeat_byte(69); let dummy_merkle_root = ExodusHash::repeat_byte(69); @@ -603,36 +625,36 @@ mod benchmarks { intervals: [34, 35], booleans: [true, true], }; - let request = ExodusRequest::, BalanceOf>::evm_governance( - exodus_session, - network_id, + let request = ExodusRequest::>::evm_governance( dummy_address, create_bond, ); let authorities = QualificationAuthorities::::get(&network_curve); - let active_dkg: ActivatedState = (&dkg_state).into(); + let active_dkg: ReadyDkgAuthorities = (&dkg_state).into(); let active_dkg_index = active_dkg.get_dkg_index(); QualificationDkgState::::insert(&network_curve, dkg_state); ActiveDkgAuthorities::::insert(&network_curve, active_dkg); ActiveAuthorities::::insert(&network_curve, authorities); - ExodusRequests::::insert((network_curve, exodus_session), request); + ExodusRequests::::insert((network_curve, dummy_context), request); let mut roast_state = RoastSessionState::>::default(); roast_state.status = RoastStatus::GroupCommitments; roast_state.nonce_committers.insert(authority_index); let roast_session = 3; - RoastSessions::::insert(&exodus_session, authority_index, roast_session); - RoastSessionStates::::insert(exodus_session, roast_session, roast_state); + let current_block = ::BlockNumberProvider::current_block_number(); + RoastSessions::::insert(&dummy_context, authority_index, roast_session); + RoastSessionStates::::insert(dummy_context, roast_session, roast_state); let group_package = PackageContext::default() + .with_block_number(current_block) .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(authority_index) .with_dkg_index(active_dkg_index) .with_exodus_group_commitment( - exodus_session, + dummy_context, dummy_group_commitment.clone(), dummy_bytes, ) @@ -652,16 +674,16 @@ mod benchmarks { dummy_group_commitment, ).expect("Group commtiment should be valid"); - let roast_state = RoastSessionStates::::get(&exodus_session, roast_session); + let roast_state = RoastSessionStates::::get(&dummy_context, roast_session); assert!(roast_state.nonce_committers.contains(authority_index)); assert!(roast_state.group_committers.contains(authority_index)); - let consensus = GroupCommitmentsConsensus::::get(exodus_session, roast_session); + let consensus = GroupCommitmentsConsensus::::get(dummy_context, roast_session); assert!(consensus.participants.contains(authority_index)); assert_eq!(&consensus.element_bytes, &bounded_group_commitment); assert_eq!(consensus.merkle_root, dummy_merkle_root); - let group_commitment_key = (exodus_session, roast_session, dummy_merkle_root, bounded_group_commitment); + let group_commitment_key = (dummy_context, roast_session, dummy_merkle_root, bounded_group_commitment); let participants = GroupCommitters::::get(group_commitment_key); assert!(participants.contains(authority_index)); @@ -684,7 +706,13 @@ mod benchmarks { dkg_state.insert_index(authority_index); let dkg_index = dkg_state.get_dkg_index(); - let exodus_session = CurrentExodus::::get(); + let all_exodus_sessions = CurrentExodusSessions::::get(); + let exodus_session = all_exodus_sessions + .get(&network_id) + .copied() + .unwrap_or(0); + + let exodus_context = ExodusContext::new(network_id, exodus_session); let mut dummy_element = vec![0u8; network_curve.element_bytes_len()]; dummy_element[0] = 0x02; @@ -704,9 +732,7 @@ mod benchmarks { intervals: [34, 35], booleans: [true, true], }; - let request = ExodusRequest::, BalanceOf>::evm_governance( - exodus_session, - network_id, + let request = ExodusRequest::>::evm_governance( dummy_address, create_bond, ); @@ -751,13 +777,13 @@ mod benchmarks { }); let authorities = QualificationAuthorities::::get(&network_curve); - let active_dkg: ActivatedState = (&dkg_state).into(); + let active_dkg: ReadyDkgAuthorities = (&dkg_state).into(); let active_dkg_index = active_dkg.get_dkg_index(); QualificationDkgState::::insert(&network_curve, dkg_state); ActiveDkgAuthorities::::insert(&network_curve, active_dkg); ActiveAuthorities::::insert(&network_curve, authorities); - ExodusRequests::::insert((network_curve, exodus_session), request); + ExodusRequests::::insert((network_curve, exodus_context), request); let mut roast_state = RoastSessionState::>::default(); roast_state.status = RoastStatus::PartialSignatures; @@ -770,8 +796,8 @@ mod benchmarks { }); let roast_session = 3; - RoastSessions::::insert(&exodus_session, authority_index, roast_session); - RoastSessionStates::::insert(exodus_session, roast_session, roast_state); + RoastSessions::::insert(&exodus_context, authority_index, roast_session); + RoastSessionStates::::insert(exodus_context, roast_session, roast_state); let bounded_group_commitment = BoundedVec::try_from(dummy_group_commitment).unwrap(); let dummy_verifying_key = BoundedVec::try_from(dummy_element.clone()).unwrap(); @@ -784,9 +810,9 @@ mod benchmarks { ActiveVerifyingKey::::insert(&network_curve, dummy_verifying_key); VerifyingShares::::insert((&network_curve, active_dkg_index, authority_index), dummy_verifying_share); - NonceCommitments::::insert((&exodus_session, &roast_session), authority_index, dummy_nonce); + NonceCommitments::::insert((&exodus_context, &roast_session), authority_index, dummy_nonce); - GroupCommitmentsConsensus::::mutate(&exodus_session, &roast_session, |consensus| { + GroupCommitmentsConsensus::::mutate(&exodus_context, &roast_session, |consensus| { consensus.element_bytes = bounded_group_commitment.clone(); consensus.merkle_root = dummy_merkle_root; (0..threshold).for_each(|i| { @@ -794,12 +820,14 @@ mod benchmarks { }); }); + let current_block = ::BlockNumberProvider::current_block_number(); let share_package = PackageContext::default() + .with_block_number(current_block) .with_network_curve(NetworkCurve::Secp256k1) .with_authority_index(authority_index) .with_dkg_index(active_dkg_index) .with_exodus_signature_share( - exodus_session, + exodus_context, dummy_signature_share.clone(), dummy_merkle_proof, dummy_binding_factor @@ -815,13 +843,13 @@ mod benchmarks { #[extrinsic_call] _(RawOrigin::None, exodus_package, signature); - let maybe_roast_session = RoastSessions::::get(&exodus_session, authority_index); + let maybe_roast_session = RoastSessions::::get(&exodus_context, authority_index); assert!(maybe_roast_session.is_none()); - let scalar_exists = SignatureScalars::::contains_key(&exodus_session, &roast_session); - let consensus_exists = GroupCommitmentsConsensus::::contains_key(&exodus_session, &roast_session); + let scalar_exists = SignatureScalars::::contains_key(&exodus_context, &roast_session); + let consensus_exists = GroupCommitmentsConsensus::::contains_key(&exodus_context, &roast_session); let committers_exists = GroupCommitters::::contains_key(( - &exodus_session, + &exodus_context, &roast_session, &dummy_merkle_root, &bounded_group_commitment, @@ -831,8 +859,8 @@ mod benchmarks { assert!(!consensus_exists); assert!(!committers_exists); - assert!(!ExodusRequests::::contains_key((&network_curve, &exodus_session))); - assert!(ExodusSignedGovernance::::contains_key((&exodus_session, network_id), dkg_index)); + assert!(!ExodusRequests::::contains_key((&network_curve, &exodus_context))); + assert!(ExodusSignedGovernance::::contains_key(dkg_index, &exodus_context)); Ok(()) } @@ -864,7 +892,11 @@ mod benchmarks { let _ = T::NetworkDataHandler::register_incoming(&network_id, amount).unwrap(); let total_issuance = T::Currency::total_issuance(); - let current_exodus_session = CurrentExodus::::get(); + let all_exodus_sessions = CurrentExodusSessions::::get(); + let current_exodus_session = all_exodus_sessions + .get(&network_id) + .copied() + .unwrap_or(0); #[extrinsic_call] _( @@ -875,8 +907,12 @@ mod benchmarks { receiver, ); - assert!(ExodusRequests::::contains_key((&network_curve, current_exodus_session))); - assert_eq!(CurrentExodus::::get(), current_exodus_session + 1); + let all_exodus_sessions = CurrentExodusSessions::::get(); + let last_exodus_session = all_exodus_sessions.get(&network_id).copied().unwrap(); + let context = ExodusContext::new(network_id, current_exodus_session); + + assert!(ExodusRequests::::contains_key((&network_curve, &context))); + assert_eq!(last_exodus_session, current_exodus_session + 1); assert_eq!(T::Currency::balance(&caller), initial_balance - amount); assert_eq!(T::Currency::total_issuance(), total_issuance - amount); diff --git a/pallets/exodus/src/lib.rs b/pallets/exodus/src/lib.rs index 79eb140..0d2208f 100644 --- a/pallets/exodus/src/lib.rs +++ b/pallets/exodus/src/lib.rs @@ -91,6 +91,7 @@ pub mod sr25519 { } const MIN_LOCK_GUARD_PERIOD: u64 = 15_000; +const BLOCKS_FOR_DKG_RESET: u64 = 5; const DB_STORAGE_PREFIX : &[u8] = b"exodus::"; const PALLET_LOG_TARGET : &str = "time:ghost-exodus"; @@ -124,12 +125,19 @@ const ROUND_NUMBER_4: u8 = 4u8; const ROUND_NUMBER_5: u8 = 5u8; const ROUND_NUMBER_6: u8 = 6u8; -const fn postcard_varint_len(len: usize) -> usize { +const fn postcard_varint_prefix_len(len: usize) -> usize { if len <= 127 { 1 } else if len <= 16383 { 2 } else { 3 } } +const fn scale_compact_prefix_len(len: usize) -> usize { + if len <= 63 { 1 } + else if len <= 16383 { 2 } + else if len <= 1073741823 { 4 } + else { 8 } +} + const fn round1_package_size( commitments_count: usize, element_bytes_len: usize, @@ -137,27 +145,24 @@ const fn round1_package_size( header_bytes_len: usize ) -> usize { let commitments_bytes_len = commitments_count.saturating_mul(element_bytes_len); - let commitment_vec_prefix = postcard_varint_len(commitments_count); + let commitment_vec_prefix = postcard_varint_prefix_len(commitments_count); - postcard_varint_len(signature_bytes_len) + postcard_varint_prefix_len(signature_bytes_len) .saturating_add(signature_bytes_len) .saturating_add(header_bytes_len) .saturating_add(commitment_vec_prefix) .saturating_add(commitments_bytes_len) } -// TODO: revisit and make it correctly compute const fn encrypted_ciphertext_bytes_len( scalar_bytes_len: usize, header_bytes_len: usize, ) -> usize { - let ciphertext_len = header_bytes_len - .saturating_add(scalar_bytes_len) - .saturating_add(AUTHENTICATION_TAG_MAX_BYTES as usize); - - let ciphertext_vec_prefix = postcard_varint_len(ciphertext_len); + let ciphertext_len = header_bytes_len.saturating_add(scalar_bytes_len); + let ciphertext_vec_prefix = postcard_varint_prefix_len(ciphertext_len); ciphertext_len.saturating_add(ciphertext_vec_prefix) + .saturating_add(AUTHENTICATION_TAG_MAX_BYTES as usize) } const fn round2_encrypted_package_size( @@ -165,15 +170,16 @@ const fn round2_encrypted_package_size( scalar_bytes_len: usize, header_bytes_len: usize, ) -> usize { - let encrypted_ciphertext_bytes_len = encrypted_ciphertext_bytes_len( - scalar_bytes_len, - header_bytes_len, - ); + let encrypted_ciphertext_bytes_len = + encrypted_ciphertext_bytes_len( + scalar_bytes_len, + header_bytes_len, + ); let ciphertext_len = encrypted_ciphertext_bytes_len .saturating_add(ENCRYPTION_NONCE_MAX_BYTES as usize); - let ciphertext_vec_prefix = postcard_varint_len(ciphertext_len); + let ciphertext_vec_prefix = scale_compact_prefix_len(ciphertext_len); let size = ciphertext_len.saturating_add(ciphertext_vec_prefix); participants.saturating_mul(size) @@ -185,7 +191,7 @@ const fn round2_package_size( header_bytes_len: usize, ) -> usize { let signing_share_len = header_bytes_len.saturating_add(scalar_bytes_len); - let signing_share_vec_prefix = postcard_varint_len(signing_share_len); + let signing_share_vec_prefix = postcard_varint_prefix_len(signing_share_len); let size = signing_share_vec_prefix.saturating_add(signing_share_len); participants.saturating_mul(size) @@ -221,9 +227,6 @@ type EvmBytes32 = sp_core::H256; type ParticipantsBitmap = BoundedBitmap::MaxAuthoritiesChunks>; type BitmapByAuthority = BoundedBTreeMap, ::MaxAuthorities>; -type QualifyingState = PendingDkgAuthorities, BlockNumberFor>; -type ActivatedState = ReadyDkgAuthorities>; - pub type BalanceOf = <::Currency as Inspect<::AccountId>>::Balance; pub type NetworkIdOf = <::NetworkDataHandler as NetworkDataBasicHandler>::NetworkId; @@ -354,7 +357,7 @@ pub struct SignatureMaxBytes(sp_std::marker::PhantomData); impl Get for SignatureMaxBytes { fn get() -> u32 { let signature_max_bytes = ELEMENT_MAX_BYTES + SCALAR_MAX_BYTES; - postcard_varint_len(signature_max_bytes as usize) as u32 + postcard_varint_prefix_len(signature_max_bytes as usize) as u32 } } @@ -426,6 +429,9 @@ pub mod pallet { #[pallet::constant] type BaseEvmAmount: Get; + #[pallet::constant] + type MaxExodusRequestCount: Get; + type WeightInfo: WeightInfo; } @@ -468,19 +474,19 @@ pub mod pallet { }, NonceCommitmentRegistered { authority_index: AuthIndex, - exodus_session: ExodusSession, + exodus_context: ExodusContext>, network_curve: NetworkCurve, roast_session: RoastSession, }, GroupCommitmentRegistered { authority_index: AuthIndex, - exodus_session: ExodusSession, + exodus_context: ExodusContext>, network_curve: NetworkCurve, roast_session: RoastSession, }, PartialSignatureRegistered { authority_index: AuthIndex, - exodus_session: ExodusSession, + exodus_context: ExodusContext>, network_curve: NetworkCurve, roast_session: RoastSession, }, @@ -547,9 +553,16 @@ pub mod pallet { } #[pallet::storage] - #[pallet::getter(fn current_exodus)] - pub(super) type CurrentExodus = - StorageValue<_, ExodusSession, ValueQuery>; + #[pallet::getter(fn current_exodus_sessions)] + pub(super) type CurrentExodusSessions = StorageValue< + _, + BoundedBTreeMap< + NetworkIdOf, + ExodusSession, + ::MaxNetworks, + >, + ValueQuery + >; #[pallet::storage] #[pallet::getter(fn exodus_requests)] @@ -557,9 +570,9 @@ pub mod pallet { _, ( NMapKey, - NMapKey, + NMapKey>>, ), - ExodusRequest, BalanceOf>, + ExodusRequest>, OptionQuery, >; @@ -567,7 +580,7 @@ pub mod pallet { #[pallet::getter(fn roast_states)] pub(super) type RoastSessionStates = StorageDoubleMap< _, - Twox64Concat, ExodusSession, + Twox64Concat, ExodusContext>, Twox64Concat, RoastSession, RoastSessionState>, ValueQuery, @@ -577,7 +590,7 @@ pub mod pallet { #[pallet::getter(fn nonce_commitments)] pub(super) type NonceCommitments = StorageDoubleMap< _, - Twox64Concat, (ExodusSession, RoastSession), + Twox64Concat, (ExodusContext>, RoastSession), Twox64Concat, AuthIndex, ExodusSeparatedNonce, OptionQuery, @@ -588,10 +601,10 @@ pub mod pallet { pub(super) type GroupCommitters = StorageNMap< _, ( - NMapKey, - NMapKey, - NMapKey, - NMapKey>>, + NMapKey>>, + NMapKey, + NMapKey, + NMapKey>>, ), ParticipantsBitmap, ValueQuery, @@ -601,7 +614,7 @@ pub mod pallet { #[pallet::getter(fn group_commitments_consensus)] pub(super) type GroupCommitmentsConsensus = StorageDoubleMap< _, - Twox64Concat, ExodusSession, + Twox64Concat, ExodusContext>, Twox64Concat, RoastSession, ConsensusState>, ValueQuery, @@ -611,7 +624,7 @@ pub mod pallet { #[pallet::getter(fn signature_scalars)] pub(super) type SignatureScalars = StorageDoubleMap< _, - Twox64Concat, ExodusSession, + Twox64Concat, ExodusContext>, Twox64Concat, RoastSession, BoundedVec>, ValueQuery, @@ -622,7 +635,7 @@ pub mod pallet { #[pallet::getter(fn roast_sessions)] pub(super) type RoastSessions = StorageDoubleMap< _, - Twox64Concat, ExodusSession, + Twox64Concat, ExodusContext>, Twox64Concat, AuthIndex, RoastSession, OptionQuery, @@ -632,9 +645,9 @@ pub mod pallet { #[pallet::getter(fn exodus_signatured_rotations)] pub(super) type ExodusSignedRotations = StorageDoubleMap< _, - Twox64Concat, (ExodusSession, NetworkType), Twox64Concat, DkgIndex, - ExodusSignedMessage, BalanceOf>, + Twox64Concat, ExodusContext>, + ExodusSignedMessage>, OptionQuery, >; @@ -642,9 +655,9 @@ pub mod pallet { #[pallet::getter(fn exodus_signed_bridges)] pub(super) type ExodusSignedBridges = StorageDoubleMap< _, - Twox64Concat, (ExodusSession, NetworkIdOf), Twox64Concat, DkgIndex, - ExodusSignedMessage, BalanceOf>, + Twox64Concat, ExodusContext>, + ExodusSignedMessage>, OptionQuery, >; @@ -652,9 +665,9 @@ pub mod pallet { #[pallet::getter(fn exodus_signed_governance)] pub(super) type ExodusSignedGovernance = StorageDoubleMap< _, - Twox64Concat, (ExodusSession, NetworkIdOf), Twox64Concat, DkgIndex, - ExodusSignedMessage, BalanceOf>, + Twox64Concat, ExodusContext>, + ExodusSignedMessage>, OptionQuery, >; @@ -785,7 +798,7 @@ pub mod pallet { pub(super) type ActiveDkgAuthorities = StorageMap< _, Twox64Concat, NetworkCurve, - ActivatedState, + ReadyDkgAuthorities, ValueQuery, >; @@ -803,7 +816,7 @@ pub mod pallet { pub(super) type QualificationDkgState = StorageMap< _, Twox64Concat, NetworkCurve, - QualifyingState, + PendingDkgAuthorities, ValueQuery, >; @@ -852,23 +865,26 @@ pub mod pallet { SCALAR_MAX_BYTES, ); - for network_type in T::NetworkDataHandler::iter_types_by_curve(&network_curve) { + for (network_id, network_type) in T::NetworkDataHandler::iter_types_by_curve(&network_curve) { let rotation_message_size = match network_type { NetworkType::Evm => { let dummy_public_address = EvmBytes32::repeat_byte(69u8); let dummy_exodus_request = - ExodusRequest::, BalanceOf>::evm_rotation( - 0, dummy_public_address, 0, + ExodusRequest::>::evm_rotation( + dummy_public_address, + 0u8, // parity ); + let dummy_exodus_context = ExodusContext::new(network_id, 0); + let mut message_buffer = [0u8; MAX_MESSAGE_SIZE as usize]; dummy_exodus_request - .get_message(&mut message_buffer) + .get_message(&mut message_buffer, dummy_exodus_context) .map(|message| message.len() as u32) .unwrap_or(u32::MAX) }, - _ => 0, + _ => MAX_MESSAGE_SIZE, }; assert!( @@ -893,7 +909,7 @@ pub mod pallet { ))] pub fn register_round0_package( origin: OriginFor, - dkg_package: DkgPackage, + dkg_package: DkgPackage, T>, signature: ::Signature, ) -> DispatchResult { ensure_none(origin)?; @@ -949,7 +965,7 @@ pub mod pallet { ))] pub fn register_round1_package( origin: OriginFor, - dkg_package: DkgPackage, + dkg_package: DkgPackage, T>, signature: ::Signature, ) -> DispatchResult { ensure_none(origin)?; @@ -1029,7 +1045,7 @@ pub mod pallet { ))] pub fn register_encrypted_round2_packages( origin: OriginFor, - dkg_package: DkgPackage, + dkg_package: DkgPackage, T>, signature: ::Signature, ) -> DispatchResult { ensure_none(origin)?; @@ -1102,7 +1118,7 @@ pub mod pallet { ))] pub fn register_round3_complaints( origin: OriginFor, - dkg_package: DkgPackage, + dkg_package: DkgPackage, T>, signature: ::Signature, ) -> DispatchResult { ensure_none(origin)?; @@ -1158,7 +1174,7 @@ pub mod pallet { ))] pub fn register_round4_justifications( origin: OriginFor, - dkg_package: DkgPackage, + dkg_package: DkgPackage, T>, signature: ::Signature, ) -> DispatchResult { ensure_none(origin)?; @@ -1223,7 +1239,7 @@ pub mod pallet { ))] pub fn register_round5_verifying_package( origin: OriginFor, - dkg_package: DkgPackage, + dkg_package: DkgPackage, T>, signature:::Signature, ) -> DispatchResult { ensure_none(origin)?; @@ -1302,7 +1318,7 @@ pub mod pallet { ))] pub fn register_round6_public_share_package( origin: OriginFor, - dkg_package: DkgPackage, + dkg_package: DkgPackage, T>, signature:::Signature, ) -> DispatchResult { ensure_none(origin)?; @@ -1380,13 +1396,14 @@ pub mod pallet { ))] pub fn register_nonce_commitment( origin: OriginFor, - exodus_package: ExodusPackage, + exodus_package: ExodusPackage, BlockNumberFor, T>, signature:::Signature, ) -> DispatchResult { ensure_none(origin)?; let network_curve = exodus_package.get_network_curve(); let authority_index = exodus_package.get_authority_index(); + let exodus_context = exodus_package.copy_exodus_context(); let (mut exodus_request, roast_session) = Self::validate_exodus_nonce_commitment_size_and_get_metadata( @@ -1398,18 +1415,18 @@ pub mod pallet { return Err(Error::::ExodusNoncePackageInvalidProof.into()); }; + let threshold = ActiveAuthorities::::decode_len(&network_curve) .map(|max_participants| get_byzantium_quorum_threshold(max_participants)) .ok_or(Error::::DkgAuthoritiesNotInitialized)?; - let exodus_session = package.session; - ensure!( - !RoastSessions::::contains_key(&exodus_session, authority_index), + !RoastSessions::::contains_key(exodus_context, authority_index), Error::::ExodusAlreadyInRoastSession, ); - let mut roast_state = RoastSessionStates::::get(&exodus_session, roast_session); + let mut roast_state = + RoastSessionStates::::get(&exodus_context, roast_session); ensure!( roast_state.status == RoastStatus::NonceCommitments, @@ -1438,13 +1455,13 @@ pub mod pallet { exodus_request.next_roast_session = roast_session .saturating_add(1); - ExodusRequests::::insert((&network_curve, &exodus_session), exodus_request); + ExodusRequests::::insert((network_curve, exodus_context), exodus_request); } - RoastSessions::::insert(&exodus_session, &authority_index, roast_session); - RoastSessionStates::::insert(&exodus_session, &roast_session, roast_state); + RoastSessions::::insert(exodus_context, &authority_index, roast_session); + RoastSessionStates::::insert(exodus_context, &roast_session, roast_state); NonceCommitments::::insert( - (exodus_session, roast_session), authority_index, + (exodus_context, roast_session), authority_index, ExodusSeparatedNonce::new( package.hiding_commitment, package.binding_commitment, @@ -1453,7 +1470,7 @@ pub mod pallet { Self::deposit_event(Event::::NonceCommitmentRegistered { authority_index, - exodus_session, + exodus_context, network_curve, roast_session, }); @@ -1469,13 +1486,14 @@ pub mod pallet { ))] pub fn register_group_commitment( origin: OriginFor, - exodus_package: ExodusPackage, + exodus_package: ExodusPackage, BlockNumberFor, T>, signature:::Signature, ) -> DispatchResult { ensure_none(origin)?; let network_curve = exodus_package.get_network_curve(); let authority_index = exodus_package.get_authority_index(); + let exodus_context = exodus_package.copy_exodus_context(); let roast_session = Self::validate_exodus_group_commitment_size_and_get_metadata( @@ -1491,43 +1509,37 @@ pub mod pallet { .ok_or(Error::::DkgAuthoritiesNotInitialized)?; let threshold = get_byzantium_quorum_threshold(max_authorities); - let exodus_session = package.session; ensure!( - ExodusRequests::::contains_key((&network_curve, &exodus_session)), + ExodusRequests::::contains_key((&network_curve, exodus_context)), Error::::ExodusRequestNotFound ); let group_commitment_key =( - exodus_session, + exodus_context, roast_session, package.binding_factors_root, &package.group_commitment ); - let mut group_participants = GroupCommitters::::try_mutate( - &group_commitment_key, - |participants| -> Result, DispatchError> { - ensure!( - !participants.contains(authority_index), - Error::::ExodusGroupAlreadyRegistered, - ); + let mut group_participants = + GroupCommitters::::get(&group_commitment_key); - if participants.is_empty() { - *participants = ParticipantsBitmap::::empty(max_authorities); - } + ensure!( + !group_participants.contains(authority_index), + Error::::ExodusGroupAlreadyRegistered, + ); - participants.insert(authority_index); + if group_participants.is_empty() { + group_participants = ParticipantsBitmap::::empty(max_authorities); + } - Ok(participants.clone()) - })?; - - let consensus_reached = group_participants - .count_ones::() + group_participants.insert(authority_index); + let group_participants_count = group_participants.count_ones::(); + let consensus_reached = group_participants_count .ge(&threshold.div_ceil(2)); - let mut roast_state = - RoastSessionStates::::get(&exodus_session, roast_session); + let mut roast_state = RoastSessionStates::::get(exodus_context, roast_session); ensure!( consensus_reached || roast_state.status == RoastStatus::GroupCommitments, @@ -1544,19 +1556,16 @@ pub mod pallet { Error::::ExodusSignedMessageAlreadyRegistered, ); - group_participants.insert(authority_index); roast_state.group_committers.insert(authority_index); - let group_participants_count = group_participants.count_ones::(); - if group_participants_count >= threshold.div_ceil(2) { roast_state.group_committers |= &roast_state.nonce_committers; roast_state.status = RoastStatus::PartialSignatures; } - RoastSessionStates::::insert(&exodus_session, &roast_session, roast_state); + RoastSessionStates::::insert(exodus_context, &roast_session, roast_state); GroupCommitters::::insert(&group_commitment_key, group_participants.clone()); - GroupCommitmentsConsensus::::mutate(&exodus_session, &roast_session, |consensus| { + GroupCommitmentsConsensus::::mutate(exodus_context, &roast_session, |consensus| { let consensus_count = consensus.participants .count_ones::(); @@ -1571,7 +1580,7 @@ pub mod pallet { Self::deposit_event(Event::::GroupCommitmentRegistered { network_curve, - exodus_session, + exodus_context, authority_index, roast_session }); @@ -1587,13 +1596,14 @@ pub mod pallet { ))] pub fn register_signature_share( origin: OriginFor, - exodus_package: ExodusPackage, + exodus_package: ExodusPackage, BlockNumberFor, T>, signature:::Signature, ) -> DispatchResult { ensure_none(origin)?; let network_curve = exodus_package.get_network_curve(); let authority_index = exodus_package.get_authority_index(); + let exodus_context = exodus_package.copy_exodus_context(); let (roast_session, active_dkg_index) = Self::validate_exodus_signature_share_size_and_get_metadata( @@ -1609,10 +1619,8 @@ pub mod pallet { .map(|max_participants| get_byzantium_quorum_threshold(max_participants)) .ok_or(Error::::DkgAuthoritiesNotInitialized)?; - let exodus_session = package.session; - let commitment_consensus = - GroupCommitmentsConsensus::::get(&exodus_session, &roast_session); + GroupCommitmentsConsensus::::get(exodus_context, &roast_session); let consensus_participants_count = commitment_consensus .participants @@ -1624,29 +1632,24 @@ pub mod pallet { ); let exodus_request = - ExodusRequests::::get((&network_curve, &exodus_session)) + ExodusRequests::::get((&network_curve, exodus_context)) .ok_or(Error::::ExodusRequestNotFound)?; ensure!( match exodus_request.r#type { - ExodusRequestType::EvmRotation { .. } => { + ExodusRequestType::EvmRotation { .. } | ExodusRequestType::UtxoRotation => { !ExodusSignedRotations::::contains_key( - (exodus_session, NetworkType::Evm), active_dkg_index, + active_dkg_index, exodus_context, ) }, - ExodusRequestType::UtxoRotation => { - !ExodusSignedRotations::::contains_key( - (exodus_session, NetworkType::Utxo), active_dkg_index, - ) - } - ExodusRequestType::EvmBridgeOut { network_id, .. } | ExodusRequestType::UtxoBridgeOut { network_id, .. } => { + ExodusRequestType::EvmBridgeOut { .. } | ExodusRequestType::UtxoBridgeOut { .. } => { !ExodusSignedBridges::::contains_key( - (exodus_session, network_id), active_dkg_index, + active_dkg_index, exodus_context, ) } - ExodusRequestType::EvmGovernance { network_id, .. } => { + ExodusRequestType::EvmGovernance { .. } => { !ExodusSignedGovernance::::contains_key( - (exodus_session, network_id), active_dkg_index, + active_dkg_index, exodus_context, ) } }, @@ -1654,7 +1657,7 @@ pub mod pallet { ); let mut roast_state = - RoastSessionStates::::get(&exodus_session, roast_session); + RoastSessionStates::::get(exodus_context, roast_session); ensure!( roast_state.status == RoastStatus::PartialSignatures, @@ -1684,8 +1687,10 @@ pub mod pallet { ).map_err(|_| Error::::InvalidMerkleProof)?; let nonce_commitment = - NonceCommitments::::get((exodus_session, roast_session), authority_index) - .ok_or(Error::::ExodusNonceNotRegistered)?; + NonceCommitments::::get( + (exodus_context, roast_session), + authority_index + ).ok_or(Error::::ExodusNonceNotRegistered)?; let verifying_share = VerifyingShares::::get((network_curve, active_dkg_index, authority_index)) @@ -1694,7 +1699,7 @@ pub mod pallet { let verifying_key = ActiveVerifyingKey::::get(network_curve); let mut message_buffer = [0u8; MAX_MESSAGE_SIZE as usize]; - let message = exodus_request.get_message(&mut message_buffer) + let message = exodus_request.get_message(&mut message_buffer, exodus_context) .ok_or(Error::::ExodusMessageTooBig)?; let invalid_signature_share = network_curve.verify_signature_share( @@ -1716,7 +1721,7 @@ pub mod pallet { } let signature_scalar = - SignatureScalars::::get(exodus_session, roast_session); + SignatureScalars::::get(exodus_context, roast_session); let new_signature_scalar = network_curve .accumulate_signature_scalar( @@ -1734,33 +1739,36 @@ pub mod pallet { let exodus_signed_message = ExodusSignedMessage::new( commitment_consensus.element_bytes.iter().copied::(), bounded_signature_scalar.iter().copied::(), - exodus_request.r#type.clone(), + exodus_request.r#type, ).ok_or(Error::::TooManyEntries)?; Self::deposit_event(Event::::PartialSignatureRegistered { authority_index, - exodus_session, + exodus_context, network_curve, roast_session }); - RoastSessions::::remove(&exodus_session, authority_index); + RoastSessions::::remove(exodus_context, authority_index); if roast_state.partial_signers.count_ones::() < threshold { - RoastSessionStates::::insert(&exodus_session, &roast_session, roast_state); + RoastSessionStates::::insert( + exodus_context, &roast_session, + roast_state + ); SignatureScalars::::insert( - exodus_session, roast_session, - bounded_signature_scalar, + exodus_context, roast_session, + bounded_signature_scalar ); return Ok(()); } - RoastSessionStates::::remove(&exodus_session, &roast_session); - SignatureScalars::::remove(&exodus_session, &roast_session); + RoastSessionStates::::remove(exodus_context, &roast_session); + SignatureScalars::::remove(exodus_context, &roast_session); - GroupCommitmentsConsensus::::remove(&exodus_session, &roast_session); + GroupCommitmentsConsensus::::remove(exodus_context, &roast_session); GroupCommitters::::remove(( - &exodus_session, &roast_session, + exodus_context, &roast_session, &commitment_consensus.merkle_root, &commitment_consensus.element_bytes, )); @@ -1779,30 +1787,24 @@ pub mod pallet { return Ok(()); } - ExodusRequests::::remove((&network_curve, &exodus_session)); + ExodusRequests::::remove((&network_curve, exodus_context)); match exodus_request.r#type { - ExodusRequestType::EvmRotation { .. } => { + ExodusRequestType::EvmRotation { .. } | ExodusRequestType::UtxoRotation => { ExodusSignedRotations::::insert( - (exodus_session, NetworkType::Evm), active_dkg_index, + active_dkg_index, exodus_context, exodus_signed_message, ); }, - ExodusRequestType::UtxoRotation => { - ExodusSignedRotations::::insert( - (exodus_session, NetworkType::Utxo), active_dkg_index, - exodus_signed_message, - ); - } - ExodusRequestType::EvmBridgeOut { network_id, .. } | ExodusRequestType::UtxoBridgeOut { network_id, .. } => { + ExodusRequestType::EvmBridgeOut { .. } | ExodusRequestType::UtxoBridgeOut { .. } => { ExodusSignedBridges::::insert( - (exodus_session, network_id), active_dkg_index, + active_dkg_index, exodus_context, exodus_signed_message, ); } - ExodusRequestType::EvmGovernance { network_id, .. } => { + ExodusRequestType::EvmGovernance { .. } => { ExodusSignedGovernance::::insert( - (exodus_session, network_id), active_dkg_index, + active_dkg_index, exodus_context, exodus_signed_message, ); } @@ -1880,6 +1882,12 @@ pub mod pallet { let mut dkg_qualification = QualificationDkgState::::get(&network_curve); if !dkg_qualification.is_dkg_finalized() { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + let maybe_cursor = DkgMaybeCursor::::get(&network_curve); + let release_block = dkg_qualification.get_release_block(); + + if maybe_cursor.is_none() && current_block < release_block { return weight; } + let qualification_len = match QualificationAuthorities::::decode_len(&network_curve) { Some(total_authorities) => total_authorities as AuthIndex, @@ -1888,18 +1896,6 @@ pub mod pallet { let min_threshold = get_byzantium_quorum_threshold(qualification_len); - weight.saturating_accrue(T::DbWeight::get().reads(1)); - let maybe_cursor = DkgMaybeCursor::::get(&network_curve); - - let converted_round_period: BlockNumberFor = - T::DkgRoundPeriod::get().unique_saturated_into(); - - let release_block = dkg_qualification - .get_block() - .saturating_add(converted_round_period); - - if maybe_cursor.is_none() && current_block < release_block { return weight; } - let removal_result = match dkg_qualification.get_phase() { DkgPhase::Vacant => DkgRemovalResult::new(|| Self::prepare_round0_storage(network_curve, &mut dkg_qualification, maybe_cursor)), DkgPhase::Round0 => DkgRemovalResult::new(|| Self::prepare_round1_storage(network_curve, &mut dkg_qualification, maybe_cursor)), @@ -1917,14 +1913,23 @@ pub mod pallet { if !removal_result.fully_cleared() { return weight; } let threshold_reached = min_threshold <= dkg_qualification.count_ones::(); + let is_dkg_phase_vacant = dkg_qualification.is_dkg_vacant(); - if threshold_reached || dkg_qualification.is_dkg_vacant() { + if threshold_reached || is_dkg_phase_vacant { dkg_qualification.next_phase(); } else { dkg_qualification.restart_dkg(); } - dkg_qualification.set_block(current_block); + let blocks_delay = if threshold_reached && !is_dkg_phase_vacant { + T::DkgRoundPeriod::get().unique_saturated_into() + } else { + BLOCKS_FOR_DKG_RESET.unique_saturated_into() + }; + + let next_release_block = release_block.saturating_add(blocks_delay); + + dkg_qualification.set_release_block(next_release_block); QualificationDkgState::::insert(&network_curve, dkg_qualification); weight.saturating_accrue(T::DbWeight::get().writes(1)) @@ -1944,7 +1949,7 @@ pub mod pallet { Ok(inner_results) => { for result in inner_results { match result { - Ok(action) => log::warn!(target: PALLET_LOG_TARGET, "🗺️ Exodus action '{}' at block #{:?} finished.", action, current_block), + Ok(action) => log::warn!(target: PALLET_LOG_TARGET, "🗺️ Exodus action '{:?}' at block #{:?} finished.", action, current_block), Err(error) => log::warn!(target: PALLET_LOG_TARGET, "🗺️ Exodus failed at block #{:?}: {:?}.", current_block, error), } } @@ -1963,108 +1968,92 @@ pub mod pallet { fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity { match call { Call::register_round0_package { dkg_package, signature } => { - let network_curve = dkg_package.get_network_curve(); - let authority_index = dkg_package.get_authority_index(); - let dkg_index = dkg_package.get_dkg_index(); + let package_metadata = dkg_package.get_metadata(); Self::validate_round0_package_size(dkg_package, signature)?; ValidTransaction::with_tag_prefix("GhostExodus") .priority(T::UnsignedPriority::get()) .longevity(T::UnsignedLongevity::get()) - .and_provides((network_curve, authority_index, dkg_index)) + .and_provides(package_metadata) .propagate(true) .build() }, Call::register_round1_package { dkg_package, signature } => { - let network_curve = dkg_package.get_network_curve(); - let authority_index = dkg_package.get_authority_index(); - let dkg_index = dkg_package.get_dkg_index(); + let package_metadata = dkg_package.get_metadata(); Self::validate_round1_package_size(dkg_package, signature)?; ValidTransaction::with_tag_prefix("GhostExodus") .priority(T::UnsignedPriority::get()) .longevity(T::UnsignedLongevity::get()) - .and_provides((network_curve, authority_index, dkg_index)) + .and_provides(package_metadata) .propagate(true) .build() }, Call::register_encrypted_round2_packages { dkg_package, signature } => { - let network_curve = dkg_package.get_network_curve(); - let authority_index = dkg_package.get_authority_index(); - let dkg_index = dkg_package.get_dkg_index(); + let package_metadata = dkg_package.get_metadata(); Self::validate_round2_package_size(dkg_package, signature)?; ValidTransaction::with_tag_prefix("GhostExodus") .priority(T::UnsignedPriority::get()) .longevity(T::UnsignedLongevity::get()) - .and_provides((network_curve, authority_index, dkg_index)) + .and_provides(package_metadata) .propagate(true) .build() }, Call::register_round3_complaints { dkg_package, signature } => { - let network_curve = dkg_package.get_network_curve(); - let authority_index = dkg_package.get_authority_index(); - let dkg_index = dkg_package.get_dkg_index(); + let package_metadata = dkg_package.get_metadata(); Self::validate_round3_complaints_size(dkg_package, signature)?; ValidTransaction::with_tag_prefix("GhostExodus") .priority(T::UnsignedPriority::get()) .longevity(T::UnsignedLongevity::get()) - .and_provides((network_curve, authority_index, dkg_index)) + .and_provides(package_metadata) .propagate(true) .build() }, Call::register_round4_justifications { dkg_package, signature } => { - let network_curve = dkg_package.get_network_curve(); - let authority_index = dkg_package.get_authority_index(); - let dkg_index = dkg_package.get_dkg_index(); + let package_metadata = dkg_package.get_metadata(); Self::validate_round4_justifications_size(dkg_package, signature)?; ValidTransaction::with_tag_prefix("GhostExodus") .priority(T::UnsignedPriority::get()) .longevity(T::UnsignedLongevity::get()) - .and_provides((network_curve, authority_index, dkg_index)) + .and_provides(package_metadata) .propagate(true) .build() }, Call::register_round5_verifying_package { dkg_package, signature } => { - let network_curve = dkg_package.get_network_curve(); - let authority_index = dkg_package.get_authority_index(); - let dkg_index = dkg_package.get_dkg_index(); + let package_metadata = dkg_package.get_metadata(); Self::validate_round5_package_size(dkg_package, signature)?; ValidTransaction::with_tag_prefix("GhostExodus") .priority(T::UnsignedPriority::get()) .longevity(T::UnsignedLongevity::get()) - .and_provides((network_curve, authority_index, dkg_index)) + .and_provides(package_metadata) .propagate(true) .build() }, Call::register_round6_public_share_package { dkg_package, signature } => { - let network_curve = dkg_package.get_network_curve(); - let authority_index = dkg_package.get_authority_index(); - let dkg_index = dkg_package.get_dkg_index(); + let package_metadata = dkg_package.get_metadata(); Self::validate_round6_package_size(dkg_package, signature)?; ValidTransaction::with_tag_prefix("GhostExodus") .priority(T::UnsignedPriority::get()) .longevity(T::UnsignedLongevity::get()) - .and_provides((network_curve, authority_index, dkg_index)) + .and_provides(package_metadata) .propagate(true) .build() }, Call::register_nonce_commitment { exodus_package, signature } => { - let network_curve = exodus_package.get_network_curve(); - let authority_index = exodus_package.get_authority_index(); - let exodus_session = exodus_package.get_exodus_session(); - let dkg_index = exodus_package.get_dkg_index(); + let package_metadata = exodus_package.get_metadata(); + let exodus_context = exodus_package.get_exodus_context(); let (_, roast_session) = Self::validate_exodus_nonce_commitment_size_and_get_metadata( @@ -2072,26 +2061,16 @@ pub mod pallet { signature, )?; - let nonce_metadata = ( - network_curve, - authority_index, - dkg_index, - exodus_session, - roast_session, - ); - ValidTransaction::with_tag_prefix("GhostExodus") .priority(T::UnsignedPriority::get()) .longevity(T::UnsignedLongevity::get()) - .and_provides(nonce_metadata) + .and_provides((package_metadata, exodus_context, roast_session)) .propagate(true) .build() }, Call::register_group_commitment { exodus_package, signature } => { - let network_curve = exodus_package.get_network_curve(); - let authority_index = exodus_package.get_authority_index(); - let exodus_session = exodus_package.get_exodus_session(); - let dkg_index = exodus_package.get_dkg_index(); + let package_metadata = exodus_package.get_metadata(); + let exodus_context = exodus_package.get_exodus_context(); let roast_session = Self::validate_exodus_group_commitment_size_and_get_metadata( @@ -2099,27 +2078,17 @@ pub mod pallet { signature, )?; - let group_metadata = ( - network_curve, - authority_index, - dkg_index, - exodus_session, - roast_session, - ); - ValidTransaction::with_tag_prefix("GhostExodus") .priority(T::UnsignedPriority::get()) .longevity(T::UnsignedLongevity::get()) - .and_provides(group_metadata) + .and_provides((package_metadata, exodus_context, roast_session)) .propagate(true) .build() } Call::register_signature_share { exodus_package, signature } => { - let network_curve = exodus_package.get_network_curve(); - let authority_index = exodus_package.get_authority_index(); - let exodus_session = exodus_package.get_exodus_session(); - let dkg_index = exodus_package.get_dkg_index(); + let package_metadata = exodus_package.get_metadata(); + let exodus_context = exodus_package.get_exodus_context(); let (roast_session, _) = Self::validate_exodus_signature_share_size_and_get_metadata( @@ -2127,18 +2096,10 @@ pub mod pallet { signature, )?; - let signature_share_metadata = ( - network_curve, - authority_index, - dkg_index, - exodus_session, - roast_session, - ); - ValidTransaction::with_tag_prefix("GhostExodus") .priority(T::UnsignedPriority::get()) .longevity(T::UnsignedLongevity::get()) - .and_provides(signature_share_metadata) + .and_provides((package_metadata, exodus_context, roast_session)) .propagate(true) .build() } @@ -2149,13 +2110,31 @@ pub mod pallet { } impl Pallet { - fn do_register_evm_exodus( + fn get_and_increment_exodus_session( network_id: NetworkIdOf, - request_builder: F, - ) -> DispatchResult - where - F: FnOnce(ExodusSession) -> ExodusRequest, BalanceOf>, - { + ) -> Result { + CurrentExodusSessions::::try_mutate(|all_exodus_sessions| { + let current_session = match all_exodus_sessions.get_mut(&network_id) { + Some(session_ref) => { + let current = *session_ref; + *session_ref = current.saturating_add(1); + current + }, + None => { + all_exodus_sessions.try_insert(network_id, 1) + .map_err(|_| Error::::TooManyEntries)?; + 0 + } + }; + + Ok(current_session) + }) + } + + fn do_register_evm_exodus( + network_id: NetworkIdOf, + exodus_request: ExodusRequest>, + ) -> DispatchResult { let network = T::NetworkDataHandler::get(&network_id) .ok_or(Error::::NetworkDoesNotExist)?; @@ -2166,11 +2145,10 @@ impl Pallet { ensure!(network.r#type.is_evm(), Error::::WrongNetworkType); - let exodus_session = CurrentExodus::::get(); - let request = request_builder(exodus_session); + let exodus_session = Self::get_and_increment_exodus_session(network_id)?; + let exodus_context = ExodusContext::new(network_id, exodus_session); - ExodusRequests::::insert((&network.curve, &exodus_session), request); - CurrentExodus::::put(exodus_session.saturating_add(1)); + ExodusRequests::::insert((&network.curve, &exodus_context), exodus_request); Ok(()) } @@ -2185,15 +2163,9 @@ impl Pallet { T::NetworkDataHandler::register_outgoing(&network_id, amount) .map_err(|_| Error::::BridgeOverflow)?; - Self::do_register_evm_exodus(network_id, |exodus_session| { - ExodusRequest::evm_bridge_out( - exodus_session, - network_id, - out_amount, - bounty, - receiver, - ) - }) + let evm_bridge_request = ExodusRequest::evm_bridge_out(out_amount, bounty, receiver); + + Self::do_register_evm_exodus(network_id, evm_bridge_request) } fn do_register_evm_governance( @@ -2201,14 +2173,10 @@ impl Pallet { target_address: EvmAddress, governance_action: GovernanceAction, ) -> DispatchResult { - Self::do_register_evm_exodus(network_id, |exodus_session| { - ExodusRequest::evm_governance( - exodus_session, - network_id, - target_address, - governance_action, - ) - }) + let evm_governance_request = + ExodusRequest::evm_governance(target_address, governance_action); + + Self::do_register_evm_exodus(network_id, evm_governance_request) } fn prepare_round_storage( @@ -2230,13 +2198,31 @@ impl Pallet { Some(cursor) => { match BoundedVec::::try_from(cursor) { Ok(cursor) => { - DkgMaybeCursor::::insert(&network_curve, cursor)}, - Err(_) => DkgMaybeCursor::::remove(&network_curve), // never should happen + DkgMaybeCursor::::insert(&network_curve, cursor) + }, + Err(_) => { + // NOTE: should never happen. Because we are using + // overwrites but not insertions. Set `T::MaxCursorLen` + // based on the longest key - currently the key for + // `GroupCommitters` up to 160 bytes + // + // context = u128 + u128 + prefix = 16 + 16 + 8 = 40 + // roast_session = u128 + prefix = 16 + 8 = 24 + // exodus_hash = 32 unless H256 is changed + // group_key = 32 in any case because of Blake2_256 + // + // Twox128("pallet") + Twox128("function") = 16 + 16 + // result = 40 + 24 + 32 + 32 + 32 = 160 bytes + DkgMaybeCursor::::remove(&network_curve) + } } weight.saturating_accrue(T::DbWeight::get().writes(1)); false }, None => { + weight.saturating_accrue(T::DbWeight::get().writes(1)); + DkgMaybeCursor::::remove(&network_curve); + on_complete_fn(&mut weight); true } @@ -2247,7 +2233,7 @@ impl Pallet { fn prepare_round0_storage( network_curve: NetworkCurve, - dkg_qualification: &mut QualifyingState, + dkg_qualification: &mut PendingDkgAuthorities, maybe_cursor: Option>, ) -> (Weight, bool) { Self::prepare_round_storage( @@ -2276,7 +2262,7 @@ impl Pallet { fn prepare_round1_storage( network_curve: NetworkCurve, - dkg_qualification: &mut QualifyingState, + dkg_qualification: &mut PendingDkgAuthorities, maybe_cursor: Option>, ) -> (Weight, bool) { Self::prepare_round_storage( @@ -2296,7 +2282,7 @@ impl Pallet { fn prepare_round2_storage( network_curve: NetworkCurve, - dkg_qualification: &mut QualifyingState, + dkg_qualification: &mut PendingDkgAuthorities, maybe_cursor: Option>, ) -> (Weight, bool) { let participants_len = dkg_qualification.get_indexes().active_bits_count(); @@ -2319,7 +2305,7 @@ impl Pallet { pub fn prepare_round3_storage( network_curve: NetworkCurve, - dkg_qualification: &mut QualifyingState, + dkg_qualification: &mut PendingDkgAuthorities, maybe_cursor: Option>, ) -> (Weight, bool) { Self::prepare_round_storage( @@ -2341,7 +2327,7 @@ impl Pallet { pub fn prepare_round4_storage( network_curve: NetworkCurve, min_threshold: AuthIndex, - dkg_qualification: &mut QualifyingState, + dkg_qualification: &mut PendingDkgAuthorities, maybe_cursor: Option>, ) -> (Weight, bool) { let dkg_index = dkg_qualification.get_dkg_index(); @@ -2416,7 +2402,7 @@ impl Pallet { pub fn prepare_round5_storage( network_curve: NetworkCurve, min_threshold: AuthIndex, - dkg_qualification: &mut QualifyingState, + dkg_qualification: &mut PendingDkgAuthorities, maybe_cursor: Option>, ) -> (Weight, bool) { let dkg_index = dkg_qualification.get_dkg_index(); @@ -2499,7 +2485,7 @@ impl Pallet { fn prepare_round6_storage( network_curve: NetworkCurve, min_threshold: AuthIndex, - dkg_qualification: &mut QualifyingState, + dkg_qualification: &mut PendingDkgAuthorities, maybe_cursor: Option>, ) -> (Weight, bool) { let dkg_index = dkg_qualification.get_dkg_index(); @@ -2510,19 +2496,50 @@ impl Pallet { Self::prepare_round_storage( network_curve, maybe_cursor, - |_, weight| { - let Some(prev_dkg_index) = dkg_index.checked_sub(1) else { - return None; - }; - + |cursor_slice, weight| { weight.saturating_accrue(T::DbWeight::get().writes(1)); - VerifyingSharesParticipants::::insert( - &network_curve, - &prev_dkg_index, - default_participants, + let active_dkg_index = ActiveDkgAuthorities::::get(&network_curve) + .get_dkg_index(); + + let previous_dkg_index = dkg_index.saturating_sub(1); + + if previous_dkg_index == active_dkg_index { + weight.saturating_accrue(T::DbWeight::get().writes(1)); + VerifyingSharesParticipants::::insert( + &network_curve, + &previous_dkg_index, + default_participants, + ); + + return None; + } + + + let result = VerifyingShares::::clear_prefix( + (network_curve, previous_dkg_index), + T::RemovalLimit::get(), + cursor_slice, ); - None + weight.saturating_accrue( + T::DbWeight::get().reads_writes( + (result.backend as u64).saturating_add(1), + result.backend as u64 + ) + ); + + let maybe_cursor = result.maybe_cursor; + + if maybe_cursor.is_none() { + weight.saturating_accrue(T::DbWeight::get().writes(1)); + VerifyingSharesParticipants::::insert( + &network_curve, + &previous_dkg_index, + default_participants, + ); + } + + maybe_cursor }, |weight| { weight.saturating_accrue(T::DbWeight::get().reads(1)); @@ -2540,7 +2557,7 @@ impl Pallet { fn prepare_round7_storage( network_curve: NetworkCurve, min_threshold: AuthIndex, - dkg_qualification: &mut QualifyingState, + dkg_qualification: &mut PendingDkgAuthorities, maybe_cursor: Option>, ) -> (Weight, bool) { let dkg_index = dkg_qualification.get_dkg_index(); @@ -2572,9 +2589,33 @@ impl Pallet { } weight.saturating_accrue(T::DbWeight::get().reads(2)); - let mut exodus_session = CurrentExodus::::get(); + let mut all_exodus_sessions = CurrentExodusSessions::::get(); + + let max_networks = ::MaxNetworks::get(); + let mut exodus_requests = Vec::with_capacity(max_networks as usize); + let mut too_much_entries = false; + + for (network_id, network_type) in T::NetworkDataHandler::iter_types_by_curve(&network_curve) { + if !matches!(network_type, NetworkType::Evm) { + continue; + } + + let exodus_session = match all_exodus_sessions.get_mut(&network_id) { + Some(session_ref) => { + let current = *session_ref; + *session_ref = current.saturating_add(1); + current + }, + None => { + if let Err(_) = all_exodus_sessions.try_insert(network_id, 1) { + too_much_entries = true; + } + 0 + } + }; + + if too_much_entries { break; } - for network_type in T::NetworkDataHandler::iter_types_by_curve(&network_curve) { let exodus_request = match network_type { NetworkType::Evm => { let parity_byte = verifying_key_slice[0]; @@ -2582,34 +2623,46 @@ impl Pallet { let public_key = EvmBytes32::from_slice(&verifying_key_slice[1..33]); - ExodusRequest::evm_rotation(exodus_session, public_key, parity) + ExodusRequest::evm_rotation(public_key, parity) } _ => { - continue; + unreachable!(); } }; - weight.saturating_accrue(T::DbWeight::get().writes(1)); - ExodusRequests::::insert((&network_curve, &exodus_session), exodus_request); + let exodus_context = ExodusContext::new(network_id, exodus_session); if let None = dkg_qualification - .try_push_rotation_session(exodus_session, network_type) { + .try_push_rotation_context(exodus_context) { + too_much_entries = true; break; } - exodus_session = exodus_session.saturating_add(1); + let key = (network_curve, exodus_context); + exodus_requests.push((key, exodus_request)); } - CurrentExodus::::put(exodus_session); - weight.saturating_accrue(T::DbWeight::get().writes(1)); + if too_much_entries { + dkg_qualification.nullify_indexes(); + } else { + let requests_count = exodus_requests.len() as u64; + weight.saturating_accrue(T::DbWeight::get().writes(requests_count)); - dkg_qualification.set_verifying_key(consensus.state.element_bytes); + for exodus in exodus_requests { + ExodusRequests::::insert(exodus.0, exodus.1); + } + + weight.saturating_accrue(T::DbWeight::get().writes(1)); + CurrentExodusSessions::::put(all_exodus_sessions); + + dkg_qualification.set_verifying_key(consensus.state.element_bytes); + } }) } fn prepare_finalization_storage( network_curve: NetworkCurve, - dkg_qualification: &mut QualifyingState, + dkg_qualification: &mut PendingDkgAuthorities, maybe_cursor: Option>, ) -> (Weight, bool) { Self::prepare_round_storage( @@ -2625,18 +2678,15 @@ impl Pallet { !ActiveVerifyingKey::::contains_key(network_curve); let all_rotations_have_signatures = dkg_qualification - .iter_rotation_sessions() - .fold(true, |accumulator, rotation_session| { - let session = rotation_session.exodus_session; - let r#type = rotation_session.network_type; - + .iter_rotation_contexts() + .fold(true, |accumulator, exodus_context| { weight.saturating_accrue(T::DbWeight::get().writes(1)); - ExodusRequests::::remove((&network_curve, &session)); + ExodusRequests::::remove((&network_curve, &exodus_context)); weight.saturating_accrue(T::DbWeight::get().reads(1)); let has_signature = ExodusSignedRotations::::contains_key( - (session, r#type), dkg_index, + exodus_context, ); accumulator && has_signature @@ -2644,7 +2694,7 @@ impl Pallet { if all_rotations_have_signatures || is_initial_dkg_round { let verifying_key = dkg_qualification.get_verifying_key(); - let activated_state: ActivatedState = (&*dkg_qualification).into(); + let activated_state: ReadyDkgAuthorities = (&*dkg_qualification).into(); weight.saturating_accrue(T::DbWeight::get().reads(1)); let qualified_authorities = QualificationAuthorities::::get(&network_curve); @@ -2660,7 +2710,7 @@ impl Pallet { } fn validate_round0_package_size( - dkg_package: & DkgPackage, + dkg_package: & DkgPackage, T>, signature: &::Signature, ) -> Result<(), InvalidTransaction> { let network_curve = dkg_package.get_network_curve(); @@ -2685,9 +2735,19 @@ impl Pallet { return Err(InvalidTransaction::BadSigner); } - let authority = QualificationAuthorities::::get(&network_curve) - .get(authority_index as usize) - .cloned() + let current_block = T::BlockNumberProvider::current_block_number(); + let block_longevity: BlockNumberFor = + T::UnsignedLongevity::get().unique_saturated_into(); + + let expiration_block = dkg_package.get_block_number() + .saturating_add(block_longevity); + + if current_block >= expiration_block { + return Err(InvalidTransaction::Stale); + } + + let authorities = QualificationAuthorities::::get(&network_curve); + let authority = authorities.get(authority_index as usize) .ok_or(InvalidTransaction::BadSigner)?; let encoded_package = dkg_package.encode(); @@ -2699,7 +2759,7 @@ impl Pallet { } fn validate_round1_package_size( - dkg_package: &DkgPackage, + dkg_package: &DkgPackage, T>, signature: &::Signature, ) -> Result<(), InvalidTransaction> { let DkgPackage::Round1(round1) = dkg_package else { @@ -2742,6 +2802,17 @@ impl Pallet { return Err(InvalidTransaction::BadProof); } + let current_block = T::BlockNumberProvider::current_block_number(); + let block_longevity: BlockNumberFor = + T::UnsignedLongevity::get().unique_saturated_into(); + + let expiration_block = dkg_package.get_block_number() + .saturating_add(block_longevity); + + if current_block >= expiration_block { + return Err(InvalidTransaction::Stale); + } + let authority = authorities .get(authority_index as usize) .ok_or(InvalidTransaction::BadSigner)?; @@ -2755,7 +2826,7 @@ impl Pallet { } fn validate_round2_package_size( - dkg_package: &DkgPackage, + dkg_package: &DkgPackage, T>, signature: &::Signature, ) -> Result<(), InvalidTransaction> { let DkgPackage::Round2(round2) = dkg_package else { @@ -2825,9 +2896,19 @@ impl Pallet { return Err(InvalidTransaction::BadProof); } - let authority = QualificationAuthorities::::get(&network_curve) - .get(authority_index as usize) - .cloned() + let current_block = T::BlockNumberProvider::current_block_number(); + let block_longevity: BlockNumberFor = + T::UnsignedLongevity::get().unique_saturated_into(); + + let expiration_block = dkg_package.get_block_number() + .saturating_add(block_longevity); + + if current_block >= expiration_block { + return Err(InvalidTransaction::Stale); + } + + let authorities = QualificationAuthorities::::get(&network_curve); + let authority = authorities.get(authority_index as usize) .ok_or(InvalidTransaction::BadSigner)?; let encoded_package = dkg_package.encode(); @@ -2839,7 +2920,7 @@ impl Pallet { } fn validate_round3_complaints_size( - dkg_package: &DkgPackage, + dkg_package: &DkgPackage, T>, signature: &::Signature, ) -> Result<(), InvalidTransaction> { let DkgPackage::Round3(round3_complaints) = dkg_package else { @@ -2894,9 +2975,20 @@ impl Pallet { return Err(InvalidTransaction::BadProof); } - let authority = QualificationAuthorities::::get(&network_curve) + let current_block = T::BlockNumberProvider::current_block_number(); + let block_longevity: BlockNumberFor = + T::UnsignedLongevity::get().unique_saturated_into(); + + let expiration_block = dkg_package.get_block_number() + .saturating_add(block_longevity); + + if current_block >= expiration_block { + return Err(InvalidTransaction::Stale); + } + + let authorities = QualificationAuthorities::::get(&network_curve); + let authority = authorities .get(authority_index as usize) - .cloned() .ok_or(InvalidTransaction::BadSigner)?; let encoded_package = dkg_package.encode(); @@ -2908,7 +3000,7 @@ impl Pallet { } fn validate_round4_justifications_size( - dkg_package: &DkgPackage, + dkg_package: &DkgPackage, T>, signature: &::Signature, ) -> Result<(), InvalidTransaction> { let DkgPackage::Round4(round4) = dkg_package else { @@ -2978,9 +3070,19 @@ impl Pallet { return Err(InvalidTransaction::BadProof); } - let authority = QualificationAuthorities::::get(&network_curve) - .get(authority_index as usize) - .cloned() + let current_block = T::BlockNumberProvider::current_block_number(); + let block_longevity: BlockNumberFor = + T::UnsignedLongevity::get().unique_saturated_into(); + + let expiration_block = dkg_package.get_block_number() + .saturating_add(block_longevity); + + if current_block >= expiration_block { + return Err(InvalidTransaction::Stale); + } + + let authorities = QualificationAuthorities::::get(&network_curve); + let authority = authorities.get(authority_index as usize) .ok_or(InvalidTransaction::BadSigner)?; let encoded_package = dkg_package.encode(); @@ -2992,7 +3094,7 @@ impl Pallet { } fn validate_round5_package_size( - dkg_package: &DkgPackage, + dkg_package: &DkgPackage, T>, signature: &::Signature, ) -> Result<(), InvalidTransaction> { let DkgPackage::Round5(round5) = dkg_package else { @@ -3045,9 +3147,19 @@ impl Pallet { return Err(InvalidTransaction::BadProof); } - let authority = QualificationAuthorities::::get(&network_curve) - .get(authority_index as usize) - .cloned() + let current_block = T::BlockNumberProvider::current_block_number(); + let block_longevity: BlockNumberFor = + T::UnsignedLongevity::get().unique_saturated_into(); + + let expiration_block = dkg_package.get_block_number() + .saturating_add(block_longevity); + + if current_block >= expiration_block { + return Err(InvalidTransaction::Stale); + } + + let authorities = QualificationAuthorities::::get(&network_curve); + let authority = authorities.get(authority_index as usize) .ok_or(InvalidTransaction::BadSigner)?; let encoded_package = dkg_package.encode(); @@ -3059,7 +3171,7 @@ impl Pallet { } fn validate_round6_package_size( - dkg_package: &DkgPackage, + dkg_package: &DkgPackage, T>, signature: &::Signature, ) -> Result<(), InvalidTransaction> { let network_curve = dkg_package.get_network_curve(); @@ -3093,9 +3205,19 @@ impl Pallet { return Err(InvalidTransaction::BadProof); } - let authority = QualificationAuthorities::::get(&network_curve) - .get(authority_index as usize) - .cloned() + let current_block = T::BlockNumberProvider::current_block_number(); + let block_longevity: BlockNumberFor = + T::UnsignedLongevity::get().unique_saturated_into(); + + let expiration_block = dkg_package.get_block_number() + .saturating_add(block_longevity); + + if current_block >= expiration_block { + return Err(InvalidTransaction::Stale); + } + + let authorities = QualificationAuthorities::::get(&network_curve); + let authority = authorities.get(authority_index as usize) .ok_or(InvalidTransaction::BadSigner)?; let encoded_package = dkg_package.encode(); @@ -3107,9 +3229,9 @@ impl Pallet { } fn validate_exodus_nonce_commitment_size_and_get_metadata<'a>( - exodus_package: &'a ExodusPackage, + exodus_package: &'a ExodusPackage, BlockNumberFor, T>, signature: &::Signature, - ) -> Result<(ExodusRequest, BalanceOf>, RoastSession), InvalidTransaction> { + ) -> Result<(ExodusRequest>, RoastSession), InvalidTransaction> { let ExodusPackage::NonceCommitment(package) = exodus_package else { return Err(InvalidTransaction::BadProof); }; @@ -3117,6 +3239,7 @@ impl Pallet { let network_curve = exodus_package.get_network_curve(); let authority_index = exodus_package.get_authority_index(); let exodus_dkg_index = exodus_package.get_dkg_index(); + let exodus_context = exodus_package.get_exodus_context(); if package.hiding_commitment.len() != network_curve.element_bytes_len() { return Err(InvalidTransaction::BadProof); @@ -3150,9 +3273,19 @@ impl Pallet { return Err(InvalidTransaction::BadProof); } - let authority = ActiveAuthorities::::get(&network_curve) - .get(authority_index as usize) - .cloned() + let current_block = T::BlockNumberProvider::current_block_number(); + let block_longevity: BlockNumberFor = + T::UnsignedLongevity::get().unique_saturated_into(); + + let expiration_block = exodus_package.get_block_number() + .saturating_add(block_longevity); + + if current_block >= expiration_block { + return Err(InvalidTransaction::Stale); + } + + let authorities = ActiveAuthorities::::get(&network_curve); + let authority = authorities.get(authority_index as usize) .ok_or(InvalidTransaction::BadSigner)?; let encoded_package = exodus_package.encode(); @@ -3161,11 +3294,11 @@ impl Pallet { } let exodus_request = - ExodusRequests::::get((&network_curve, &package.session)) + ExodusRequests::::get((&network_curve, exodus_context)) .ok_or(InvalidTransaction::BadProof)?; let roast_session = - match RoastSessions::::get(&package.session, &authority_index) { + match RoastSessions::::get(exodus_context, &authority_index) { Some(_) => return Err(InvalidTransaction::BadProof), None => exodus_request.next_roast_session, }; @@ -3174,7 +3307,7 @@ impl Pallet { } fn validate_exodus_group_commitment_size_and_get_metadata<'a>( - exodus_package: &'a ExodusPackage, + exodus_package: &'a ExodusPackage, BlockNumberFor, T>, signature: &::Signature, ) -> Result { let ExodusPackage::GroupCommitment(package) = exodus_package else { @@ -3184,6 +3317,7 @@ impl Pallet { let network_curve = exodus_package.get_network_curve(); let authority_index = exodus_package.get_authority_index(); let exodus_dkg_index = exodus_package.get_dkg_index(); + let exodus_context = exodus_package.get_exodus_context(); if !T::NetworkDataHandler::curve_exists(&network_curve) { return Err(InvalidTransaction::BadProof); @@ -3207,9 +3341,19 @@ impl Pallet { return Err(InvalidTransaction::BadProof); } - let authority = ActiveAuthorities::::get(&network_curve) - .get(authority_index as usize) - .cloned() + let current_block = T::BlockNumberProvider::current_block_number(); + let block_longevity: BlockNumberFor = + T::UnsignedLongevity::get().unique_saturated_into(); + + let expiration_block = exodus_package.get_block_number() + .saturating_add(block_longevity); + + if current_block >= expiration_block { + return Err(InvalidTransaction::Stale); + } + + let authorities = ActiveAuthorities::::get(&network_curve); + let authority = authorities.get(authority_index as usize) .ok_or(InvalidTransaction::BadSigner)?; let encoded_package = exodus_package.encode(); @@ -3217,12 +3361,12 @@ impl Pallet { return Err(InvalidTransaction::BadProof); } - RoastSessions::::get(&package.session, &authority_index) + RoastSessions::::get(exodus_context, &authority_index) .ok_or(InvalidTransaction::BadProof) } fn validate_exodus_signature_share_size_and_get_metadata<'a>( - exodus_package: &'a ExodusPackage, + exodus_package: &'a ExodusPackage, BlockNumberFor, T>, signature: &::Signature, ) -> Result<(RoastSession, DkgIndex), InvalidTransaction> { let ExodusPackage::SignatureShare(package) = exodus_package else { @@ -3232,6 +3376,7 @@ impl Pallet { let network_curve = exodus_package.get_network_curve(); let authority_index = exodus_package.get_authority_index(); let exodus_dkg_index = exodus_package.get_dkg_index(); + let exodus_context = exodus_package.get_exodus_context(); if package.signature_share.len() != network_curve.scalar_bytes_len() { return Err(InvalidTransaction::BadProof); @@ -3260,9 +3405,19 @@ impl Pallet { return Err(InvalidTransaction::BadProof); } - let authority = ActiveAuthorities::::get(&network_curve) - .get(authority_index as usize) - .cloned() + let current_block = T::BlockNumberProvider::current_block_number(); + let block_longevity: BlockNumberFor = + T::UnsignedLongevity::get().unique_saturated_into(); + + let expiration_block = exodus_package.get_block_number() + .saturating_add(block_longevity); + + if current_block >= expiration_block { + return Err(InvalidTransaction::Stale); + } + + let authorities = ActiveAuthorities::::get(&network_curve); + let authority = authorities.get(authority_index as usize) .ok_or(InvalidTransaction::BadSigner)?; let encoded_package = exodus_package.encode(); @@ -3271,7 +3426,7 @@ impl Pallet { } let roast_session = - RoastSessions::::get(&package.session, &authority_index) + RoastSessions::::get(exodus_context, &authority_index) .ok_or(InvalidTransaction::BadProof)?; Ok((roast_session, active_state.get_dkg_index())) @@ -3279,7 +3434,7 @@ impl Pallet { fn start_exodus( current_block: BlockNumberFor, - ) -> ExodusResult>>> { + ) -> ExodusResult>>>> { let converted_block: usize = current_block.unique_saturated_into(); let network_curve = T::NetworkDataHandler::network_for_block(converted_block) .map(|(_, network)| network.curve) @@ -3297,7 +3452,7 @@ impl Pallet { fn exodus_start( network_curve: NetworkCurve, current_block: BlockNumberFor, - ) -> ExodusResult>>> { + ) -> ExodusResult>>>> { let network_encoded = network_curve.encode(); let exodus_lock_key = Self::create_storage_key(b"exodus-lock-", &network_encoded); let lock_until = rt_offchain::Duration::from_millis(MIN_LOCK_GUARD_PERIOD); @@ -3330,8 +3485,8 @@ impl Pallet { authority_key: T::AuthorityId, network_curve: NetworkCurve, current_block: BlockNumberFor, - active_authorities: &ActivatedState - ) -> ExodusResult> { + active_authorities: &ReadyDkgAuthorities + ) -> ExodusResult>> { if !active_authorities.contains_index(authority_index) { return Err(ExodusError::InvalidParticipantId) } @@ -3369,173 +3524,201 @@ impl Pallet { let threshold = get_byzantium_quorum_threshold(max_authorities); - let mut exodus_requests = ExodusRequests::::iter_prefix((&network_curve,)) - .collect::)>>(); + let mut exodus_requests_by_network = BoundedBTreeMap::< + NetworkIdOf, + BoundedBTreeMap< + ExodusSession, + ExodusRequest>, + T::MaxExodusRequestCount + >, + ::MaxNetworks, + >::new(); - exodus_requests.sort_by_key(|&(session, _)| session); - - let is_requests_empty = exodus_requests.len() == 0; - - let mut share_candidate = None; - let mut group_candidate = None; - let mut nonce_candidate = None; - - for (session, request) in exodus_requests.iter() { - if exodus_records.is_exodus_session_banned(session) { - continue; - } - - if let Some(roast_session) = RoastSessions::::get(session, &authority_index) { - let state = RoastSessionStates::::get(session, roast_session); - let participants = GroupCommitmentsConsensus::::get(&session, roast_session).participants; - let commitment_consensus_reached = participants.count_ones::() >= threshold.div_ceil(2); - - if state.status == RoastStatus::PartialSignatures - && state.nonce_committers.contains(authority_index) - && !state.partial_signers.contains(authority_index) - && participants.contains(authority_index) - && commitment_consensus_reached - && !exodus_records.is_waiting_period_for_share(session, ¤t_block) - { - share_candidate = Some((*session, request, state.status, roast_session)); - break; - } - - if group_candidate.is_none() - && ( - (state.status == RoastStatus::GroupCommitments - || state.status == RoastStatus::PartialSignatures) - ) - && state.nonce_committers.contains(authority_index) - && !state.partial_signers.contains(authority_index) - && !participants.contains(authority_index) - && !exodus_records.is_waiting_period_for_group(session, ¤t_block) - { - group_candidate = Some((*session, request, RoastStatus::GroupCommitments, roast_session)); - break; - } - } else { - let active_roast_session = request.next_roast_session; - let state = RoastSessionStates::::get(session, active_roast_session); - - if nonce_candidate.is_none() - && state.status == RoastStatus::NonceCommitments - && !state.nonce_committers.contains(authority_index) - && !state.partial_signers.contains(authority_index) - && !state.group_committers.contains(authority_index) - && !exodus_records.is_waiting_period_for_nonce(session, ¤t_block) - { - nonce_candidate = Some((*session, request, state.status, active_roast_session)); - break; + for (context, request) in ExodusRequests::::iter_prefix((&network_curve,)) { + match exodus_requests_by_network.get_mut(&context.network_id) { + Some(session_set) => { + if let Err(_) = session_set.try_insert(context.session, request) { + log::warn!( + target: PALLET_LOG_TARGET, + "🗺️ Max exodus sessions {} reached limit for network #{:?}", + T::MaxExodusRequestCount::get(), + context.network_id + ); + } + }, + None => { + let mut new_session_set = BoundedBTreeMap::new(); + if let Ok(_) = new_session_set.try_insert(context.session, request) { + if let Err(_) = exodus_requests_by_network.try_insert(context.network_id, new_session_set) { + log::warn!( + target: PALLET_LOG_TARGET, + "🗺️ Max unique networks limit {} exceeds for network #{:?}", + ::MaxNetworks::get(), + context.network_id + ); + } + } else { + log::warn!( + target: PALLET_LOG_TARGET, + "🗺️ Max exodus sessions {} reached limit for network #{:?}", + T::MaxExodusRequestCount::get(), + context.network_id + ); + } } } } - let target_session = share_candidate - .or(group_candidate) - .or(nonce_candidate); + let mut success_count = 0; + let mut failed_count = 0; - if let Some((session, request, roast_status, roast_session)) = target_session { - match roast_status { - RoastStatus::NonceCommitments => { - Self::exodus_run_nonce_commitment( - &exodus_storage, - &mut exodus_records, - secret_share, - &authority_key, - session, - dkg_index - )?; + for (network_id, exodus_metadata) in exodus_requests_by_network { + let base_context = ExodusContext::>::default() + .with_network_id(network_id); - Ok(ExodusOk::ExodusNonceCommitted( - session, - roast_session, - authority_index, - network_curve, - )) - }, - RoastStatus::GroupCommitments => { - let mut message_buffer = [0u8; MAX_MESSAGE_SIZE as usize]; - if let Some(message) = request.get_message(&mut message_buffer) { - let group_commitment_result = + let is_requests_empty = exodus_metadata.is_empty(); + let mut best_candidate = None; + + for (exodus_session, request) in exodus_metadata { + let context = base_context.with_exodus_session(exodus_session); + + if exodus_records.is_exodus_session_banned(&context) { + continue; + } + + if let Some(roast_session) = RoastSessions::::get(context, &authority_index) { + let state = RoastSessionStates::::get(context, roast_session); + let participants = GroupCommitmentsConsensus::::get(context, roast_session).participants; + let commitment_consensus_reached = participants.count_ones::() >= threshold.div_ceil(2); + + if state.status == RoastStatus::PartialSignatures + && state.nonce_committers.contains(authority_index) + && !state.partial_signers.contains(authority_index) + && participants.contains(authority_index) + && commitment_consensus_reached + && !exodus_records.is_waiting_period_for_share(&context, ¤t_block) + { + best_candidate = Some((2, context, request, state.status, roast_session)); + break; + } + + if (state.status == RoastStatus::GroupCommitments || + state.status == RoastStatus::PartialSignatures) + && best_candidate.as_ref().map(|data| data.0 < 1).unwrap_or(true) + && state.nonce_committers.contains(authority_index) + && !state.partial_signers.contains(authority_index) + && !participants.contains(authority_index) + && !exodus_records.is_waiting_period_for_group(&context, ¤t_block) + { + best_candidate = Some((1, context, request, RoastStatus::GroupCommitments, roast_session)); + } + } else { + let active_roast_session = request.next_roast_session; + let state = RoastSessionStates::::get(&context, active_roast_session); + + if best_candidate.is_none() + && state.status == RoastStatus::NonceCommitments + && !state.nonce_committers.contains(authority_index) + && !state.partial_signers.contains(authority_index) + && !state.group_committers.contains(authority_index) + && !exodus_records.is_waiting_period_for_nonce(&context, ¤t_block) + { + best_candidate = Some((0, context, request, state.status, active_roast_session)); + } + } + } + + let exodus_result = if let Some(metadata) = best_candidate { + let (_, context, request, roast_status, roast_session) = metadata; + + match roast_status { + RoastStatus::NonceCommitments => { + Self::exodus_run_nonce_commitment( + &exodus_storage, + &mut exodus_records, + secret_share, + &authority_key, + context, + roast_session, + dkg_index + ) + }, + RoastStatus::GroupCommitments => { + let mut message_buffer = [0u8; MAX_MESSAGE_SIZE as usize]; + if let Some(message) = request.get_message(&mut message_buffer, context) { Self::exodus_run_group_commitment( &exodus_storage, &mut exodus_records, &message, &authority_key, - session, + context, roast_session, dkg_index, - ); - - group_commitment_result - .and_then(|_| { - Ok(ExodusOk::ExodusGroupCommitted( - session, - roast_session, - authority_index, - network_curve, - )) - }) - .or_else(|err| { - exodus_records.exclude_exodus_session(session); - exodus_storage.try_write_local_storage(&exodus_records); - Err(err) - }) - } else { - Err(ExodusError::CouldNotConstructMessage(authority_index)) - } - }, - RoastStatus::PartialSignatures => { - let mut message_buffer = [0u8; MAX_MESSAGE_SIZE as usize]; - if let Some(message) = request.get_message(&mut message_buffer) { - let signature_share_result = + ) + } else { + Err(ExodusError::CouldNotConstructMessage(authority_index)) + } + }, + RoastStatus::PartialSignatures => { + let mut message_buffer = [0u8; MAX_MESSAGE_SIZE as usize]; + if let Some(message) = request.get_message(&mut message_buffer, context) { Self::exodus_run_signature_share( &exodus_storage, &mut exodus_records, &message, secret_share, &authority_key, - session, + context, roast_session, dkg_index - ); - - signature_share_result - .and_then(|_| { - Ok(ExodusOk::ExodusSignedPartially( - session, - roast_session, - authority_index, - network_curve, - )) - }) - .or_else(|err| { - exodus_records.exclude_exodus_session(session); - exodus_storage.try_write_local_storage(&exodus_records); - Err(err) - }) - } else { - Err(ExodusError::CouldNotConstructMessage(authority_index)) + ) + } else { + Err(ExodusError::CouldNotConstructMessage(authority_index)) + } } - }, - } - - } else { - if is_requests_empty { - exodus_storage.force_clear_local_storage(); - Ok(ExodusOk::ExodusEmptyRequests(authority_index, network_curve)) + } } else { - Ok(ExodusOk::ExodusWaitingState(authority_index, network_curve)) + if is_requests_empty { + exodus_storage.force_clear_local_storage(); + Ok(ExodusOk::ExodusEmptyRequests(authority_index, network_curve)) + } else { + Ok(ExodusOk::ExodusWaitingState(authority_index, network_curve)) + } + }; + + match exodus_result { + Ok(ok_result) => { + success_count += 1; + log::trace!( + target: PALLET_LOG_TARGET, + "🗺️ EXODUS processing succeeded: #{:?}.", + ok_result, + ); + }, + Err(err_result) => { + failed_count += 1; + log::trace!( + target: PALLET_LOG_TARGET, + "🗺️ EXODUS processing failed: {:?}.", + err_result + ); + } } } + + Ok(ExodusOk::ExodusRequestsFinished( + authority_index, + network_curve, + success_count, + failed_count + )) } fn dkg_start( network_curve: NetworkCurve, current_block: BlockNumberFor, - dkg_qualification: QualifyingState, - ) -> ExodusResult>>> { + dkg_qualification: PendingDkgAuthorities, + ) -> ExodusResult>>>> { let network_encoded = network_curve.encode(); let network_lock_key = Self::create_storage_key(b"network-lock-", &network_encoded); let lock_until = rt_offchain::Duration::from_millis(MIN_LOCK_GUARD_PERIOD); @@ -3567,8 +3750,8 @@ impl Pallet { authority_key: T::AuthorityId, network_curve: NetworkCurve, current_block: BlockNumberFor, - qualification_authorities: &QualifyingState - ) -> ExodusResult> { + qualification_authorities: &PendingDkgAuthorities + ) -> ExodusResult>> { let block_longevity: BlockNumberFor = T::UnsignedLongevity::get().unique_saturated_into(); @@ -3623,17 +3806,18 @@ impl Pallet { fn exodus_run_nonce_commitment( exodus_storage: &ExodusStorage, NetworkCurve>, - exodus_records: &mut ExodusRecords>, + exodus_records: &mut ExodusRecords, BlockNumberFor>, secret_share: &[u8], authority_key: &T::AuthorityId, - exodus_session: ExodusSession, + exodus_context: ExodusContext>, + roast_session: RoastSession, active_dkg_index: DkgIndex, - ) -> ExodusResult<()> { + ) -> ExodusResult>> { let authority_index = exodus_storage.authority_index; let network_curve = exodus_storage.network_curve; + let exodus_context_encoded = exodus_storage.encode(); - let exodus_storage_encoded = exodus_storage.encode(); - let rng_context = [exodus_storage_encoded.as_slice(), secret_share]; + let rng_context = [exodus_context_encoded.as_slice(), secret_share]; let mut rng = Self::get_offchain_rng(&rng_context); let (nonce, hiding_commitment, binding_commitment) = network_curve @@ -3643,9 +3827,9 @@ impl Pallet { authority_key, active_dkg_index, exodus_records, - |context| { - let package = context.with_exodus_nonce_commitment( - exodus_session, + |package_context| { + let package = package_context.with_exodus_nonce_commitment( + exodus_context, hiding_commitment, binding_commitment, )?; @@ -3658,28 +3842,36 @@ impl Pallet { }, |exodus_records, next_release_block| { exodus_records.insert_exodus_nonce( - exodus_session, + exodus_context, next_release_block, nonce, ); } - ) + )?; + + Ok(ExodusOk::ExodusNonceCommitted( + exodus_context.session, + exodus_context.network_id, + roast_session, + authority_index, + network_curve + )) } fn exodus_run_group_commitment( exodus_storage: &ExodusStorage, NetworkCurve>, - exodus_records: &mut ExodusRecords>, + exodus_records: &mut ExodusRecords, BlockNumberFor>, message: &[u8], authority_key: &T::AuthorityId, - exodus_session: ExodusSession, + exodus_context: ExodusContext>, roast_session: RoastSession, active_dkg_index: DkgIndex, - ) -> ExodusResult<()> { + ) -> ExodusResult>> { let authority_index = exodus_storage.authority_index; let network_curve = exodus_storage.network_curve; let session_commitments = - NonceCommitments::::iter_prefix((exodus_session, roast_session)) + NonceCommitments::::iter_prefix((exodus_context, roast_session)) .collect::>(); let nonce_commitments = session_commitments @@ -3707,12 +3899,13 @@ impl Pallet { authority_key, active_dkg_index, exodus_records, - |context| { - let package = context.with_exodus_group_commitment( - exodus_session, + |package_context| { + let package = package_context.with_exodus_group_commitment( + exodus_context, group_commitment, binding_factors_root, )?; + Ok(ExodusPackage::GroupCommitment(package)) }, |exodus_package, signature| { @@ -3722,30 +3915,38 @@ impl Pallet { }, |exodus_records, next_release_block| { exodus_records.insert_exodus_group( - exodus_session, + exodus_context, next_release_block, self_binding_factor, binding_factors_proof, ) } - ) + )?; + + Ok(ExodusOk::ExodusGroupCommitted( + exodus_context.session, + exodus_context.network_id, + roast_session, + authority_index, + network_curve, + )) } fn exodus_run_signature_share( exodus_storage: &ExodusStorage, NetworkCurve>, - exodus_records: &mut ExodusRecords>, + exodus_records: &mut ExodusRecords, BlockNumberFor>, message: &[u8], secret_share: &[u8], authority_key: &T::AuthorityId, - exodus_session: ExodusSession, + exodus_context: ExodusContext>, roast_session: RoastSession, active_dkg_index: DkgIndex, - ) -> ExodusResult<()> { + ) -> ExodusResult>> { let authority_index = exodus_storage.authority_index; let network_curve = exodus_storage.network_curve; let session_commitments = - NonceCommitments::::iter_prefix((exodus_session, roast_session)) + NonceCommitments::::iter_prefix((exodus_context, roast_session)) .collect::>(); let nonce_commitments = session_commitments @@ -3756,7 +3957,8 @@ impl Pallet { .collect::>(); let signing_package = network_curve.init_signing_package(&nonce_commitments, &message)?; - let secret_nonce = exodus_records.get_secret_nonce(&exodus_session) + let secret_nonce = exodus_records + .get_secret_nonce(&exodus_context) .ok_or(ExodusError::TransactionSubmissionFailed(authority_index))?; let signature_share = network_curve.partial_sign_message( @@ -3766,7 +3968,7 @@ impl Pallet { )?; let exodus_group_record = match exodus_records - .get_group_record(&exodus_session) { + .get_group_record(&exodus_context) { Some(exodus_group_record) => exodus_group_record, None => { let verifying_key = ActiveVerifyingKey::::get(&network_curve); @@ -3791,13 +3993,14 @@ impl Pallet { authority_key, active_dkg_index, exodus_records, - |context| { - let package = context.with_exodus_signature_share( - exodus_session, + |package_context| { + let package = package_context.with_exodus_signature_share( + exodus_context, signature_share, exodus_group_record.binding_factors_proof, exodus_group_record.self_binding_factor, )?; + Ok(ExodusPackage::SignatureShare(package)) }, |exodus_package, signature| { @@ -3806,16 +4009,27 @@ impl Pallet { .map_err(|_| ExodusError::TransactionSubmissionFailed(authority_index)) }, |exodus_records, next_release_block| { - exodus_records.insert_exodus_share(exodus_session, next_release_block); + exodus_records.insert_exodus_share( + exodus_context, + next_release_block + ); } - ) + )?; + + Ok(ExodusOk::ExodusSignedPartially( + exodus_context.session, + exodus_context.network_id, + roast_session, + authority_index, + network_curve, + )) } fn dkg_run_round0( dkg_storage: DkgStorage, NetworkCurve>, - qualification_state: &QualifyingState, + qualification_state: &PendingDkgAuthorities, authority_key: T::AuthorityId, - ) -> ExodusResult> { + ) -> ExodusResult>> { let network_curve = dkg_storage.network_curve; let authority_index = dkg_storage.authority_index; @@ -3881,14 +4095,13 @@ impl Pallet { DkgStorageEnum::Round0 { package_hash } )?; - let current_dkg_index = QualificationDkgState::::get(&network_curve) - .get_dkg_index(); + let current_dkg_index = qualification_state.get_dkg_index(); dkg_storage.prepare_signed_call_with_context( &authority_key, current_dkg_index, &mut round0_metadata, - |context: PackageContext| { + |context| { Ok(DkgPackage::Round0(context.with_dkg_round0(package_hash))) }, |dkg_package, signature| { @@ -3903,9 +4116,9 @@ impl Pallet { fn dkg_run_round1( dkg_storage: DkgStorage, NetworkCurve>, - qualification_state: &QualifyingState, + qualification_state: &PendingDkgAuthorities, authority_key: T::AuthorityId, - ) -> ExodusResult> { + ) -> ExodusResult>> { let network_curve = dkg_storage.network_curve; let authority_index = dkg_storage.authority_index; @@ -3956,22 +4169,27 @@ impl Pallet { )); } - let Some(DkgStorageEnum::Round1 { public_package, .. }) = - round1_metadata.into_inner() else { - return Ok(ExodusOk::DkgRoundNothingStored( - ROUND_NUMBER_1, - authority_index, - network_curve, - )); - }; + let (stored_hash_matches, external_hash_matches) = { + let Some(DkgStorageEnum::Round1 { public_package, .. }) = + round1_metadata.into_inner() else { + return Ok(ExodusOk::DkgRoundNothingStored( + ROUND_NUMBER_1, + authority_index, + network_curve, + )); + }; - let public_package_clone = public_package.clone(); + let stored_package_hash = ExodusHash::from(blake2_256(public_package)); + let external_package_hash = Round0Packages::::get( + &network_curve, + &authority_index + ); - let stored_package_hash = ExodusHash::from(blake2_256(&public_package_clone)); - let external_package_hash = Round0Packages::::get(&network_curve, &authority_index); - - let stored_hash_matches = stored_package_hash.eq(package_hash); - let external_hash_matches = external_package_hash.eq(package_hash); + ( + stored_package_hash.eq(package_hash), + external_package_hash.eq(package_hash) + ) + }; if !stored_hash_matches || !external_hash_matches { return Ok(ExodusOk::DkgRoundNothingStored( @@ -3981,15 +4199,27 @@ impl Pallet { )); } - let current_dkg_index = QualificationDkgState::::get(&network_curve) - .get_dkg_index(); + let public_package_vec = match round1_metadata.into_inner() { + Some(DkgStorageEnum::Round1 { public_package, .. }) => { + public_package.to_vec() + } + _ => { + return Ok(ExodusOk::DkgRoundNothingStored( + ROUND_NUMBER_1, + authority_index, + network_curve, + )); + } + }; + + let current_dkg_index = qualification_state.get_dkg_index(); dkg_storage.prepare_signed_call_with_context( &authority_key, current_dkg_index, &mut round1_metadata, |context| { - let package = context.with_dkg_round1(public_package_clone)?; + let package = context.with_dkg_round1(public_package_vec)?; Ok(DkgPackage::Round1(package)) }, |dkg_package, signature| { @@ -4004,9 +4234,9 @@ impl Pallet { fn dkg_run_round2( dkg_storage: DkgStorage, NetworkCurve>, - qualification_state: &QualifyingState, + qualification_state: &PendingDkgAuthorities, authority_key: T::AuthorityId, - ) -> ExodusResult> { + ) -> ExodusResult>> { let network_curve = dkg_storage.network_curve; let authority_index = dkg_storage.authority_index; @@ -4100,8 +4330,7 @@ impl Pallet { DkgStorageEnum::Round2 { secret_package, public_packages }, )?; - let current_dkg_index = QualificationDkgState::::get(&network_curve) - .get_dkg_index(); + let current_dkg_index = qualification_state.get_dkg_index(); dkg_storage.prepare_signed_call_with_context( &authority_key, @@ -4125,9 +4354,9 @@ impl Pallet { fn dkg_run_round3( dkg_storage: DkgStorage, NetworkCurve>, - qualification_state: &QualifyingState, + qualification_state: &PendingDkgAuthorities, authority_key: T::AuthorityId, - ) -> ExodusResult> { + ) -> ExodusResult>> { let network_curve = dkg_storage.network_curve; let authority_index = dkg_storage.authority_index; @@ -4191,8 +4420,7 @@ impl Pallet { DkgStorageEnum::Round3 { decrypted }, )?; - let current_dkg_index = QualificationDkgState::::get(&network_curve) - .get_dkg_index(); + let current_dkg_index = qualification_state.get_dkg_index(); dkg_storage.prepare_signed_call_with_context( &authority_key, @@ -4214,9 +4442,9 @@ impl Pallet { fn dkg_run_round4( dkg_storage: DkgStorage, NetworkCurve>, - qualification_state: &QualifyingState, + qualification_state: &PendingDkgAuthorities, authority_key: T::AuthorityId, - ) -> ExodusResult> { + ) -> ExodusResult>> { let network_curve = dkg_storage.network_curve; let authority_index = dkg_storage.authority_index; @@ -4300,8 +4528,7 @@ impl Pallet { DkgStorageEnum::Round4, )?; - let current_dkg_index = QualificationDkgState::::get(&network_curve) - .get_dkg_index(); + let current_dkg_index = qualification_state.get_dkg_index(); dkg_storage.prepare_signed_call_with_context( &authority_key, @@ -4324,9 +4551,9 @@ impl Pallet { fn dkg_run_round5( dkg_storage: DkgStorage, NetworkCurve>, - qualification_state: &QualifyingState, + qualification_state: &PendingDkgAuthorities, authority_key: T::AuthorityId, - ) -> ExodusResult> { + ) -> ExodusResult>> { let network_curve = dkg_storage.network_curve; let authority_index = dkg_storage.authority_index; @@ -4451,8 +4678,7 @@ impl Pallet { *byte |= 1 << (self_global_index & 7); } - let current_dkg_index = QualificationDkgState::::get(&network_curve) - .get_dkg_index(); + let current_dkg_index = qualification_state.get_dkg_index(); dkg_storage.prepare_signed_call_with_context( &authority_key, @@ -4479,9 +4705,9 @@ impl Pallet { fn dkg_run_round6( dkg_storage: DkgStorage, NetworkCurve>, - qualification_state: &QualifyingState, + qualification_state: &PendingDkgAuthorities, authority_key: T::AuthorityId, - ) -> ExodusResult> { + ) -> ExodusResult>> { let network_curve = dkg_storage.network_curve; let authority_index = dkg_storage.authority_index; @@ -4534,9 +4760,7 @@ impl Pallet { .dkg_derive_verifying_share(secret_share)?; let owned_merkle_proof = core::mem::take(merkle_proof); - - let current_dkg_index = QualificationDkgState::::get(&network_curve) - .get_dkg_index(); + let current_dkg_index = qualification_state.get_dkg_index(); dkg_storage.prepare_signed_call_with_context( &authority_key, @@ -4558,43 +4782,68 @@ impl Pallet { fn dkg_run_reset( dkg_storage: DkgStorage, NetworkCurve>, - qualification_state: &QualifyingState, - ) -> ExodusResult> { + qualification_state: &PendingDkgAuthorities, + ) -> ExodusResult>> { let network_curve = dkg_storage.network_curve; let authority_index = dkg_storage.authority_index; - match qualification_state.get_dkg_index().checked_sub(1) { - Some(dkg_index) => { - let dkg_prefixed_index = Self::create_dkg_based_prefix(dkg_index); + let qualification_dkg_index = qualification_state.get_dkg_index(); + let active_dkg_index = ActiveDkgAuthorities::::get(&network_curve) + .get_dkg_index(); - let storage_exists = { - dkg_storage - .read_local_storage(&dkg_prefixed_index, authority_index) - .storage_exists() - }; + let dkg_indexes_to_purge = if active_dkg_index < qualification_dkg_index { + // some failed qualification rounds accumulated + active_dkg_index.saturating_add(1)..=qualification_dkg_index + } else { + // qualification passed succesfully + let previous_active_dkg_index = active_dkg_index + .checked_sub(1) + .ok_or(ExodusError::UnexpectedRound)?; - if storage_exists { - [DKG_ROUND0_PREFIX, DKG_ROUND1_PREFIX, DKG_ROUND2_PREFIX, DKG_ROUND3_PREFIX, DKG_ROUND4_PREFIX] - .iter() - .for_each(|prefix| dkg_storage.clear_local_storage(prefix)); + previous_active_dkg_index..=previous_active_dkg_index + }; - Self::clear_incoming_offchain_packages( + let mut removed_count = 0; + for dkg_index in dkg_indexes_to_purge { + let dkg_prefixed_index = Self::create_dkg_based_prefix(dkg_index); + + let storage_exists = { + dkg_storage + .read_local_storage(&dkg_prefixed_index, authority_index) + .storage_exists() + }; + + if storage_exists { + [DKG_ROUND0_PREFIX, DKG_ROUND1_PREFIX, DKG_ROUND2_PREFIX, DKG_ROUND3_PREFIX, DKG_ROUND4_PREFIX] + .iter() + .for_each(|prefix| dkg_storage.clear_local_storage(prefix)); + + Self::clear_incoming_offchain_packages( + network_curve, + &[ROUND_NUMBER_1, ROUND_NUMBER_2, ROUND_NUMBER_4], + T::MaxAuthorities::get() as AuthIndex, + ); + + dkg_storage.clear_local_storage(&dkg_prefixed_index); + removed_count += 1; + + log::trace!( + target: PALLET_LOG_TARGET, + "🗺️ EXODUS processing succeeded: #{:?}.", + ExodusOk::DkgLocalStoragePurged::>( + authority_index, network_curve, - &[ROUND_NUMBER_1, ROUND_NUMBER_2, ROUND_NUMBER_4], - T::MaxAuthorities::get() as AuthIndex, - ); - - dkg_storage.clear_local_storage(&dkg_prefixed_index); - } - - Ok(ExodusOk::DkgPreviousPurged(authority_index, network_curve)) - }, - None => Err(ExodusError::UnexpectedRound), + dkg_index, + ) + ); + } } + + Ok(ExodusOk::DkgPreviousPurged(authority_index, network_curve, removed_count)) } pub fn validate_compliants_and_justifications( - qualification_state: &QualifyingState, + qualification_state: &PendingDkgAuthorities, round1_packages: &mut BTreeMap>, round2_packages: &mut BTreeMap>, network_curve: NetworkCurve, @@ -4734,7 +4983,7 @@ impl Pallet { } fn ecdh_decrypt_round2_packages( - qualification_state: &QualifyingState, + qualification_state: &PendingDkgAuthorities, round1_packages: &BTreeMap>, round1_secret_package: &Vec, authority_index: AuthIndex, @@ -4970,13 +5219,12 @@ impl Pallet { local_authorities.sort(); authorities - .into_iter() .enumerate() .filter_map(move |(index, authority)| { local_authorities .binary_search(&authority) .ok() - .map(|location| (index as AuthIndex, local_authorities[location].clone())) + .map(|_| (index as AuthIndex, authority)) }) } @@ -4985,23 +5233,40 @@ impl Pallet { network_curves: impl Iterator, ) { let authorities_len = authorities.len(); - let next_authorities = + let mut next_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::force_from( authorities, Some("exodus reached maximum number of authorities"), ); let block_number = T::BlockNumberProvider::current_block_number(); + let mut peekable_curves = network_curves.peekable(); - for network_curve in network_curves { + while let Some(network_curve) = peekable_curves.next() { let active_authorities = ActiveAuthorities::::get(&network_curve); let keys_changed = active_authorities != next_authorities; if keys_changed { - QualificationAuthorities::::insert(&network_curve, next_authorities.clone()); + let converted_reset_period: BlockNumberFor = + BLOCKS_FOR_DKG_RESET.unique_saturated_into(); + + let release_block = converted_reset_period + .saturating_add(block_number); + + let authorities_to_insert = if peekable_curves.peek().is_none() { + sp_std::mem::take(&mut next_authorities) + } else { + next_authorities.clone() + }; + + QualificationAuthorities::::insert( + &network_curve, + authorities_to_insert, + ); + QualificationDkgState::::mutate(&network_curve, |state| { state.new_dkg(authorities_len); - state.set_block(block_number); + state.set_release_block(release_block); }); } } diff --git a/pallets/exodus/src/mock.rs b/pallets/exodus/src/mock.rs index 460790b..a73cc4b 100644 --- a/pallets/exodus/src/mock.rs +++ b/pallets/exodus/src/mock.rs @@ -136,14 +136,15 @@ impl ghost_exodus::Config for Runtime { type DisabledValidators = Session; type UnsignedPriority = ConstU64<{ 1 << 20 }>; - type UnsignedLongevity = ConstU64<1>; + type UnsignedLongevity = ConstU64<5>; type MaxAuthorities = ConstU32<10>; type MaxAuthoritiesChunks = ConstU32<1>; - type DkgRoundPeriod = ConstU64<100>; + type DkgRoundPeriod = ConstU64<300>; type RemovalLimit = ConstU32<20>; - type MaxCursorLen = ConstU32<20>; + type MaxCursorLen = ConstU32<256>; type BaseEvmAmount = ConstU128<5>; + type MaxExodusRequestCount = ConstU32<40>; type WeightInfo = (); } diff --git a/pallets/exodus/src/tests.rs b/pallets/exodus/src/tests.rs index 9f5ff94..ff10768 100644 --- a/pallets/exodus/src/tests.rs +++ b/pallets/exodus/src/tests.rs @@ -70,12 +70,7 @@ fn run_increase_block_by(ext: &mut sp_io::TestExternalities, increase_by: u64) { fn run_to_next_round(ext: &mut sp_io::TestExternalities, curve: NetworkCurve) { let target_block = ext.execute_with(|| { - let stored = QualificationDkgState::::get(curve).get_block(); - let delay = <::DkgRoundPeriod as frame_support::traits::Get< - frame_system::pallet_prelude::BlockNumberFor - >>::get(); - - stored + delay + QualificationDkgState::::get(curve).get_release_block() }); ext.execute_with(|| { @@ -139,9 +134,9 @@ fn execute_dkg_happy_path( where F1: FnMut( DkgStorage, NetworkCurve>, - &QualifyingState, + &PendingDkgAuthorities, UintAuthorityId, - ) -> Result, ExodusError>, + ) -> Result>, ExodusError>, F2: FnMut(Call) -> Result<(), TestError>, F3: FnMut(AuthIndex, NetworkCurve) -> Result<(), TestError>, @@ -203,78 +198,6 @@ where Ok(()) } -fn execute_exodus_happy_path( - ext: &mut sp_io::TestExternalities, - tx_pool_state: &std::sync::Arc>, - curve: NetworkCurve, - authority: u64, - mut apply_transaction: F1, - mut check_storage_changed: F2, -) -> Result<(), TestError> -where - F1: FnMut(Call) -> Result<(), TestError>, - F2: FnMut(ExodusOk) -> Result<(), TestError>, -{ - use sp_runtime::transaction_validity::TransactionSource; - - let state = ext.execute_with(|| QualificationDkgState::::get(&curve)); - if state.is_dkg_pending() { - return Err(TestError::PhaseMismatch); - } - - let active_authorities = ext.execute_with(|| { - ActiveDkgAuthorities::::get(&curve) - }); - - let current_block = ext.execute_with(|| Exodus::current_block_number()); - let authority_index = authority as AuthIndex; - let authority_key = authority.into(); - - let exodus_process_result = ext.execute_with(|| { - Exodus::exodus_run_process( - authority_index, - authority_key, - curve, - current_block, - &active_authorities, - ) - }); - - match exodus_process_result { - Ok(ExodusOk::ExodusEmptyRequests(_, _)) => return Ok(()), - Err(_) => return Err(TestError::WorkerExecutionFailed), - _ => {} - } - - let mut write_pool = tx_pool_state.write(); - let raw_tx = write_pool.transactions.pop().ok_or(TestError::PoolIsEmpty)?; - let runtime_call = decode_transaction::(&raw_tx) - .ok_or(TestError::TransactionDecodeFailed)?; - - if let RuntimeCall::Exodus(inner_exodus_call) = runtime_call { - ext.execute_with(|| { - let is_mempool_ok = Exodus::validate_unsigned(TransactionSource::InBlock, &inner_exodus_call).is_ok(); - if !is_mempool_ok { - return Err(TestError::MempoolRejectedTransaction); - } - - apply_transaction(inner_exodus_call) - .map_err(|_| TestError::OnChainApplicationFailed)?; - - Ok(()) - })?; - - } else { - return Err(TestError::UnexpectedRuntimeCall); - } - write_pool.transactions.clear(); - ext.execute_with(|| { - check_storage_changed(exodus_process_result.unwrap()) - })?; - - Ok(()) -} - fn execute_round0_happy_path( ext: &mut sp_io::TestExternalities, tx_pool_state: &std::sync::Arc>, @@ -701,7 +624,8 @@ fn run_initial_dkg_session(raw_authorities: &[u64], curve: NetworkCurve) -> ( let member = *authority as AuthIndex; assert!(state.contains_index(member)); } - assert_eq!(state.get_block(), start_block + round_delay * 9); + + assert_eq!(state.get_release_block(), start_block + round_delay * 8 + 2 * BLOCKS_FOR_DKG_RESET); assert_eq!(state.get_dkg_index(), 1); assert_eq!(state.get_phase(), DkgPhase::Finalized); @@ -738,278 +662,74 @@ fn run_dkg_session( let (start_block, round_delay) = run_succesfull_dkg_session_partially(ext, tx_pool_state, &raw_authorities, curve); - let register_nonce = |call| { - match call { - Call::register_nonce_commitment { exodus_package, signature } => { - Exodus::register_nonce_commitment(RuntimeOrigin::none(), exodus_package, signature) - .map_err(|_| TestError::OnChainApplicationFailed) - } - _ => Err(TestError::CallMismatch), - } - }; - - let register_group = |call| { - match call { - Call::register_group_commitment { exodus_package, signature } => { - Exodus::register_group_commitment(RuntimeOrigin::none(), exodus_package, signature) - .map_err(|_| TestError::OnChainApplicationFailed) - } - _ => Err(TestError::CallMismatch), - } - }; - - let register_share = |call| { - match call { - Call::register_signature_share { exodus_package, signature } => { - Exodus::register_signature_share(RuntimeOrigin::none(), exodus_package, signature) - .map_err(|_| TestError::OnChainApplicationFailed) - } - _ => Err(TestError::CallMismatch), - } - }; - - let check_nonce = |exodus_ok| { - match exodus_ok { - ExodusOk::ExodusNonceCommitted(exodus, roast, idx, _) => { - if !NonceCommitments::::contains_key((exodus, roast), idx) { - return Err(TestError::InvalidStorageAfter); - } - - if !RoastSessions::::contains_key(exodus, idx) { - return Err(TestError::InvalidStorageAfter); - } - - let roast_state = RoastSessionStates::::get(exodus, roast); - - if !roast_state.nonce_committers.contains(idx) - || roast_state.group_committers.contains(idx) - || roast_state.partial_signers.contains(idx) { - return Err(TestError::InvalidStorageAfter); - } - - Ok(()) - } - _ => Err(TestError::InvalidStorageAfter), - } - }; - - let check_group = |exodus_ok| { - match exodus_ok { - ExodusOk::ExodusGroupCommitted(exodus, roast, idx, _) => { - if !RoastSessions::::contains_key(exodus, idx) { - return Err(TestError::InvalidStorageAfter); - } - - if !GroupCommitters::::iter_prefix_values((exodus, roast)) - .any(|participants| participants.contains(idx)) { - return Err(TestError::InvalidStorageAfter); - } - - let roast_state = RoastSessionStates::::get(exodus, roast); - - let is_nonce_committer = roast_state.nonce_committers.contains(idx); - let is_group_committer = roast_state.group_committers.contains(idx); - - if !is_nonce_committer || !is_group_committer { - return Err(TestError::InvalidStorageAfter); - } - - Ok(()) - } - _ => Err(TestError::InvalidStorageAfter), - } - }; - - let check_share = |exodus_ok| { - match exodus_ok { - ExodusOk::ExodusSignedPartially(exodus, roast, idx, curve) => { - if RoastSessions::::contains_key(exodus, idx) { - return Err(TestError::InvalidStorageAfter); - } - - if RoastSessions::::contains_key(exodus, idx) { - return Err(TestError::InvalidStorageAfter); - } - - if ExodusRequests::::contains_key((curve, exodus)) { - let roast_state = RoastSessionStates::::get(exodus, roast); - if !roast_state.partial_signers.contains(idx) - || !roast_state.nonce_committers.contains(idx) - || !roast_state.group_committers.contains(idx) { - return Err(TestError::InvalidStorageAfter); - } - - } else { - if RoastSessionStates::::contains_key(exodus, roast) { - return Err(TestError::InvalidStorageAfter); - } - - if SignatureScalars::::contains_key(exodus, roast) { - return Err(TestError::InvalidStorageAfter); - } - - if GroupCommitmentsConsensus::::contains_key(exodus, roast) { - return Err(TestError::InvalidStorageAfter); - } - } - - Ok(()) - } - _ => Err(TestError::InvalidStorageAfter), - } - }; - - let threshold = ext.execute_with(|| { - let len = ActiveAuthorities::::decode_len(&curve) - .unwrap_or(0); - get_byzantium_quorum_threshold(len) as u16 - }); - - let mut exodus_steps_outer = BTreeMap::new(); - let mut roast_sessions_outer = BTreeMap::new(); - let mut roast_participants_outer = BTreeMap::new(); - let mut malicious_participants_outer = BTreeMap::new(); - let mut throttled_participants_outer = BTreeMap::new(); - - let malicious = prev_authorities.first().copied().unwrap(); - let throttled = prev_authorities.last().copied().unwrap(); - let prev_len = prev_authorities.len() as AuthIndex; + let total_validators = prev_authorities.len(); + let threshold = get_byzantium_quorum_threshold(total_validators); + let max_allowed_to_skip = total_validators.saturating_sub(threshold); loop { - let mut current_pending_requests: Vec = ext.execute_with(|| { + let mut skipped_in_this_round = 0; + + let has_pending_requests = ext.execute_with(|| { ExodusRequests::::iter_keys() - .filter_map(|(current_curve, exodus_session)| { - curve.eq(¤t_curve).then(|| exodus_session) - }) - .collect() + .any(|(current_curve, _context)| current_curve == curve) }); - current_pending_requests.sort(); + if !has_pending_requests { break; } - if let Some(exodus_session) = current_pending_requests.first() { - if !current_pending_requests.contains(exodus_session) { continue; } + for auth in prev_authorities.iter() { + let authority_index = *auth as AuthIndex; + let current_block = ext.execute_with(|| System::block_number()); + let current_block_u64: u64 = current_block.unique_saturated_into(); + let should_throttle = (current_block_u64 + *auth) % 3 == 0; - let exodus_steps: &mut BTreeMap = exodus_steps_outer.entry(*exodus_session).or_default(); - let roast_sessions: &mut BTreeMap = roast_sessions_outer.entry(*exodus_session).or_default(); - let roast_participants: &mut BTreeMap = roast_participants_outer.entry(*exodus_session).or_default(); - - let malicious_participants: &mut BTreeSet = malicious_participants_outer.entry(*exodus_session).or_default(); - let throttled_participants: &mut BTreeSet = throttled_participants_outer.entry(*exodus_session).or_default(); - - for auth in prev_authorities.iter() { - let request_exists = ext.execute_with(|| { - ExodusRequests::::contains_key((&curve, &exodus_session)) - }); - - if !request_exists { break; } - if malicious_participants.contains(auth) { continue; } - if throttled_participants.contains(auth) { continue; } - - let mut current_step = exodus_steps.get(&auth).copied().unwrap_or(0); - let mut roast_session = roast_sessions.get(&auth).copied().unwrap_or(0); - - let mut participants = roast_participants - .get(&roast_session) - .copied() - .unwrap_or((0, 0, 0)); - - let mut has_enough_nonces = participants.0 >= threshold; - let mut has_enough_groups = participants.1 >= threshold; - - if has_enough_nonces && current_step == 0 { - roast_session += 1; - - participants = roast_participants - .get(&roast_session) - .copied() - .unwrap_or((0, 0, 0)); - - has_enough_nonces = participants.0 >= threshold; - has_enough_groups = participants.1 >= threshold; - } - - let result = match current_step { - 0 => { - current_step += 1; - participants.0 += 1; - execute_exodus_happy_path(ext, tx_pool_state, curve, *auth, register_nonce, check_nonce) - }, - 1 if has_enough_nonces => { - participants.1 += 1; - let result = execute_exodus_happy_path(ext, tx_pool_state, curve, *auth, register_group, check_group); - - if malicious == *auth && prev_len > threshold { - ext.execute_with(|| { - let raw_vec = vec![69u8; ELEMENT_MAX_BYTES as usize]; - let fake_group_commitment = - BoundedVec::>::try_from(raw_vec) - .unwrap(); - - let empty_participants = ParticipantsBitmap::::empty(prev_len); - let mut self_participant = empty_participants.clone(); - self_participant.insert(malicious); - - let (correct_commitment, correct_root) = GroupCommitmentsConsensus::::mutate( - &exodus_session, - &roast_session, - |consensus| { - let binding_root = consensus.merkle_root; - let group_commitment = consensus - .element_bytes - .clone(); - - *consensus = ConsensusState::new( - fake_group_commitment.clone(), - consensus.participants.clone(), - DUMMY_HASH, - ); - - (group_commitment, binding_root) - }); - - let prev_group_key = (exodus_session, roast_session, correct_root, correct_commitment); - GroupCommitters::::insert(prev_group_key, empty_participants); - - let fake_group_key = (exodus_session, roast_session, DUMMY_HASH, fake_group_commitment); - GroupCommitters::::insert(fake_group_key, self_participant); - - malicious_participants.insert(malicious); - }); - } else { - current_step += 1; - } - - result - } - 2 if has_enough_groups => { - if prev_len > threshold + 1 && *auth == throttled { - throttled_participants.insert(*auth); - continue; - } - - current_step = 0; - participants.2 += 1; - execute_exodus_happy_path(ext, tx_pool_state, curve, *auth, register_share, check_share).unwrap(); - - exodus_steps.insert(*auth, current_step); - roast_sessions.insert(*auth, roast_session + 1); - roast_participants.insert(roast_session, participants); - - continue; - } - _ => continue, - }; - - result.unwrap(); - exodus_steps.insert(*auth, current_step); - roast_sessions.insert(*auth, roast_session); - roast_participants.insert(roast_session, participants); + if should_throttle && skipped_in_this_round < max_allowed_to_skip { + skipped_in_this_round += 1; + run_increase_block_by(ext, 1); + continue; } - run_increase_block_by(ext, 1); + ext.execute_with(|| { + let authority_key = (*auth).into(); + let active_authorities = ActiveDkgAuthorities::::get(curve); - } else { - break; + let _ = Exodus::exodus_run_process( + authority_index, + authority_key, + curve, + current_block, + &active_authorities, + ); + + let txs = tx_pool_state.write().transactions.drain(..).collect::>(); + + for raw_tx in txs.iter() { + let runtime_call = decode_transaction::(&raw_tx) + .unwrap(); + + if let RuntimeCall::Exodus(inner_exodus_call) = runtime_call { + Exodus::validate_unsigned(TransactionSource::InBlock, &inner_exodus_call).unwrap(); + + let dispatch_result = match inner_exodus_call { + Call::register_nonce_commitment { exodus_package, signature } => { + Exodus::register_nonce_commitment(RuntimeOrigin::none(), exodus_package, signature) + }, + Call::register_group_commitment { exodus_package, signature } => { + Exodus::register_group_commitment(RuntimeOrigin::none(), exodus_package, signature) + }, + Call::register_signature_share { exodus_package, signature } => { + Exodus::register_signature_share(RuntimeOrigin::none(), exodus_package, signature) + }, + _ => panic!("Unexpected Call inside Exodus Pallet"), + }; + + dispatch_result.unwrap(); + } else { + panic!("Expected one of RuntimeCall::Exodus"); + } + } + }); + + run_increase_block_by(ext, 1); } } @@ -1025,7 +745,7 @@ fn run_dkg_session( let member = authority as AuthIndex; assert!(state.contains_index(member)); } - assert_eq!(state.get_block(), start_block + round_delay * 9); + assert_eq!(state.get_release_block(), start_block + round_delay * 8 + 2 * BLOCKS_FOR_DKG_RESET); assert_eq!(state.get_dkg_index(), next_dkg_index); assert_eq!(state.get_phase(), DkgPhase::Finalized); @@ -1094,10 +814,13 @@ fn test_dkg_robustness_against_malformed_scale_slots() { blob: malformed_blob, }; + let current_block_number = Exodus::current_block_number(); + let dummy_context = PackageContext::default() .with_authority_index(malicious_validator as AuthIndex) .with_network_curve(curve) .with_dkg_index(dkg_index) + .with_block_number(current_block_number) .with_dkg_round2(dummy_encryption_bundle) .unwrap(); @@ -1203,6 +926,14 @@ fn test_all_validators_can_execute_dkg() { let (mut ext, tx_pool_state) = run_initial_dkg_session(&authorities_1, curve); + let mut authorities_2 = authorities_1.clone(); + let new_authority = authorities_2.iter().max().unwrap() + 1; + authorities_2.push(new_authority); + authorities_2.push(new_authority + 1); + authorities_2.push(new_authority + 2); + + run_dkg_session(&mut ext, &tx_pool_state, authorities_1.clone(), authorities_2.clone(), curve); + ext.execute_with(|| { let network_data = NetworkDataBuilder::default() .with_network_curve(curve) @@ -1248,13 +979,10 @@ fn test_all_validators_can_execute_dkg() { Exodus::do_register_evm_governance(network_id, dummy_address, create).unwrap(); }); - let mut authorities_2 = authorities_1.clone(); - let new_authority = authorities_2.iter().max().unwrap() + 1; - authorities_2.push(new_authority); - authorities_2.push(new_authority + 1); - authorities_2.push(new_authority + 2); + let mut authorities_3 = FixedAuthorities::get(); + authorities_3.push(new_authority); - run_dkg_session(&mut ext, &tx_pool_state, authorities_1, authorities_2.clone(), curve); + run_dkg_session(&mut ext, &tx_pool_state, authorities_2.clone(), authorities_3, curve); ext.execute_with(|| { Exodus::do_register_evm_bridge_out_exodus( @@ -1279,10 +1007,7 @@ fn test_all_validators_can_execute_dkg() { Exodus::do_register_evm_governance(network_id, dummy_address, audit).unwrap(); }); - let mut authorities_3 = FixedAuthorities::get(); - authorities_3.push(new_authority); - - run_dkg_session(&mut ext, &tx_pool_state, authorities_2, authorities_3, curve); + run_dkg_session(&mut ext, &tx_pool_state, authorities_1, authorities_2.clone(), curve); let rotations_count = ext.execute_with(|| { ExodusSignedRotations::::iter().count() @@ -1300,7 +1025,7 @@ fn test_all_validators_can_execute_dkg() { ExodusRequests::::count() }); - assert_eq!(rotations_count, 2); + assert_eq!(rotations_count, 5); assert_eq!(bridges_count, 2); assert_eq!(governances_count, 15); assert_eq!(pending_exodus_requests, 0); @@ -1331,14 +1056,10 @@ fn test_exodus_dkg_robustness() { QualificationDkgState::::get(curve).get_dkg_index() }); - let (start_block, round_delay) = ext.execute_with(|| { - let round_delay = <::DkgRoundPeriod as frame_support::traits::Get< - frame_system::pallet_prelude::BlockNumberFor - >>::get(); - - let start_block = System::block_number(); - - (start_block, round_delay) + let round_delay = ext.execute_with(|| { + <::DkgRoundPeriod as frame_support::traits::Get< + frame_system::pallet_prelude::BlockNumberFor + >>::get() }); let mut failed_at_round = None; @@ -1419,8 +1140,9 @@ fn test_exodus_dkg_robustness() { }) .count(); - let estimated_block = start_block + round_delay * (total_round_idx as u64); - assert_eq!(state.get_block(), estimated_block); + let current_system_block = ext.execute_with(|| System::block_number()); + + assert_eq!(state.get_release_block(), current_system_block + round_delay); assert_eq!(state.get_dkg_index(), dkg_index); assert_eq!(state.get_phase(), DkgPhase::Finalized); diff --git a/pallets/exodus/src/types/dkg.rs b/pallets/exodus/src/types/dkg.rs index e87fdf1..69395a3 100644 --- a/pallets/exodus/src/types/dkg.rs +++ b/pallets/exodus/src/types/dkg.rs @@ -1,28 +1,45 @@ use sp_runtime::{RuntimeDebug, traits::UniqueSaturatedInto}; use codec::{Encode, Decode}; use scale_info::TypeInfo; -use strum::EnumCount; use frame_support::{BoundedVec, traits::ConstU32}; -use ghost_helpers::networks::NetworkType; -use ghost_traits::bounded_bitmap::{ - BoundedBitmapGenerator, BoundedBitmapReader, BoundedBitmapWriter, +use ghost_traits::{ + networks::NetworkDataBasicHandler, + bounded_bitmap::{ + BoundedBitmapGenerator, BoundedBitmapReader, BoundedBitmapWriter, + }, }; -use crate::{ELEMENT_MAX_BYTES, ExodusSession, DkgIndex}; +use crate::{ + ELEMENT_MAX_BYTES, Config, ExodusContext, NetworkIdOf, + ParticipantsBitmap, BlockNumberFor, DkgIndex, +}; -type RotationSessions = BoundedVec>; +type RotationSessions = BoundedVec< + ExodusContext>, + <::NetworkDataHandler as NetworkDataBasicHandler>::MaxNetworks, +>; -#[derive(Default, Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] -pub struct ReadyDkgAuthorities { - authorities_indexes: AuthoritiesBitmap, +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct ReadyDkgAuthorities { + authorities_indexes: ParticipantsBitmap, dkg_index: DkgIndex, } -impl ReadyDkgAuthorities +impl Default for ReadyDkgAuthorities { + fn default() -> Self { + Self { + authorities_indexes: Default::default(), + dkg_index: Default::default(), + } + } +} + +impl ReadyDkgAuthorities where - AuthoritiesBitmap: BoundedBitmapReader, + ParticipantsBitmap: BoundedBitmapReader, { pub fn contains_index(&self, index: N) -> bool where N: UniqueSaturatedInto, @@ -30,7 +47,7 @@ where self.authorities_indexes.contains(index) } - pub fn get_indexes(&self) -> &AuthoritiesBitmap { + pub fn get_indexes(&self) -> &ParticipantsBitmap { &self.authorities_indexes } @@ -39,15 +56,15 @@ where } } -impl - From<&PendingDkgAuthorities> - for ReadyDkgAuthorities +impl + From<&PendingDkgAuthorities> + for ReadyDkgAuthorities where - Block: Copy, - AuthoritiesBitmap: BoundedBitmapWriter + BoundedBitmapReader + BoundedBitmapGenerator + Clone, - for<'a> AuthoritiesBitmap: core::ops::BitAndAssign<&'a AuthoritiesBitmap>, + BlockNumberFor: Copy, + ParticipantsBitmap: BoundedBitmapWriter + BoundedBitmapReader + BoundedBitmapGenerator + Clone, + for<'a> ParticipantsBitmap: core::ops::BitAndAssign<&'a ParticipantsBitmap>, { - fn from(pending: &PendingDkgAuthorities) -> Self { + fn from(pending: &PendingDkgAuthorities) -> Self { Self { authorities_indexes: pending.get_indexes().clone(), dkg_index: pending.get_dkg_index(), @@ -55,34 +72,40 @@ where } } -#[derive(Default, Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] -pub struct RotationSession { - pub exodus_session: ExodusSession, - pub network_type: NetworkType -} - -#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] -pub struct PendingDkgAuthorities { +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct PendingDkgAuthorities { verifying_key: BoundedVec>, - authorities_indexes: AuthoritiesBitmap, - rotation_sessions: RotationSessions, - dkg_state: DkgState, + authorities_indexes: ParticipantsBitmap, + rotation_sessions: RotationSessions, + dkg_state: DkgState>, } -impl PendingDkgAuthorities +impl Default for PendingDkgAuthorities { + fn default() -> Self { + Self { + verifying_key: Default::default(), + authorities_indexes: Default::default(), + rotation_sessions: Default::default(), + dkg_state: Default::default(), + } + } +} + +impl PendingDkgAuthorities where - Block: Copy, - AuthoritiesBitmap: BoundedBitmapWriter + BoundedBitmapReader + BoundedBitmapGenerator + Clone, - for<'a> AuthoritiesBitmap: core::ops::BitAndAssign<&'a AuthoritiesBitmap>, + BlockNumberFor: Copy, + ParticipantsBitmap: BoundedBitmapWriter + BoundedBitmapReader + BoundedBitmapGenerator + Clone, + for<'a> ParticipantsBitmap: core::ops::BitAndAssign<&'a ParticipantsBitmap>, { pub fn new_dkg(&mut self, authorities_len: usize) { - self.authorities_indexes = AuthoritiesBitmap::empty(authorities_len); + self.authorities_indexes = ParticipantsBitmap::::empty(authorities_len); self.dkg_state.restart(); self.rotation_sessions = Default::default(); } - pub fn set_block(&mut self, block: Block) { - self.dkg_state.set_block(block); + pub fn set_release_block(&mut self, block: BlockNumberFor) { + self.dkg_state.set_release_block(block); } pub fn set_verifying_key( @@ -109,7 +132,7 @@ where } #[inline(always)] - pub fn intersect_indexes(&mut self, mask: &AuthoritiesBitmap) { + pub fn intersect_indexes(&mut self, mask: &ParticipantsBitmap) { self.authorities_indexes &= mask; } @@ -125,8 +148,8 @@ where self.dkg_state.get_dkg_index() } - pub fn get_block(&self) -> Block { - self.dkg_state.get_block() + pub fn get_release_block(&self) -> BlockNumberFor { + self.dkg_state.get_release_block() } pub fn get_phase(&self) -> DkgPhase { @@ -183,17 +206,11 @@ where self.authorities_indexes.insert(i) } - pub fn try_push_rotation_session( + pub fn try_push_rotation_context( &mut self, - exodus_session: ExodusSession, - network_type: NetworkType, + context: ExodusContext>, ) -> Option<()> { - let rotation_session = RotationSession { - exodus_session, - network_type, - }; - - self.rotation_sessions.try_push(rotation_session).ok() + self.rotation_sessions.try_push(context).ok() } pub fn count_ones(&self) -> N @@ -208,43 +225,44 @@ where self.authorities_indexes.highest_bit() } - pub fn get_indexes(&self) -> &AuthoritiesBitmap { + pub fn get_indexes(&self) -> &ParticipantsBitmap { &self.authorities_indexes } - pub fn iter_rotation_sessions(&self) -> impl Iterator { + pub fn iter_rotation_contexts( + &self + ) -> impl Iterator>> { self.rotation_sessions.iter() } - } #[derive(Default, Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct DkgState { - block: Block, - index: DkgIndex, - phase: DkgPhase, + release_block: Block, + dkg_index: DkgIndex, + dkg_phase: DkgPhase, } impl DkgState { - fn is_dkg_pending(&self) -> bool { self.phase.is_dkg_pending() } - fn is_dkg_vacant(&self) -> bool { self.phase.is_dkg_vacant() } + fn is_dkg_pending(&self) -> bool { self.dkg_phase.is_dkg_pending() } + fn is_dkg_vacant(&self) -> bool { self.dkg_phase.is_dkg_vacant() } - fn get_dkg_index(&self) -> DkgIndex { self.index } - fn get_phase(&self) -> DkgPhase { self.phase } - fn get_block(&self) -> Block { self.block } + fn get_dkg_index(&self) -> DkgIndex { self.dkg_index } + fn get_phase(&self) -> DkgPhase { self.dkg_phase } + fn get_release_block(&self) -> Block { self.release_block } fn restart(&mut self) { - self.phase = Default::default(); - self.index = self.index.checked_add(1) + self.dkg_phase = Default::default(); + self.dkg_index = self.dkg_index.checked_add(1) .unwrap_or_default(); } fn next_phase(&mut self) { - self.phase.next_phase() + self.dkg_phase.next_phase() } - fn set_block(&mut self, block: Block) { - self.block = block; + fn set_release_block(&mut self, release_block: Block) { + self.release_block = release_block; } } diff --git a/pallets/exodus/src/types/exodus_request.rs b/pallets/exodus/src/types/exodus_request.rs index 1e624e1..66c22f2 100644 --- a/pallets/exodus/src/types/exodus_request.rs +++ b/pallets/exodus/src/types/exodus_request.rs @@ -6,7 +6,7 @@ use sp_runtime::{ }; use crate::{ - EvmAddress, EvmBytes32, ExodusSession, RoastSession, + EvmAddress, EvmBytes32, ExodusContext, RoastSession, MAX_MESSAGE_SIZE, }; @@ -172,7 +172,7 @@ pub enum GovernanceStatus { RewardManager = 4, } -#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)] +#[derive(Encode, Decode, Clone, Copy, PartialEq, TypeInfo, RuntimeDebug)] pub enum GovernanceAction { SetDistributor { distributor: EvmAddress }, SetWarmupPeriod { warmup_period: u128 }, @@ -308,60 +308,59 @@ impl GovernanceAction { } } -#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)] -pub struct ExodusRequest { - pub r#type: ExodusRequestType, +#[derive(Encode, Decode, Clone, Copy, PartialEq, TypeInfo, RuntimeDebug)] +pub struct ExodusRequest { + pub r#type: ExodusRequestType, pub next_roast_session: RoastSession, } -#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)] -pub enum ExodusRequestType { - EvmRotation { - exodus_session: ExodusSession, - public_key: EvmBytes32, - parity: u8, - }, +#[derive(Encode, Decode, Clone, Copy, PartialEq, TypeInfo, RuntimeDebug)] +pub enum ExodusRequestType { + EvmRotation { public_key: EvmBytes32, parity: u8 }, UtxoRotation, - EvmBridgeOut { - exodus_session: ExodusSession, - network_id: NetworkId, - amount: Balance, - bounty: Perbill, - receiver: EvmAddress, - }, - EvmGovernance { - exodus_session: ExodusSession, - network_id: NetworkId, - target: EvmAddress, - action: GovernanceAction, - }, - UtxoBridgeOut { network_id: NetworkId }, + EvmBridgeOut { amount: Balance, bounty: Perbill, receiver: EvmAddress }, + EvmGovernance { target: EvmAddress, action: GovernanceAction }, + UtxoBridgeOut, } -impl ExodusRequestType +impl ExodusRequestType where - NetworkId: UniqueSaturatedInto + Copy, Balance: UniqueSaturatedInto + Copy, { - pub fn get_message<'a>( + pub fn get_message<'a, NetworkId>( &self, message_buffer: &'a mut [u8; MAX_MESSAGE_SIZE as usize], - ) -> Option<&'a [u8]> { + exodus_context: ExodusContext, + ) -> Option<&'a [u8]> + where + NetworkId: UniqueSaturatedInto + Copy + Default, + { match &self { - ExodusRequestType::EvmRotation { exodus_session, public_key, parity } => { + ExodusRequestType::EvmRotation { public_key, parity } => { + let mut buffer = [0u8; 32]; + buffer[0] = *parity; + + let chain_id_u64: u64 = exodus_context.network_id + .unique_saturated_into(); + + buffer[1..9].copy_from_slice(&chain_id_u64.to_be_bytes()); + + let packed_metadata = EvmBytes32::from(buffer); + abi_encode_evm_message!( message_buffer, - "rotate(uint256,bytes32,uint8)", - [exodus_session, public_key, parity] + "rotate(uint256,bytes32,uint256)", + [&exodus_context.session, public_key, &packed_metadata] ) }, - ExodusRequestType::EvmGovernance { exodus_session, network_id, target, action } => { + ExodusRequestType::EvmGovernance { target, action } => { let mut action_buffer = [0u8; MAX_MESSAGE_SIZE as usize]; let action_payload = action.get_message(&mut action_buffer)?; let mut metadata_buffer = [0u8; 32]; + let chain_id_u64: u64 = exodus_context.network_id + .unique_saturated_into(); - let chain_id_u64: u64 = (*network_id).unique_saturated_into(); metadata_buffer[0..8].copy_from_slice(&chain_id_u64.to_be_bytes()); metadata_buffer[8..28].copy_from_slice(target.as_ref()); @@ -370,14 +369,16 @@ where abi_encode_evm_message!( message_buffer, "govern(uint256,uint256,bytes)", - [exodus_session, &packed_metadata, action_payload] + [&exodus_context.session, &packed_metadata, action_payload] ) }, - ExodusRequestType::EvmBridgeOut { exodus_session, network_id, amount, bounty, receiver } => { + ExodusRequestType::EvmBridgeOut { amount, bounty, receiver } => { let mut buffer = [0u8; 32]; buffer[0..20].copy_from_slice(receiver.as_ref()); - let chain_id_u64: u64 = (*network_id).unique_saturated_into(); + let chain_id_u64: u64 = exodus_context.network_id + .unique_saturated_into(); + buffer[20..28].copy_from_slice(&chain_id_u64.to_be_bytes()); let scaled_bounty = multiply_by_rational_with_rounding( @@ -400,7 +401,7 @@ where abi_encode_evm_message!( message_buffer, "recall(uint256,uint256,uint256)", - [exodus_session, &amount_u256, &packed_metadata] + [&exodus_context.session, &amount_u256, &packed_metadata] ) } _ => None @@ -408,41 +409,31 @@ where } } -impl ExodusRequest +impl ExodusRequest where - NetworkId: UniqueSaturatedInto + Copy, Balance: UniqueSaturatedInto + Copy, { - pub fn get_message<'a>( + pub fn get_message<'a, NetworkId>( &self, message_buffer: &'a mut [u8; MAX_MESSAGE_SIZE as usize], - ) -> Option<&'a [u8]> { - self.r#type.get_message(message_buffer) + exodus_context: ExodusContext, + ) -> Option<&'a [u8]> + where + NetworkId: UniqueSaturatedInto + Copy + Default, + { + self.r#type.get_message(message_buffer, exodus_context) } - pub fn evm_rotation( - exodus_session: ExodusSession, - public_key: EvmBytes32, - parity: u8, - ) -> Self { + pub fn evm_rotation(public_key: EvmBytes32, parity: u8) -> Self { let rotation = ExodusRequestType::EvmRotation { - exodus_session, public_key, parity, }; Self::default_request_for(rotation) } - pub fn evm_bridge_out( - exodus_session: ExodusSession, - network_id: NetworkId, - amount: Balance, - bounty: Perbill, - receiver: EvmAddress, - ) -> Self { + pub fn evm_bridge_out(amount: Balance, bounty: Perbill, receiver: EvmAddress) -> Self { let evm_bridge_out = ExodusRequestType::EvmBridgeOut { - exodus_session, - network_id, amount, bounty, receiver, @@ -450,15 +441,8 @@ where Self::default_request_for(evm_bridge_out) } - pub fn evm_governance( - exodus_session: ExodusSession, - network_id: NetworkId, - target: EvmAddress, - action: GovernanceAction, - ) -> Self { + pub fn evm_governance(target: EvmAddress, action: GovernanceAction) -> Self { let evm_governance = ExodusRequestType::EvmGovernance { - exodus_session, - network_id, target, action, }; @@ -472,10 +456,11 @@ where } } - fn default_request_for( - request_type: ExodusRequestType, - ) -> Self { - Self { r#type: request_type, next_roast_session: Default::default() } + fn default_request_for(request_type: ExodusRequestType) -> Self { + Self { + r#type: request_type, + next_roast_session: Default::default() + } } } @@ -488,43 +473,49 @@ mod tests { fn test_rotation_message_valid() { let mut buffer = [0u8; MAX_MESSAGE_SIZE as usize]; let public_key = EvmBytes32::repeat_byte(0x11); - let session = 69u64; + let session = 69; + let network_id = 420; - let rotation_request = ExodusRequest::::evm_rotation( - session, + let context = ExodusContext::new(network_id, session); + + let rotation_request = ExodusRequest::::evm_rotation( public_key.clone(), 1, ); - let calldata = rotation_request.get_message(&mut buffer).unwrap(); + let calldata = rotation_request.get_message(&mut buffer, context).unwrap(); assert_eq!(calldata.len(), 100); - let expected_hash = keccak_256("rotate(uint256,bytes32,uint8)".as_bytes()); + let expected_hash = keccak_256("rotate(uint256,bytes32,uint256)".as_bytes()); assert_eq!(calldata[0..4], expected_hash[0..4]); assert_eq!(calldata[35], 0x45); assert_eq!(&calldata[4..35], &[0u8; 31]); assert_eq!(&calldata[36..68], public_key.as_ref()); - assert_eq!(calldata[99], 1); - assert_eq!(&calldata[68..99], &[0u8; 31]); + + assert_eq!(calldata[68], 1); + assert_eq!(&calldata[69..75], &[0u8; 6]); + assert_eq!(calldata[75], 0x01); + assert_eq!(calldata[76], 0xA4); + assert_eq!(&calldata[77..100], &[0u8; 23]); } #[test] fn test_bridge_out_message_valid() { let mut buffer = [0u8; MAX_MESSAGE_SIZE as usize]; let receiver = EvmAddress::repeat_byte(0xAA); - let network_id = 420u64; - let session = 69u64; + let network_id = 420; + let session = 69; - let bridge_request = ExodusRequest::::evm_bridge_out( - session, - network_id, + let context = ExodusContext::new(network_id, session); + + let bridge_request = ExodusRequest::::evm_bridge_out( 1_000_000u128, sp_runtime::Perbill::from_percent(1), receiver.clone() ); - let calldata = bridge_request.get_message(&mut buffer).unwrap(); + let calldata = bridge_request.get_message(&mut buffer, context).unwrap(); assert_eq!(calldata.len(), 100); let expected_hash = keccak_256("recall(uint256,uint256,uint256)".as_bytes()); @@ -549,8 +540,8 @@ mod tests { fn test_governance_message_valid() { let mut buffer = [0u8; MAX_MESSAGE_SIZE as usize]; let target = EvmAddress::repeat_byte(0xAA); - let network_id = 420u64; - let session = 69u64; + let network_id = 420; + let session = 69; let actions = vec![ GovernanceAction::SetDistributor { distributor: EvmAddress::repeat_byte(0x11) }, @@ -592,16 +583,16 @@ mod tests { for (index, action) in actions.into_iter().enumerate() { let action_clone = action.clone(); + let current_session = session + (index as u64); + let context = ExodusContext::new(network_id, current_session); - let governance_request = ExodusRequest::::evm_governance( - session, - network_id, + let governance_request = ExodusRequest::::evm_governance( target, action, ); let message = governance_request - .get_message(&mut buffer) + .get_message(&mut buffer, context) .get_or_insert(&[]) .to_vec(); @@ -610,7 +601,7 @@ mod tests { let expected_master_hash = keccak_256("govern(uint256,uint256,bytes)".as_bytes()); assert_eq!(message[0..4], expected_master_hash[0..4]); - assert_eq!(message[35], 69); + assert_eq!(message[35], current_session as u8); assert_eq!(&message[4..35], &[0u8; 31]); assert_eq!(message[43], 0xA4); diff --git a/pallets/exodus/src/types/local_storage.rs b/pallets/exodus/src/types/local_storage.rs index 5f5327a..f978496 100644 --- a/pallets/exodus/src/types/local_storage.rs +++ b/pallets/exodus/src/types/local_storage.rs @@ -8,7 +8,7 @@ use sp_runtime::{ }; use super::{DkgPackage, ExodusPackage, ExodusOk, PackageContext}; -use crate::{AuthIndex, DkgIndex, ExodusSession, ExodusHash, ExodusError}; +use crate::{AuthIndex, ExodusContext, DkgIndex, ExodusHash, ExodusError}; fn create_storage_key(prefix: &[u8], index: &[u8], network: &[u8]) -> Vec { let mut key = crate::DB_STORAGE_PREFIX.to_vec(); @@ -59,10 +59,10 @@ pub struct ExodusShare; #[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] pub struct ExodusExile; -type ExodusNonces = BTreeMap>; -type ExodusGroups = BTreeMap>; -type ExodusShares = BTreeMap>; -type ExodusExiles = BTreeMap>; +type ExodusNonces = BTreeMap, PersistentData>; +type ExodusGroups = BTreeMap, PersistentData>; +type ExodusShares = BTreeMap, PersistentData>; +type ExodusExiles = BTreeMap, PersistentData>; pub struct ExodusStorageWrapper { pub inner: ExodusInnerType, @@ -113,34 +113,37 @@ where self } - pub fn read_local_storage(&self) -> ExodusRecords { + pub fn read_local_storage(&self) -> ExodusRecords + where + NI: Default + Encode + Decode + AtLeast32BitUnsigned, + { let encoded_index = self.authority_index.encode(); let encoded_network = self.network_curve.encode(); let nonces_key = create_storage_key(crate::EXODUS_PREFIX_NONCES, &encoded_index, &encoded_network); let exodus_nonces = StorageValueRef::persistent(&nonces_key) - .get::>() + .get::>() .ok() .flatten() .unwrap_or_default(); let shares_key = create_storage_key(crate::EXODUS_PREFIX_SHARES, &encoded_index, &encoded_network); let exodus_shares = StorageValueRef::persistent(&shares_key) - .get::>() + .get::>() .ok() .flatten() .unwrap_or_default(); let groups_key = create_storage_key(crate::EXODUS_PREFIX_BLENDS, &encoded_index, &encoded_network); let exodus_groups = StorageValueRef::persistent(&groups_key) - .get::>() + .get::>() .ok() .flatten() .unwrap_or_default(); let exiles_key = create_storage_key(crate::EXODUS_PREFIX_EXILES, &encoded_index, &encoded_network); let exodus_exiles = StorageValueRef::persistent(&exiles_key) - .get::>() + .get::>() .ok() .flatten() .unwrap_or_default(); @@ -153,11 +156,11 @@ where } } - pub fn prepare_signed_call_with_context( + pub fn prepare_signed_call_with_context( &self, authority_key: &A, current_dkg_index: DkgIndex, - exodus_records: &mut ExodusRecords, + exodus_records: &mut ExodusRecords, make_data: F1, submit_call: F2, insert_data: F3, @@ -165,11 +168,13 @@ where where T: crate::pallet::Config, A: RuntimeAppPublic, - F1: FnOnce(PackageContext) -> Result, ExodusError>, - F2: FnOnce(ExodusPackage, A::Signature) -> Result<(), ExodusError>, - F3: FnOnce(&mut ExodusRecords, B), + NI: Default + Encode + Decode + AtLeast32BitUnsigned, + F1: FnOnce(PackageContext) -> Result, ExodusError>, + F2: FnOnce(ExodusPackage, A::Signature) -> Result<(), ExodusError>, + F3: FnOnce(&mut ExodusRecords, B), { let context = PackageContext::default() + .with_block_number(self.current_block) .with_authority_index(self.authority_index) .with_network_curve(self.network_curve) .with_dkg_index(current_dkg_index); @@ -205,7 +210,10 @@ where StorageValueRef::persistent(&exiles_key).clear(); } - pub fn try_write_local_storage(&self, records: &ExodusRecords) { + pub fn try_write_local_storage(&self, records: &ExodusRecords) + where + NI: Encode + Decode + AtLeast32BitUnsigned, + { let encoded_index = self.authority_index.encode(); let encoded_network = self.network_curve.encode(); @@ -231,99 +239,104 @@ where } } -pub struct ExodusRecords +pub struct ExodusRecords where + NetworkId: Default, BlockNumber: AtLeast32BitUnsigned, { - exodus_nonces: ExodusStorageWrapper>, - exodus_shares: ExodusStorageWrapper>, - exodus_groups: ExodusStorageWrapper>, - exodus_exiles: ExodusStorageWrapper>, + exodus_nonces: ExodusStorageWrapper>, + exodus_shares: ExodusStorageWrapper>, + exodus_groups: ExodusStorageWrapper>, + exodus_exiles: ExodusStorageWrapper>, } -impl ExodusRecords +impl ExodusRecords where BlockNumber: AtLeast32BitUnsigned + Default, + NetworkId: AtLeast32BitUnsigned + Default, { - pub fn get_secret_nonce(&self, exodus_session: &ExodusSession) -> Option<&[u8]> { + pub fn get_secret_nonce( + &self, + exodus_context: &ExodusContext, + ) -> Option<&[u8]> { self.exodus_nonces.inner - .get(exodus_session) + .get(exodus_context) .map(|v| v.data.0.as_slice()) } pub fn get_group_record( &self, - exodus_session: &ExodusSession, + exodus_context: &ExodusContext, ) -> Option { self.exodus_groups.inner - .get(exodus_session) + .get(exodus_context) .cloned() .map(|storage| storage.data) } pub fn is_exodus_session_banned( &self, - exodus_session: &ExodusSession, + exodus_context: &ExodusContext, ) -> bool { - self.exodus_exiles.inner.contains_key(exodus_session) + self.exodus_exiles.inner.contains_key(exodus_context) } pub fn is_group_registered( &self, - exodus_session: &ExodusSession, + exodus_context: &ExodusContext, ) -> bool { - self.exodus_groups.inner.contains_key(exodus_session) + self.exodus_groups.inner.contains_key(exodus_context) } pub fn is_waiting_period_for_nonce( &self, - exodus_session: &ExodusSession, - current_block: &BlockNumber + exodus_context: &ExodusContext, + current_block: &BlockNumber, ) -> bool { self.exodus_nonces.inner - .get(exodus_session) + .get(exodus_context) .map(|data| data.release_block.gt(current_block)) .unwrap_or(false) } pub fn is_waiting_period_for_share( &self, - exodus_session: &ExodusSession, + exodus_context: &ExodusContext, current_block: &BlockNumber, ) -> bool { self.exodus_shares.inner - .get(exodus_session) + .get(exodus_context) .map(|data| data.release_block.gt(¤t_block)) .unwrap_or(false) } pub fn is_waiting_period_for_group( &self, - exodus_session: &ExodusSession, + exodus_context: &ExodusContext, current_block: &BlockNumber, ) -> bool { self.exodus_groups.inner - .get(exodus_session) + .get(exodus_context) .map(|data| data.release_block.gt(¤t_block)) .unwrap_or(false) } pub fn exclude_exodus_session( &mut self, - exodus_session: ExodusSession, + exodus_context: ExodusContext ) { let exodus_exile = PersistentData { data: ExodusExile, release_block: Default::default(), }; - self.exodus_exiles.inner.insert(exodus_session, exodus_exile); self.exodus_exiles.data_changed = true; + self.exodus_exiles.inner.insert(exodus_context, exodus_exile); } pub fn insert_exodus_nonce( &mut self, - exodus_session: ExodusSession, + exodus_context: ExodusContext, next_release_block: BlockNumber, nonce_commitment: Vec, ) { @@ -332,13 +345,13 @@ where release_block: next_release_block, }; - self.exodus_nonces.inner.insert(exodus_session, exodus_nonce); self.exodus_nonces.data_changed = true; + self.exodus_nonces.inner.insert(exodus_context, exodus_nonce); } pub fn insert_exodus_share( &mut self, - exodus_session: ExodusSession, + exodus_context: ExodusContext, next_release_block: BlockNumber, ) { let exodus_share = PersistentData { @@ -346,13 +359,13 @@ where release_block: next_release_block, }; - self.exodus_shares.inner.insert(exodus_session, exodus_share); self.exodus_shares.data_changed = true; + self.exodus_shares.inner.insert(exodus_context, exodus_share); } pub fn insert_exodus_group( &mut self, - exodus_session: ExodusSession, + exodus_context: ExodusContext, next_release_block: BlockNumber, self_binding_factor: Vec, binding_factors_proof: Vec, @@ -362,8 +375,8 @@ where release_block: next_release_block, }; - self.exodus_groups.inner.insert(exodus_session, exodus_group); self.exodus_groups.data_changed = true; + self.exodus_groups.inner.insert(exodus_context, exodus_group); } } @@ -408,10 +421,10 @@ where self.current_block.saturating_add(self.block_longevity) } - pub fn get_dkg_result( + pub fn get_dkg_result( &self, dkg_record: DkgRecord, - ) -> Result, ExodusError> { + ) -> Result, ExodusError> { match dkg_record.prefix_slice { crate::DKG_ROUND0_PREFIX => return Ok(ExodusOk::DkgRoundRegistered(0u8, self.authority_index, self.network_curve)), crate::DKG_ROUND1_PREFIX => return Ok(ExodusOk::DkgRoundRegistered(1u8, self.authority_index, self.network_curve)), @@ -464,10 +477,11 @@ where where T: crate::pallet::Config, A: RuntimeAppPublic, - F1: FnOnce(PackageContext) -> Result, ExodusError>, - F2: FnOnce(DkgPackage, A::Signature) -> Result<(), ExodusError>, + F1: FnOnce(PackageContext) -> Result, ExodusError>, + F2: FnOnce(DkgPackage, A::Signature) -> Result<(), ExodusError>, { let context = PackageContext::default() + .with_block_number(self.current_block) .with_authority_index(self.authority_index) .with_network_curve(self.network_curve) .with_dkg_index(current_dkg_index); diff --git a/pallets/exodus/src/types/packages.rs b/pallets/exodus/src/types/packages.rs index 72914b3..18d1b4c 100644 --- a/pallets/exodus/src/types/packages.rs +++ b/pallets/exodus/src/types/packages.rs @@ -6,37 +6,58 @@ use codec::{Encode, Decode}; use frame_support::{BoundedVec, traits::ConstU32}; use crate::{AuthIndex, DkgIndex, ExodusSession, ExodusHash, ExodusError}; -pub trait PackageMetadata +pub trait PackageMetadata where NetworkCurve: Copy, + BlockNumber: Copy, { - fn context(&self) -> &PackageContext; + fn package_context(&self) -> &PackageContext; fn get_authority_index(&self) -> AuthIndex { - self.context().get_authority_index() + self.package_context().get_authority_index() } fn get_network_curve(&self) -> NetworkCurve { - self.context().get_network_curve() + self.package_context().get_network_curve() } fn get_dkg_index(&self) -> DkgIndex { - self.context().get_dkg_index() + self.package_context().get_dkg_index() } + fn get_block_number(&self) -> BlockNumber { + self.package_context().get_block_number() + } + fn get_metadata(&self) -> (NetworkCurve, AuthIndex, DkgIndex) { + let network_curve = self.get_network_curve(); + let authority_index = self.get_authority_index(); + let dkg_index = self.get_dkg_index(); + + (network_curve, authority_index, dkg_index) + } +} + +pub trait ExodusMetadata +where + NetworkId: Default + Copy, +{ + fn get_exodus_context(&self) -> &ExodusContext; + fn copy_exodus_context(&self) -> ExodusContext; } #[derive(Encode, Decode, Clone, RuntimeDebug, TypeInfo, PartialEq, Eq)] #[scale_info(skip_type_params(T))] -pub enum ExodusPackage +pub enum ExodusPackage where + NetworkId: Default, T: crate::pallet::Config, { - NonceCommitment(ExodusNonceCommitment), - GroupCommitment(ExodusGroupCommitment), - SignatureShare(ExodusSignatureShare), + NonceCommitment(ExodusNonceCommitment), + GroupCommitment(ExodusGroupCommitment), + SignatureShare(ExodusSignatureShare), } -impl ExodusPackage +impl ExodusPackage where T: crate::pallet::Config, + NI: Default, { pub fn bytes_len(&self) -> u32 { let length = match self { @@ -54,32 +75,24 @@ where length as u32 } - - pub fn get_exodus_session(&self) -> ExodusSession { - match &self { - Self::SignatureShare(package) => package.session, - Self::NonceCommitment(package) => package.session, - Self::GroupCommitment(package) => package.session, - } - } } #[derive(Encode, Decode, Clone, RuntimeDebug, TypeInfo, PartialEq, Eq)] #[scale_info(skip_type_params(T))] -pub enum DkgPackage +pub enum DkgPackage where T: crate::pallet::Config, { - Round0(Round0Package), - Round1(Round1Package), - Round2(Round2Package), - Round3(Round3Package), - Round4(Round4Package), - Round5(Round5Package), - Round6(Round6Package), + Round0(Round0Package), + Round1(Round1Package), + Round2(Round2Package), + Round3(Round3Package), + Round4(Round4Package), + Round5(Round5Package), + Round6(Round6Package), } -impl DkgPackage +impl DkgPackage where T: crate::pallet::Config, { @@ -102,15 +115,17 @@ where } #[derive(Default, RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] -pub struct PackageContext { +pub struct PackageContext { authority_index: AuthIndex, network_curve: NetworkCurve, + block_number: BlockNumber, dkg_index: DkgIndex, } -impl PackageContext +impl PackageContext where NetworkCurve: Copy, + BlockNumber: Copy, { pub fn get_authority_index(&self) -> AuthIndex { self.authority_index @@ -124,6 +139,10 @@ where self.dkg_index } + pub fn get_block_number(&self) -> BlockNumber { + self.block_number + } + pub fn with_network_curve(mut self, network_curve: NetworkCurve) -> Self { self.network_curve = network_curve; self @@ -138,6 +157,11 @@ where self.dkg_index = dkg_index; self } + + pub fn with_block_number(mut self, block_number: BlockNumber) -> Self { + self.block_number = block_number; + self + } } #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] @@ -214,89 +238,112 @@ impl TryFrom for JustificationsBundle } } +#[derive(RuntimeDebug, Default, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Encode, Decode, TypeInfo)] +pub struct ExodusContext { + pub session: ExodusSession, + pub network_id: NetworkId, +} + +impl ExodusContext { + pub fn new(network_id: NetworkId, session: ExodusSession) -> Self { + Self { session, network_id } + } + + pub fn with_network_id(mut self, network_id: NetworkId) -> Self { + self.network_id = network_id; + self + } + + pub fn with_exodus_session(mut self, session: ExodusSession) -> Self { + self.session = session; + self + } +} + #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] #[scale_info(skip_type_params(T))] -pub struct ExodusNonceCommitment { - context: PackageContext, +pub struct ExodusNonceCommitment { + package_context: PackageContext, + exodus_context: ExodusContext, pub hiding_commitment: BoundedVec>, pub binding_commitment: BoundedVec>, - pub session: ExodusSession, } #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] -pub struct ExodusGroupCommitment { - context: PackageContext, +pub struct ExodusGroupCommitment { + package_context: PackageContext, + exodus_context: ExodusContext, pub group_commitment: BoundedVec>, pub binding_factors_root: ExodusHash, - pub session: ExodusSession, } #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] #[scale_info(skip_type_params(T))] -pub struct ExodusSignatureShare +pub struct ExodusSignatureShare where + NetworkId: Default, T: crate::pallet::Config, { - context: PackageContext, + package_context: PackageContext, + exodus_context: ExodusContext, pub binding_factors_proof: BoundedVec>, pub signature_share: BoundedVec>, pub self_binding_factor: BoundedVec>, - pub session: ExodusSession, } #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] -pub struct Round0Package { - context: PackageContext, +pub struct Round0Package { + context: PackageContext, pub package_hash: ExodusHash, } #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] #[scale_info(skip_type_params(T))] -pub struct Round1Package +pub struct Round1Package where T: crate::pallet::Config, { - context: PackageContext, + context: PackageContext, pub package: BoundedVec>, } #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] #[scale_info(skip_type_params(T))] -pub struct Round2Package +pub struct Round2Package where T: crate::pallet::Config, { - context: PackageContext, + context: PackageContext, pub bundle: EncryptionBundledPackages, } #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] #[scale_info(skip_type_params(T))] -pub struct Round3Package +pub struct Round3Package where T: crate::pallet::Config, { - context: PackageContext, pub indices: BoundedVec>, + context: PackageContext, } #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] #[scale_info(skip_type_params(T))] -pub struct Round4Package +pub struct Round4Package where T: crate::pallet::Config, { - context: PackageContext, + context: PackageContext, pub bundle: JustificationsBundledPackages, } #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] #[scale_info(skip_type_params(T))] -pub struct Round5Package +pub struct Round5Package where T: crate::pallet::Config, { - context: PackageContext, + context: PackageContext, pub key_members: BoundedVec>, pub verifying_key: BoundedVec>, pub merkle_root: ExodusHash, @@ -304,22 +351,22 @@ where #[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)] #[scale_info(skip_type_params(T))] -pub struct Round6Package +pub struct Round6Package where T: crate::pallet::Config, { - context: PackageContext, + context: PackageContext, pub verifying_share: BoundedVec>, pub merkle_proof: BoundedVec>, } -impl PackageContext { - pub fn with_exodus_nonce_commitment( +impl PackageContext { + pub fn with_exodus_nonce_commitment( self, - session: ExodusSession, + exodus_context: ExodusContext, hiding_commitment_vec: Vec, binding_commitment_vec: Vec, - ) -> Result, ExodusError> { + ) -> Result, ExodusError> { let hiding_commitment = BoundedVec::>::try_from(hiding_commitment_vec) .map_err(|_| ExodusError::DeserializationError)?; @@ -328,34 +375,39 @@ impl PackageContext { BoundedVec::>::try_from(binding_commitment_vec) .map_err(|_| ExodusError::DeserializationError)?; - Ok(ExodusNonceCommitment { context: self, hiding_commitment, binding_commitment, session }) + Ok(ExodusNonceCommitment { + package_context: self, + exodus_context, + hiding_commitment, + binding_commitment, + }) } - pub fn with_exodus_group_commitment( + pub fn with_exodus_group_commitment( self, - session: ExodusSession, + exodus_context: ExodusContext, commitment_vec: Vec, binding_factors_root: ExodusHash, - ) -> Result, ExodusError> { + ) -> Result, ExodusError> { let group_commitment = BoundedVec::>::try_from(commitment_vec) .map_err(|_| ExodusError::DeserializationError)?; Ok(ExodusGroupCommitment { - context: self, + package_context: self, + exodus_context, group_commitment, binding_factors_root, - session, }) } - pub fn with_exodus_signature_share( + pub fn with_exodus_signature_share( self, - session: ExodusSession, + exodus_context: ExodusContext, signature_share_vec: Vec, binding_factors_proof_vec: Vec, self_binding_factor_vec: Vec, - ) -> Result, ExodusError> { + ) -> Result, ExodusError> { let signature_share = BoundedVec::>::try_from(signature_share_vec) .map_err(|_| ExodusError::DeserializationError)?; @@ -369,25 +421,25 @@ impl PackageContext { .map_err(|_| ExodusError::DeserializationError)?; Ok(ExodusSignatureShare { - context: self, + package_context: self, + exodus_context, binding_factors_proof, self_binding_factor, signature_share, - session, }) } pub fn with_dkg_round0( self, package_hash: ExodusHash, - ) -> Round0Package { + ) -> Round0Package { Round0Package { context: self, package_hash } } pub fn with_dkg_round1( self, package_vec: Vec, - ) -> Result, ExodusError> { + ) -> Result, ExodusError> { let package = BoundedVec::>::try_from(package_vec) .map_err(|_| ExodusError::DeserializationError)?; @@ -398,7 +450,7 @@ impl PackageContext { pub fn with_dkg_round2( self, bundle_raw: BundledPackages, - ) -> Result, ExodusError> { + ) -> Result, ExodusError> { let bundle: EncryptionBundledPackages = bundle_raw.try_into()?; Ok(Round2Package { context: self, bundle }) } @@ -406,7 +458,7 @@ impl PackageContext { pub fn with_dkg_round3( self, indices_vec: Vec - ) -> Result, ExodusError> { + ) -> Result, ExodusError> { let indices = BoundedVec::>::try_from(indices_vec) .map_err(|_| ExodusError::DeserializationError)?; @@ -417,7 +469,7 @@ impl PackageContext { pub fn with_dkg_round4( self, bundle_raw: BundledPackages, - ) -> Result, ExodusError> { + ) -> Result, ExodusError> { let bundle: JustificationsBundledPackages = bundle_raw.try_into()?; Ok(Round4Package { context: self, bundle }) } @@ -427,7 +479,7 @@ impl PackageContext { verifying_key_vec: Vec, key_members_vec: Vec, merkle_root: ExodusHash, - ) -> Result, ExodusError> { + ) -> Result, ExodusError> { let key_members = BoundedVec::>::try_from(key_members_vec) .map_err(|_| ExodusError::DeserializationError)?; @@ -442,7 +494,7 @@ impl PackageContext { self, verifying_share_vec: Vec, merkle_proof_vec: Vec, - ) -> Result, ExodusError> { + ) -> Result, ExodusError> { let verifying_share = BoundedVec::>::try_from(verifying_share_vec) .map_err(|_| ExodusError::DeserializationError)?; @@ -455,26 +507,51 @@ impl PackageContext { } } -impl PackageMetadata for ExodusPackage +impl ExodusMetadata for ExodusPackage where - NC: Copy, + NI: Default + Copy, T: crate::pallet::Config, { - fn context(&self) -> &PackageContext { + fn copy_exodus_context(&self) -> ExodusContext { match self { - Self::NonceCommitment(package) => &package.context, - Self::GroupCommitment(package) => &package.context, - Self::SignatureShare(package) => &package.context, + Self::NonceCommitment(package) => (*package).exodus_context, + Self::GroupCommitment(package) => (*package).exodus_context, + Self::SignatureShare(package) => (*package).exodus_context, + } + } + + fn get_exodus_context(&self) -> &ExodusContext { + match self { + Self::NonceCommitment(package) => &package.exodus_context, + Self::GroupCommitment(package) => &package.exodus_context, + Self::SignatureShare(package) => &package.exodus_context, } } } -impl PackageMetadata for DkgPackage +impl PackageMetadata for ExodusPackage where + NI: Default, NC: Copy, + B: Copy, T: crate::pallet::Config, { - fn context(&self) -> &PackageContext { + fn package_context(&self) -> &PackageContext { + match self { + Self::NonceCommitment(package) => &package.package_context, + Self::GroupCommitment(package) => &package.package_context, + Self::SignatureShare(package) => &package.package_context, + } + } +} + +impl PackageMetadata for DkgPackage +where + NC: Copy, + B: Copy, + T: crate::pallet::Config, +{ + fn package_context(&self) -> &PackageContext { match self { Self::Round0(package) => &package.context, Self::Round1(package) => &package.context, diff --git a/pallets/exodus/src/types/signature.rs b/pallets/exodus/src/types/signature.rs index e255fed..f5e0d07 100644 --- a/pallets/exodus/src/types/signature.rs +++ b/pallets/exodus/src/types/signature.rs @@ -1,28 +1,32 @@ use scale_info::TypeInfo; use codec::{Encode, Decode}; use frame_support::{BoundedVec, traits::ConstU32}; -use sp_runtime::{RuntimeDebug, traits::UniqueSaturatedInto}; +use sp_runtime::{ + RuntimeDebug, + traits::{AtLeast32BitUnsigned, UniqueSaturatedInto}, +}; + +use crate::ExodusContext; +use super::ExodusRequestType; type ExodusNonceT = BoundedVec>; type ExodusSignatureT = BoundedVec>; -type Type = super::ExodusRequestType; #[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)] -pub struct ExodusSignedMessage { +pub struct ExodusSignedMessage { pub nonce: ExodusNonceT, pub signature: ExodusSignatureT, - pub r#type: Type, + pub r#type: ExodusRequestType, } -impl ExodusSignedMessage +impl ExodusSignedMessage where - NetworkId: UniqueSaturatedInto + Copy + Default, Balance: UniqueSaturatedInto + Copy + Default, { pub fn new( nonce_iter: E, signature_iter: S, - r#type: Type + r#type: ExodusRequestType, ) -> Option where E: Iterator + ExactSizeIterator, @@ -37,10 +41,11 @@ where Some(Self { nonce, signature, r#type }) } - pub fn get_message<'a>( + pub fn get_message<'a, NetworkId: Default + Copy + AtLeast32BitUnsigned>( &self, message_buffer: &'a mut [u8; crate::MAX_MESSAGE_SIZE as usize], + exodus_context: ExodusContext, ) -> Option<&'a [u8]> { - self.r#type.get_message(message_buffer) + self.r#type.get_message(message_buffer, exodus_context) } } diff --git a/pallets/exodus/src/types/success.rs b/pallets/exodus/src/types/success.rs index 6436e9b..0250c9f 100644 --- a/pallets/exodus/src/types/success.rs +++ b/pallets/exodus/src/types/success.rs @@ -1,16 +1,19 @@ +use sp_runtime::traits::UniqueSaturatedInto; use crate::{AuthIndex, ExodusSession, DkgIndex, RoastSession}; -pub enum ExodusOk { +pub enum ExodusOk { + ExodusRequestsFinished(AuthIndex, NetworkCurve, u32, u32), ExodusEmptyRequests(AuthIndex, NetworkCurve), ExodusWaitingState(AuthIndex, NetworkCurve), - ExodusNonceCommitted(ExodusSession, RoastSession, AuthIndex, NetworkCurve), - ExodusGroupCommitted(ExodusSession, RoastSession, AuthIndex, NetworkCurve), - ExodusSignedPartially(ExodusSession, RoastSession, AuthIndex, NetworkCurve), + ExodusNonceCommitted(ExodusSession, NetworkId, RoastSession, AuthIndex, NetworkCurve), + ExodusGroupCommitted(ExodusSession, NetworkId, RoastSession, AuthIndex, NetworkCurve), + ExodusSignedPartially(ExodusSession, NetworkId, RoastSession, AuthIndex, NetworkCurve), ExodusVerifyingShareMissed(AuthIndex, DkgIndex, NetworkCurve), DkgNotPartOfRound(u8, AuthIndex, NetworkCurve), DkgRoundRegistered(u8, AuthIndex, NetworkCurve), - DkgPreviousPurged(AuthIndex, NetworkCurve), + DkgPreviousPurged(AuthIndex, NetworkCurve, u32), + DkgLocalStoragePurged(AuthIndex, NetworkCurve, DkgIndex), DkgRoundsCleared(AuthIndex, NetworkCurve), DkgRoundAlreadyPassed(u8, AuthIndex, NetworkCurve), DkgRoundWaitingPeriod(u8, AuthIndex, NetworkCurve), @@ -18,27 +21,41 @@ pub enum ExodusOk { DkgRoundMaxSignersNarrowed(u8, AuthIndex, AuthIndex, AuthIndex, NetworkCurve), } -impl core::fmt::Display for ExodusOk +impl core::fmt::Debug for ExodusOk where - NetworkCurve: core::fmt::Display + NetworkCurve: core::fmt::Display, + NetworkId: UniqueSaturatedInto + Copy, { fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { match self { - ExodusOk::ExodusEmptyRequests(auth_index, curve) => write!(fmt, "No EXODUS requests found by authority #{auth_index} for curve {curve}."), - ExodusOk::ExodusWaitingState(auth_index, curve) => write!(fmt, "Waiting for EXODUS requests to be processed by authority #{auth_index} for curve {curve}."), - ExodusOk::ExodusNonceCommitted(exodus, roast, auth_index, curve) => write!(fmt, "Nonce committed for EXODUS {exodus:?} on ROAST session {roast:?} by authority #{auth_index} for curve {curve}."), - ExodusOk::ExodusGroupCommitted(exodus, roast, auth_index, curve) => write!(fmt, "Aggregated nonce and binding factor proof committed for EXODUS {exodus:?} on ROAST session {roast:?} by authority #{auth_index} for curve {curve}."), - ExodusOk::ExodusSignedPartially(exodus, roast, auth_index, curve) => write!(fmt, "Partial signature created for EXODUS {exodus:?} on ROAST session {roast:?} by authority #{auth_index} for curve {curve}."), - ExodusOk::ExodusVerifyingShareMissed(auth_index, dkg_index, curve) => write!(fmt, "EXODUS Round no verifying share for authority #{auth_index} based on DKG session #{dkg_index} for curve {curve}."), + ExodusOk::ExodusRequestsFinished(auth_index, curve, success_count, failed_count) => { + write!(fmt, "EXODUS requests processing round completed by authority #{auth_index} for curve {curve}: {success_count} tasks succeeded, {failed_count} tasks failed.") + } + ExodusOk::ExodusEmptyRequests(auth_index, curve) => write!(fmt, "No EXODUS requests found for authority #{auth_index} on curve {curve}."), + ExodusOk::ExodusWaitingState(auth_index, curve) => write!(fmt, "Waiting for EXODUS requests to be processed by authority #{auth_index} on curve {curve}."), + ExodusOk::ExodusNonceCommitted(exodus, network_id, roast, auth_index, curve) => { + let network_id_u64: u64 = (*network_id).unique_saturated_into(); + write!(fmt, "Nonce committed for EXODUS {exodus:?} on ROAST session {roast:?} by authority #{auth_index} for curve {curve} on network #{network_id_u64}.") + }, + ExodusOk::ExodusGroupCommitted(exodus, network_id, roast, auth_index, curve) => { + let network_id_u64: u64 = (*network_id).unique_saturated_into(); + write!(fmt, "Aggregated nonce and binding factor proof committed for EXODUS {exodus:?} on ROAST session {roast:?} by authority #{auth_index} for curve {curve} on network #{network_id_u64}.") + }, + ExodusOk::ExodusSignedPartially(exodus, network_id, roast, auth_index, curve) => { + let network_id_u64: u64 = (*network_id).unique_saturated_into(); + write!(fmt, "Partial signature created for EXODUS {exodus:?} on ROAST session {roast:?} by authority #{auth_index} for curve {curve} on network #{network_id_u64}.") + }, + ExodusOk::ExodusVerifyingShareMissed(auth_index, dkg_index, curve) => write!(fmt, "No verifying share found for authority #{auth_index} in EXODUS round based on DKG session #{dkg_index} for curve {curve}."), - ExodusOk::DkgNotPartOfRound(round, auth_index, curve) => write!(fmt, "DKG authority #{auth_index} for network curve {curve} is not part of Round{round}."), - ExodusOk::DkgRoundRegistered(round, auth_index, curve) => write!(fmt, "DKG Round{round} registered by authority #{auth_index} for network curve {curve}."), - ExodusOk::DkgPreviousPurged(auth_index, curve) => write!(fmt, "Storage prepared for new DKG session by authority #{auth_index} for network curve {curve}."), + ExodusOk::DkgNotPartOfRound(round, auth_index, curve) => write!(fmt, "DKG authority #{auth_index} is not part of Round {round} for network curve {curve}."), + ExodusOk::DkgRoundRegistered(round, auth_index, curve) => write!(fmt, "DKG Round {round} registered by authority #{auth_index} for network curve {curve}."), + ExodusOk::DkgPreviousPurged(auth_index, curve, removed_count) => write!(fmt, "Storage prepared for a new DKG session by authority #{auth_index} for network curve {curve}. {removed_count} entries removed."), + ExodusOk::DkgLocalStoragePurged(auth_index, curve, dkg_index) => write!(fmt, "Storage for DKG session #{dkg_index} cleared by authority #{auth_index} for network curve {curve}."), ExodusOk::DkgRoundsCleared(auth_index, curve) => write!(fmt, "DKG Rounds cleared from storage by authority #{auth_index} for network curve {curve}."), - ExodusOk::DkgRoundAlreadyPassed(round, auth_index, curve) => write!(fmt, "DKG Round{round} already done by authority #{auth_index} for network curve {curve}."), - ExodusOk::DkgRoundWaitingPeriod(round, auth_index, curve) => write!(fmt, "DKG Round{round} waiting to be applied by authority #{auth_index} for network curve {curve}."), - ExodusOk::DkgRoundNothingStored(round, auth_index, curve) => write!(fmt, "DKG nothing stored from Round{round} by authority #{auth_index} for network curve {curve}."), - ExodusOk::DkgRoundMaxSignersNarrowed(round, auth_index, prev_max, curr_max, curve) => write!(fmt, "DKG Round{round} narrowed max_signers from {prev_max} to {curr_max} by {auth_index} for network curve {curve}."), + ExodusOk::DkgRoundAlreadyPassed(round, auth_index, curve) => write!(fmt, "DKG Round {round} already completed by authority #{auth_index} for network curve {curve}."), + ExodusOk::DkgRoundWaitingPeriod(round, auth_index, curve) => write!(fmt, "DKG Round {round} is waiting to be applied by authority #{auth_index} for network curve {curve}."), + ExodusOk::DkgRoundNothingStored(round, auth_index, curve) => write!(fmt, "No data stored for DKG Round {round} by authority #{auth_index} for network curve {curve}."), + ExodusOk::DkgRoundMaxSignersNarrowed(round, auth_index, prev_max, curr_max, curve) => write!(fmt, "DKG Round {round} narrowed max_signers from {prev_max} to {curr_max} by authority #{auth_index} for network curve {curve}."), } } } diff --git a/pallets/networks/Cargo.toml b/pallets/networks/Cargo.toml index c1c186d..b73f6b9 100644 --- a/pallets/networks/Cargo.toml +++ b/pallets/networks/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghost-networks" -version = "0.2.10" +version = "0.2.11" description = "Registry and lifecycle management for external network metadata and cryptographic profiles." license.workspace = true authors.workspace = true diff --git a/pallets/networks/src/lib.rs b/pallets/networks/src/lib.rs index 6f10886..91f79c6 100644 --- a/pallets/networks/src/lib.rs +++ b/pallets/networks/src/lib.rs @@ -197,17 +197,21 @@ pub mod module { StorageValue<_, BoundedVec, ValueQuery>; #[pallet::storage] - #[pallet::getter(fn network_curves)] - pub type NetworkCurves = StorageMap< + #[pallet::getter(fn network_types)] + pub type NetworkTypes = StorageMap< _, Twox64Concat, NetworkCurve, - BoundedBTreeMap, + BoundedBTreeMap< + NetworkType, + BoundedVec, + T::MaxNetworks, + >, ValueQuery, >; - #[pallet::storage] - #[pallet::getter(fn network_curves_vec)] - pub type NetworkCurvesVec = + #[pallet::storage] + #[pallet::getter(fn network_curves)] + pub type NetworkCurves = StorageValue<_, BoundedVec, ValueQuery>; #[pallet::storage] @@ -429,24 +433,26 @@ impl Pallet { modified_indexes.try_push(chain_id.clone()) .map_err(|_| Error::::TooManyNetworks)?; - let mut modified_curves_map = NetworkCurves::::get(network_curve); - if let Some(counter) = modified_curves_map.get_mut(&network_type) { - *counter = counter.saturating_add(1); + let mut modified_types = NetworkTypes::::get(network_curve); + if let Some(chain_ids) = modified_types.get_mut(&network_type) { + chain_ids.try_push(chain_id).map_err(|_| Error::::TooManyNetworks)?; } else { - modified_curves_map.try_insert(network_type, 1u32) + let mut new_ids_vec = BoundedVec::::default(); + new_ids_vec.try_push(chain_id).map_err(|_| Error::::TooManyNetworks)?; + modified_types.try_insert(network_type, new_ids_vec) .map_err(|_| Error::::TooManyNetworks)?; } - let mut modified_curves_vec = NetworkCurvesVec::::get(); - if !modified_curves_vec.contains(&network_curve) { - modified_curves_vec.try_push(network_curve) + let mut modified_curves = NetworkCurves::::get(); + if !modified_curves.contains(&network_curve) { + modified_curves.try_push(network_curve) .map_err(|_| Error::::TooManyNetworks)?; } Networks::::insert(&chain_id, network); NetworkIndexes::::put(modified_indexes); - NetworkCurves::::insert(network_curve, modified_curves_map); - NetworkCurvesVec::::put(modified_curves_vec); + NetworkTypes::::insert(network_curve, modified_types); + NetworkCurves::::put(modified_curves); Self::deposit_event(Event::::NetworkRegistered { chain_id }); Ok(()) @@ -460,38 +466,38 @@ impl Pallet { let network_curve = network.curve; let network_type = network.r#type; - let mut modified_curves_map = NetworkCurves::::get(network_curve); + let mut modified_types = NetworkTypes::::get(network_curve); - let counter = modified_curves_map + let chain_ids = modified_types .get_mut(&network_type) .ok_or(Error::::NetworkTypeDoesNotExist)?; - *counter = counter.saturating_sub(1); + chain_ids.retain(|id| id != &chain_id); - let mut should_remove_curve_from_vec = false; - if *counter == 0 { - modified_curves_map.remove(&network_type); - if modified_curves_map.is_empty() { - should_remove_curve_from_vec = true; + let mut should_remove_curve = false; + if chain_ids.is_empty() { + modified_types.remove(&network_type); + if modified_types.is_empty() { + should_remove_curve = true; } } let mut modified_indexes = NetworkIndexes::::get(); modified_indexes.retain(|id| id != &chain_id); - let mut modified_curves_vec = NetworkCurvesVec::::get(); - if should_remove_curve_from_vec { - modified_curves_vec.retain(|curve| curve != &network_curve); + let mut modified_curves = NetworkCurves::::get(); + if should_remove_curve { + modified_curves.retain(|curve| curve != &network_curve); } Networks::::remove(&chain_id); NetworkIndexes::::put(modified_indexes); - NetworkCurvesVec::::put(modified_curves_vec); + NetworkCurves::::put(modified_curves); - if modified_curves_map.is_empty() { - NetworkCurves::::remove(network_curve); + if modified_types.is_empty() { + NetworkTypes::::remove(network_curve); } else { - NetworkCurves::::insert(network_curve, modified_curves_map); + NetworkTypes::::insert(network_curve, modified_types); } Self::deposit_event(Event::::NetworkRemoved { chain_id }); @@ -625,15 +631,46 @@ impl Pallet { chain_id: T::NetworkId, network_type: NetworkType, ) -> DispatchResult { - Networks::::try_mutate(&chain_id, |maybe_network| -> DispatchResult { - let network = maybe_network.as_mut().ok_or(Error::::NetworkDoesNotExist)?; - network.r#type = network_type; - Ok(()) - })?; + let mut network = Networks::::get(&chain_id) + .ok_or(Error::::NetworkDoesNotExist)?; + + let network_curve = network.curve; + let prev_type = network.r#type; + + if prev_type == network_type { + return Ok(()); + } + + let mut modified_types = NetworkTypes::::get(network_curve); + let prev_chain_ids = modified_types + .get_mut(&prev_type) + .ok_or(Error::::NetworkTypeDoesNotExist)?; + + prev_chain_ids.retain(|id| id != &chain_id); + + if prev_chain_ids.is_empty() { + modified_types.remove(&prev_type); + } + + if let Some(chain_ids) = modified_types.get_mut(&network_type) { + chain_ids.try_push(chain_id).map_err(|_| Error::::TooManyNetworks)?; + } else { + let mut new_ids_vec = BoundedVec::::default(); + new_ids_vec.try_push(chain_id).map_err(|_| Error::::TooManyNetworks)?; + modified_types.try_insert(network_type, new_ids_vec) + .map_err(|_| Error::::TooManyNetworks)?; + } + + network.r#type = network_type; + + Networks::::insert(&chain_id, network); + NetworkTypes::::insert(network_curve, modified_types); + Self::deposit_event(Event::::NetworkTypeUpdated { chain_id, network_type, }); + Ok(()) } @@ -652,50 +689,53 @@ impl Pallet { return Ok(()); } - let mut modified_prev_map = NetworkCurves::::get(prev_curve); - let counter = modified_prev_map + let mut modified_prev_types = NetworkTypes::::get(prev_curve); + let prev_chain_ids = modified_prev_types .get_mut(&network_type) .ok_or(Error::::NetworkTypeDoesNotExist)?; - *counter = counter.saturating_sub(1); + prev_chain_ids.retain(|id| id != &chain_id); - let mut should_remove_prev_from_vec = false; - if *counter == 0 { - modified_prev_map.remove(&network_type); - if modified_prev_map.is_empty() { - should_remove_prev_from_vec = true; + let mut should_remove_prev_curve = false; + if prev_chain_ids.is_empty() { + modified_prev_types.remove(&network_type); + if modified_prev_types.is_empty() { + should_remove_prev_curve = true; } } - let mut modified_curr_map = NetworkCurves::::get(network_curve); - if let Some(counter) = modified_curr_map.get_mut(&network_type) { - *counter = counter.saturating_add(1); + let mut modified_curr_types = NetworkTypes::::get(network_curve); + if let Some(chain_ids) = modified_curr_types.get_mut(&network_type) { + chain_ids.try_push(chain_id) + .map_err(|_| Error::::TooManyNetworks)?; } else { - modified_curr_map.try_insert(network_type, 1u32) + let mut new_ids_vec = BoundedVec::::default(); + new_ids_vec.try_push(chain_id).map_err(|_| Error::::TooManyNetworks)?; + modified_curr_types.try_insert(network_type, new_ids_vec) .map_err(|_| Error::::TooManyNetworks)?; } - let mut modified_curves_vec = NetworkCurvesVec::::get(); + let mut modified_curves = NetworkCurves::::get(); - if should_remove_prev_from_vec { - modified_curves_vec.retain(|curve| curve != &prev_curve); + if should_remove_prev_curve { + modified_curves.retain(|curve| curve != &prev_curve); } - if !modified_curves_vec.contains(&network_curve) { - modified_curves_vec.try_push(network_curve) + if !modified_curves.contains(&network_curve) { + modified_curves.try_push(network_curve) .map_err(|_| Error::::TooManyNetworks)?; } network.curve = network_curve; Networks::::insert(&chain_id, network); - NetworkCurves::::insert(network_curve, modified_curr_map); - NetworkCurvesVec::::put(modified_curves_vec); + NetworkTypes::::insert(network_curve, modified_curr_types); + NetworkCurves::::put(modified_curves); - if modified_prev_map.is_empty() { - NetworkCurves::::remove(prev_curve); + if modified_prev_types.is_empty() { + NetworkTypes::::remove(prev_curve); } else { - NetworkCurves::::insert(prev_curve, modified_prev_map); + NetworkTypes::::insert(prev_curve, modified_prev_types); } Self::deposit_event(Event::::NetworkCurveUpdated { @@ -761,6 +801,7 @@ impl NetworkDataBasicHandler for Pallet { type NetworkId = T::NetworkId; type NetworkType = NetworkType; type NetworkCurve = NetworkCurve; + type MaxNetworks = T::MaxNetworks; } impl NetworkDataInspectHandler for Pallet { @@ -775,7 +816,7 @@ impl NetworkDataInspectHandler for Pallet { } fn curve_exists(curve: &Self::NetworkCurve) -> bool { - NetworkCurves::::contains_key(curve) + NetworkTypes::::contains_key(curve) } fn get(n: &Self::NetworkId) -> Option { @@ -804,15 +845,17 @@ impl NetworkDataInspectHandler for Pallet { } fn iter_curves() -> impl Iterator { - NetworkCurvesVec::::get().into_iter() + NetworkCurves::::get().into_iter() } fn iter_types_by_curve( curve: &Self::NetworkCurve - ) -> impl Iterator { - NetworkCurves::::get(curve) - .into_iter() - .map(|(network_type, _)| network_type) + ) -> impl Iterator { + let network_types = NetworkTypes::::get(curve); + + network_types.into_iter().flat_map(|(network_type, network_ids)| { + network_ids.into_iter().map(move |network_id| (network_id, network_type)) + }) } } diff --git a/pallets/networks/src/tests.rs b/pallets/networks/src/tests.rs index 2180cfd..8f69b7c 100644 --- a/pallets/networks/src/tests.rs +++ b/pallets/networks/src/tests.rs @@ -45,8 +45,8 @@ fn register_and_check_network(chain_id: u32, network: NetworkData) { )); assert_eq!(Networks::::get(chain_id), Some(network)); assert_eq!(NetworkIndexes::::get(), vec![chain_id]); - assert!(NetworkCurves::::contains_key(&network_curve)); - assert!(NetworkCurvesVec::::get().contains(&network_curve)); + assert!(NetworkTypes::::contains_key(&network_curve)); + assert!(NetworkCurves::::get().contains(&network_curve)); } #[test] @@ -55,8 +55,8 @@ fn could_add_network_from_authority() { let (chain_id, network) = prepare_network_data(None, None); assert_eq!(Networks::::get(chain_id), None); assert_eq!(NetworkIndexes::::get(), vec![]); - assert!(!NetworkCurves::::contains_key(&CURRENT_CURVE)); - assert!(!NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); + assert!(!NetworkTypes::::contains_key(&CURRENT_CURVE)); + assert!(!NetworkCurves::::get().contains(&CURRENT_CURVE)); assert_ok!(GhostNetworks::register_network( RuntimeOrigin::signed(RegistererAccount::get()), @@ -68,8 +68,8 @@ fn could_add_network_from_authority() { )); assert_eq!(Networks::::get(chain_id), Some(network)); assert_eq!(NetworkIndexes::::get(), vec![chain_id]); - assert!(NetworkCurves::::contains_key(&CURRENT_CURVE)); - assert!(NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); + assert!(NetworkTypes::::contains_key(&CURRENT_CURVE)); + assert!(NetworkCurves::::get().contains(&CURRENT_CURVE)); }); } @@ -79,8 +79,8 @@ fn could_not_add_network_from_random_account() { let (chain_id, network) = prepare_network_data(None, None); assert_eq!(Networks::::get(chain_id), None); assert_eq!(NetworkIndexes::::get(), vec![]); - assert!(!NetworkCurves::::contains_key(&CURRENT_CURVE)); - assert!(!NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); + assert!(!NetworkTypes::::contains_key(&CURRENT_CURVE)); + assert!(!NetworkCurves::::get().contains(&CURRENT_CURVE)); assert_err!( GhostNetworks::register_network( @@ -108,8 +108,8 @@ fn could_not_add_network_from_random_account() { ); assert_eq!(Networks::::get(chain_id), None); assert_eq!(NetworkIndexes::::get(), vec![]); - assert!(!NetworkCurves::::contains_key(&CURRENT_CURVE)); - assert!(!NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); + assert!(!NetworkTypes::::contains_key(&CURRENT_CURVE)); + assert!(!NetworkCurves::::get().contains(&CURRENT_CURVE)); }); } @@ -351,10 +351,10 @@ fn could_update_network_curve_from_authority_account() { let (chain_id, network) = prepare_network_data(None, None); register_and_check_network(chain_id, network.clone()); - assert!(!NetworkCurves::::contains_key(&new_curve)); - assert!(NetworkCurves::::contains_key(&CURRENT_CURVE)); - assert!(!NetworkCurvesVec::::get().contains(&new_curve)); - assert!(NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); + assert!(!NetworkTypes::::contains_key(&new_curve)); + assert!(NetworkTypes::::contains_key(&CURRENT_CURVE)); + assert!(!NetworkCurves::::get().contains(&new_curve)); + assert!(NetworkCurves::::get().contains(&CURRENT_CURVE)); assert_ok!(GhostNetworks::update_network_curve( RuntimeOrigin::signed(UpdaterAccount::get()), @@ -370,10 +370,10 @@ fn could_update_network_curve_from_authority_account() { let mut final_network = network.clone(); final_network.r#curve = new_curve; - assert!(NetworkCurves::::contains_key(&new_curve)); - assert!(!NetworkCurves::::contains_key(&CURRENT_CURVE)); - assert!(NetworkCurvesVec::::get().contains(&new_curve)); - assert!(!NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); + assert!(NetworkTypes::::contains_key(&new_curve)); + assert!(!NetworkTypes::::contains_key(&CURRENT_CURVE)); + assert!(NetworkCurves::::get().contains(&new_curve)); + assert!(!NetworkCurves::::get().contains(&CURRENT_CURVE)); assert_eq!(Networks::::get(chain_id), Some(final_network.clone())); assert_ne!(network, final_network); @@ -1050,8 +1050,8 @@ fn could_remove_network_from_authority_account() { )); assert_eq!(Networks::::get(chain_id), None); assert_eq!(NetworkIndexes::::get(), vec![]); - assert!(!NetworkCurves::::contains_key(&CURRENT_CURVE)); - assert!(!NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); + assert!(!NetworkTypes::::contains_key(&CURRENT_CURVE)); + assert!(!NetworkCurves::::get().contains(&CURRENT_CURVE)); }); } @@ -1077,8 +1077,8 @@ fn could_not_remove_network_from_random_account() { ); assert_eq!(Networks::::get(chain_id), Some(network)); assert_eq!(NetworkIndexes::::get(), vec![chain_id]); - assert!(NetworkCurves::::contains_key(&CURRENT_CURVE)); - assert!(NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); + assert!(NetworkTypes::::contains_key(&CURRENT_CURVE)); + assert!(NetworkCurves::::get().contains(&CURRENT_CURVE)); }); } @@ -1093,8 +1093,8 @@ fn could_not_remove_non_existent_network() { ); assert_eq!(Networks::::get(chain_id), None); assert_eq!(NetworkIndexes::::get(), vec![]); - assert!(!NetworkCurves::::contains_key(&CURRENT_CURVE)); - assert!(!NetworkCurvesVec::::get().contains(&CURRENT_CURVE)); + assert!(!NetworkTypes::::contains_key(&CURRENT_CURVE)); + assert!(!NetworkCurves::::get().contains(&CURRENT_CURVE)); }); } diff --git a/pallets/traits/Cargo.toml b/pallets/traits/Cargo.toml index 373c90d..4f7f9a1 100644 --- a/pallets/traits/Cargo.toml +++ b/pallets/traits/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghost-traits" -version = "0.4.7" +version = "0.4.8" description = "Shared traits including `GhostHasher`, `NetworkDataBasicHandler`, `BoundedBTreeMap`, `MerkleTree` and more." license.workspace = true authors.workspace = true diff --git a/pallets/traits/src/networks.rs b/pallets/traits/src/networks.rs index 73257b0..9ef4f7f 100644 --- a/pallets/traits/src/networks.rs +++ b/pallets/traits/src/networks.rs @@ -1,4 +1,7 @@ -use frame_support::{pallet_prelude::*, storage::PrefixIterator}; +use frame_support::{ + pallet_prelude::*, + storage::PrefixIterator, +}; use sp_std::vec::Vec; use sp_runtime::{ offchain::http::PendingRequest, @@ -7,14 +10,14 @@ use sp_runtime::{ }; pub trait NetworkDataBasicHandler { - type NetworkCurve: Parameter + type NetworkCurve: Parameter + Member + Default + Copy + TypeInfo + MaybeSerializeDeserialize + MaxEncodedLen; - type NetworkType: Parameter + type NetworkType: Parameter + Member + Default + Copy @@ -29,6 +32,7 @@ pub trait NetworkDataBasicHandler { + TypeInfo + MaybeSerializeDeserialize + MaxEncodedLen; + type MaxNetworks: Get; } pub trait NetworkDataInspectHandler: NetworkDataBasicHandler { @@ -40,7 +44,9 @@ pub trait NetworkDataInspectHandler: NetworkDataBasicHandler { fn iter() -> PrefixIterator<(Self::NetworkId, Network)>; fn iter_indexes() -> impl Iterator; fn iter_curves() -> impl Iterator; - fn iter_types_by_curve(n: &Self::NetworkCurve) -> impl Iterator; + fn iter_types_by_curve( + c: &Self::NetworkCurve + ) -> impl Iterator; } pub trait NetworkDataMutateHandler: NetworkDataInspectHandler {