Uncle Stinky 2026-10-15 21:02:02 +03:00
parent 653bf08486
commit bdbf7bd20f
Signed by: st1nky
GPG Key ID: 016064BD97603B40
18 changed files with 1618 additions and 1402 deletions

View File

@ -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

View File

@ -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

View File

@ -2,6 +2,27 @@
sp_api::decl_runtime_apis! {
pub trait ExodusApi<NetworkId> {
fn verifying_key(network_id: NetworkId) -> Vec<u8>;
fn get_signed_rotation_message(
session: ExodusSession,
network_id: NetworkId,
dkg_index: DkgIndex
) -> Vec<u8>;
fn get_signed_bridge_message(
session: ExodusSession,
network_id: NetworkId,
dkg_index: DkgIndex
) -> Vec<u8>;
fn get_signed_governance_message(
session: ExodusSession,
network_id: NetworkId,
dkg_index: DkgIndex
) -> Vec<u8>;
fn active_verifying_key(network_curve: NetworkCurve) -> Vec<u8>;
fn pending_exodus_request_count(network_curve: NetworkCurve) -> u32;
fn is_qualification_onging(network_curve: NetworkCurve) -> bool;
}
}

View File

@ -7,7 +7,7 @@ use frame_system::RawOrigin;
use ghost_helpers::networks::NetworkDataBuilder;
fn prepare_pallet<T: Config>(authorities_len: usize) -> (
PendingDkgAuthorities<ParticipantsBitmap<T>, BlockNumberFor<T>>,
PendingDkgAuthorities<T>,
NetworkCurve,
T::AuthorityId,
AuthIndex
@ -99,7 +99,9 @@ mod benchmarks {
let package_hash = ExodusHash::from(blake2_256(&public_package));
Round0Packages::<T>::insert(network_curve, authority_index, package_hash);
let current_block = <T as Config>::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 = <T as Config>::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 = <T as Config>::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 = <T as Config>::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 = <T as Config>::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 = <T as Config>::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::<T>::get();
let all_exodus_sessions = CurrentExodusSessions::<T>::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::<NetworkIdOf<T>, BalanceOf<T>>::evm_governance(
exodus_session,
network_id,
let request = ExodusRequest::<BalanceOf<T>>::evm_governance(
dummy_address,
create_bond,
);
let current_block = <T as Config>::BlockNumberProvider::current_block_number();
let authorities = QualificationAuthorities::<T>::get(&network_curve);
let active_dkg: ActivatedState<T> = (&dkg_state).into();
let active_dkg: ReadyDkgAuthorities<T> = (&dkg_state).into();
let active_dkg_index = active_dkg.get_dkg_index();
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
ActiveDkgAuthorities::<T>::insert(&network_curve, active_dkg);
ActiveAuthorities::<T>::insert(&network_curve, authorities);
ExodusRequests::<T>::insert((network_curve, exodus_session), request);
ExodusRequests::<T>::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::<T>::get(&exodus_session, authority_index)
let roast_session = RoastSessions::<T>::get(&dummy_context, authority_index)
.expect("ROAST session should exist after nonce commitment");
let roast_state = RoastSessionStates::<T>::get(&exodus_session, roast_session);
let roast_state = RoastSessionStates::<T>::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::<T>::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::<T>::get();
let all_exodus_sessions = CurrentExodusSessions::<T>::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::<NetworkIdOf<T>, BalanceOf<T>>::evm_governance(
exodus_session,
network_id,
let request = ExodusRequest::<BalanceOf<T>>::evm_governance(
dummy_address,
create_bond,
);
let authorities = QualificationAuthorities::<T>::get(&network_curve);
let active_dkg: ActivatedState<T> = (&dkg_state).into();
let active_dkg: ReadyDkgAuthorities<T> = (&dkg_state).into();
let active_dkg_index = active_dkg.get_dkg_index();
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
ActiveDkgAuthorities::<T>::insert(&network_curve, active_dkg);
ActiveAuthorities::<T>::insert(&network_curve, authorities);
ExodusRequests::<T>::insert((network_curve, exodus_session), request);
ExodusRequests::<T>::insert((network_curve, dummy_context), request);
let mut roast_state = RoastSessionState::<ParticipantsBitmap::<T>>::default();
roast_state.status = RoastStatus::GroupCommitments;
roast_state.nonce_committers.insert(authority_index);
let roast_session = 3;
RoastSessions::<T>::insert(&exodus_session, authority_index, roast_session);
RoastSessionStates::<T>::insert(exodus_session, roast_session, roast_state);
let current_block = <T as Config>::BlockNumberProvider::current_block_number();
RoastSessions::<T>::insert(&dummy_context, authority_index, roast_session);
RoastSessionStates::<T>::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::<T>::get(&exodus_session, roast_session);
let roast_state = RoastSessionStates::<T>::get(&dummy_context, roast_session);
assert!(roast_state.nonce_committers.contains(authority_index));
assert!(roast_state.group_committers.contains(authority_index));
let consensus = GroupCommitmentsConsensus::<T>::get(exodus_session, roast_session);
let consensus = GroupCommitmentsConsensus::<T>::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::<T>::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::<T>::get();
let all_exodus_sessions = CurrentExodusSessions::<T>::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::<NetworkIdOf<T>, BalanceOf<T>>::evm_governance(
exodus_session,
network_id,
let request = ExodusRequest::<BalanceOf<T>>::evm_governance(
dummy_address,
create_bond,
);
@ -751,13 +777,13 @@ mod benchmarks {
});
let authorities = QualificationAuthorities::<T>::get(&network_curve);
let active_dkg: ActivatedState<T> = (&dkg_state).into();
let active_dkg: ReadyDkgAuthorities<T> = (&dkg_state).into();
let active_dkg_index = active_dkg.get_dkg_index();
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
ActiveDkgAuthorities::<T>::insert(&network_curve, active_dkg);
ActiveAuthorities::<T>::insert(&network_curve, authorities);
ExodusRequests::<T>::insert((network_curve, exodus_session), request);
ExodusRequests::<T>::insert((network_curve, exodus_context), request);
let mut roast_state = RoastSessionState::<ParticipantsBitmap::<T>>::default();
roast_state.status = RoastStatus::PartialSignatures;
@ -770,8 +796,8 @@ mod benchmarks {
});
let roast_session = 3;
RoastSessions::<T>::insert(&exodus_session, authority_index, roast_session);
RoastSessionStates::<T>::insert(exodus_session, roast_session, roast_state);
RoastSessions::<T>::insert(&exodus_context, authority_index, roast_session);
RoastSessionStates::<T>::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::<T>::insert(&network_curve, dummy_verifying_key);
VerifyingShares::<T>::insert((&network_curve, active_dkg_index, authority_index), dummy_verifying_share);
NonceCommitments::<T>::insert((&exodus_session, &roast_session), authority_index, dummy_nonce);
NonceCommitments::<T>::insert((&exodus_context, &roast_session), authority_index, dummy_nonce);
GroupCommitmentsConsensus::<T>::mutate(&exodus_session, &roast_session, |consensus| {
GroupCommitmentsConsensus::<T>::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 = <T as Config>::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::<T>::get(&exodus_session, authority_index);
let maybe_roast_session = RoastSessions::<T>::get(&exodus_context, authority_index);
assert!(maybe_roast_session.is_none());
let scalar_exists = SignatureScalars::<T>::contains_key(&exodus_session, &roast_session);
let consensus_exists = GroupCommitmentsConsensus::<T>::contains_key(&exodus_session, &roast_session);
let scalar_exists = SignatureScalars::<T>::contains_key(&exodus_context, &roast_session);
let consensus_exists = GroupCommitmentsConsensus::<T>::contains_key(&exodus_context, &roast_session);
let committers_exists = GroupCommitters::<T>::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::<T>::contains_key((&network_curve, &exodus_session)));
assert!(ExodusSignedGovernance::<T>::contains_key((&exodus_session, network_id), dkg_index));
assert!(!ExodusRequests::<T>::contains_key((&network_curve, &exodus_context)));
assert!(ExodusSignedGovernance::<T>::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::<T>::get();
let all_exodus_sessions = CurrentExodusSessions::<T>::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::<T>::contains_key((&network_curve, current_exodus_session)));
assert_eq!(CurrentExodus::<T>::get(), current_exodus_session + 1);
let all_exodus_sessions = CurrentExodusSessions::<T>::get();
let last_exodus_session = all_exodus_sessions.get(&network_id).copied().unwrap();
let context = ExodusContext::new(network_id, current_exodus_session);
assert!(ExodusRequests::<T>::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);

File diff suppressed because it is too large Load Diff

View File

@ -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 = ();
}

View File

@ -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::<Runtime>::get(curve).get_block();
let delay = <<Runtime as Config>::DkgRoundPeriod as frame_support::traits::Get<
frame_system::pallet_prelude::BlockNumberFor<Runtime>
>>::get();
stored + delay
QualificationDkgState::<Runtime>::get(curve).get_release_block()
});
ext.execute_with(|| {
@ -139,9 +134,9 @@ fn execute_dkg_happy_path<F1, F2, F3>(
where
F1: FnMut(
DkgStorage<BlockNumberFor<Runtime>, NetworkCurve>,
&QualifyingState<Runtime>,
&PendingDkgAuthorities<Runtime>,
UintAuthorityId,
) -> Result<ExodusOk<NetworkCurve>, ExodusError>,
) -> Result<ExodusOk<NetworkCurve, NetworkIdOf<Runtime>>, ExodusError>,
F2: FnMut(Call<Runtime>) -> Result<(), TestError>,
F3: FnMut(AuthIndex, NetworkCurve) -> Result<(), TestError>,
@ -203,78 +198,6 @@ where
Ok(())
}
fn execute_exodus_happy_path<F1, F2>(
ext: &mut sp_io::TestExternalities,
tx_pool_state: &std::sync::Arc<parking_lot::RwLock<sp_core::offchain::testing::PoolState>>,
curve: NetworkCurve,
authority: u64,
mut apply_transaction: F1,
mut check_storage_changed: F2,
) -> Result<(), TestError>
where
F1: FnMut(Call<Runtime>) -> Result<(), TestError>,
F2: FnMut(ExodusOk<NetworkCurve>) -> Result<(), TestError>,
{
use sp_runtime::transaction_validity::TransactionSource;
let state = ext.execute_with(|| QualificationDkgState::<Runtime>::get(&curve));
if state.is_dkg_pending() {
return Err(TestError::PhaseMismatch);
}
let active_authorities = ext.execute_with(|| {
ActiveDkgAuthorities::<Runtime>::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::<Runtime, RuntimeCall, ()>(&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<parking_lot::RwLock<sp_core::offchain::testing::PoolState>>,
@ -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::<Runtime>::contains_key((exodus, roast), idx) {
return Err(TestError::InvalidStorageAfter);
}
if !RoastSessions::<Runtime>::contains_key(exodus, idx) {
return Err(TestError::InvalidStorageAfter);
}
let roast_state = RoastSessionStates::<Runtime>::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::<Runtime>::contains_key(exodus, idx) {
return Err(TestError::InvalidStorageAfter);
}
if !GroupCommitters::<Runtime>::iter_prefix_values((exodus, roast))
.any(|participants| participants.contains(idx)) {
return Err(TestError::InvalidStorageAfter);
}
let roast_state = RoastSessionStates::<Runtime>::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::<Runtime>::contains_key(exodus, idx) {
return Err(TestError::InvalidStorageAfter);
}
if RoastSessions::<Runtime>::contains_key(exodus, idx) {
return Err(TestError::InvalidStorageAfter);
}
if ExodusRequests::<Runtime>::contains_key((curve, exodus)) {
let roast_state = RoastSessionStates::<Runtime>::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::<Runtime>::contains_key(exodus, roast) {
return Err(TestError::InvalidStorageAfter);
}
if SignatureScalars::<Runtime>::contains_key(exodus, roast) {
return Err(TestError::InvalidStorageAfter);
}
if GroupCommitmentsConsensus::<Runtime>::contains_key(exodus, roast) {
return Err(TestError::InvalidStorageAfter);
}
}
Ok(())
}
_ => Err(TestError::InvalidStorageAfter),
}
};
let threshold = ext.execute_with(|| {
let len = ActiveAuthorities::<Runtime>::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<ExodusSession> = ext.execute_with(|| {
let mut skipped_in_this_round = 0;
let has_pending_requests = ext.execute_with(|| {
ExodusRequests::<Runtime>::iter_keys()
.filter_map(|(current_curve, exodus_session)| {
curve.eq(&current_curve).then(|| exodus_session)
})
.collect()
.any(|(current_curve, _context)| current_curve == curve)
});
current_pending_requests.sort();
if let Some(exodus_session) = current_pending_requests.first() {
if !current_pending_requests.contains(exodus_session) { continue; }
let exodus_steps: &mut BTreeMap<u64, RoastSession> = exodus_steps_outer.entry(*exodus_session).or_default();
let roast_sessions: &mut BTreeMap<u64, RoastSession> = roast_sessions_outer.entry(*exodus_session).or_default();
let roast_participants: &mut BTreeMap<RoastSession, (u16, u16, u16)> = roast_participants_outer.entry(*exodus_session).or_default();
let malicious_participants: &mut BTreeSet<u64> = malicious_participants_outer.entry(*exodus_session).or_default();
let throttled_participants: &mut BTreeSet<u64> = throttled_participants_outer.entry(*exodus_session).or_default();
if !has_pending_requests { break; }
for auth in prev_authorities.iter() {
let request_exists = ext.execute_with(|| {
ExodusRequests::<Runtime>::contains_key((&curve, &exodus_session))
});
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;
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;
if should_throttle && skipped_in_this_round < max_allowed_to_skip {
skipped_in_this_round += 1;
run_increase_block_by(ext, 1);
continue;
}
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::<u8, ConstU32<{ ELEMENT_MAX_BYTES }>>::try_from(raw_vec)
.unwrap();
let authority_key = (*auth).into();
let active_authorities = ActiveDkgAuthorities::<Runtime>::get(curve);
let empty_participants = ParticipantsBitmap::<Runtime>::empty(prev_len);
let mut self_participant = empty_participants.clone();
self_participant.insert(malicious);
let (correct_commitment, correct_root) = GroupCommitmentsConsensus::<Runtime>::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,
let _ = Exodus::exodus_run_process(
authority_index,
authority_key,
curve,
current_block,
&active_authorities,
);
(group_commitment, binding_root)
});
let txs = tx_pool_state.write().transactions.drain(..).collect::<Vec<_>>();
let prev_group_key = (exodus_session, roast_session, correct_root, correct_commitment);
GroupCommitters::<Runtime>::insert(prev_group_key, empty_participants);
for raw_tx in txs.iter() {
let runtime_call = decode_transaction::<Runtime, RuntimeCall, ()>(&raw_tx)
.unwrap();
let fake_group_key = (exodus_session, roast_session, DUMMY_HASH, fake_group_commitment);
GroupCommitters::<Runtime>::insert(fake_group_key, self_participant);
if let RuntimeCall::Exodus(inner_exodus_call) = runtime_call {
Exodus::validate_unsigned(TransactionSource::InBlock, &inner_exodus_call).unwrap();
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,
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"),
};
result.unwrap();
exodus_steps.insert(*auth, current_step);
roast_sessions.insert(*auth, roast_session);
roast_participants.insert(roast_session, participants);
dispatch_result.unwrap();
} else {
panic!("Expected one of RuntimeCall::Exodus");
}
}
});
run_increase_block_by(ext, 1);
} else {
break;
}
}
@ -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::<Runtime>::iter().count()
@ -1300,7 +1025,7 @@ fn test_all_validators_can_execute_dkg() {
ExodusRequests::<Runtime>::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::<Runtime>::get(curve).get_dkg_index()
});
let (start_block, round_delay) = ext.execute_with(|| {
let round_delay = <<Runtime as Config>::DkgRoundPeriod as frame_support::traits::Get<
let round_delay = ext.execute_with(|| {
<<Runtime as Config>::DkgRoundPeriod as frame_support::traits::Get<
frame_system::pallet_prelude::BlockNumberFor<Runtime>
>>::get();
let start_block = System::block_number();
(start_block, round_delay)
>>::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);

View File

@ -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::{
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<RotationSession, ConstU32<{ NetworkType::COUNT as u32 }>>;
type RotationSessions<T> = BoundedVec<
ExodusContext<NetworkIdOf<T>>,
<<T as Config>::NetworkDataHandler as NetworkDataBasicHandler>::MaxNetworks,
>;
#[derive(Default, Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub struct ReadyDkgAuthorities<AuthoritiesBitmap> {
authorities_indexes: AuthoritiesBitmap,
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct ReadyDkgAuthorities<T: Config> {
authorities_indexes: ParticipantsBitmap<T>,
dkg_index: DkgIndex,
}
impl<AuthoritiesBitmap> ReadyDkgAuthorities<AuthoritiesBitmap>
impl<T: Config> Default for ReadyDkgAuthorities<T> {
fn default() -> Self {
Self {
authorities_indexes: Default::default(),
dkg_index: Default::default(),
}
}
}
impl<T: Config> ReadyDkgAuthorities<T>
where
AuthoritiesBitmap: BoundedBitmapReader,
ParticipantsBitmap<T>: BoundedBitmapReader,
{
pub fn contains_index<N>(&self, index: N) -> bool
where N: UniqueSaturatedInto<usize>,
@ -30,7 +47,7 @@ where
self.authorities_indexes.contains(index)
}
pub fn get_indexes(&self) -> &AuthoritiesBitmap {
pub fn get_indexes(&self) -> &ParticipantsBitmap<T> {
&self.authorities_indexes
}
@ -39,15 +56,15 @@ where
}
}
impl<AuthoritiesBitmap, Block>
From<&PendingDkgAuthorities<AuthoritiesBitmap, Block>>
for ReadyDkgAuthorities<AuthoritiesBitmap>
impl<T: Config>
From<&PendingDkgAuthorities<T>>
for ReadyDkgAuthorities<T>
where
Block: Copy,
AuthoritiesBitmap: BoundedBitmapWriter + BoundedBitmapReader + BoundedBitmapGenerator + Clone,
for<'a> AuthoritiesBitmap: core::ops::BitAndAssign<&'a AuthoritiesBitmap>,
BlockNumberFor<T>: Copy,
ParticipantsBitmap<T>: BoundedBitmapWriter + BoundedBitmapReader + BoundedBitmapGenerator + Clone,
for<'a> ParticipantsBitmap<T>: core::ops::BitAndAssign<&'a ParticipantsBitmap<T>>,
{
fn from(pending: &PendingDkgAuthorities<AuthoritiesBitmap, Block>) -> Self {
fn from(pending: &PendingDkgAuthorities<T>) -> 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<AuthoritiesBitmap, Block> {
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct PendingDkgAuthorities<T: Config> {
verifying_key: BoundedVec<u8, ConstU32<ELEMENT_MAX_BYTES>>,
authorities_indexes: AuthoritiesBitmap,
rotation_sessions: RotationSessions,
dkg_state: DkgState<Block>,
authorities_indexes: ParticipantsBitmap<T>,
rotation_sessions: RotationSessions<T>,
dkg_state: DkgState<BlockNumberFor<T>>,
}
impl<AuthoritiesBitmap, Block> PendingDkgAuthorities<AuthoritiesBitmap, Block>
impl<T: Config> Default for PendingDkgAuthorities<T> {
fn default() -> Self {
Self {
verifying_key: Default::default(),
authorities_indexes: Default::default(),
rotation_sessions: Default::default(),
dkg_state: Default::default(),
}
}
}
impl<T: Config> PendingDkgAuthorities<T>
where
Block: Copy,
AuthoritiesBitmap: BoundedBitmapWriter + BoundedBitmapReader + BoundedBitmapGenerator + Clone,
for<'a> AuthoritiesBitmap: core::ops::BitAndAssign<&'a AuthoritiesBitmap>,
BlockNumberFor<T>: Copy,
ParticipantsBitmap<T>: BoundedBitmapWriter + BoundedBitmapReader + BoundedBitmapGenerator + Clone,
for<'a> ParticipantsBitmap<T>: core::ops::BitAndAssign<&'a ParticipantsBitmap<T>>,
{
pub fn new_dkg(&mut self, authorities_len: usize) {
self.authorities_indexes = AuthoritiesBitmap::empty(authorities_len);
self.authorities_indexes = ParticipantsBitmap::<T>::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<T>) {
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<T>) {
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<T> {
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<NetworkIdOf<T>>,
) -> 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<N>(&self) -> N
@ -208,43 +225,44 @@ where
self.authorities_indexes.highest_bit()
}
pub fn get_indexes(&self) -> &AuthoritiesBitmap {
pub fn get_indexes(&self) -> &ParticipantsBitmap<T> {
&self.authorities_indexes
}
pub fn iter_rotation_sessions(&self) -> impl Iterator<Item = &RotationSession> {
pub fn iter_rotation_contexts(
&self
) -> impl Iterator<Item = &ExodusContext<NetworkIdOf<T>>> {
self.rotation_sessions.iter()
}
}
#[derive(Default, Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)]
pub struct DkgState<Block> {
block: Block,
index: DkgIndex,
phase: DkgPhase,
release_block: Block,
dkg_index: DkgIndex,
dkg_phase: DkgPhase,
}
impl<Block: Copy> DkgState<Block> {
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;
}
}

View File

@ -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<NetworkId, Balance> {
pub r#type: ExodusRequestType<NetworkId, Balance>,
#[derive(Encode, Decode, Clone, Copy, PartialEq, TypeInfo, RuntimeDebug)]
pub struct ExodusRequest<Balance> {
pub r#type: ExodusRequestType<Balance>,
pub next_roast_session: RoastSession,
}
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)]
pub enum ExodusRequestType<NetworkId, Balance> {
EvmRotation {
exodus_session: ExodusSession,
public_key: EvmBytes32,
parity: u8,
},
#[derive(Encode, Decode, Clone, Copy, PartialEq, TypeInfo, RuntimeDebug)]
pub enum ExodusRequestType<Balance> {
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<NetworkId, Balance> ExodusRequestType<NetworkId, Balance>
impl<Balance> ExodusRequestType<Balance>
where
NetworkId: UniqueSaturatedInto<u64> + Copy,
Balance: UniqueSaturatedInto<u128> + 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<NetworkId>,
) -> Option<&'a [u8]>
where
NetworkId: UniqueSaturatedInto<u64> + 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<NetworkId, Balance> ExodusRequest<NetworkId, Balance>
impl<Balance> ExodusRequest<Balance>
where
NetworkId: UniqueSaturatedInto<u64> + Copy,
Balance: UniqueSaturatedInto<u128> + 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<NetworkId>,
) -> Option<&'a [u8]>
where
NetworkId: UniqueSaturatedInto<u64> + 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<NetworkId, Balance>,
) -> Self {
Self { r#type: request_type, next_roast_session: Default::default() }
fn default_request_for(request_type: ExodusRequestType<Balance>) -> 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::<u64, u128>::evm_rotation(
session,
let context = ExodusContext::new(network_id, session);
let rotation_request = ExodusRequest::<u128>::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::<u64, u128>::evm_bridge_out(
session,
network_id,
let context = ExodusContext::new(network_id, session);
let bridge_request = ExodusRequest::<u128>::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::<u64, u128>::evm_governance(
session,
network_id,
let governance_request = ExodusRequest::<u128>::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);

View File

@ -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<u8> {
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<BlockNumber> = BTreeMap<ExodusSession, PersistentData<BlockNumber, ExodusNonce>>;
type ExodusGroups<BlockNumber> = BTreeMap<ExodusSession, PersistentData<BlockNumber, ExodusGroup>>;
type ExodusShares<BlockNumber> = BTreeMap<ExodusSession, PersistentData<BlockNumber, ExodusShare>>;
type ExodusExiles<BlockNumber> = BTreeMap<ExodusSession, PersistentData<BlockNumber, ExodusExile>>;
type ExodusNonces<NetworkId, BlockNumber> = BTreeMap<ExodusContext<NetworkId>, PersistentData<BlockNumber, ExodusNonce>>;
type ExodusGroups<NetworkId, BlockNumber> = BTreeMap<ExodusContext<NetworkId>, PersistentData<BlockNumber, ExodusGroup>>;
type ExodusShares<NetworkId, BlockNumber> = BTreeMap<ExodusContext<NetworkId>, PersistentData<BlockNumber, ExodusShare>>;
type ExodusExiles<NetworkId, BlockNumber> = BTreeMap<ExodusContext<NetworkId>, PersistentData<BlockNumber, ExodusExile>>;
pub struct ExodusStorageWrapper<ExodusInnerType> {
pub inner: ExodusInnerType,
@ -113,34 +113,37 @@ where
self
}
pub fn read_local_storage(&self) -> ExodusRecords<B> {
pub fn read_local_storage<NI>(&self) -> ExodusRecords<NI, B>
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::<ExodusNonces<B>>()
.get::<ExodusNonces<NI, B>>()
.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::<ExodusShares<B>>()
.get::<ExodusShares<NI, B>>()
.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::<ExodusGroups<B>>()
.get::<ExodusGroups<NI, B>>()
.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::<ExodusExiles<B>>()
.get::<ExodusExiles<NI, B>>()
.ok()
.flatten()
.unwrap_or_default();
@ -153,11 +156,11 @@ where
}
}
pub fn prepare_signed_call_with_context<T, A, F1, F2, F3>(
pub fn prepare_signed_call_with_context<T, A, F1, F2, F3, NI>(
&self,
authority_key: &A,
current_dkg_index: DkgIndex,
exodus_records: &mut ExodusRecords<B>,
exodus_records: &mut ExodusRecords<NI, B>,
make_data: F1,
submit_call: F2,
insert_data: F3,
@ -165,11 +168,13 @@ where
where
T: crate::pallet::Config,
A: RuntimeAppPublic,
F1: FnOnce(PackageContext<NC>) -> Result<ExodusPackage<NC, T>, ExodusError>,
F2: FnOnce(ExodusPackage<NC, T>, A::Signature) -> Result<(), ExodusError>,
F3: FnOnce(&mut ExodusRecords<B>, B),
NI: Default + Encode + Decode + AtLeast32BitUnsigned,
F1: FnOnce(PackageContext<NC, B>) -> Result<ExodusPackage<NC, NI, B, T>, ExodusError>,
F2: FnOnce(ExodusPackage<NC, NI, B, T>, A::Signature) -> Result<(), ExodusError>,
F3: FnOnce(&mut ExodusRecords<NI, B>, 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<B>) {
pub fn try_write_local_storage<NI: Default>(&self, records: &ExodusRecords<NI, B>)
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<BlockNumber>
pub struct ExodusRecords<NetworkId, BlockNumber>
where
NetworkId: Default,
BlockNumber: AtLeast32BitUnsigned,
{
exodus_nonces: ExodusStorageWrapper<ExodusNonces<BlockNumber>>,
exodus_shares: ExodusStorageWrapper<ExodusShares<BlockNumber>>,
exodus_groups: ExodusStorageWrapper<ExodusGroups<BlockNumber>>,
exodus_exiles: ExodusStorageWrapper<ExodusExiles<BlockNumber>>,
exodus_nonces: ExodusStorageWrapper<ExodusNonces<NetworkId, BlockNumber>>,
exodus_shares: ExodusStorageWrapper<ExodusShares<NetworkId, BlockNumber>>,
exodus_groups: ExodusStorageWrapper<ExodusGroups<NetworkId, BlockNumber>>,
exodus_exiles: ExodusStorageWrapper<ExodusExiles<NetworkId, BlockNumber>>,
}
impl<BlockNumber> ExodusRecords<BlockNumber>
impl<NetworkId, BlockNumber> ExodusRecords<NetworkId, BlockNumber>
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<NetworkId>,
) -> 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<NetworkId>,
) -> Option<ExodusGroup> {
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<NetworkId>,
) -> 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<NetworkId>,
) -> 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<NetworkId>,
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<NetworkId>,
current_block: &BlockNumber,
) -> bool {
self.exodus_shares.inner
.get(exodus_session)
.get(exodus_context)
.map(|data| data.release_block.gt(&current_block))
.unwrap_or(false)
}
pub fn is_waiting_period_for_group(
&self,
exodus_session: &ExodusSession,
exodus_context: &ExodusContext<NetworkId>,
current_block: &BlockNumber,
) -> bool {
self.exodus_groups.inner
.get(exodus_session)
.get(exodus_context)
.map(|data| data.release_block.gt(&current_block))
.unwrap_or(false)
}
pub fn exclude_exodus_session(
&mut self,
exodus_session: ExodusSession,
exodus_context: ExodusContext<NetworkId>
) {
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<NetworkId>,
next_release_block: BlockNumber,
nonce_commitment: Vec<u8>,
) {
@ -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<NetworkId>,
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<NetworkId>,
next_release_block: BlockNumber,
self_binding_factor: Vec<u8>,
binding_factors_proof: Vec<ExodusHash>,
@ -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<NI>(
&self,
dkg_record: DkgRecord<B>,
) -> Result<ExodusOk<NC>, ExodusError> {
) -> Result<ExodusOk<NC, NI>, 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<NC>) -> Result<DkgPackage<NC, T>, ExodusError>,
F2: FnOnce(DkgPackage<NC, T>, A::Signature) -> Result<(), ExodusError>,
F1: FnOnce(PackageContext<NC, B>) -> Result<DkgPackage<NC, B, T>, ExodusError>,
F2: FnOnce(DkgPackage<NC, B, T>, 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);

View File

@ -6,37 +6,58 @@ use codec::{Encode, Decode};
use frame_support::{BoundedVec, traits::ConstU32};
use crate::{AuthIndex, DkgIndex, ExodusSession, ExodusHash, ExodusError};
pub trait PackageMetadata<NetworkCurve>
pub trait PackageMetadata<NetworkCurve, BlockNumber>
where
NetworkCurve: Copy,
BlockNumber: Copy,
{
fn context(&self) -> &PackageContext<NetworkCurve>;
fn package_context(&self) -> &PackageContext<NetworkCurve, BlockNumber>;
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<NetworkId>
where
NetworkId: Default + Copy,
{
fn get_exodus_context(&self) -> &ExodusContext<NetworkId>;
fn copy_exodus_context(&self) -> ExodusContext<NetworkId>;
}
#[derive(Encode, Decode, Clone, RuntimeDebug, TypeInfo, PartialEq, Eq)]
#[scale_info(skip_type_params(T))]
pub enum ExodusPackage<NetworkCurve, T>
pub enum ExodusPackage<NetworkCurve, NetworkId, BlockNumber, T>
where
NetworkId: Default,
T: crate::pallet::Config,
{
NonceCommitment(ExodusNonceCommitment<NetworkCurve>),
GroupCommitment(ExodusGroupCommitment<NetworkCurve>),
SignatureShare(ExodusSignatureShare<NetworkCurve, T>),
NonceCommitment(ExodusNonceCommitment<NetworkCurve, NetworkId, BlockNumber>),
GroupCommitment(ExodusGroupCommitment<NetworkCurve, NetworkId, BlockNumber>),
SignatureShare(ExodusSignatureShare<NetworkCurve, NetworkId, BlockNumber, T>),
}
impl<NetworkCurve, T> ExodusPackage<NetworkCurve, T>
impl<NC, NI, B, T> ExodusPackage<NC, NI, B, T>
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<NetworkCurve, T>
pub enum DkgPackage<NetworkCurve, BlockNumber, T>
where
T: crate::pallet::Config,
{
Round0(Round0Package<NetworkCurve>),
Round1(Round1Package<NetworkCurve, T>),
Round2(Round2Package<NetworkCurve, T>),
Round3(Round3Package<NetworkCurve, T>),
Round4(Round4Package<NetworkCurve, T>),
Round5(Round5Package<NetworkCurve, T>),
Round6(Round6Package<NetworkCurve, T>),
Round0(Round0Package<NetworkCurve, BlockNumber>),
Round1(Round1Package<NetworkCurve, BlockNumber, T>),
Round2(Round2Package<NetworkCurve, BlockNumber, T>),
Round3(Round3Package<NetworkCurve, BlockNumber, T>),
Round4(Round4Package<NetworkCurve, BlockNumber, T>),
Round5(Round5Package<NetworkCurve, BlockNumber, T>),
Round6(Round6Package<NetworkCurve, BlockNumber, T>),
}
impl<NetworkCurve, T> DkgPackage<NetworkCurve, T>
impl<NC, B, T> DkgPackage<NC, B, T>
where
T: crate::pallet::Config,
{
@ -102,15 +115,17 @@ where
}
#[derive(Default, RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
pub struct PackageContext<NetworkCurve> {
pub struct PackageContext<NetworkCurve, BlockNumber> {
authority_index: AuthIndex,
network_curve: NetworkCurve,
block_number: BlockNumber,
dkg_index: DkgIndex,
}
impl<NetworkCurve> PackageContext<NetworkCurve>
impl<NetworkCurve, BlockNumber> PackageContext<NetworkCurve, BlockNumber>
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<T: crate::pallet::Config> TryFrom<BundledPackages> for JustificationsBundle
}
}
#[derive(RuntimeDebug, Default, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Encode, Decode, TypeInfo)]
pub struct ExodusContext<NetworkId: Default> {
pub session: ExodusSession,
pub network_id: NetworkId,
}
impl<NetworkId: Default + Copy> ExodusContext<NetworkId> {
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<NetworkCurve> {
context: PackageContext<NetworkCurve>,
pub struct ExodusNonceCommitment<NetworkCurve, NetworkId: Default, BlockNumber> {
package_context: PackageContext<NetworkCurve, BlockNumber>,
exodus_context: ExodusContext<NetworkId>,
pub hiding_commitment: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
pub binding_commitment: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
pub session: ExodusSession,
}
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
pub struct ExodusGroupCommitment<NetworkCurve> {
context: PackageContext<NetworkCurve>,
pub struct ExodusGroupCommitment<NetworkCurve, NetworkId: Default, BlockNumber> {
package_context: PackageContext<NetworkCurve, BlockNumber>,
exodus_context: ExodusContext<NetworkId>,
pub group_commitment: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
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<NetworkCurve, T>
pub struct ExodusSignatureShare<NetworkCurve, NetworkId, BlockNumber, T>
where
NetworkId: Default,
T: crate::pallet::Config,
{
context: PackageContext<NetworkCurve>,
package_context: PackageContext<NetworkCurve, BlockNumber>,
exodus_context: ExodusContext<NetworkId>,
pub binding_factors_proof: BoundedVec<ExodusHash, crate::BindingFactorsProof<T>>,
pub signature_share: BoundedVec<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>,
pub self_binding_factor: BoundedVec<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>,
pub session: ExodusSession,
}
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
pub struct Round0Package<NetworkCurve> {
context: PackageContext<NetworkCurve>,
pub struct Round0Package<NetworkCurve, BlockNumber> {
context: PackageContext<NetworkCurve, BlockNumber>,
pub package_hash: ExodusHash,
}
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct Round1Package<NetworkCurve, T>
pub struct Round1Package<NetworkCurve, BlockNumber, T>
where
T: crate::pallet::Config,
{
context: PackageContext<NetworkCurve>,
context: PackageContext<NetworkCurve, BlockNumber>,
pub package: BoundedVec<u8, crate::Round1MaxBytes<T>>,
}
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct Round2Package<NetworkCurve, T>
pub struct Round2Package<NetworkCurve, BlockNumber, T>
where
T: crate::pallet::Config,
{
context: PackageContext<NetworkCurve>,
context: PackageContext<NetworkCurve, BlockNumber>,
pub bundle: EncryptionBundledPackages<T>,
}
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct Round3Package<NetworkCurve, T>
pub struct Round3Package<NetworkCurve, BlockNumber, T>
where
T: crate::pallet::Config,
{
context: PackageContext<NetworkCurve>,
pub indices: BoundedVec<u8, crate::AccusedIndicesMaxBytes<T>>,
context: PackageContext<NetworkCurve, BlockNumber>,
}
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct Round4Package<NetworkCurve, T>
pub struct Round4Package<NetworkCurve, BlockNumber, T>
where
T: crate::pallet::Config,
{
context: PackageContext<NetworkCurve>,
context: PackageContext<NetworkCurve, BlockNumber>,
pub bundle: JustificationsBundledPackages<T>,
}
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct Round5Package<NetworkCurve, T>
pub struct Round5Package<NetworkCurve, BlockNumber, T>
where
T: crate::pallet::Config,
{
context: PackageContext<NetworkCurve>,
context: PackageContext<NetworkCurve, BlockNumber>,
pub key_members: BoundedVec<u8, crate::MaxAuthoritiesBitmaskSize<T>>,
pub verifying_key: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
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<NetworkCurve, T>
pub struct Round6Package<NetworkCurve, BlockNumber, T>
where
T: crate::pallet::Config,
{
context: PackageContext<NetworkCurve>,
context: PackageContext<NetworkCurve, BlockNumber>,
pub verifying_share: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
pub merkle_proof: BoundedVec<ExodusHash, crate::MerkleProofMaxSize<T>>,
}
impl<NetworkCurve> PackageContext<NetworkCurve> {
pub fn with_exodus_nonce_commitment(
impl<NetworkCurve, BlockNumber> PackageContext<NetworkCurve, BlockNumber> {
pub fn with_exodus_nonce_commitment<NetworkId: Default>(
self,
session: ExodusSession,
exodus_context: ExodusContext<NetworkId>,
hiding_commitment_vec: Vec<u8>,
binding_commitment_vec: Vec<u8>,
) -> Result<ExodusNonceCommitment<NetworkCurve>, ExodusError> {
) -> Result<ExodusNonceCommitment<NetworkCurve, NetworkId, BlockNumber>, ExodusError> {
let hiding_commitment =
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::try_from(hiding_commitment_vec)
.map_err(|_| ExodusError::DeserializationError)?;
@ -328,34 +375,39 @@ impl<NetworkCurve> PackageContext<NetworkCurve> {
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::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<NetworkId: Default>(
self,
session: ExodusSession,
exodus_context: ExodusContext<NetworkId>,
commitment_vec: Vec<u8>,
binding_factors_root: ExodusHash,
) -> Result<ExodusGroupCommitment<NetworkCurve>, ExodusError> {
) -> Result<ExodusGroupCommitment<NetworkCurve, NetworkId, BlockNumber>, ExodusError> {
let group_commitment =
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::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<T: crate::pallet::Config>(
pub fn with_exodus_signature_share<NetworkId: Default, T: crate::pallet::Config>(
self,
session: ExodusSession,
exodus_context: ExodusContext<NetworkId>,
signature_share_vec: Vec<u8>,
binding_factors_proof_vec: Vec<ExodusHash>,
self_binding_factor_vec: Vec<u8>,
) -> Result<ExodusSignatureShare<NetworkCurve, T>, ExodusError> {
) -> Result<ExodusSignatureShare<NetworkCurve, NetworkId, BlockNumber, T>, ExodusError> {
let signature_share =
BoundedVec::<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>::try_from(signature_share_vec)
.map_err(|_| ExodusError::DeserializationError)?;
@ -369,25 +421,25 @@ impl<NetworkCurve> PackageContext<NetworkCurve> {
.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<NetworkCurve> {
) -> Round0Package<NetworkCurve, BlockNumber> {
Round0Package { context: self, package_hash }
}
pub fn with_dkg_round1<T: crate::pallet::Config>(
self,
package_vec: Vec<u8>,
) -> Result<Round1Package<NetworkCurve, T>, ExodusError> {
) -> Result<Round1Package<NetworkCurve, BlockNumber, T>, ExodusError> {
let package =
BoundedVec::<u8, crate::Round1MaxBytes<T>>::try_from(package_vec)
.map_err(|_| ExodusError::DeserializationError)?;
@ -398,7 +450,7 @@ impl<NetworkCurve> PackageContext<NetworkCurve> {
pub fn with_dkg_round2<T: crate::pallet::Config>(
self,
bundle_raw: BundledPackages,
) -> Result<Round2Package<NetworkCurve, T>, ExodusError> {
) -> Result<Round2Package<NetworkCurve, BlockNumber, T>, ExodusError> {
let bundle: EncryptionBundledPackages<T> = bundle_raw.try_into()?;
Ok(Round2Package { context: self, bundle })
}
@ -406,7 +458,7 @@ impl<NetworkCurve> PackageContext<NetworkCurve> {
pub fn with_dkg_round3<T: crate::pallet::Config>(
self,
indices_vec: Vec<u8>
) -> Result<Round3Package<NetworkCurve, T>, ExodusError> {
) -> Result<Round3Package<NetworkCurve, BlockNumber, T>, ExodusError> {
let indices =
BoundedVec::<u8, crate::AccusedIndicesMaxBytes<T>>::try_from(indices_vec)
.map_err(|_| ExodusError::DeserializationError)?;
@ -417,7 +469,7 @@ impl<NetworkCurve> PackageContext<NetworkCurve> {
pub fn with_dkg_round4<T: crate::pallet::Config>(
self,
bundle_raw: BundledPackages,
) -> Result<Round4Package<NetworkCurve, T>, ExodusError> {
) -> Result<Round4Package<NetworkCurve, BlockNumber, T>, ExodusError> {
let bundle: JustificationsBundledPackages<T> = bundle_raw.try_into()?;
Ok(Round4Package { context: self, bundle })
}
@ -427,7 +479,7 @@ impl<NetworkCurve> PackageContext<NetworkCurve> {
verifying_key_vec: Vec<u8>,
key_members_vec: Vec<u8>,
merkle_root: ExodusHash,
) -> Result<Round5Package<NetworkCurve, T>, ExodusError> {
) -> Result<Round5Package<NetworkCurve, BlockNumber, T>, ExodusError> {
let key_members = BoundedVec::<u8, crate::MaxAuthoritiesBitmaskSize<T>>::try_from(key_members_vec)
.map_err(|_| ExodusError::DeserializationError)?;
@ -442,7 +494,7 @@ impl<NetworkCurve> PackageContext<NetworkCurve> {
self,
verifying_share_vec: Vec<u8>,
merkle_proof_vec: Vec<ExodusHash>,
) -> Result<Round6Package<NetworkCurve, T>, ExodusError> {
) -> Result<Round6Package<NetworkCurve, BlockNumber, T>, ExodusError> {
let verifying_share =
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::try_from(verifying_share_vec)
.map_err(|_| ExodusError::DeserializationError)?;
@ -455,26 +507,51 @@ impl<NetworkCurve> PackageContext<NetworkCurve> {
}
}
impl<NC, T> PackageMetadata<NC> for ExodusPackage<NC, T>
impl<NC, NI, B, T> ExodusMetadata<NI> for ExodusPackage<NC, NI, B, T>
where
NC: Copy,
NI: Default + Copy,
T: crate::pallet::Config,
{
fn context(&self) -> &PackageContext<NC> {
fn copy_exodus_context(&self) -> ExodusContext<NI> {
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<NI> {
match self {
Self::NonceCommitment(package) => &package.exodus_context,
Self::GroupCommitment(package) => &package.exodus_context,
Self::SignatureShare(package) => &package.exodus_context,
}
}
}
impl<NC, T> PackageMetadata<NC> for DkgPackage<NC, T>
impl<NC, NI, B, T> PackageMetadata<NC, B> for ExodusPackage<NC, NI, B, T>
where
NI: Default,
NC: Copy,
B: Copy,
T: crate::pallet::Config,
{
fn context(&self) -> &PackageContext<NC> {
fn package_context(&self) -> &PackageContext<NC, B> {
match self {
Self::NonceCommitment(package) => &package.package_context,
Self::GroupCommitment(package) => &package.package_context,
Self::SignatureShare(package) => &package.package_context,
}
}
}
impl<NC, B, T> PackageMetadata<NC, B> for DkgPackage<NC, B, T>
where
NC: Copy,
B: Copy,
T: crate::pallet::Config,
{
fn package_context(&self) -> &PackageContext<NC, B> {
match self {
Self::Round0(package) => &package.context,
Self::Round1(package) => &package.context,

View File

@ -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<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>;
type ExodusSignatureT = BoundedVec<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>;
type Type<NetworkId, Balance> = super::ExodusRequestType<NetworkId, Balance>;
#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, RuntimeDebug)]
pub struct ExodusSignedMessage<NetworkId: Default, Balance: Default> {
pub struct ExodusSignedMessage<Balance: Default> {
pub nonce: ExodusNonceT,
pub signature: ExodusSignatureT,
pub r#type: Type<NetworkId, Balance>,
pub r#type: ExodusRequestType<Balance>,
}
impl<NetworkId, Balance> ExodusSignedMessage<NetworkId, Balance>
impl<Balance> ExodusSignedMessage<Balance>
where
NetworkId: UniqueSaturatedInto<u64> + Copy + Default,
Balance: UniqueSaturatedInto<u128> + Copy + Default,
{
pub fn new<E, S>(
nonce_iter: E,
signature_iter: S,
r#type: Type<NetworkId, Balance>
r#type: ExodusRequestType<Balance>,
) -> Option<Self>
where
E: Iterator<Item = u8> + 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<NetworkId>,
) -> Option<&'a [u8]> {
self.r#type.get_message(message_buffer)
self.r#type.get_message(message_buffer, exodus_context)
}
}

View File

@ -1,16 +1,19 @@
use sp_runtime::traits::UniqueSaturatedInto;
use crate::{AuthIndex, ExodusSession, DkgIndex, RoastSession};
pub enum ExodusOk<NetworkCurve> {
pub enum ExodusOk<NetworkCurve, NetworkId> {
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<NetworkCurve> {
DkgRoundMaxSignersNarrowed(u8, AuthIndex, AuthIndex, AuthIndex, NetworkCurve),
}
impl<NetworkCurve> core::fmt::Display for ExodusOk<NetworkCurve>
impl<NetworkCurve, NetworkId> core::fmt::Debug for ExodusOk<NetworkCurve, NetworkId>
where
NetworkCurve: core::fmt::Display
NetworkCurve: core::fmt::Display,
NetworkId: UniqueSaturatedInto<u64> + 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}."),
}
}
}

View File

@ -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

View File

@ -197,17 +197,21 @@ pub mod module {
StorageValue<_, BoundedVec<T::NetworkId, T::MaxNetworks>, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn network_curves)]
pub type NetworkCurves<T: Config> = StorageMap<
#[pallet::getter(fn network_types)]
pub type NetworkTypes<T: Config> = StorageMap<
_,
Twox64Concat, NetworkCurve,
BoundedBTreeMap<NetworkType, u32, T::MaxNetworks>,
BoundedBTreeMap<
NetworkType,
BoundedVec<T::NetworkId, T::MaxNetworks>,
T::MaxNetworks,
>,
ValueQuery,
>;
#[pallet::storage]
#[pallet::getter(fn network_curves_vec)]
pub type NetworkCurvesVec<T: Config> =
#[pallet::getter(fn network_curves)]
pub type NetworkCurves<T: Config> =
StorageValue<_, BoundedVec<NetworkCurve, T::MaxNetworks>, ValueQuery>;
#[pallet::storage]
@ -429,24 +433,26 @@ impl<T: Config> Pallet<T> {
modified_indexes.try_push(chain_id.clone())
.map_err(|_| Error::<T>::TooManyNetworks)?;
let mut modified_curves_map = NetworkCurves::<T>::get(network_curve);
if let Some(counter) = modified_curves_map.get_mut(&network_type) {
*counter = counter.saturating_add(1);
let mut modified_types = NetworkTypes::<T>::get(network_curve);
if let Some(chain_ids) = modified_types.get_mut(&network_type) {
chain_ids.try_push(chain_id).map_err(|_| Error::<T>::TooManyNetworks)?;
} else {
modified_curves_map.try_insert(network_type, 1u32)
let mut new_ids_vec = BoundedVec::<T::NetworkId, T::MaxNetworks>::default();
new_ids_vec.try_push(chain_id).map_err(|_| Error::<T>::TooManyNetworks)?;
modified_types.try_insert(network_type, new_ids_vec)
.map_err(|_| Error::<T>::TooManyNetworks)?;
}
let mut modified_curves_vec = NetworkCurvesVec::<T>::get();
if !modified_curves_vec.contains(&network_curve) {
modified_curves_vec.try_push(network_curve)
let mut modified_curves = NetworkCurves::<T>::get();
if !modified_curves.contains(&network_curve) {
modified_curves.try_push(network_curve)
.map_err(|_| Error::<T>::TooManyNetworks)?;
}
Networks::<T>::insert(&chain_id, network);
NetworkIndexes::<T>::put(modified_indexes);
NetworkCurves::<T>::insert(network_curve, modified_curves_map);
NetworkCurvesVec::<T>::put(modified_curves_vec);
NetworkTypes::<T>::insert(network_curve, modified_types);
NetworkCurves::<T>::put(modified_curves);
Self::deposit_event(Event::<T>::NetworkRegistered { chain_id });
Ok(())
@ -460,38 +466,38 @@ impl<T: Config> Pallet<T> {
let network_curve = network.curve;
let network_type = network.r#type;
let mut modified_curves_map = NetworkCurves::<T>::get(network_curve);
let mut modified_types = NetworkTypes::<T>::get(network_curve);
let counter = modified_curves_map
let chain_ids = modified_types
.get_mut(&network_type)
.ok_or(Error::<T>::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::<T>::get();
modified_indexes.retain(|id| id != &chain_id);
let mut modified_curves_vec = NetworkCurvesVec::<T>::get();
if should_remove_curve_from_vec {
modified_curves_vec.retain(|curve| curve != &network_curve);
let mut modified_curves = NetworkCurves::<T>::get();
if should_remove_curve {
modified_curves.retain(|curve| curve != &network_curve);
}
Networks::<T>::remove(&chain_id);
NetworkIndexes::<T>::put(modified_indexes);
NetworkCurvesVec::<T>::put(modified_curves_vec);
NetworkCurves::<T>::put(modified_curves);
if modified_curves_map.is_empty() {
NetworkCurves::<T>::remove(network_curve);
if modified_types.is_empty() {
NetworkTypes::<T>::remove(network_curve);
} else {
NetworkCurves::<T>::insert(network_curve, modified_curves_map);
NetworkTypes::<T>::insert(network_curve, modified_types);
}
Self::deposit_event(Event::<T>::NetworkRemoved { chain_id });
@ -625,15 +631,46 @@ impl<T: Config> Pallet<T> {
chain_id: T::NetworkId,
network_type: NetworkType,
) -> DispatchResult {
Networks::<T>::try_mutate(&chain_id, |maybe_network| -> DispatchResult {
let network = maybe_network.as_mut().ok_or(Error::<T>::NetworkDoesNotExist)?;
let mut network = Networks::<T>::get(&chain_id)
.ok_or(Error::<T>::NetworkDoesNotExist)?;
let network_curve = network.curve;
let prev_type = network.r#type;
if prev_type == network_type {
return Ok(());
}
let mut modified_types = NetworkTypes::<T>::get(network_curve);
let prev_chain_ids = modified_types
.get_mut(&prev_type)
.ok_or(Error::<T>::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::<T>::TooManyNetworks)?;
} else {
let mut new_ids_vec = BoundedVec::<T::NetworkId, T::MaxNetworks>::default();
new_ids_vec.try_push(chain_id).map_err(|_| Error::<T>::TooManyNetworks)?;
modified_types.try_insert(network_type, new_ids_vec)
.map_err(|_| Error::<T>::TooManyNetworks)?;
}
network.r#type = network_type;
Ok(())
})?;
Networks::<T>::insert(&chain_id, network);
NetworkTypes::<T>::insert(network_curve, modified_types);
Self::deposit_event(Event::<T>::NetworkTypeUpdated {
chain_id,
network_type,
});
Ok(())
}
@ -652,50 +689,53 @@ impl<T: Config> Pallet<T> {
return Ok(());
}
let mut modified_prev_map = NetworkCurves::<T>::get(prev_curve);
let counter = modified_prev_map
let mut modified_prev_types = NetworkTypes::<T>::get(prev_curve);
let prev_chain_ids = modified_prev_types
.get_mut(&network_type)
.ok_or(Error::<T>::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::<T>::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::<T>::get(network_curve);
if let Some(chain_ids) = modified_curr_types.get_mut(&network_type) {
chain_ids.try_push(chain_id)
.map_err(|_| Error::<T>::TooManyNetworks)?;
} else {
modified_curr_map.try_insert(network_type, 1u32)
let mut new_ids_vec = BoundedVec::<T::NetworkId, T::MaxNetworks>::default();
new_ids_vec.try_push(chain_id).map_err(|_| Error::<T>::TooManyNetworks)?;
modified_curr_types.try_insert(network_type, new_ids_vec)
.map_err(|_| Error::<T>::TooManyNetworks)?;
}
let mut modified_curves_vec = NetworkCurvesVec::<T>::get();
let mut modified_curves = NetworkCurves::<T>::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::<T>::TooManyNetworks)?;
}
network.curve = network_curve;
Networks::<T>::insert(&chain_id, network);
NetworkCurves::<T>::insert(network_curve, modified_curr_map);
NetworkCurvesVec::<T>::put(modified_curves_vec);
NetworkTypes::<T>::insert(network_curve, modified_curr_types);
NetworkCurves::<T>::put(modified_curves);
if modified_prev_map.is_empty() {
NetworkCurves::<T>::remove(prev_curve);
if modified_prev_types.is_empty() {
NetworkTypes::<T>::remove(prev_curve);
} else {
NetworkCurves::<T>::insert(prev_curve, modified_prev_map);
NetworkTypes::<T>::insert(prev_curve, modified_prev_types);
}
Self::deposit_event(Event::<T>::NetworkCurveUpdated {
@ -761,6 +801,7 @@ impl<T: Config> NetworkDataBasicHandler for Pallet<T> {
type NetworkId = T::NetworkId;
type NetworkType = NetworkType;
type NetworkCurve = NetworkCurve;
type MaxNetworks = T::MaxNetworks;
}
impl<T: Config> NetworkDataInspectHandler<NetworkData> for Pallet<T> {
@ -775,7 +816,7 @@ impl<T: Config> NetworkDataInspectHandler<NetworkData> for Pallet<T> {
}
fn curve_exists(curve: &Self::NetworkCurve) -> bool {
NetworkCurves::<T>::contains_key(curve)
NetworkTypes::<T>::contains_key(curve)
}
fn get(n: &Self::NetworkId) -> Option<NetworkData> {
@ -804,15 +845,17 @@ impl<T: Config> NetworkDataInspectHandler<NetworkData> for Pallet<T> {
}
fn iter_curves() -> impl Iterator<Item = Self::NetworkCurve> {
NetworkCurvesVec::<T>::get().into_iter()
NetworkCurves::<T>::get().into_iter()
}
fn iter_types_by_curve(
curve: &Self::NetworkCurve
) -> impl Iterator<Item = Self::NetworkType> {
NetworkCurves::<T>::get(curve)
.into_iter()
.map(|(network_type, _)| network_type)
) -> impl Iterator<Item = (T::NetworkId, Self::NetworkType)> {
let network_types = NetworkTypes::<T>::get(curve);
network_types.into_iter().flat_map(|(network_type, network_ids)| {
network_ids.into_iter().map(move |network_id| (network_id, network_type))
})
}
}

View File

@ -45,8 +45,8 @@ fn register_and_check_network(chain_id: u32, network: NetworkData) {
));
assert_eq!(Networks::<Test>::get(chain_id), Some(network));
assert_eq!(NetworkIndexes::<Test>::get(), vec![chain_id]);
assert!(NetworkCurves::<Test>::contains_key(&network_curve));
assert!(NetworkCurvesVec::<Test>::get().contains(&network_curve));
assert!(NetworkTypes::<Test>::contains_key(&network_curve));
assert!(NetworkCurves::<Test>::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::<Test>::get(chain_id), None);
assert_eq!(NetworkIndexes::<Test>::get(), vec![]);
assert!(!NetworkCurves::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE));
assert!(!NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurves::<Test>::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::<Test>::get(chain_id), Some(network));
assert_eq!(NetworkIndexes::<Test>::get(), vec![chain_id]);
assert!(NetworkCurves::<Test>::contains_key(&CURRENT_CURVE));
assert!(NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE));
assert!(NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(NetworkCurves::<Test>::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::<Test>::get(chain_id), None);
assert_eq!(NetworkIndexes::<Test>::get(), vec![]);
assert!(!NetworkCurves::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE));
assert!(!NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurves::<Test>::get().contains(&CURRENT_CURVE));
assert_err!(
GhostNetworks::register_network(
@ -108,8 +108,8 @@ fn could_not_add_network_from_random_account() {
);
assert_eq!(Networks::<Test>::get(chain_id), None);
assert_eq!(NetworkIndexes::<Test>::get(), vec![]);
assert!(!NetworkCurves::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE));
assert!(!NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurves::<Test>::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::<Test>::contains_key(&new_curve));
assert!(NetworkCurves::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurvesVec::<Test>::get().contains(&new_curve));
assert!(NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE));
assert!(!NetworkTypes::<Test>::contains_key(&new_curve));
assert!(NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurves::<Test>::get().contains(&new_curve));
assert!(NetworkCurves::<Test>::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::<Test>::contains_key(&new_curve));
assert!(!NetworkCurves::<Test>::contains_key(&CURRENT_CURVE));
assert!(NetworkCurvesVec::<Test>::get().contains(&new_curve));
assert!(!NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE));
assert!(NetworkTypes::<Test>::contains_key(&new_curve));
assert!(!NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(NetworkCurves::<Test>::get().contains(&new_curve));
assert!(!NetworkCurves::<Test>::get().contains(&CURRENT_CURVE));
assert_eq!(Networks::<Test>::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::<Test>::get(chain_id), None);
assert_eq!(NetworkIndexes::<Test>::get(), vec![]);
assert!(!NetworkCurves::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE));
assert!(!NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurves::<Test>::get().contains(&CURRENT_CURVE));
});
}
@ -1077,8 +1077,8 @@ fn could_not_remove_network_from_random_account() {
);
assert_eq!(Networks::<Test>::get(chain_id), Some(network));
assert_eq!(NetworkIndexes::<Test>::get(), vec![chain_id]);
assert!(NetworkCurves::<Test>::contains_key(&CURRENT_CURVE));
assert!(NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE));
assert!(NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(NetworkCurves::<Test>::get().contains(&CURRENT_CURVE));
});
}
@ -1093,8 +1093,8 @@ fn could_not_remove_non_existent_network() {
);
assert_eq!(Networks::<Test>::get(chain_id), None);
assert_eq!(NetworkIndexes::<Test>::get(), vec![]);
assert!(!NetworkCurves::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurvesVec::<Test>::get().contains(&CURRENT_CURVE));
assert!(!NetworkTypes::<Test>::contains_key(&CURRENT_CURVE));
assert!(!NetworkCurves::<Test>::get().contains(&CURRENT_CURVE));
});
}

View File

@ -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

View File

@ -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,
@ -29,6 +32,7 @@ pub trait NetworkDataBasicHandler {
+ TypeInfo
+ MaybeSerializeDeserialize
+ MaxEncodedLen;
type MaxNetworks: Get<u32>;
}
pub trait NetworkDataInspectHandler<Network>: NetworkDataBasicHandler {
@ -40,7 +44,9 @@ pub trait NetworkDataInspectHandler<Network>: NetworkDataBasicHandler {
fn iter() -> PrefixIterator<(Self::NetworkId, Network)>;
fn iter_indexes() -> impl Iterator<Item = Self::NetworkId>;
fn iter_curves() -> impl Iterator<Item = Self::NetworkCurve>;
fn iter_types_by_curve(n: &Self::NetworkCurve) -> impl Iterator<Item = Self::NetworkType>;
fn iter_types_by_curve(
c: &Self::NetworkCurve
) -> impl Iterator<Item = (Self::NetworkId, Self::NetworkType)>;
}
pub trait NetworkDataMutateHandler<Network, Balance>: NetworkDataInspectHandler<Network> {