fix GJKR arbitration, avoid invalid kicks, and synchronize round 5 output
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
This commit is contained in:
parent
d488bf9a43
commit
1dc61fb17d
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ghost-exodus"
|
name = "ghost-exodus"
|
||||||
version = "0.0.24"
|
version = "0.0.25"
|
||||||
description = "Threshold signature generation with DKG included"
|
description = "Threshold signature generation with DKG included"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
|
|||||||
@ -344,12 +344,17 @@ mod benchmarks {
|
|||||||
|
|
||||||
let dummy_merkle_root = ExodusHash::repeat_byte(69);
|
let dummy_merkle_root = ExodusHash::repeat_byte(69);
|
||||||
let dummy_verifying_key = vec![69u8; network_curve.element_bytes_len()];
|
let dummy_verifying_key = vec![69u8; network_curve.element_bytes_len()];
|
||||||
|
let dummy_key_members = vec![15];
|
||||||
|
|
||||||
let round5_package = PackageContext::default()
|
let round5_package = PackageContext::default()
|
||||||
.with_network_curve(NetworkCurve::Secp256k1)
|
.with_network_curve(NetworkCurve::Secp256k1)
|
||||||
.with_authority_index(authority_index)
|
.with_authority_index(authority_index)
|
||||||
.with_dkg_index(dkg_index)
|
.with_dkg_index(dkg_index)
|
||||||
.with_dkg_round5(dummy_verifying_key.clone(), dummy_merkle_root)
|
.with_dkg_round5(
|
||||||
|
dummy_verifying_key.clone(),
|
||||||
|
dummy_key_members.clone(),
|
||||||
|
dummy_merkle_root,
|
||||||
|
)
|
||||||
.expect("Round5 package should be valid");
|
.expect("Round5 package should be valid");
|
||||||
|
|
||||||
let dkg_package = DkgPackage::Round5(round5_package);
|
let dkg_package = DkgPackage::Round5(round5_package);
|
||||||
@ -366,7 +371,11 @@ mod benchmarks {
|
|||||||
dummy_verifying_key,
|
dummy_verifying_key,
|
||||||
).expect("Bounded verifying key should be correct");
|
).expect("Bounded verifying key should be correct");
|
||||||
|
|
||||||
let metadata_hashes = [dummy_merkle_root.as_ref(), bounded_verifying_key.as_ref()];
|
let metadata_hashes = [
|
||||||
|
dummy_merkle_root.as_ref(),
|
||||||
|
bounded_verifying_key.as_ref(),
|
||||||
|
dummy_key_members.as_ref(),
|
||||||
|
];
|
||||||
let verifying_hash = cumulative_hash::<SubstrateBlake2Hasher, _>(metadata_hashes);
|
let verifying_hash = cumulative_hash::<SubstrateBlake2Hasher, _>(metadata_hashes);
|
||||||
|
|
||||||
let verification_key = (network_curve, dkg_index, verifying_hash);
|
let verification_key = (network_curve, dkg_index, verifying_hash);
|
||||||
@ -374,10 +383,11 @@ mod benchmarks {
|
|||||||
assert!(participants.contains(authority_index));
|
assert!(participants.contains(authority_index));
|
||||||
|
|
||||||
let consensus = VerifyingKeyConsensus::<T>::get(&network_curve, dkg_index);
|
let consensus = VerifyingKeyConsensus::<T>::get(&network_curve, dkg_index);
|
||||||
assert!(consensus.participants.contains(authority_index));
|
assert!(consensus.state.participants.contains(authority_index));
|
||||||
assert_eq!(consensus.participants.count_ones::<AuthIndex>(), 1);
|
assert_eq!(consensus.state.participants.count_ones::<AuthIndex>(), 1);
|
||||||
assert_eq!(consensus.merkle_root, dummy_merkle_root);
|
assert_eq!(consensus.state.merkle_root, dummy_merkle_root);
|
||||||
assert_eq!(consensus.element_bytes, bounded_verifying_key);
|
assert_eq!(consensus.state.element_bytes, bounded_verifying_key);
|
||||||
|
assert_eq!(consensus.members.as_ref(), dummy_key_members);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -444,8 +454,8 @@ mod benchmarks {
|
|||||||
});
|
});
|
||||||
|
|
||||||
VerifyingKeyConsensus::<T>::mutate(&network_curve, &dkg_index, |consensus| {
|
VerifyingKeyConsensus::<T>::mutate(&network_curve, &dkg_index, |consensus| {
|
||||||
consensus.participants.insert(authority_index);
|
consensus.state.participants.insert(authority_index);
|
||||||
consensus.merkle_root = dummy_merkle_root;
|
consensus.state.merkle_root = dummy_merkle_root;
|
||||||
});
|
});
|
||||||
|
|
||||||
let round6_package = PackageContext::default()
|
let round6_package = PackageContext::default()
|
||||||
|
|||||||
@ -411,7 +411,7 @@ pub mod pallet {
|
|||||||
type MaxAuthorities: Get<u32>;
|
type MaxAuthorities: Get<u32>;
|
||||||
|
|
||||||
#[pallet::constant]
|
#[pallet::constant]
|
||||||
type MaxAuthoritiesChunks: Get<u32>;
|
type MaxAuthoritiesChunks: Get<u32> + core::fmt::Debug;
|
||||||
|
|
||||||
#[pallet::constant]
|
#[pallet::constant]
|
||||||
type DkgRoundPeriod: Get<u64>;
|
type DkgRoundPeriod: Get<u64>;
|
||||||
@ -499,6 +499,7 @@ pub mod pallet {
|
|||||||
BridgeOverflow,
|
BridgeOverflow,
|
||||||
TooManyPackages,
|
TooManyPackages,
|
||||||
WrongNetworkType,
|
WrongNetworkType,
|
||||||
|
MemberNotQualified,
|
||||||
InvalidMerkleProof,
|
InvalidMerkleProof,
|
||||||
NetworkDoesNotExist,
|
NetworkDoesNotExist,
|
||||||
NoActiveAuthorities,
|
NoActiveAuthorities,
|
||||||
@ -730,7 +731,10 @@ pub mod pallet {
|
|||||||
_,
|
_,
|
||||||
Twox64Concat, NetworkCurve,
|
Twox64Concat, NetworkCurve,
|
||||||
Twox64Concat, DkgIndex,
|
Twox64Concat, DkgIndex,
|
||||||
ConsensusState<ParticipantsBitmap<T>>,
|
QualifiedConsensusState<
|
||||||
|
ParticipantsBitmap<T>,
|
||||||
|
BoundedVec<u8, MaxAuthoritiesBitmaskSize<T>>
|
||||||
|
>,
|
||||||
ValueQuery,
|
ValueQuery,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
@ -1230,12 +1234,16 @@ pub mod pallet {
|
|||||||
return Err(Error::<T>::Round5PackageInvalidProof.into());
|
return Err(Error::<T>::Round5PackageInvalidProof.into());
|
||||||
};
|
};
|
||||||
|
|
||||||
let metadata_hashes = [round5.merkle_root.as_ref(), round5.verifying_key.as_ref()];
|
|
||||||
let verifying_hash = cumulative_hash::<SubstrateBlake2Hasher, _>(metadata_hashes);
|
|
||||||
|
|
||||||
let state = QualificationDkgState::<T>::get(&network_curve);
|
let state = QualificationDkgState::<T>::get(&network_curve);
|
||||||
ensure!(state.is_fifth_phase(), Error::<T>::DkgWrongRound);
|
ensure!(state.is_fifth_phase(), Error::<T>::DkgWrongRound);
|
||||||
|
|
||||||
|
let metadata_hashes = [
|
||||||
|
round5.merkle_root.as_ref(),
|
||||||
|
round5.verifying_key.as_ref(),
|
||||||
|
round5.key_members.as_ref(),
|
||||||
|
];
|
||||||
|
let verifying_hash = cumulative_hash::<SubstrateBlake2Hasher, _>(metadata_hashes);
|
||||||
|
|
||||||
let dkg_index = state.get_dkg_index();
|
let dkg_index = state.get_dkg_index();
|
||||||
let verification_key = (network_curve, dkg_index, verifying_hash);
|
let verification_key = (network_curve, dkg_index, verifying_hash);
|
||||||
|
|
||||||
@ -1257,15 +1265,16 @@ pub mod pallet {
|
|||||||
Ok(bitmap.clone())
|
Ok(bitmap.clone())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
VerifyingKeyConsensus::<T>::mutate(&network_curve, &dkg_index, |state| {
|
VerifyingKeyConsensus::<T>::mutate(&network_curve, &dkg_index, |current| {
|
||||||
let state_participants_count = state.participants.count_ones::<AuthIndex>();
|
let state_participants_count = current.state.participants.count_ones::<AuthIndex>();
|
||||||
let latest_participants_count = latest_participants.count_ones::<AuthIndex>();
|
let latest_participants_count = latest_participants.count_ones::<AuthIndex>();
|
||||||
|
|
||||||
if state_participants_count < latest_participants_count {
|
if state_participants_count < latest_participants_count {
|
||||||
*state = ConsensusState::new(
|
*current = QualifiedConsensusState::new(
|
||||||
round5.verifying_key,
|
round5.verifying_key,
|
||||||
latest_participants,
|
latest_participants,
|
||||||
round5.merkle_root,
|
round5.merkle_root,
|
||||||
|
round5.key_members,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1314,12 +1323,12 @@ pub mod pallet {
|
|||||||
ensure!(state.is_sixth_phase(), Error::<T>::DkgWrongRound);
|
ensure!(state.is_sixth_phase(), Error::<T>::DkgWrongRound);
|
||||||
|
|
||||||
let dkg_index = state.get_dkg_index();
|
let dkg_index = state.get_dkg_index();
|
||||||
let consensus = VerifyingKeyConsensus::<T>::get(&network_curve, &dkg_index);
|
let qualified_consensus = VerifyingKeyConsensus::<T>::get(&network_curve, &dkg_index);
|
||||||
|
|
||||||
network_curve.verify_merkle_proof(
|
network_curve.verify_merkle_proof(
|
||||||
&round6.verifying_share,
|
&round6.verifying_share,
|
||||||
&round6.merkle_proof,
|
&round6.merkle_proof,
|
||||||
consensus.merkle_root,
|
qualified_consensus.state.merkle_root,
|
||||||
authority_index,
|
authority_index,
|
||||||
).map_err(|_| Error::<T>::InvalidMerkleProof)?;
|
).map_err(|_| Error::<T>::InvalidMerkleProof)?;
|
||||||
|
|
||||||
@ -2513,10 +2522,13 @@ impl<T: Config> Pallet<T> {
|
|||||||
},
|
},
|
||||||
|weight| {
|
|weight| {
|
||||||
weight.saturating_accrue(T::DbWeight::get().reads(1));
|
weight.saturating_accrue(T::DbWeight::get().reads(1));
|
||||||
let state = VerifyingKeyConsensus::<T>::get(&network_curve, &dkg_index);
|
let consensus = VerifyingKeyConsensus::<T>::get(&network_curve, &dkg_index);
|
||||||
|
|
||||||
if state.participants.count_ones::<AuthIndex>() < min_threshold {
|
if consensus.state.participants.count_ones::<AuthIndex>() < min_threshold {
|
||||||
dkg_qualification.nullify_indexes();
|
dkg_qualification.nullify_indexes();
|
||||||
|
} else {
|
||||||
|
let key_members = ParticipantsBitmap::<T>::from_bitmask(&consensus.members);
|
||||||
|
dkg_qualification.intersect_indexes(&key_members);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -2548,7 +2560,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
|
|
||||||
weight.saturating_accrue(T::DbWeight::get().reads(1));
|
weight.saturating_accrue(T::DbWeight::get().reads(1));
|
||||||
let consensus = VerifyingKeyConsensus::<T>::get(&network_curve, &dkg_index);
|
let consensus = VerifyingKeyConsensus::<T>::get(&network_curve, &dkg_index);
|
||||||
let verifying_key_slice = consensus.element_bytes.as_slice();
|
let verifying_key_slice = consensus.state.element_bytes.as_slice();
|
||||||
|
|
||||||
if verifying_key_slice.len() != network_curve.element_bytes_len() {
|
if verifying_key_slice.len() != network_curve.element_bytes_len() {
|
||||||
dkg_qualification.nullify_indexes();
|
dkg_qualification.nullify_indexes();
|
||||||
@ -2587,7 +2599,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
CurrentExodus::<T>::put(exodus_session);
|
CurrentExodus::<T>::put(exodus_session);
|
||||||
weight.saturating_accrue(T::DbWeight::get().writes(1));
|
weight.saturating_accrue(T::DbWeight::get().writes(1));
|
||||||
|
|
||||||
dkg_qualification.set_verifying_key(consensus.element_bytes);
|
dkg_qualification.set_verifying_key(consensus.state.element_bytes);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2979,6 +2991,10 @@ impl<T: Config> Pallet<T> {
|
|||||||
dkg_package: &DkgPackage<NetworkCurve, T>,
|
dkg_package: &DkgPackage<NetworkCurve, T>,
|
||||||
signature: &<T::AuthorityId as RuntimeAppPublic>::Signature,
|
signature: &<T::AuthorityId as RuntimeAppPublic>::Signature,
|
||||||
) -> Result<(), InvalidTransaction> {
|
) -> Result<(), InvalidTransaction> {
|
||||||
|
let DkgPackage::Round5(round5) = dkg_package else {
|
||||||
|
return Err(InvalidTransaction::BadProof);
|
||||||
|
};
|
||||||
|
|
||||||
let network_curve = dkg_package.get_network_curve();
|
let network_curve = dkg_package.get_network_curve();
|
||||||
let authority_index = dkg_package.get_authority_index();
|
let authority_index = dkg_package.get_authority_index();
|
||||||
let package_dkg_index = dkg_package.get_dkg_index();
|
let package_dkg_index = dkg_package.get_dkg_index();
|
||||||
@ -3009,6 +3025,22 @@ impl<T: Config> Pallet<T> {
|
|||||||
return Err(InvalidTransaction::BadProof);
|
return Err(InvalidTransaction::BadProof);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let has_non_qualified_member = round5.key_members
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.any(|(byte_idx, &byte)| {
|
||||||
|
if byte == 0 { return false; }
|
||||||
|
(0..8).any(|bit_idx| {
|
||||||
|
if (byte & (1u8 << bit_idx)) == 0 { return false; }
|
||||||
|
let global_index = byte_idx * 8 + bit_idx;
|
||||||
|
!state.contains_index(global_index)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if has_non_qualified_member {
|
||||||
|
return Err(InvalidTransaction::BadProof);
|
||||||
|
}
|
||||||
|
|
||||||
let authority = QualificationAuthorities::<T>::get(&network_curve)
|
let authority = QualificationAuthorities::<T>::get(&network_curve)
|
||||||
.get(authority_index as usize)
|
.get(authority_index as usize)
|
||||||
.cloned()
|
.cloned()
|
||||||
@ -4328,7 +4360,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
|
|
||||||
let verification_found_for_self =
|
let verification_found_for_self =
|
||||||
VerifyingKeyConsensus::<T>::iter_values()
|
VerifyingKeyConsensus::<T>::iter_values()
|
||||||
.any(|state| state.participants.contains(authority_index));
|
.any(|consensus| consensus.state.participants.contains(authority_index));
|
||||||
|
|
||||||
if verification_found_for_self {
|
if verification_found_for_self {
|
||||||
return Ok(ExodusOk::DkgRoundAlreadyPassed(
|
return Ok(ExodusOk::DkgRoundAlreadyPassed(
|
||||||
@ -4415,6 +4447,30 @@ impl<T: Config> Pallet<T> {
|
|||||||
DkgStorageEnum::Round5 { secret_share, merkle_proof }
|
DkgStorageEnum::Round5 { secret_share, merkle_proof }
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let padded_participants = qualification_state
|
||||||
|
.highest_bit::<usize>()
|
||||||
|
.map(|bit_pos| bit_pos.saturating_add(1))
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let bitmask_len = padded_participants.div_ceil(8);
|
||||||
|
|
||||||
|
let mut key_members_bitmask = vec![0u8; bitmask_len];
|
||||||
|
|
||||||
|
for &member_index in round1_packages.keys() {
|
||||||
|
let global_index = member_index as usize;
|
||||||
|
let byte_index = global_index >> 3usize;
|
||||||
|
let bit_index = global_index & 7usize;
|
||||||
|
|
||||||
|
if let Some(byte) = key_members_bitmask.get_mut(byte_index) {
|
||||||
|
*byte |= 1u8 << bit_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let self_global_index = authority_index as usize;
|
||||||
|
if let Some(byte) = key_members_bitmask.get_mut(self_global_index >> 3) {
|
||||||
|
*byte |= 1 << (self_global_index & 7);
|
||||||
|
}
|
||||||
|
|
||||||
let current_dkg_index = QualificationDkgState::<T>::get(&network_curve)
|
let current_dkg_index = QualificationDkgState::<T>::get(&network_curve)
|
||||||
.get_dkg_index();
|
.get_dkg_index();
|
||||||
|
|
||||||
@ -4423,7 +4479,11 @@ impl<T: Config> Pallet<T> {
|
|||||||
current_dkg_index,
|
current_dkg_index,
|
||||||
&mut round_metadata,
|
&mut round_metadata,
|
||||||
|context| {
|
|context| {
|
||||||
let package = context.with_dkg_round5(verifying_key, merkle_root)?;
|
let package = context.with_dkg_round5(
|
||||||
|
verifying_key,
|
||||||
|
key_members_bitmask,
|
||||||
|
merkle_root,
|
||||||
|
)?;
|
||||||
Ok(DkgPackage::Round5(package))
|
Ok(DkgPackage::Round5(package))
|
||||||
},
|
},
|
||||||
|dkg_package, signature| {
|
|dkg_package, signature| {
|
||||||
@ -4573,32 +4633,40 @@ impl<T: Config> Pallet<T> {
|
|||||||
|
|
||||||
let round2_package_size = blob_len.saturating_div(padded_participants);
|
let round2_package_size = blob_len.saturating_div(padded_participants);
|
||||||
|
|
||||||
|
let mut kicked = sp_std::collections::btree_set::BTreeSet::new();
|
||||||
|
let mut all_justifications = sp_std::collections::btree_map::BTreeMap::new();
|
||||||
|
|
||||||
for (&accuser_index, accused_bitmap) in Complaints::<T>::get(network_curve).iter() {
|
for (&accuser_index, accused_bitmap) in Complaints::<T>::get(network_curve).iter() {
|
||||||
if !qualification_state.contains_index(accuser_index) { continue; }
|
if !qualification_state.contains_index(accuser_index) {
|
||||||
if accuser_index == authority_index { continue; }
|
round1_packages.remove(&accuser_index);
|
||||||
|
round2_packages.remove(&accuser_index);
|
||||||
let raw_round2_packages: BTreeMap<AuthIndex, Vec<u8>> =
|
continue;
|
||||||
Self::gather_incoming_offchain_packages(
|
}
|
||||||
network_curve,
|
|
||||||
accuser_index,
|
|
||||||
ROUND_NUMBER_4,
|
|
||||||
&accused_bitmap,
|
|
||||||
round2_package_size,
|
|
||||||
|mut raw_slice| Vec::<u8>::decode(&mut raw_slice).ok(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut kicked = sp_std::collections::btree_set::BTreeSet::new();
|
|
||||||
|
|
||||||
for accused_index in accused_bitmap.iter::<AuthIndex>() {
|
for accused_index in accused_bitmap.iter::<AuthIndex>() {
|
||||||
if !qualification_state.contains_index(accused_index) { continue; }
|
if !qualification_state.contains_index(accused_index) { continue; }
|
||||||
if kicked.contains(&accused_index) { continue; }
|
if kicked.contains(&accused_index) { continue; }
|
||||||
if accused_index == authority_index { continue; }
|
if accused_index == authority_index { continue; }
|
||||||
|
|
||||||
|
let justifications = all_justifications.entry(accused_index)
|
||||||
|
.or_insert_with(|| {
|
||||||
|
let key = Self::create_offchain_dkg_key(network_curve, accused_index, ROUND_NUMBER_4);
|
||||||
|
StorageValueRef::persistent(&key).get::<BundledPackages>().ok().flatten()
|
||||||
|
});
|
||||||
|
|
||||||
|
let maybe_justification = justifications.as_ref().and_then(|bundle| {
|
||||||
|
Self::locate_package_bounds(bundle, round2_package_size, accuser_index)
|
||||||
|
.and_then(|(start, end)| {
|
||||||
|
bundle.blob.get(start..end).and_then(|raw_slice| {
|
||||||
|
Vec::<u8>::decode(&mut &raw_slice[..]).ok()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
let maybe_round1_package = round1_packages.get(&accused_index);
|
let maybe_round1_package = round1_packages.get(&accused_index);
|
||||||
let maybe_round2_package = raw_round2_packages.get(&accused_index);
|
|
||||||
|
|
||||||
let (round1_package, round2_package) =
|
let (round1_package, round2_package) =
|
||||||
match (maybe_round1_package, maybe_round2_package) {
|
match (maybe_round1_package, maybe_justification) {
|
||||||
(Some(round1_package), Some(round2_package)) => {
|
(Some(round1_package), Some(round2_package)) => {
|
||||||
(round1_package, round2_package)
|
(round1_package, round2_package)
|
||||||
},
|
},
|
||||||
@ -4613,13 +4681,13 @@ impl<T: Config> Pallet<T> {
|
|||||||
match network_curve.dkg_verify_private_package(
|
match network_curve.dkg_verify_private_package(
|
||||||
accuser_index,
|
accuser_index,
|
||||||
round1_package,
|
round1_package,
|
||||||
round2_package,
|
&round2_package,
|
||||||
) {
|
) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
if accuser_index != authority_index { continue; }
|
if accuser_index != authority_index { continue; }
|
||||||
round2_packages.insert(
|
round2_packages.insert(
|
||||||
accused_index,
|
accused_index,
|
||||||
round2_package.clone(),
|
round2_package,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
|||||||
@ -603,7 +603,27 @@ fn run_succesfull_dkg_session_partially(
|
|||||||
assert_eq!(prev_dkg_index + 1, dkg_index);
|
assert_eq!(prev_dkg_index + 1, dkg_index);
|
||||||
|
|
||||||
ext.execute_with(|| {
|
ext.execute_with(|| {
|
||||||
let dummy_oversized_bitmap = ParticipantsBitmap::<Runtime>::all_ones(1337);
|
let max_authorities_len = raw_authorities.len();
|
||||||
|
|
||||||
|
let max_authority_index = raw_authorities.iter().max().cloned().unwrap_or_default() as usize;
|
||||||
|
let padded_participants = max_authority_index.saturating_add(1);
|
||||||
|
let bitmask_len = padded_participants.div_ceil(8);
|
||||||
|
|
||||||
|
let mut key_members_raw = vec![0u8; bitmask_len];
|
||||||
|
|
||||||
|
for &authority in raw_authorities.iter() {
|
||||||
|
let global_index = authority as usize;
|
||||||
|
let byte_index = global_index >> 3;
|
||||||
|
let bit_index = global_index & 7;
|
||||||
|
if let Some(byte) = key_members_raw.get_mut(byte_index) {
|
||||||
|
*byte |= 1 << bit_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let dummy_key_members: BoundedVec<u8, crate::MaxAuthoritiesBitmaskSize<Runtime>> =
|
||||||
|
key_members_raw.try_into().unwrap();
|
||||||
|
|
||||||
|
let dummy_oversized_bitmap = ParticipantsBitmap::<Runtime>::all_ones(max_authorities_len);
|
||||||
let mut map: BitmapByAuthority<Runtime> = Default::default();
|
let mut map: BitmapByAuthority<Runtime> = Default::default();
|
||||||
|
|
||||||
for authority in raw_authorities.iter() {
|
for authority in raw_authorities.iter() {
|
||||||
@ -625,10 +645,11 @@ fn run_succesfull_dkg_session_partially(
|
|||||||
vec![69u8; ELEMENT_MAX_BYTES as usize],
|
vec![69u8; ELEMENT_MAX_BYTES as usize],
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
let dummy_consensus_state = ConsensusState::new(
|
let dummy_consensus_state = QualifiedConsensusState::new(
|
||||||
dummy_verifying_key,
|
dummy_verifying_key,
|
||||||
dummy_oversized_bitmap,
|
dummy_oversized_bitmap,
|
||||||
DUMMY_HASH,
|
DUMMY_HASH,
|
||||||
|
dummy_key_members,
|
||||||
);
|
);
|
||||||
|
|
||||||
VerifyingKeyConsensus::<Runtime>::insert(curve, prev_dkg_index, dummy_consensus_state);
|
VerifyingKeyConsensus::<Runtime>::insert(curve, prev_dkg_index, dummy_consensus_state);
|
||||||
@ -1021,6 +1042,56 @@ fn run_dkg_session(
|
|||||||
assert_ne!(prev_active_authorities, active_authorities);
|
assert_ne!(prev_active_authorities, active_authorities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_exodus_dkg_distributed_synced_exclusion() {
|
||||||
|
let (mut ext, tx_pool_state) = new_test_ext();
|
||||||
|
let curve = NetworkCurve::Secp256k1;
|
||||||
|
|
||||||
|
let honest_validators = vec![0, 1, 2, 4, 5, 6, 7, 8, 9];
|
||||||
|
let malicious_validator = 3;
|
||||||
|
let all_validators = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||||
|
|
||||||
|
let authorities: Vec<UintAuthorityId> = all_validators.iter().map(|&id| id.into()).collect();
|
||||||
|
ext.execute_with(|| Exodus::start_dkg_qualification(authorities, NetworkCurve::iter()));
|
||||||
|
|
||||||
|
run_to_next_round(&mut ext, curve);
|
||||||
|
for &authority in &all_validators {
|
||||||
|
assert_ok!(execute_round0_happy_path(&mut ext, &tx_pool_state, curve, authority));
|
||||||
|
}
|
||||||
|
|
||||||
|
run_to_next_round(&mut ext, curve);
|
||||||
|
for &authority in &all_validators {
|
||||||
|
assert_ok!(execute_round1_happy_path(&mut ext, &tx_pool_state, curve, authority));
|
||||||
|
}
|
||||||
|
|
||||||
|
let remaining_rounds = [
|
||||||
|
execute_round2_happy_path,
|
||||||
|
execute_round3_happy_path,
|
||||||
|
execute_round4_happy_path,
|
||||||
|
execute_round5_happy_path,
|
||||||
|
execute_round6_happy_path,
|
||||||
|
];
|
||||||
|
|
||||||
|
for dkg_round in remaining_rounds {
|
||||||
|
run_to_next_round(&mut ext, curve);
|
||||||
|
for &authority in &honest_validators {
|
||||||
|
assert_ok!(dkg_round(&mut ext, &tx_pool_state, curve, authority));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run_to_next_round(&mut ext, curve);
|
||||||
|
|
||||||
|
ext.execute_with(|| {
|
||||||
|
let state = QualificationDkgState::<Runtime>::get(curve);
|
||||||
|
assert_eq!(state.get_phase(), DkgPhase::Round7);
|
||||||
|
assert!(!state.contains_index(malicious_validator));
|
||||||
|
|
||||||
|
for &authority in &honest_validators {
|
||||||
|
assert!(state.contains_index(authority));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_all_validators_can_execute_initial_dkg() {
|
fn test_all_validators_can_execute_initial_dkg() {
|
||||||
let curve = NetworkCurve::Secp256k1;
|
let curve = NetworkCurve::Secp256k1;
|
||||||
|
|||||||
@ -6,6 +6,30 @@ use frame_support::{BoundedVec, traits::ConstU32};
|
|||||||
|
|
||||||
use crate::{ExodusHash, ELEMENT_MAX_BYTES};
|
use crate::{ExodusHash, ELEMENT_MAX_BYTES};
|
||||||
|
|
||||||
|
#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
|
||||||
|
pub struct QualifiedConsensusState<Bitmap, Bitmask> {
|
||||||
|
pub state: ConsensusState<Bitmap>,
|
||||||
|
pub members: Bitmask,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Bitmap, Bitmask> QualifiedConsensusState<Bitmap, Bitmask> {
|
||||||
|
pub fn new(
|
||||||
|
element_bytes: BoundedVec<u8, ConstU32<ELEMENT_MAX_BYTES>>,
|
||||||
|
participants: Bitmap,
|
||||||
|
merkle_root: ExodusHash,
|
||||||
|
members: Bitmask,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
members,
|
||||||
|
state: ConsensusState::new(
|
||||||
|
element_bytes,
|
||||||
|
participants,
|
||||||
|
merkle_root,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
|
#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
|
||||||
pub struct ConsensusState<Bitmap> {
|
pub struct ConsensusState<Bitmap> {
|
||||||
pub element_bytes: BoundedVec<u8, ConstU32<ELEMENT_MAX_BYTES>>,
|
pub element_bytes: BoundedVec<u8, ConstU32<ELEMENT_MAX_BYTES>>,
|
||||||
|
|||||||
@ -75,7 +75,7 @@ where
|
|||||||
Round2(Round2Package<NetworkCurve, T>),
|
Round2(Round2Package<NetworkCurve, T>),
|
||||||
Round3(Round3Package<NetworkCurve, T>),
|
Round3(Round3Package<NetworkCurve, T>),
|
||||||
Round4(Round4Package<NetworkCurve, T>),
|
Round4(Round4Package<NetworkCurve, T>),
|
||||||
Round5(Round5Package<NetworkCurve>),
|
Round5(Round5Package<NetworkCurve, T>),
|
||||||
Round6(Round6Package<NetworkCurve, T>),
|
Round6(Round6Package<NetworkCurve, T>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
#[derive(Default, RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
|
||||||
pub struct PackageContext<NetworkCurve> {
|
pub struct PackageContext<NetworkCurve> {
|
||||||
authority_index: AuthIndex,
|
authority_index: AuthIndex,
|
||||||
network_curve: NetworkCurve,
|
network_curve: NetworkCurve,
|
||||||
@ -140,13 +140,13 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
|
||||||
pub struct BundledPackages {
|
pub struct BundledPackages {
|
||||||
pub blob: Vec<u8>,
|
pub blob: Vec<u8>,
|
||||||
pub bitmask: Vec<u8>,
|
pub bitmask: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
|
||||||
#[scale_info(skip_type_params(T))]
|
#[scale_info(skip_type_params(T))]
|
||||||
pub struct EncryptionBundledPackages<T>
|
pub struct EncryptionBundledPackages<T>
|
||||||
where
|
where
|
||||||
@ -180,7 +180,7 @@ impl<T: crate::pallet::Config> TryFrom<BundledPackages> for EncryptionBundledPac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
|
||||||
#[scale_info(skip_type_params(T))]
|
#[scale_info(skip_type_params(T))]
|
||||||
pub struct JustificationsBundledPackages<T>
|
pub struct JustificationsBundledPackages<T>
|
||||||
where
|
where
|
||||||
@ -214,7 +214,7 @@ impl<T: crate::pallet::Config> TryFrom<BundledPackages> for JustificationsBundle
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
|
||||||
#[scale_info(skip_type_params(T))]
|
#[scale_info(skip_type_params(T))]
|
||||||
pub struct ExodusNonceCommitment<NetworkCurve> {
|
pub struct ExodusNonceCommitment<NetworkCurve> {
|
||||||
context: PackageContext<NetworkCurve>,
|
context: PackageContext<NetworkCurve>,
|
||||||
@ -223,7 +223,7 @@ pub struct ExodusNonceCommitment<NetworkCurve> {
|
|||||||
pub session: ExodusSession,
|
pub session: ExodusSession,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
|
||||||
pub struct ExodusGroupCommitment<NetworkCurve> {
|
pub struct ExodusGroupCommitment<NetworkCurve> {
|
||||||
context: PackageContext<NetworkCurve>,
|
context: PackageContext<NetworkCurve>,
|
||||||
pub group_commitment: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
|
pub group_commitment: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
|
||||||
@ -231,7 +231,7 @@ pub struct ExodusGroupCommitment<NetworkCurve> {
|
|||||||
pub session: ExodusSession,
|
pub session: ExodusSession,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
|
||||||
#[scale_info(skip_type_params(T))]
|
#[scale_info(skip_type_params(T))]
|
||||||
pub struct ExodusSignatureShare<NetworkCurve, T>
|
pub struct ExodusSignatureShare<NetworkCurve, T>
|
||||||
where
|
where
|
||||||
@ -244,13 +244,13 @@ where
|
|||||||
pub session: ExodusSession,
|
pub session: ExodusSession,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
|
||||||
pub struct Round0Package<NetworkCurve> {
|
pub struct Round0Package<NetworkCurve> {
|
||||||
context: PackageContext<NetworkCurve>,
|
context: PackageContext<NetworkCurve>,
|
||||||
pub package_hash: ExodusHash,
|
pub package_hash: ExodusHash,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
|
||||||
#[scale_info(skip_type_params(T))]
|
#[scale_info(skip_type_params(T))]
|
||||||
pub struct Round1Package<NetworkCurve, T>
|
pub struct Round1Package<NetworkCurve, T>
|
||||||
where
|
where
|
||||||
@ -260,7 +260,7 @@ where
|
|||||||
pub package: BoundedVec<u8, crate::Round1MaxBytes<T>>,
|
pub package: BoundedVec<u8, crate::Round1MaxBytes<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
|
||||||
#[scale_info(skip_type_params(T))]
|
#[scale_info(skip_type_params(T))]
|
||||||
pub struct Round2Package<NetworkCurve, T>
|
pub struct Round2Package<NetworkCurve, T>
|
||||||
where
|
where
|
||||||
@ -270,7 +270,7 @@ where
|
|||||||
pub bundle: EncryptionBundledPackages<T>,
|
pub bundle: EncryptionBundledPackages<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
|
||||||
#[scale_info(skip_type_params(T))]
|
#[scale_info(skip_type_params(T))]
|
||||||
pub struct Round3Package<NetworkCurve, T>
|
pub struct Round3Package<NetworkCurve, T>
|
||||||
where
|
where
|
||||||
@ -280,7 +280,7 @@ where
|
|||||||
pub indices: BoundedVec<u8, crate::AccusedIndicesMaxBytes<T>>,
|
pub indices: BoundedVec<u8, crate::AccusedIndicesMaxBytes<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
|
||||||
#[scale_info(skip_type_params(T))]
|
#[scale_info(skip_type_params(T))]
|
||||||
pub struct Round4Package<NetworkCurve, T>
|
pub struct Round4Package<NetworkCurve, T>
|
||||||
where
|
where
|
||||||
@ -290,14 +290,19 @@ where
|
|||||||
pub bundle: JustificationsBundledPackages<T>,
|
pub bundle: JustificationsBundledPackages<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
|
||||||
pub struct Round5Package<NetworkCurve> {
|
#[scale_info(skip_type_params(T))]
|
||||||
|
pub struct Round5Package<NetworkCurve, T>
|
||||||
|
where
|
||||||
|
T: crate::pallet::Config,
|
||||||
|
{
|
||||||
context: PackageContext<NetworkCurve>,
|
context: PackageContext<NetworkCurve>,
|
||||||
|
pub key_members: BoundedVec<u8, crate::MaxAuthoritiesBitmaskSize<T>>,
|
||||||
pub verifying_key: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
|
pub verifying_key: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
|
||||||
pub merkle_root: ExodusHash,
|
pub merkle_root: ExodusHash,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
|
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
|
||||||
#[scale_info(skip_type_params(T))]
|
#[scale_info(skip_type_params(T))]
|
||||||
pub struct Round6Package<NetworkCurve, T>
|
pub struct Round6Package<NetworkCurve, T>
|
||||||
where
|
where
|
||||||
@ -417,16 +422,20 @@ impl<NetworkCurve> PackageContext<NetworkCurve> {
|
|||||||
Ok(Round4Package { context: self, bundle })
|
Ok(Round4Package { context: self, bundle })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_dkg_round5(
|
pub fn with_dkg_round5<T: crate::pallet::Config>(
|
||||||
self,
|
self,
|
||||||
verifying_key_vec: Vec<u8>,
|
verifying_key_vec: Vec<u8>,
|
||||||
|
key_members_vec: Vec<u8>,
|
||||||
merkle_root: ExodusHash,
|
merkle_root: ExodusHash,
|
||||||
) -> Result<Round5Package<NetworkCurve>, ExodusError> {
|
) -> Result<Round5Package<NetworkCurve, T>, ExodusError> {
|
||||||
|
let key_members = BoundedVec::<u8, crate::MaxAuthoritiesBitmaskSize<T>>::try_from(key_members_vec)
|
||||||
|
.map_err(|_| ExodusError::DeserializationError)?;
|
||||||
|
|
||||||
let verifying_key =
|
let verifying_key =
|
||||||
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::try_from(verifying_key_vec)
|
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::try_from(verifying_key_vec)
|
||||||
.map_err(|_| ExodusError::DeserializationError)?;
|
.map_err(|_| ExodusError::DeserializationError)?;
|
||||||
|
|
||||||
Ok(Round5Package { context: self, verifying_key, merkle_root })
|
Ok(Round5Package { context: self, key_members, verifying_key, merkle_root })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_dkg_round6<T: crate::pallet::Config>(
|
pub fn with_dkg_round6<T: crate::pallet::Config>(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user