refactor package storage to use classic bitmap and dense data blob
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
This commit is contained in:
parent
c22925d758
commit
d18b8ba11f
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ghost-exodus"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
description = "Threshold signature generation with DKG included"
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
@ -125,9 +125,9 @@ mod benchmarks {
|
||||
|
||||
#[benchmark]
|
||||
fn register_encrypted_round2_packages(
|
||||
c: Linear<1, { T::MaxAuthorities::get() / 8 }>
|
||||
c: Linear<1, { T::MaxAuthorities::get() }>
|
||||
) -> Result<(), BenchmarkError> {
|
||||
let max_signers = (c * 8) as AuthIndex;
|
||||
let max_signers = c as AuthIndex;
|
||||
|
||||
let (mut dkg_state, network_curve, authority, authority_index) =
|
||||
prepare_pallet::<T>(max_signers as usize);
|
||||
@ -149,21 +149,18 @@ mod benchmarks {
|
||||
|
||||
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
|
||||
|
||||
let bitmask_len = padded_participants.div_ceil(8);
|
||||
let blob_len = round2_encrypted_package_size(
|
||||
padded_participants,
|
||||
padded_participants - 1,
|
||||
network_curve.scalar_bytes_len(),
|
||||
network_curve.header_bytes_len(),
|
||||
);
|
||||
|
||||
let mut bitmask = vec![0u8; bitmask_len];
|
||||
let mut bitmask = ParticipantsBitmap::<T>::empty(padded_participants);
|
||||
let blob = vec![0u8; blob_len];
|
||||
|
||||
(0..max_signers).for_each(|i| {
|
||||
if i != authority_index {
|
||||
let byte_index = (i / 8) as usize;
|
||||
let bit_index = (i % 8) as u8;
|
||||
bitmask[byte_index] |= 1 << bit_index;
|
||||
bitmask.insert(i);
|
||||
}
|
||||
});
|
||||
|
||||
@ -195,9 +192,9 @@ mod benchmarks {
|
||||
|
||||
#[benchmark]
|
||||
fn register_round3_complaints(
|
||||
c: Linear<1, { T::MaxAuthorities::get() / 8 }>
|
||||
c: Linear<1, { T::MaxAuthorities::get() }>
|
||||
) -> Result<(), BenchmarkError> {
|
||||
let max_signers = (c * 8) as AuthIndex;
|
||||
let max_signers = c as AuthIndex;
|
||||
|
||||
let (mut dkg_state, network_curve, authority, authority_index) =
|
||||
prepare_pallet::<T>(max_signers as usize);
|
||||
@ -215,14 +212,11 @@ mod benchmarks {
|
||||
|
||||
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
|
||||
|
||||
let bitvec_len = (max_signers + 7) / 8;
|
||||
let mut accused_indexes = vec![0u8; bitvec_len as usize];
|
||||
let mut accused_indexes = ParticipantsBitmap::<T>::empty(max_signers);
|
||||
|
||||
(0..max_signers).for_each(|i| {
|
||||
if i != authority_index {
|
||||
let byte_index = (i / 8) as usize;
|
||||
let bit_index = (i % 8) as u8;
|
||||
accused_indexes[byte_index] |= 1 << bit_index;
|
||||
accused_indexes.insert(i);
|
||||
}
|
||||
});
|
||||
|
||||
@ -255,9 +249,9 @@ mod benchmarks {
|
||||
|
||||
#[benchmark]
|
||||
fn register_round4_justifications(
|
||||
c: Linear<1, { T::MaxAuthorities::get() / 8 }>
|
||||
c: Linear<1, { T::MaxAuthorities::get() }>
|
||||
) -> Result<(), BenchmarkError> {
|
||||
let max_signers = (c * 8) as AuthIndex;
|
||||
let max_signers = c as AuthIndex;
|
||||
|
||||
let (mut dkg_state, network_curve, authority, authority_index) =
|
||||
prepare_pallet::<T>(max_signers as usize);
|
||||
@ -281,21 +275,18 @@ mod benchmarks {
|
||||
|
||||
QualificationDkgState::<T>::insert(&network_curve, dkg_state);
|
||||
|
||||
let bitmask_len = padded_participants.div_ceil(8);
|
||||
let blob_len = round2_package_size(
|
||||
padded_participants,
|
||||
padded_participants - 1,
|
||||
network_curve.scalar_bytes_len(),
|
||||
network_curve.header_bytes_len(),
|
||||
);
|
||||
|
||||
let mut bitmask = vec![0u8; bitmask_len];
|
||||
let mut bitmask = ParticipantsBitmap::<T>::empty(padded_participants);
|
||||
let blob = vec![0u8; blob_len];
|
||||
|
||||
(0..max_signers).for_each(|i| {
|
||||
if i != authority_index {
|
||||
let byte_index = (i / 8) as usize;
|
||||
let bit_index = (i % 8) as u8;
|
||||
bitmask[byte_index] |= 1 << bit_index;
|
||||
bitmask.insert(i);
|
||||
}
|
||||
});
|
||||
|
||||
@ -352,7 +343,7 @@ mod benchmarks {
|
||||
|
||||
let dummy_merkle_root = ExodusHash::repeat_byte(69);
|
||||
let dummy_verifying_key = vec![69u8; network_curve.element_bytes_len()];
|
||||
let dummy_key_members = vec![15];
|
||||
let dummy_key_members = ParticipantsBitmap::<T>::empty(4);
|
||||
|
||||
let current_block = <T as Config>::BlockNumberProvider::current_block_number();
|
||||
let round5_package = PackageContext::default()
|
||||
@ -384,7 +375,7 @@ mod benchmarks {
|
||||
let metadata_hashes = [
|
||||
dummy_merkle_root.as_ref(),
|
||||
bounded_verifying_key.as_ref(),
|
||||
dummy_key_members.as_ref(),
|
||||
dummy_key_members.inner_as_ref(),
|
||||
];
|
||||
let verifying_hash = cumulative_hash::<SubstrateBlake2Hasher, _>(metadata_hashes);
|
||||
|
||||
@ -397,7 +388,7 @@ mod benchmarks {
|
||||
assert_eq!(consensus.state.participants.count_ones::<AuthIndex>(), 1);
|
||||
assert_eq!(consensus.state.merkle_root, dummy_merkle_root);
|
||||
assert_eq!(consensus.state.element_bytes, bounded_verifying_key);
|
||||
assert_eq!(consensus.members.as_ref(), dummy_key_members);
|
||||
assert_eq!(consensus.members, dummy_key_members);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ pub mod sr25519 {
|
||||
}
|
||||
|
||||
const MIN_LOCK_GUARD_PERIOD: u64 = 15_000;
|
||||
const BLOCKS_FOR_DKG_RESET: u64 = 5;
|
||||
const BLOCKS_FOR_DKG_RESET : u64 = 5;
|
||||
|
||||
const DB_STORAGE_PREFIX : &[u8] = b"exodus::";
|
||||
const PALLET_LOG_TARGET : &str = "time:ghost-exodus";
|
||||
@ -160,10 +160,7 @@ const fn encrypted_ciphertext_bytes_len(
|
||||
header_bytes_len: usize,
|
||||
) -> usize {
|
||||
let ciphertext_len = header_bytes_len.saturating_add(scalar_bytes_len);
|
||||
let ciphertext_vec_prefix = postcard_varint_prefix_len(ciphertext_len);
|
||||
|
||||
ciphertext_len.saturating_add(ciphertext_vec_prefix)
|
||||
.saturating_add(AUTHENTICATION_TAG_MAX_BYTES as usize)
|
||||
ciphertext_len.saturating_add(AUTHENTICATION_TAG_MAX_BYTES as usize)
|
||||
}
|
||||
|
||||
const fn round2_encrypted_package_size(
|
||||
@ -177,11 +174,13 @@ const fn round2_encrypted_package_size(
|
||||
header_bytes_len,
|
||||
);
|
||||
|
||||
let ciphertext_len = encrypted_ciphertext_bytes_len
|
||||
.saturating_add(ENCRYPTION_NONCE_MAX_BYTES as usize);
|
||||
let ciphertext_vec_prefix = scale_compact_prefix_len(
|
||||
encrypted_ciphertext_bytes_len
|
||||
);
|
||||
|
||||
let ciphertext_vec_prefix = scale_compact_prefix_len(ciphertext_len);
|
||||
let size = ciphertext_len.saturating_add(ciphertext_vec_prefix);
|
||||
let size = encrypted_ciphertext_bytes_len
|
||||
.saturating_add(ciphertext_vec_prefix)
|
||||
.saturating_add(ENCRYPTION_NONCE_MAX_BYTES as usize);
|
||||
|
||||
participants.saturating_mul(size)
|
||||
}
|
||||
@ -198,22 +197,6 @@ const fn round2_package_size(
|
||||
participants.saturating_mul(size)
|
||||
}
|
||||
|
||||
const fn nonce_commitment_package_size(
|
||||
element_max_bytes: usize,
|
||||
header_max_bytes: usize,
|
||||
) -> usize {
|
||||
header_max_bytes
|
||||
.saturating_add(element_max_bytes)
|
||||
.saturating_add(element_max_bytes)
|
||||
}
|
||||
|
||||
const fn signature_share_bytes_len(
|
||||
scalar_bytes_len: usize,
|
||||
header_bytes_len: usize,
|
||||
) -> usize {
|
||||
header_bytes_len.saturating_add(scalar_bytes_len)
|
||||
}
|
||||
|
||||
type DkgIndex = ghost_helpers::DkgIndexU32;
|
||||
type AuthIndex = ghost_helpers::AuthIndexU16;
|
||||
type BitmapChunk = ghost_helpers::BitmapChunkU32;
|
||||
@ -261,14 +244,6 @@ impl<T: Config> Get<u32> for MaxEvmRotationSessionsSize<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MaxAuthoritiesBitmaskSize<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Config> Get<u32> for MaxAuthoritiesBitmaskSize<T> {
|
||||
fn get() -> u32 {
|
||||
let max_authorities = T::MaxAuthorities::get();
|
||||
max_authorities.div_ceil(8)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Round1MaxBytes<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Config> Get<u32> for Round1MaxBytes<T> {
|
||||
fn get() -> u32 {
|
||||
@ -311,14 +286,6 @@ impl<T: Config> Get<u32> for EncryptedRound2BlobMaxBytes<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AccusedIndicesMaxBytes<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Config> Get<u32> for AccusedIndicesMaxBytes<T> {
|
||||
fn get() -> u32 {
|
||||
let max_authorities = T::MaxAuthorities::get();
|
||||
max_authorities.saturating_add(7).div_ceil(8)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MerkleProofMaxSize<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Config> Get<u32> for MerkleProofMaxSize<T> {
|
||||
fn get() -> u32 {
|
||||
@ -341,16 +308,6 @@ impl<T: Config> Get<u32> for BindingFactorsProof<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NonceCommitmentMaxBytes<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Config> Get<u32> for NonceCommitmentMaxBytes<T> {
|
||||
fn get() -> u32 {
|
||||
nonce_commitment_package_size(
|
||||
ELEMENT_MAX_BYTES as usize,
|
||||
HEADER_MAX_BYTES as usize,
|
||||
) as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CiphertextMaxBytes<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Config> Get<u32> for CiphertextMaxBytes<T> {
|
||||
fn get() -> u32 {
|
||||
@ -361,24 +318,6 @@ impl<T: Config> Get<u32> for CiphertextMaxBytes<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SignatureShareMaxBytes<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Config> Get<u32> for SignatureShareMaxBytes<T> {
|
||||
fn get() -> u32 {
|
||||
signature_share_bytes_len(
|
||||
SCALAR_MAX_BYTES as usize,
|
||||
HEADER_MAX_BYTES as usize,
|
||||
) as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SignatureMaxBytes<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Config> Get<u32> for SignatureMaxBytes<T> {
|
||||
fn get() -> u32 {
|
||||
let signature_max_bytes = ELEMENT_MAX_BYTES + SCALAR_MAX_BYTES;
|
||||
postcard_varint_prefix_len(signature_max_bytes as usize) as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ZeroScalarDefault<T>(sp_std::marker::PhantomData<T>);
|
||||
impl<T: Config> Get<BoundedVec<u8, ConstU32<{ SCALAR_MAX_BYTES }>>> for ZeroScalarDefault<T> {
|
||||
fn get() -> BoundedVec<u8, sp_core::ConstU32<{ SCALAR_MAX_BYTES }>> {
|
||||
@ -756,10 +695,7 @@ pub mod pallet {
|
||||
_,
|
||||
Twox64Concat, NetworkCurve,
|
||||
Twox64Concat, DkgIndex,
|
||||
QualifiedConsensusState<
|
||||
ParticipantsBitmap<T>,
|
||||
BoundedVec<u8, MaxAuthoritiesBitmaskSize<T>>
|
||||
>,
|
||||
QualifiedConsensusState<ParticipantsBitmap<T>>,
|
||||
ValueQuery,
|
||||
>;
|
||||
|
||||
@ -1090,19 +1026,6 @@ pub mod pallet {
|
||||
let state = QualificationDkgState::<T>::get(&network_curve);
|
||||
ensure!(state.is_second_phase(), Error::<T>::DkgWrongRound);
|
||||
|
||||
let encrypted_indexes = ParticipantsBitmap::<T>::from_bitmask(
|
||||
&round2_bundle.bundle.bitmask,
|
||||
);
|
||||
|
||||
let diff = state.get_indexes() ^ &encrypted_indexes;
|
||||
let only_one_lost = diff.count_ones::<AuthIndex>() == 1;
|
||||
let only_authority_index = diff.contains(authority_index);
|
||||
|
||||
ensure!(
|
||||
only_one_lost && only_authority_index,
|
||||
Error::<T>::Round2PackagesWrongCoefficients,
|
||||
);
|
||||
|
||||
Round2Packages::<T>::try_mutate(&network_curve,
|
||||
|round2_packages| -> DispatchResult {
|
||||
ensure!(
|
||||
@ -1115,8 +1038,11 @@ pub mod pallet {
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let encrypted_count = round2_bundle.bundle.bitmask
|
||||
.count_ones::<AuthIndex>();
|
||||
|
||||
Self::deposit_event(Event::<T>::Round2PackagesRegistered {
|
||||
encrypted_count: encrypted_indexes.count_ones::<AuthIndex>(),
|
||||
encrypted_count,
|
||||
authority_index,
|
||||
network_curve,
|
||||
});
|
||||
@ -1160,11 +1086,7 @@ pub mod pallet {
|
||||
return Err(Error::<T>::Round3PackageInvalidProof.into());
|
||||
};
|
||||
|
||||
let complaints_bitmap = ParticipantsBitmap::<T>::from_bitmask(
|
||||
&round3_complaints.indices
|
||||
);
|
||||
|
||||
let complaints_count: AuthIndex = complaints_bitmap.count_ones();
|
||||
let complaints_count: AuthIndex = round3_complaints.indices.count_ones();
|
||||
let state = QualificationDkgState::<T>::get(&network_curve);
|
||||
ensure!(state.is_third_phase(), Error::<T>::DkgWrongRound);
|
||||
|
||||
@ -1176,7 +1098,7 @@ pub mod pallet {
|
||||
Error::<T>::Round3PackagesAlreadyRegistered,
|
||||
);
|
||||
|
||||
complaints.try_insert(authority_index, complaints_bitmap)
|
||||
complaints.try_insert(authority_index, round3_complaints.indices)
|
||||
.map_err(|_| Error::<T>::TooManyPackages)?;
|
||||
|
||||
Ok(())
|
||||
@ -1216,14 +1138,12 @@ pub mod pallet {
|
||||
return Err(Error::<T>::Round4PackageInvalidProof.into());
|
||||
};
|
||||
|
||||
let justifications_bitmap = ParticipantsBitmap::<T>::from_bitmask(
|
||||
&round4_bundle.bundle.bitmask
|
||||
);
|
||||
|
||||
let justifications_count: AuthIndex = justifications_bitmap.count_ones();
|
||||
let justifications_count = round4_bundle.bundle.bitmask.count_ones();
|
||||
let state = QualificationDkgState::<T>::get(&network_curve);
|
||||
ensure!(state.is_fourth_phase(), Error::<T>::DkgWrongRound);
|
||||
|
||||
let encoded_bundle = round4_bundle.bundle.encode();
|
||||
|
||||
Justifications::<T>::try_mutate(
|
||||
&network_curve,
|
||||
|justifications| -> DispatchResult {
|
||||
@ -1232,8 +1152,10 @@ pub mod pallet {
|
||||
Error::<T>::Round4PackagesAlreadyRegistered,
|
||||
);
|
||||
|
||||
justifications.try_insert(authority_index, justifications_bitmap)
|
||||
.map_err(|_| Error::<T>::TooManyPackages)?;
|
||||
justifications.try_insert(
|
||||
authority_index,
|
||||
round4_bundle.bundle.bitmask
|
||||
).map_err(|_| Error::<T>::TooManyPackages)?;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
@ -1252,7 +1174,7 @@ pub mod pallet {
|
||||
|
||||
sp_io::offchain_index::set(
|
||||
&index_key,
|
||||
&round4_bundle.bundle.encode(),
|
||||
&encoded_bundle
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@ -1287,7 +1209,7 @@ pub mod pallet {
|
||||
let metadata_hashes = [
|
||||
round5.merkle_root.as_ref(),
|
||||
round5.verifying_key.as_ref(),
|
||||
round5.key_members.as_ref(),
|
||||
round5.key_members.inner_as_ref(),
|
||||
];
|
||||
let verifying_hash = cumulative_hash::<SubstrateBlake2Hasher, _>(metadata_hashes);
|
||||
|
||||
@ -1360,17 +1282,12 @@ pub mod pallet {
|
||||
return Err(Error::<T>::Round6PackageInvalidProof.into());
|
||||
};
|
||||
|
||||
let expected_share_len = network_curve.element_bytes_len();
|
||||
ensure!(
|
||||
round6.verifying_share.len() == expected_share_len,
|
||||
Error::<T>::Round6PackageInvalidProof,
|
||||
);
|
||||
|
||||
let state = QualificationDkgState::<T>::get(&network_curve);
|
||||
ensure!(state.is_sixth_phase(), Error::<T>::DkgWrongRound);
|
||||
|
||||
let dkg_index = state.get_dkg_index();
|
||||
let qualified_consensus = VerifyingKeyConsensus::<T>::get(&network_curve, &dkg_index);
|
||||
let qualified_consensus =
|
||||
VerifyingKeyConsensus::<T>::get(&network_curve, &dkg_index);
|
||||
|
||||
network_curve.verify_merkle_proof(
|
||||
&round6.verifying_share,
|
||||
@ -2581,8 +2498,7 @@ impl<T: Config> Pallet<T> {
|
||||
if consensus.state.participants.count_ones::<AuthIndex>() < min_threshold {
|
||||
dkg_qualification.nullify_indexes();
|
||||
} else {
|
||||
let key_members = ParticipantsBitmap::<T>::from_bitmask(&consensus.members);
|
||||
dkg_qualification.intersect_indexes(&key_members);
|
||||
dkg_qualification.intersect_indexes(&consensus.members);
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -2962,39 +2878,24 @@ impl<T: Config> Pallet<T> {
|
||||
return Err(InvalidTransaction::BadSigner);
|
||||
}
|
||||
|
||||
let bundle_packages_count = round2.bundle.bitmask
|
||||
.iter()
|
||||
.map(|byte| byte.count_ones() as usize)
|
||||
.sum::<usize>()
|
||||
.saturating_add(1);
|
||||
let bitmask_count = round2.bundle.bitmask.count_ones::<usize>();
|
||||
let count_without_self = state.count_ones::<usize>().saturating_sub(1);
|
||||
|
||||
if bundle_packages_count != state.count_ones::<usize>() {
|
||||
// exclude self from the count; the first coefficient is never sent
|
||||
if count_without_self != bitmask_count {
|
||||
return Err(InvalidTransaction::BadProof);
|
||||
}
|
||||
|
||||
let has_invalid_round2_recipients = round2.bundle.bitmask
|
||||
.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;
|
||||
if global_index == authority_index as usize { return true; }
|
||||
!state.contains_index(global_index)
|
||||
})
|
||||
});
|
||||
let diff = state.get_indexes() ^ &round2.bundle.bitmask;
|
||||
let only_one_lost = diff.count_ones::<AuthIndex>() == 1;
|
||||
let only_authority_index = diff.contains(authority_index);
|
||||
|
||||
if has_invalid_round2_recipients {
|
||||
if !only_one_lost || !only_authority_index {
|
||||
return Err(InvalidTransaction::BadProof);
|
||||
}
|
||||
|
||||
let padded_participants = state.highest_bit::<usize>()
|
||||
.map(|bit_pos| bit_pos.saturating_add(1))
|
||||
.unwrap_or_default();
|
||||
|
||||
let expected_blob_len = round2_encrypted_package_size(
|
||||
padded_participants,
|
||||
count_without_self,
|
||||
network_curve.scalar_bytes_len(),
|
||||
network_curve.header_bytes_len(),
|
||||
);
|
||||
@ -3056,29 +2957,20 @@ impl<T: Config> Pallet<T> {
|
||||
return Err(InvalidTransaction::BadSigner);
|
||||
}
|
||||
|
||||
let estimated_indices_len = state
|
||||
let qualification_highest_bit = state.highest_bit::<usize>().unwrap_or(0);
|
||||
let indices_highest_bit = round3_complaints.indices
|
||||
.highest_bit::<usize>()
|
||||
.map(|bit_index| bit_index.saturating_add(1).div_ceil(8))
|
||||
.unwrap_or_default();
|
||||
.unwrap_or(0);
|
||||
|
||||
if estimated_indices_len != round3_complaints.indices.len() {
|
||||
if qualification_highest_bit < indices_highest_bit {
|
||||
return Err(InvalidTransaction::BadProof);
|
||||
}
|
||||
|
||||
let has_invalid_accused_indexes = round3_complaints.indices
|
||||
.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;
|
||||
if global_index == authority_index as usize { return true; }
|
||||
!state.contains_index(global_index)
|
||||
})
|
||||
});
|
||||
let contains_self = round3_complaints.indices.contains(authority_index);
|
||||
let extra_bits = !state.get_indexes() & &round3_complaints.indices;
|
||||
let has_extra_bits = extra_bits.count_ones::<AuthIndex>() > 0;
|
||||
|
||||
if has_invalid_accused_indexes {
|
||||
if has_extra_bits || contains_self {
|
||||
return Err(InvalidTransaction::BadProof);
|
||||
}
|
||||
|
||||
@ -3136,39 +3028,26 @@ impl<T: Config> Pallet<T> {
|
||||
return Err(InvalidTransaction::BadSigner);
|
||||
}
|
||||
|
||||
let bundle_packages_count = round4.bundle.bitmask
|
||||
.iter()
|
||||
.map(|byte| byte.count_ones() as usize)
|
||||
.sum::<usize>()
|
||||
.saturating_add(1);
|
||||
|
||||
if bundle_packages_count > state.count_ones::<usize>() {
|
||||
let qualification_highest_bit = state.highest_bit::<usize>().unwrap_or(0);
|
||||
let bitmask_highest_bit = round4.bundle.bitmask
|
||||
.highest_bit::<usize>()
|
||||
.unwrap_or(0);
|
||||
|
||||
if qualification_highest_bit < bitmask_highest_bit {
|
||||
return Err(InvalidTransaction::BadProof);
|
||||
}
|
||||
|
||||
let has_invalid_justifications = round4.bundle.bitmask
|
||||
.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;
|
||||
if global_index == authority_index as usize { return true; }
|
||||
!state.contains_index(global_index)
|
||||
})
|
||||
});
|
||||
let contains_self = round4.bundle.bitmask.contains(authority_index);
|
||||
let extra_bits = !state.get_indexes() & &round4.bundle.bitmask;
|
||||
let has_extra_bits = extra_bits.count_ones::<AuthIndex>() > 0;
|
||||
|
||||
if has_invalid_justifications {
|
||||
if has_extra_bits || contains_self {
|
||||
return Err(InvalidTransaction::BadProof);
|
||||
}
|
||||
|
||||
let padded_participants = state.highest_bit::<usize>()
|
||||
.map(|bit_pos| bit_pos.saturating_add(1))
|
||||
.unwrap_or_default();
|
||||
|
||||
let expected_blob_len = round2_package_size(
|
||||
padded_participants,
|
||||
round4.bundle.bitmask.count_ones::<usize>(),
|
||||
network_curve.scalar_bytes_len(),
|
||||
network_curve.header_bytes_len(),
|
||||
);
|
||||
@ -3230,27 +3109,12 @@ impl<T: Config> Pallet<T> {
|
||||
return Err(InvalidTransaction::BadSigner);
|
||||
}
|
||||
|
||||
let estimated_max_size = network_curve
|
||||
.element_bytes_len()
|
||||
.saturating_add(ExodusHash::len_bytes());
|
||||
let extra_bits = !state.get_indexes() & &round5.key_members;
|
||||
let has_extra_bits = extra_bits.count_ones::<usize>() > 0;
|
||||
let verifying_key_valid =
|
||||
round5.verifying_key.len() == network_curve.element_bytes_len();
|
||||
|
||||
if dkg_package.bytes_len() as usize != estimated_max_size {
|
||||
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 {
|
||||
if !verifying_key_valid || has_extra_bits {
|
||||
return Err(InvalidTransaction::BadProof);
|
||||
}
|
||||
|
||||
@ -3281,6 +3145,10 @@ impl<T: Config> Pallet<T> {
|
||||
dkg_package: &DkgPackage<NetworkCurve, BlockNumberFor<T>, T>,
|
||||
signature: &<T::AuthorityId as RuntimeAppPublic>::Signature,
|
||||
) -> Result<(), InvalidTransaction> {
|
||||
let DkgPackage::Round6(round6) = dkg_package else {
|
||||
return Err(InvalidTransaction::BadProof);
|
||||
};
|
||||
|
||||
let network_curve = dkg_package.get_network_curve();
|
||||
let authority_index = dkg_package.get_authority_index();
|
||||
let package_dkg_index = dkg_package.get_dkg_index();
|
||||
@ -3303,12 +3171,16 @@ impl<T: Config> Pallet<T> {
|
||||
return Err(InvalidTransaction::BadSigner);
|
||||
}
|
||||
|
||||
if round6.verifying_share.len() != network_curve.element_bytes_len() {
|
||||
return Err(InvalidTransaction::BadProof);
|
||||
}
|
||||
|
||||
let estimated_proof_count = state
|
||||
.count_ones::<usize>()
|
||||
.next_power_of_two()
|
||||
.trailing_zeros();
|
||||
|
||||
if dkg_package.bytes_len() != estimated_proof_count {
|
||||
if round6.merkle_proof.len() != estimated_proof_count as usize {
|
||||
return Err(InvalidTransaction::BadProof);
|
||||
}
|
||||
|
||||
@ -4405,24 +4277,11 @@ impl<T: Config> Pallet<T> {
|
||||
.map(|bit_pos| bit_pos.saturating_add(1))
|
||||
.unwrap_or_default();
|
||||
|
||||
let bitmask_len = padded_participants.div_ceil(8);
|
||||
let blob_len = round2_encrypted_package_size(
|
||||
padded_participants,
|
||||
network_curve.scalar_bytes_len(),
|
||||
network_curve.header_bytes_len(),
|
||||
);
|
||||
let max_package_size = blob_len.saturating_div(padded_participants);
|
||||
|
||||
let mut bitmask = vec![0u8; bitmask_len];
|
||||
let mut blob = vec![0u8; blob_len];
|
||||
|
||||
Self::ecdh_encrypt_round2_packages(
|
||||
let (bitmask, blob) = Self::ecdh_encrypt_round2_packages(
|
||||
&round1_packages,
|
||||
&public_packages,
|
||||
round1_secret_package,
|
||||
&mut bitmask,
|
||||
&mut blob,
|
||||
max_package_size,
|
||||
padded_participants,
|
||||
authority_index,
|
||||
qualification_state.get_dkg_index(),
|
||||
network_curve,
|
||||
@ -4595,36 +4454,30 @@ impl<T: Config> Pallet<T> {
|
||||
.map(|bit_pos| bit_pos.saturating_add(1))
|
||||
.unwrap_or_default();
|
||||
|
||||
let bitmask_len = padded_participants.div_ceil(8);
|
||||
let blob_len = round2_package_size(
|
||||
padded_participants,
|
||||
network_curve.scalar_bytes_len(),
|
||||
network_curve.header_bytes_len(),
|
||||
);
|
||||
|
||||
let round2_package_size = blob_len.saturating_div(padded_participants);
|
||||
let mut bitmask = ParticipantsBitmap::<T>::empty(padded_participants);
|
||||
let mut blob = Vec::with_capacity(blob_len);
|
||||
|
||||
let mut bitmask = vec![0u8; bitmask_len];
|
||||
let mut blob = vec![0u8; blob_len];
|
||||
Complaints::<T>::get(&network_curve)
|
||||
.iter()
|
||||
.filter(|(&index, _)| qualification_state.contains_index(index))
|
||||
.filter(|(&index, _)| authority_index != index)
|
||||
.filter(|(_, bitmap)| bitmap.contains(authority_index))
|
||||
.filter_map(|(accuser_index, _)| {
|
||||
public_packages.get(accuser_index).map(|pkg| (accuser_index, pkg))
|
||||
})
|
||||
.for_each(|(&accuser_index, round2_package)| {
|
||||
let encoded = round2_package.encode();
|
||||
|
||||
for (&accuser_index, bitmap) in Complaints::<T>::get(&network_curve).iter() {
|
||||
if !bitmap.contains(authority_index) { continue; }
|
||||
if !qualification_state.contains_index(accuser_index) { continue; }
|
||||
if authority_index == accuser_index { continue; }
|
||||
|
||||
let round2_package = match public_packages.get(&accuser_index) {
|
||||
Some(round2_package) => round2_package,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
Self::prepare_bundled_data(
|
||||
&mut bitmask,
|
||||
&mut blob,
|
||||
&round2_package.encode(),
|
||||
round2_package_size,
|
||||
accuser_index,
|
||||
);
|
||||
}
|
||||
if let Some(_) = bitmask.insert(accuser_index) {
|
||||
blob.extend_from_slice(&encoded);
|
||||
}
|
||||
});
|
||||
|
||||
round4_metadata.update_with_block(
|
||||
Some(dkg_storage.get_release_block()),
|
||||
@ -4762,23 +4615,13 @@ impl<T: Config> Pallet<T> {
|
||||
.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];
|
||||
let mut key_members_bitmask = ParticipantsBitmap::<T>::empty(
|
||||
padded_participants
|
||||
);
|
||||
|
||||
key_members_bitmask.insert(authority_index);
|
||||
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);
|
||||
key_members_bitmask.insert(member_index);
|
||||
}
|
||||
|
||||
let current_dkg_index = qualification_state.get_dkg_index();
|
||||
@ -4969,29 +4812,29 @@ impl<T: Config> Pallet<T> {
|
||||
}
|
||||
|
||||
for accused_index in accused_bitmap.iter::<AuthIndex>() {
|
||||
if !qualification_state.contains_index(accused_index) { continue; }
|
||||
if kicked.contains(&accused_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()
|
||||
})
|
||||
})
|
||||
});
|
||||
if !qualification_state.contains_index(accused_index)
|
||||
|| kicked.contains(&accused_index)
|
||||
|| accused_index == authority_index
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let maybe_round1_package = round1_packages.get(&accused_index);
|
||||
let maybe_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::<JustificationsBundledPackages<T>>().ok().flatten()
|
||||
})
|
||||
.as_ref()
|
||||
.and_then(|bundle| {
|
||||
bundle.get_package_by_index::<Vec<u8>>(
|
||||
accuser_index,
|
||||
round2_package_size
|
||||
)
|
||||
});
|
||||
|
||||
let (round1_package, round2_package) =
|
||||
match (maybe_round1_package, maybe_justification) {
|
||||
match (maybe_round1_package, maybe_justifications) {
|
||||
(Some(round1_package), Some(round2_package)) => {
|
||||
(round1_package, round2_package)
|
||||
},
|
||||
@ -5019,7 +4862,6 @@ impl<T: Config> Pallet<T> {
|
||||
round1_packages.remove(&accused_index);
|
||||
round2_packages.remove(&accused_index);
|
||||
kicked.insert(accused_index);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5027,26 +4869,17 @@ impl<T: Config> Pallet<T> {
|
||||
}
|
||||
|
||||
pub fn locate_package_bounds(
|
||||
bundle: &BundledPackages,
|
||||
bundle: &BundledPackages<T>,
|
||||
single_bytes_len: usize,
|
||||
authority_index: AuthIndex,
|
||||
) -> Option<(usize, usize)> {
|
||||
let global_index = authority_index as usize;
|
||||
let byte_index = global_index >> 3usize;
|
||||
let bit_index = global_index & 7usize;
|
||||
let bit_mask = 1u8 << bit_index;
|
||||
let popcount = bundle.bitmask.count_ones_before(authority_index)?;
|
||||
let popcount: usize = popcount.unique_saturated_into();
|
||||
|
||||
let bundle_bitmask_by_index = bundle.bitmask.get(byte_index)?;
|
||||
if (bundle_bitmask_by_index & bit_mask) == 0 {
|
||||
return None;
|
||||
}
|
||||
let start_offset = popcount.checked_mul(single_bytes_len)?;
|
||||
let end_offset = start_offset.checked_add(single_bytes_len)?;
|
||||
|
||||
let start_offset = global_index * single_bytes_len;
|
||||
let end_offset = start_offset + single_bytes_len;
|
||||
|
||||
if end_offset > bundle.blob.len() {
|
||||
return None;
|
||||
}
|
||||
if end_offset > bundle.blob.len() { return None; }
|
||||
|
||||
Some((start_offset, end_offset))
|
||||
}
|
||||
@ -5088,17 +4921,15 @@ impl<T: Config> Pallet<T> {
|
||||
fn ecdh_decrypt_round2_packages(
|
||||
qualification_state: &PendingDkgAuthorities<T>,
|
||||
round1_packages: &BTreeMap<AuthIndex, Vec<u8>>,
|
||||
round1_secret_package: &Vec<u8>,
|
||||
round1_secret_package: &[u8],
|
||||
authority_index: AuthIndex,
|
||||
network_curve: NetworkCurve,
|
||||
) -> (Vec<u8>, BTreeMap<AuthIndex, Vec<u8>>) {
|
||||
) -> (ParticipantsBitmap<T>, BTreeMap<AuthIndex, Vec<u8>>) {
|
||||
let dkg_index = qualification_state.get_dkg_index();
|
||||
|
||||
let bitmask_len = qualification_state
|
||||
let len = qualification_state
|
||||
.highest_bit::<usize>()
|
||||
.map(|bit_pos| {
|
||||
bit_pos.saturating_add(1).div_ceil(8)
|
||||
})
|
||||
.map(|bit_pos| bit_pos.saturating_add(1))
|
||||
.unwrap_or_default();
|
||||
|
||||
let encryption_size = round2_encrypted_package_size(
|
||||
@ -5107,25 +4938,24 @@ impl<T: Config> Pallet<T> {
|
||||
network_curve.header_bytes_len(),
|
||||
);
|
||||
|
||||
let mut accused_indexes = ParticipantsBitmap::<T>::empty(len);
|
||||
let mut decrypted = BTreeMap::new();
|
||||
|
||||
round1_packages
|
||||
.iter()
|
||||
.filter(|(&sender_index, _)| sender_index != authority_index)
|
||||
.map(|(&sender_index, round1_sender_package)| {
|
||||
let key = Self::create_offchain_dkg_key(network_curve, sender_index, ROUND_NUMBER_2);
|
||||
.for_each(|(&sender_index, round1_sender_package)| {
|
||||
let key = Self::create_offchain_dkg_key(
|
||||
network_curve,
|
||||
sender_index,
|
||||
ROUND_NUMBER_2
|
||||
);
|
||||
|
||||
let decryption_result = StorageValueRef::persistent(&key)
|
||||
.get::<BundledPackages>()
|
||||
.get::<EncryptionBundledPackages<T>>()
|
||||
.ok()
|
||||
.flatten()
|
||||
.and_then(|bundle| {
|
||||
Self::locate_package_bounds(&bundle, encryption_size, authority_index)
|
||||
.and_then(|(start, end)| {
|
||||
bundle.blob.get(start..end).and_then(|raw_slice| {
|
||||
EncryptionData::decode(&mut &raw_slice[..]).ok()
|
||||
})
|
||||
})
|
||||
})
|
||||
.ok_or(sender_index)
|
||||
.and_then(|bundle| bundle.get_package_by_index(authority_index, encryption_size))
|
||||
.and_then(|encrypted_round2_package: EncryptionData<T>| {
|
||||
let info = network_curve.ecdh_prepare_additional_info(
|
||||
sender_index,
|
||||
@ -5134,64 +4964,54 @@ impl<T: Config> Pallet<T> {
|
||||
sp_std::marker::PhantomData::<EncryptionData<T>>,
|
||||
);
|
||||
|
||||
network_curve
|
||||
.ecdh_get_cipher_from_keys(
|
||||
round1_sender_package,
|
||||
round1_secret_package,
|
||||
&info,
|
||||
sp_std::marker::PhantomData::<EncryptionData<T>>,
|
||||
)
|
||||
.and_then(|cipher| {
|
||||
network_curve.ecdh_decrypt_package(
|
||||
&cipher,
|
||||
&encrypted_round2_package,
|
||||
&info,
|
||||
)
|
||||
})
|
||||
.and_then(|decrypted| {
|
||||
network_curve.dkg_verify_private_package(
|
||||
authority_index,
|
||||
round1_sender_package,
|
||||
&decrypted,
|
||||
).map(|_| decrypted)
|
||||
})
|
||||
.map_err(|_| sender_index)
|
||||
})
|
||||
.map(|decrypted_package| (sender_index, decrypted_package));
|
||||
let cipher = network_curve.ecdh_get_cipher_from_keys(
|
||||
round1_sender_package,
|
||||
round1_secret_package,
|
||||
&info,
|
||||
sp_std::marker::PhantomData::<EncryptionData<T>>,
|
||||
).ok()?;
|
||||
|
||||
decryption_result
|
||||
})
|
||||
.fold(
|
||||
(vec![0u8; bitmask_len], BTreeMap::new()),
|
||||
|(mut accused_indexes, mut decrypted), decryption_result| {
|
||||
match decryption_result {
|
||||
Ok((sender_index, package)) => { decrypted.insert(sender_index, package); },
|
||||
Err(sender_index) => {
|
||||
let global_index = sender_index as usize;
|
||||
let byte_index = global_index >> 3usize;
|
||||
let decrypted = network_curve.ecdh_decrypt_package(
|
||||
&cipher,
|
||||
&encrypted_round2_package,
|
||||
&info,
|
||||
).ok()?;
|
||||
|
||||
if let Some(byte) = accused_indexes.get_mut(byte_index) {
|
||||
let bit_index = global_index & 7usize;
|
||||
*byte |= 1u8 << bit_index;
|
||||
}
|
||||
},
|
||||
}
|
||||
(accused_indexes, decrypted)
|
||||
network_curve.dkg_verify_private_package(
|
||||
authority_index,
|
||||
round1_sender_package,
|
||||
&decrypted,
|
||||
).map(|_| decrypted).ok()
|
||||
});
|
||||
|
||||
match decryption_result {
|
||||
Some(package) => { decrypted.insert(sender_index, package); },
|
||||
None => { accused_indexes.insert(sender_index); },
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
(accused_indexes, decrypted)
|
||||
}
|
||||
|
||||
fn ecdh_encrypt_round2_packages(
|
||||
round1_packages: &BTreeMap<AuthIndex, Vec<u8>>,
|
||||
round2_packages: &BTreeMap<AuthIndex, Vec<u8>>,
|
||||
round1_secret_package: &[u8],
|
||||
bitmask: &mut Vec<u8>,
|
||||
blob: &mut Vec<u8>,
|
||||
max_size: usize,
|
||||
padded_participants: usize,
|
||||
authority_index: AuthIndex,
|
||||
dkg_index: DkgIndex,
|
||||
network_curve: NetworkCurve,
|
||||
) {
|
||||
) -> (ParticipantsBitmap<T>, Vec<u8>) {
|
||||
let blob_len = round2_encrypted_package_size(
|
||||
padded_participants,
|
||||
network_curve.scalar_bytes_len(),
|
||||
network_curve.header_bytes_len(),
|
||||
);
|
||||
let max_size = blob_len.saturating_div(padded_participants);
|
||||
|
||||
let mut bitmask = ParticipantsBitmap::<T>::empty(padded_participants);
|
||||
let mut blob = Vec::with_capacity(blob_len);
|
||||
|
||||
let authority_index_bytes = authority_index.to_le_bytes();
|
||||
let dkg_index_bytes = dkg_index.to_le_bytes();
|
||||
let network_curve_bytes = [network_curve as u8];
|
||||
@ -5207,10 +5027,9 @@ impl<T: Config> Pallet<T> {
|
||||
for (&receiver_index, round2_package) in round2_packages.iter() {
|
||||
if receiver_index == authority_index { continue; }
|
||||
|
||||
let round1_receiver_package = match round1_packages
|
||||
.get(&receiver_index) {
|
||||
Some(package) => package,
|
||||
None => continue,
|
||||
let Some(round1_receiver_package) = round1_packages
|
||||
.get(&receiver_index) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let info = network_curve.ecdh_prepare_additional_info(
|
||||
@ -5220,57 +5039,38 @@ impl<T: Config> Pallet<T> {
|
||||
sp_std::marker::PhantomData::<EncryptionData<T>>,
|
||||
);
|
||||
|
||||
let encrypted_data: EncryptionData<T> = match network_curve
|
||||
let maybe_encoded_bytes = network_curve
|
||||
.ecdh_get_cipher_from_keys(
|
||||
&round1_receiver_package,
|
||||
&round1_secret_package,
|
||||
&info,
|
||||
sp_std::marker::PhantomData::<EncryptionData<T>>,
|
||||
)
|
||||
.and_then(|cipher| network_curve.ecdh_encrypt_package(
|
||||
&cipher,
|
||||
&info,
|
||||
&round2_package,
|
||||
&mut rng,
|
||||
)) {
|
||||
Ok(encrypted_data) => encrypted_data,
|
||||
Err(_) => break,
|
||||
};
|
||||
.and_then(|cipher| {
|
||||
network_curve.ecdh_encrypt_package(
|
||||
&cipher,
|
||||
&info,
|
||||
&round2_package,
|
||||
&mut rng
|
||||
)
|
||||
})
|
||||
.map(|data: EncryptionData<T>| data.encode())
|
||||
.ok()
|
||||
.filter(|b| b.len() == max_size);
|
||||
|
||||
Self::prepare_bundled_data(
|
||||
bitmask,
|
||||
blob,
|
||||
&encrypted_data.encode(),
|
||||
max_size,
|
||||
receiver_index,
|
||||
);
|
||||
match maybe_encoded_bytes {
|
||||
Some(encoded) => {
|
||||
if let Some(_) = bitmask.insert(receiver_index) {
|
||||
blob.extend_from_slice(&encoded);
|
||||
}
|
||||
},
|
||||
None => {
|
||||
blob.extend_from_slice(&vec![0u8; max_size]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare_bundled_data(
|
||||
bitmask: &mut Vec<u8>,
|
||||
blob: &mut Vec<u8>,
|
||||
encoded_bytes: &[u8],
|
||||
max_size: usize,
|
||||
authority_index: AuthIndex,
|
||||
) {
|
||||
let global_index = authority_index as usize;
|
||||
let byte_index = global_index >> 3usize;
|
||||
let bit_index = global_index & 7usize;
|
||||
|
||||
if let Some(byte) = bitmask.get_mut(byte_index) {
|
||||
if encoded_bytes.len() > max_size { return; }
|
||||
|
||||
let start_offset = global_index.saturating_mul(max_size);
|
||||
let end_offset = start_offset.saturating_add(max_size);
|
||||
|
||||
if end_offset > blob.len() { return; }
|
||||
|
||||
let target_slice = &mut blob[start_offset..end_offset];
|
||||
target_slice[..encoded_bytes.len()].copy_from_slice(&encoded_bytes);
|
||||
|
||||
*byte |= 1 << bit_index;
|
||||
}
|
||||
(bitmask, blob)
|
||||
}
|
||||
|
||||
fn get_offchain_rng(context: &[&[u8]]) -> ChaCha20Rng {
|
||||
|
||||
@ -41,7 +41,7 @@ impl frame_system::Config for Runtime {
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub static FixedAuthorities: Vec<u64> = vec![0, 1, 2, 3];
|
||||
pub static FixedAuthorities: Vec<u64> = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
}
|
||||
|
||||
pub struct TestSessionManager;
|
||||
@ -137,10 +137,10 @@ impl ghost_exodus::Config for Runtime {
|
||||
|
||||
type UnsignedPriority = ConstU64<{ 1 << 20 }>;
|
||||
type UnsignedLongevity = ConstU64<5>;
|
||||
type MaxAuthorities = ConstU32<10>;
|
||||
type MaxAuthoritiesChunks = ConstU32<1>;
|
||||
type MaxAuthorities = ConstU32<15>;
|
||||
type MaxAuthoritiesChunks = ConstU32<2>;
|
||||
|
||||
type DkgRoundPeriod = ConstU64<300>;
|
||||
type DkgRoundPeriod = ConstU64<1000>;
|
||||
type RemovalLimit = ConstU32<20>;
|
||||
type MaxCursorLen = ConstU32<256>;
|
||||
type BaseEvmAmount = ConstU128<5>;
|
||||
|
||||
@ -543,9 +543,7 @@ fn run_succesfull_dkg_session_partially(
|
||||
}
|
||||
}
|
||||
|
||||
let dummy_key_members: BoundedVec<u8, crate::MaxAuthoritiesBitmaskSize<Runtime>> =
|
||||
key_members_raw.try_into().unwrap();
|
||||
|
||||
let dummy_key_members = ParticipantsBitmap::<Runtime>::empty(padded_participants);
|
||||
let dummy_oversized_bitmap = ParticipantsBitmap::<Runtime>::all_ones(max_authorities_len);
|
||||
let mut map: BitmapByAuthority<Runtime> = Default::default();
|
||||
|
||||
@ -767,9 +765,13 @@ fn test_dkg_robustness_against_malformed_scale_slots() {
|
||||
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 malicious_validator = 3u64;
|
||||
let all_validators = FixedAuthorities::get();
|
||||
let honest_validators = all_validators
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|&x| x != malicious_validator)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let authorities: Vec<UintAuthorityId> = all_validators.iter().map(|&id| id.into()).collect();
|
||||
ext.execute_with(|| Exodus::start_dkg_qualification(authorities, NetworkCurve::iter()));
|
||||
@ -791,18 +793,15 @@ fn test_dkg_robustness_against_malformed_scale_slots() {
|
||||
|
||||
ext.execute_with(|| {
|
||||
let dkg_index = QualificationDkgState::<Runtime>::get(curve).get_dkg_index();
|
||||
let padded_participants = all_validators.len();
|
||||
|
||||
let padded_participants = 10usize;
|
||||
let bitmask_len = padded_participants.div_ceil(8);
|
||||
|
||||
let mut perfect_bitmask = vec![0u8; bitmask_len];
|
||||
let mut perfect_bitmask = ParticipantsBitmap::<Runtime>::empty(padded_participants);
|
||||
for &recipient in &honest_validators {
|
||||
let idx = recipient as usize;
|
||||
perfect_bitmask[idx >> 3] |= 1 << (idx & 7);
|
||||
perfect_bitmask.insert(recipient);
|
||||
}
|
||||
|
||||
let expected_blob_len = round2_encrypted_package_size(
|
||||
padded_participants,
|
||||
padded_participants - 1,
|
||||
curve.scalar_bytes_len(),
|
||||
curve.header_bytes_len(),
|
||||
);
|
||||
@ -825,7 +824,6 @@ fn test_dkg_robustness_against_malformed_scale_slots() {
|
||||
.unwrap();
|
||||
|
||||
let dummy_package = DkgPackage::Round2(dummy_context);
|
||||
|
||||
let malicious_signer: UintAuthorityId = malicious_validator.into();
|
||||
let dummy_signature = malicious_signer.sign(&dummy_package.encode()).unwrap();
|
||||
|
||||
@ -863,9 +861,13 @@ 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 malicious_validator = 3u64;
|
||||
let all_validators = FixedAuthorities::get();
|
||||
let honest_validators = all_validators
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|&x| x != malicious_validator)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let authorities: Vec<UintAuthorityId> = all_validators.iter().map(|&id| id.into()).collect();
|
||||
ext.execute_with(|| Exodus::start_dkg_qualification(authorities, NetworkCurve::iter()));
|
||||
@ -908,6 +910,63 @@ fn test_exodus_dkg_distributed_synced_exclusion() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_accused_authority_survives_dkg_via_justification() {
|
||||
let (mut ext, tx_pool_state) = new_test_ext();
|
||||
|
||||
let curve = NetworkCurve::Secp256k1;
|
||||
let reporter_index = 5;
|
||||
let malicious_index = 3;
|
||||
let raw_authorities = FixedAuthorities::get();
|
||||
|
||||
let authorities: Vec<UintAuthorityId> = raw_authorities.iter().map(|&id| id.into()).collect();
|
||||
let authorities_len = authorities.len();
|
||||
|
||||
ext.execute_with(|| Exodus::start_dkg_qualification(authorities, NetworkCurve::iter()));
|
||||
|
||||
let dkg_rounds = [
|
||||
execute_round0_happy_path,
|
||||
execute_round1_happy_path,
|
||||
execute_round2_happy_path,
|
||||
execute_round3_happy_path,
|
||||
];
|
||||
|
||||
for dkg_round in dkg_rounds {
|
||||
run_to_next_round(&mut ext, curve);
|
||||
for authority in raw_authorities.iter() {
|
||||
assert_ok!(dkg_round(&mut ext, &tx_pool_state, curve, *authority));
|
||||
}
|
||||
}
|
||||
|
||||
ext.execute_with(|| {
|
||||
let mut complaints = ParticipantsBitmap::<Runtime>::empty(authorities_len);
|
||||
complaints.insert(malicious_index);
|
||||
|
||||
Complaints::<Runtime>::mutate(curve, |complaints_map| {
|
||||
complaints_map.try_insert(reporter_index, complaints).unwrap();
|
||||
});
|
||||
});
|
||||
|
||||
let remaining_rounds = [
|
||||
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 raw_authorities.iter() {
|
||||
assert_ok!(dkg_round(&mut ext, &tx_pool_state, curve, *authority));
|
||||
}
|
||||
}
|
||||
|
||||
run_to_next_round(&mut ext, curve);
|
||||
|
||||
let state = ext.execute_with(|| QualificationDkgState::<Runtime>::get(curve));
|
||||
assert_eq!(state.get_phase(), DkgPhase::Round7);
|
||||
assert_eq!(state.count_ones::<usize>(), raw_authorities.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_all_validators_can_execute_initial_dkg() {
|
||||
let curve = NetworkCurve::Secp256k1;
|
||||
@ -1302,6 +1361,11 @@ fn test_prepare_round5_kick_on_empty_justifications() {
|
||||
|
||||
let auth: Vec<AuthIndex> = raw_authorities.iter().map(|&id| id as AuthIndex).collect();
|
||||
|
||||
for &curr_auth in auth.iter() {
|
||||
complaints_map.try_insert(curr_auth, ParticipantsBitmap::<Runtime>::empty(authorities_len)).unwrap();
|
||||
justifications_map.try_insert(curr_auth, ParticipantsBitmap::<Runtime>::empty(authorities_len)).unwrap();
|
||||
}
|
||||
|
||||
let mut complaints_from_1 = ParticipantsBitmap::<Runtime>::empty(authorities_len);
|
||||
complaints_from_1.insert(auth[0]);
|
||||
complaints_from_1.insert(auth[3]);
|
||||
@ -1311,9 +1375,6 @@ fn test_prepare_round5_kick_on_empty_justifications() {
|
||||
complaints_from_2.insert(auth[0]);
|
||||
complaints_map.try_insert(auth[2], complaints_from_2).unwrap();
|
||||
|
||||
complaints_map.try_insert(auth[0], ParticipantsBitmap::<Runtime>::empty(authorities_len)).unwrap();
|
||||
complaints_map.try_insert(auth[3], ParticipantsBitmap::<Runtime>::empty(authorities_len)).unwrap();
|
||||
|
||||
let mut justifications_from_0 = ParticipantsBitmap::<Runtime>::empty(authorities_len);
|
||||
justifications_from_0.insert(auth[1]);
|
||||
justifications_from_0.insert(auth[2]);
|
||||
@ -1323,9 +1384,6 @@ fn test_prepare_round5_kick_on_empty_justifications() {
|
||||
justifications_from_3.insert(auth[0]);
|
||||
justifications_map.try_insert(auth[3], justifications_from_3).unwrap();
|
||||
|
||||
justifications_map.try_insert(auth[1], ParticipantsBitmap::<Runtime>::empty(authorities_len)).unwrap();
|
||||
justifications_map.try_insert(auth[2], ParticipantsBitmap::<Runtime>::empty(authorities_len)).unwrap();
|
||||
|
||||
Complaints::<Runtime>::insert(curve, complaints_map);
|
||||
Justifications::<Runtime>::insert(curve, justifications_map);
|
||||
});
|
||||
|
||||
@ -7,17 +7,17 @@ use frame_support::{BoundedVec, traits::ConstU32};
|
||||
use crate::{ExodusHash, ELEMENT_MAX_BYTES};
|
||||
|
||||
#[derive(Default, Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)]
|
||||
pub struct QualifiedConsensusState<Bitmap, Bitmask> {
|
||||
pub struct QualifiedConsensusState<Bitmap> {
|
||||
pub state: ConsensusState<Bitmap>,
|
||||
pub members: Bitmask,
|
||||
pub members: Bitmap,
|
||||
}
|
||||
|
||||
impl<Bitmap, Bitmask> QualifiedConsensusState<Bitmap, Bitmask> {
|
||||
impl<Bitmap> QualifiedConsensusState<Bitmap> {
|
||||
pub fn new(
|
||||
element_bytes: BoundedVec<u8, ConstU32<ELEMENT_MAX_BYTES>>,
|
||||
participants: Bitmap,
|
||||
merkle_root: ExodusHash,
|
||||
members: Bitmask,
|
||||
members: Bitmap,
|
||||
) -> Self {
|
||||
Self {
|
||||
members,
|
||||
|
||||
@ -3,8 +3,32 @@ use scale_info::TypeInfo;
|
||||
use sp_runtime::RuntimeDebug;
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
use sp_runtime::traits::UniqueSaturatedInto;
|
||||
use frame_support::{BoundedVec, traits::ConstU32};
|
||||
use crate::{AuthIndex, DkgIndex, ExodusSession, ExodusHash, ExodusError};
|
||||
use ghost_traits::bounded_bitmap::BoundedBitmapReader;
|
||||
|
||||
use crate::{
|
||||
AuthIndex, DkgIndex, ExodusSession, ExodusHash, ExodusError,
|
||||
ParticipantsBitmap,
|
||||
};
|
||||
|
||||
pub trait IndexedBundle<T>
|
||||
where
|
||||
T: crate::pallet::Config,
|
||||
{
|
||||
fn blob(&self) -> &[u8];
|
||||
fn bitmask(&self) -> &ParticipantsBitmap<T>;
|
||||
|
||||
fn get_package_by_index<D: Decode>(&self, index: AuthIndex, size: usize) -> Option<D> {
|
||||
let index_usize = index.unique_saturated_into();
|
||||
let popcount = self.bitmask().count_ones_before::<usize>(index_usize)?;
|
||||
|
||||
popcount.checked_mul(size)
|
||||
.and_then(|start| start.checked_add(size).map(|end| (start, end)))
|
||||
.and_then(|(start, end)| self.blob().get(start..end))
|
||||
.and_then(|raw_slice| D::decode(&mut &raw_slice[..]).ok())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PackageMetadata<NetworkCurve, BlockNumber>
|
||||
where
|
||||
@ -77,7 +101,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Clone, RuntimeDebug, TypeInfo, PartialEq, Eq)]
|
||||
#[derive(RuntimeDebug, Encode, Decode, Clone, TypeInfo, PartialEq, Eq)]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub enum DkgPackage<NetworkCurve, BlockNumber, T>
|
||||
where
|
||||
@ -100,12 +124,19 @@ where
|
||||
let length = match self {
|
||||
Self::Round0(_) => ExodusHash::len_bytes(),
|
||||
Self::Round1(round1) => round1.package.len(),
|
||||
Self::Round2(round2) => round2.bundle.bitmask.len(),
|
||||
Self::Round3(round3) => round3.indices.len(),
|
||||
Self::Round4(round4) => round4.bundle.bitmask.len(),
|
||||
Self::Round2(round2) => round2.bundle.bitmask.count_ones(),
|
||||
Self::Round3(round3) => {
|
||||
round3.indices.highest_bit::<usize>().unwrap_or_default()
|
||||
},
|
||||
Self::Round4(round4) => round4.bundle.bitmask.count_ones(),
|
||||
Self::Round5(round5) => {
|
||||
let key_members_size = round5.key_members
|
||||
.highest_bit::<usize>()
|
||||
.unwrap_or_default();
|
||||
|
||||
round5.verifying_key.len()
|
||||
.saturating_add(ExodusHash::len_bytes())
|
||||
.saturating_add(key_members_size)
|
||||
},
|
||||
Self::Round6(round6) => round6.merkle_proof.len(),
|
||||
};
|
||||
@ -165,9 +196,21 @@ where
|
||||
}
|
||||
|
||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
|
||||
pub struct BundledPackages {
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct BundledPackages<T>
|
||||
where
|
||||
T: crate::pallet::Config
|
||||
{
|
||||
pub bitmask: ParticipantsBitmap<T>,
|
||||
pub blob: Vec<u8>,
|
||||
pub bitmask: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<T> IndexedBundle<T> for BundledPackages<T>
|
||||
where
|
||||
T: crate::pallet::Config,
|
||||
{
|
||||
fn blob(&self) -> &[u8] { &self.blob }
|
||||
fn bitmask(&self) -> &ParticipantsBitmap<T> { &self.bitmask }
|
||||
}
|
||||
|
||||
#[derive(RuntimeDebug, Clone, Eq, PartialEq, Encode, Decode, TypeInfo)]
|
||||
@ -177,30 +220,25 @@ where
|
||||
T: crate::pallet::Config,
|
||||
{
|
||||
pub blob: BoundedVec<u8, crate::EncryptedRound2BlobMaxBytes<T>>,
|
||||
pub bitmask: BoundedVec<u8, crate::MaxAuthoritiesBitmaskSize<T>>
|
||||
pub bitmask: ParticipantsBitmap<T>,
|
||||
}
|
||||
|
||||
impl<T: crate::pallet::Config> EncryptionBundledPackages<T> {
|
||||
pub fn len(&self) -> usize {
|
||||
let blob_len = self.blob.len();
|
||||
let bitmask_len = self.bitmask.len();
|
||||
|
||||
bitmask_len.saturating_add(blob_len)
|
||||
}
|
||||
impl<T> IndexedBundle<T> for EncryptionBundledPackages<T>
|
||||
where
|
||||
T: crate::pallet::Config,
|
||||
{
|
||||
fn blob(&self) -> &[u8] { &self.blob }
|
||||
fn bitmask(&self) -> &ParticipantsBitmap<T> { &self.bitmask }
|
||||
}
|
||||
|
||||
impl<T: crate::pallet::Config> TryFrom<BundledPackages> for EncryptionBundledPackages<T> {
|
||||
impl<T: crate::pallet::Config> TryFrom<BundledPackages<T>> for EncryptionBundledPackages<T> {
|
||||
type Error = ExodusError;
|
||||
|
||||
fn try_from(source: BundledPackages) -> Result<Self, Self::Error> {
|
||||
let blob =
|
||||
BoundedVec::<u8, crate::EncryptedRound2BlobMaxBytes<T>>::try_from(source.blob)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
|
||||
let bitmask = BoundedVec::<u8, crate::MaxAuthoritiesBitmaskSize<T>>::try_from(source.bitmask)
|
||||
fn try_from(source: BundledPackages<T>) -> Result<Self, Self::Error> {
|
||||
let blob = BoundedVec::try_from(source.blob)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
|
||||
Ok(Self { blob, bitmask })
|
||||
Ok(Self { blob, bitmask: source.bitmask })
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,30 +249,25 @@ where
|
||||
T: crate::pallet::Config,
|
||||
{
|
||||
pub blob: BoundedVec<u8, crate::Round2BlobMaxBytes<T>>,
|
||||
pub bitmask: BoundedVec<u8, crate::MaxAuthoritiesBitmaskSize<T>>
|
||||
pub bitmask: ParticipantsBitmap<T>,
|
||||
}
|
||||
|
||||
impl<T: crate::pallet::Config> JustificationsBundledPackages<T> {
|
||||
pub fn len(&self) -> usize {
|
||||
let blob_len = self.blob.len();
|
||||
let bitmask_len = self.bitmask.len();
|
||||
|
||||
bitmask_len.saturating_add(blob_len)
|
||||
}
|
||||
impl<T> IndexedBundle<T> for JustificationsBundledPackages<T>
|
||||
where
|
||||
T: crate::pallet::Config,
|
||||
{
|
||||
fn blob(&self) -> &[u8] { &self.blob }
|
||||
fn bitmask(&self) -> &ParticipantsBitmap<T> { &self.bitmask }
|
||||
}
|
||||
|
||||
impl<T: crate::pallet::Config> TryFrom<BundledPackages> for JustificationsBundledPackages<T> {
|
||||
impl<T: crate::pallet::Config> TryFrom<BundledPackages<T>> for JustificationsBundledPackages<T> {
|
||||
type Error = ExodusError;
|
||||
|
||||
fn try_from(source: BundledPackages) -> Result<Self, Self::Error> {
|
||||
let blob =
|
||||
BoundedVec::<u8, crate::Round2BlobMaxBytes<T>>::try_from(source.blob)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
|
||||
let bitmask = BoundedVec::<u8, crate::MaxAuthoritiesBitmaskSize<T>>::try_from(source.bitmask)
|
||||
fn try_from(source: BundledPackages<T>) -> Result<Self, Self::Error> {
|
||||
let blob = BoundedVec::try_from(source.blob)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
|
||||
Ok(Self { blob, bitmask })
|
||||
Ok(Self { blob, bitmask: source.bitmask })
|
||||
}
|
||||
}
|
||||
|
||||
@ -323,7 +356,7 @@ pub struct Round3Package<NetworkCurve, BlockNumber, T>
|
||||
where
|
||||
T: crate::pallet::Config,
|
||||
{
|
||||
pub indices: BoundedVec<u8, crate::AccusedIndicesMaxBytes<T>>,
|
||||
pub indices: ParticipantsBitmap<T>,
|
||||
context: PackageContext<NetworkCurve, BlockNumber>,
|
||||
}
|
||||
|
||||
@ -344,7 +377,7 @@ where
|
||||
T: crate::pallet::Config,
|
||||
{
|
||||
context: PackageContext<NetworkCurve, BlockNumber>,
|
||||
pub key_members: BoundedVec<u8, crate::MaxAuthoritiesBitmaskSize<T>>,
|
||||
pub key_members: ParticipantsBitmap<T>,
|
||||
pub verifying_key: BoundedVec<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>,
|
||||
pub merkle_root: ExodusHash,
|
||||
}
|
||||
@ -367,13 +400,11 @@ impl<NetworkCurve, BlockNumber> PackageContext<NetworkCurve, BlockNumber> {
|
||||
hiding_commitment_vec: Vec<u8>,
|
||||
binding_commitment_vec: Vec<u8>,
|
||||
) -> 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)?;
|
||||
let hiding_commitment = BoundedVec::try_from(hiding_commitment_vec)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
|
||||
let binding_commitment =
|
||||
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::try_from(binding_commitment_vec)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
let binding_commitment = BoundedVec::try_from(binding_commitment_vec)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
|
||||
Ok(ExodusNonceCommitment {
|
||||
package_context: self,
|
||||
@ -389,9 +420,8 @@ impl<NetworkCurve, BlockNumber> PackageContext<NetworkCurve, BlockNumber> {
|
||||
commitment_vec: Vec<u8>,
|
||||
binding_factors_root: ExodusHash,
|
||||
) -> Result<ExodusGroupCommitment<NetworkCurve, NetworkId, BlockNumber>, ExodusError> {
|
||||
let group_commitment =
|
||||
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::try_from(commitment_vec)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
let group_commitment = BoundedVec::try_from(commitment_vec)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
|
||||
Ok(ExodusGroupCommitment {
|
||||
package_context: self,
|
||||
@ -408,13 +438,11 @@ impl<NetworkCurve, BlockNumber> PackageContext<NetworkCurve, BlockNumber> {
|
||||
binding_factors_proof_vec: Vec<ExodusHash>,
|
||||
self_binding_factor_vec: Vec<u8>,
|
||||
) -> 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)?;
|
||||
let signature_share = BoundedVec::try_from(signature_share_vec)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
|
||||
let binding_factors_proof =
|
||||
BoundedVec::<ExodusHash, crate::BindingFactorsProof<T>>::try_from(binding_factors_proof_vec)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
let binding_factors_proof = BoundedVec::try_from(binding_factors_proof_vec)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
|
||||
let self_binding_factor =
|
||||
BoundedVec::<u8, ConstU32<{ crate::SCALAR_MAX_BYTES }>>::try_from(self_binding_factor_vec)
|
||||
@ -440,16 +468,15 @@ impl<NetworkCurve, BlockNumber> PackageContext<NetworkCurve, BlockNumber> {
|
||||
self,
|
||||
package_vec: Vec<u8>,
|
||||
) -> Result<Round1Package<NetworkCurve, BlockNumber, T>, ExodusError> {
|
||||
let package =
|
||||
BoundedVec::<u8, crate::Round1MaxBytes<T>>::try_from(package_vec)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
let package = BoundedVec::try_from(package_vec)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
|
||||
Ok(Round1Package { context: self, package })
|
||||
}
|
||||
|
||||
pub fn with_dkg_round2<T: crate::pallet::Config>(
|
||||
self,
|
||||
bundle_raw: BundledPackages,
|
||||
bundle_raw: BundledPackages<T>,
|
||||
) -> Result<Round2Package<NetworkCurve, BlockNumber, T>, ExodusError> {
|
||||
let bundle: EncryptionBundledPackages<T> = bundle_raw.try_into()?;
|
||||
Ok(Round2Package { context: self, bundle })
|
||||
@ -457,18 +484,14 @@ impl<NetworkCurve, BlockNumber> PackageContext<NetworkCurve, BlockNumber> {
|
||||
|
||||
pub fn with_dkg_round3<T: crate::pallet::Config>(
|
||||
self,
|
||||
indices_vec: Vec<u8>
|
||||
indices: ParticipantsBitmap<T>,
|
||||
) -> Result<Round3Package<NetworkCurve, BlockNumber, T>, ExodusError> {
|
||||
let indices =
|
||||
BoundedVec::<u8, crate::AccusedIndicesMaxBytes<T>>::try_from(indices_vec)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
|
||||
Ok(Round3Package { context: self, indices })
|
||||
}
|
||||
|
||||
pub fn with_dkg_round4<T: crate::pallet::Config>(
|
||||
self,
|
||||
bundle_raw: BundledPackages,
|
||||
bundle_raw: BundledPackages<T>,
|
||||
) -> Result<Round4Package<NetworkCurve, BlockNumber, T>, ExodusError> {
|
||||
let bundle: JustificationsBundledPackages<T> = bundle_raw.try_into()?;
|
||||
Ok(Round4Package { context: self, bundle })
|
||||
@ -477,16 +500,12 @@ impl<NetworkCurve, BlockNumber> PackageContext<NetworkCurve, BlockNumber> {
|
||||
pub fn with_dkg_round5<T: crate::pallet::Config>(
|
||||
self,
|
||||
verifying_key_vec: Vec<u8>,
|
||||
key_members_vec: Vec<u8>,
|
||||
key_members: ParticipantsBitmap<T>,
|
||||
merkle_root: ExodusHash,
|
||||
) -> Result<Round5Package<NetworkCurve, BlockNumber, T>, ExodusError> {
|
||||
let key_members = BoundedVec::<u8, crate::MaxAuthoritiesBitmaskSize<T>>::try_from(key_members_vec)
|
||||
let verifying_key = BoundedVec::try_from(verifying_key_vec)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
|
||||
let verifying_key =
|
||||
BoundedVec::<u8, ConstU32<{ crate::ELEMENT_MAX_BYTES }>>::try_from(verifying_key_vec)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
|
||||
Ok(Round5Package { context: self, key_members, verifying_key, merkle_root })
|
||||
}
|
||||
|
||||
@ -495,13 +514,11 @@ impl<NetworkCurve, BlockNumber> PackageContext<NetworkCurve, BlockNumber> {
|
||||
verifying_share_vec: Vec<u8>,
|
||||
merkle_proof_vec: Vec<ExodusHash>,
|
||||
) -> 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)?;
|
||||
let verifying_share = BoundedVec::try_from(verifying_share_vec)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
|
||||
let merkle_proof =
|
||||
BoundedVec::<ExodusHash, crate::MerkleProofMaxSize<T>>::try_from(merkle_proof_vec)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
let merkle_proof = BoundedVec::try_from(merkle_proof_vec)
|
||||
.map_err(|_| ExodusError::DeserializationError)?;
|
||||
|
||||
Ok(Round6Package { context: self, verifying_share, merkle_proof })
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ghost-helpers"
|
||||
version = "0.0.14"
|
||||
version = "0.0.15"
|
||||
description = "Cryptographic utility suite for custom runtimes: optimized Bitmaps, UTXO parsing, Merkle Tree proofs, and Hash Chain components."
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
@ -2,8 +2,8 @@ use codec::{Decode, Encode};
|
||||
use num_traits::PrimInt;
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use sp_runtime::traits::UniqueSaturatedInto;
|
||||
use sp_arithmetic::traits::AtLeast8BitUnsigned;
|
||||
use sp_runtime::{traits::UniqueSaturatedInto, RuntimeDebug};
|
||||
use sp_std::{cmp::{min, max}, marker::PhantomData, vec, vec::Vec};
|
||||
|
||||
use ghost_traits::bounded_bitmap as traits;
|
||||
@ -33,7 +33,7 @@ pub fn validate_bitmap_sizes<B: traits::BoundedBitmapMetadata>(authorities: u32)
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
|
||||
#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]
|
||||
#[scale_info(skip_type_params(MaxChunks))]
|
||||
pub struct BoundedBitmap<Inner, MaxChunks>
|
||||
where
|
||||
@ -43,6 +43,19 @@ where
|
||||
active_bits_count: u32,
|
||||
}
|
||||
|
||||
impl<Inner: core::fmt::Debug, MaxChunks> core::fmt::Debug for BoundedBitmap<Inner, MaxChunks>
|
||||
where
|
||||
MaxChunks: frame_support::traits::Get<u32>,
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
let slice: &[Inner] = &self.data;
|
||||
f.debug_struct("BoundedBitmap")
|
||||
.field("data", &slice)
|
||||
.field("active_bits_count", &self.active_bits_count)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Inner, MaxChunks> BoundedBitmap<Inner, MaxChunks>
|
||||
where
|
||||
MaxChunks: Get<u32>,
|
||||
@ -54,6 +67,15 @@ where
|
||||
Inner: BitAndAssign + BitOrAssign + BitXorAssign,
|
||||
Inner: Shl<usize, Output = Inner> + Shr<usize, Output = Inner>,
|
||||
{
|
||||
pub fn inner_as_ref(&self) -> &[u8] {
|
||||
let slice: &[Inner] = self.data.as_ref();
|
||||
let byte_len = slice.len() * core::mem::size_of::<Inner>();
|
||||
|
||||
unsafe {
|
||||
core::slice::from_raw_parts(slice.as_ptr() as *const u8, byte_len)
|
||||
}
|
||||
}
|
||||
|
||||
fn combine<F>(&self, rhs: &Self, op: F) -> Self
|
||||
where
|
||||
Inner: Copy,
|
||||
@ -146,41 +168,6 @@ where
|
||||
new_bitmap
|
||||
}
|
||||
|
||||
fn from_bitmask<Outer>(values: &Vec<Outer>) -> Self
|
||||
where
|
||||
Outer: AtLeast8BitUnsigned + PrimInt,
|
||||
{
|
||||
let value_bits = inner_bits::<Outer>();
|
||||
let n = values.iter().enumerate().rev()
|
||||
.find(|(_, &val)| val != Outer::zero())
|
||||
.map(|(idx, &val)| {
|
||||
let active_bits = value_bits - (val.leading_zeros() as usize);
|
||||
idx.saturating_mul(value_bits).saturating_add(active_bits)
|
||||
})
|
||||
.unwrap_or(0);
|
||||
|
||||
let mut new_bitmap = Self::empty(n);
|
||||
|
||||
for (value_index, value) in values.iter().enumerate() {
|
||||
for bit_index in 0..value_bits {
|
||||
let bit_mask = Outer::one() << bit_index;
|
||||
if (*value & bit_mask) == Outer::zero() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let index = value_index
|
||||
.saturating_mul(value_bits)
|
||||
.saturating_add(bit_index);
|
||||
|
||||
if let None = new_bitmap.insert(index as u32) {
|
||||
return new_bitmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new_bitmap
|
||||
}
|
||||
|
||||
fn all_ones<N>(n: N) -> Self
|
||||
where
|
||||
N: UniqueSaturatedInto<usize>,
|
||||
@ -307,6 +294,35 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
fn count_ones_before<N>(&self, index: N) -> Option<N>
|
||||
where
|
||||
usize: UniqueSaturatedInto<N>,
|
||||
N: UniqueSaturatedInto<usize>,
|
||||
{
|
||||
let index: usize = index.unique_saturated_into();
|
||||
|
||||
if index >= self.active_bits_count as usize { return None; }
|
||||
|
||||
let shift = inner_shift::<Inner>();
|
||||
let element_index = index >> shift;
|
||||
|
||||
let bit_index = index & ((1 << shift) - 1);
|
||||
|
||||
let current_element = self.data.get(element_index)?;
|
||||
let lower_bits_mask = (Inner::one() << bit_index) - Inner::one();
|
||||
let bits_before = *current_element & lower_bits_mask;
|
||||
|
||||
self.data.get(0..element_index)?
|
||||
.iter()
|
||||
.try_fold(0usize, |acc, &element| {
|
||||
acc.checked_add(element.count_ones() as usize)
|
||||
})
|
||||
.and_then(|popcount| {
|
||||
popcount.checked_add(bits_before.count_ones() as usize)
|
||||
})
|
||||
.map(|total| total.unique_saturated_into())
|
||||
}
|
||||
|
||||
fn count_ones<N>(&self) -> N
|
||||
where
|
||||
u32: UniqueSaturatedInto<N>,
|
||||
@ -425,6 +441,14 @@ where
|
||||
self.highest_bit()
|
||||
}
|
||||
|
||||
fn count_ones_before<N>(&self, index: N) -> Option<N>
|
||||
where
|
||||
usize: UniqueSaturatedInto<N>,
|
||||
N: UniqueSaturatedInto<usize>,
|
||||
{
|
||||
self.count_ones_before(index)
|
||||
}
|
||||
|
||||
fn count_ones<N>(&self) -> N
|
||||
where
|
||||
u32: UniqueSaturatedInto<N>,
|
||||
@ -476,13 +500,6 @@ where
|
||||
Self::empty_from(bitmap_ref)
|
||||
}
|
||||
|
||||
fn from_bitmask<Outer>(values_ref: &Vec<Outer>) -> Self
|
||||
where
|
||||
Outer: AtLeast8BitUnsigned + PrimInt,
|
||||
{
|
||||
Self::from_bitmask(values_ref)
|
||||
}
|
||||
|
||||
fn from_vec<Outer>(values_ref: &Vec<Outer>) -> Self
|
||||
where
|
||||
Outer: AtLeast8BitUnsigned + TryInto<usize> + PartialEq + Copy,
|
||||
@ -1099,50 +1116,72 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_bitmask_where_each_u8_is_a_bit() {
|
||||
let byte1 = 77u8;
|
||||
let byte2 = 42u8;
|
||||
let byte3 = 69u8;
|
||||
fn test_count_ones_before_basic() {
|
||||
let w = <$inner_type>::BITS as usize;
|
||||
let idx = |x: usize| -> $inner_type {
|
||||
x.try_into().ok().expect("bit index fits inner type")
|
||||
};
|
||||
|
||||
let raw_bits: Vec<u8> = vec![byte1, byte2, byte3];
|
||||
let bitmap = TestBitmap::from_bitmask(&raw_bits);
|
||||
let mut indices: Vec<$inner_type> = vec![
|
||||
0u8.into(),
|
||||
1u8.into(),
|
||||
3u8.into()
|
||||
];
|
||||
|
||||
let count_ones = raw_bits.iter().map(|b| b.count_ones()).sum::<u32>();
|
||||
let filled_bits = (raw_bits.len() as u32 - 1) * 8;
|
||||
let expected_bits = filled_bits + (8 - byte3.leading_zeros());
|
||||
indices.extend([idx(w), idx(w + 1), idx(w + 3)] as [$inner_type; 3]);
|
||||
let bitmap = TestBitmap::from_vec(&indices);
|
||||
|
||||
assert_eq!(bitmap.active_bits_count(), expected_bits);
|
||||
assert_eq!(bitmap.count_ones::<u32>(), count_ones);
|
||||
assert_eq!(bitmap.count_ones_before(0), Some(0));
|
||||
assert_eq!(bitmap.count_ones_before(1), Some(1));
|
||||
assert_eq!(bitmap.count_ones_before(2), Some(2));
|
||||
assert_eq!(bitmap.count_ones_before(3), Some(2));
|
||||
assert_eq!(bitmap.count_ones_before(4), Some(3));
|
||||
assert_eq!(bitmap.count_ones_before(w), Some(3));
|
||||
|
||||
// 77 - 10110010
|
||||
assert!(bitmap.contains(0u32));
|
||||
assert!(!bitmap.contains(1u32));
|
||||
assert!(bitmap.contains(2u32));
|
||||
assert!(bitmap.contains(3u32));
|
||||
assert!(!bitmap.contains(4u32));
|
||||
assert!(!bitmap.contains(5u32));
|
||||
assert!(bitmap.contains(6u32));
|
||||
assert!(!bitmap.contains(7u32));
|
||||
assert_eq!(bitmap.count_ones_before(w + 1), Some(4));
|
||||
assert_eq!(bitmap.count_ones_before(w + 4), Some(6));
|
||||
assert_eq!(bitmap.count_ones_before(2 * w), Some(6));
|
||||
}
|
||||
|
||||
// 42 - 01010100
|
||||
assert!(!bitmap.contains(8u32));
|
||||
assert!(bitmap.contains(9u32));
|
||||
assert!(!bitmap.contains(10u32));
|
||||
assert!(bitmap.contains(11u32));
|
||||
assert!(!bitmap.contains(12u32));
|
||||
assert!(bitmap.contains(13u32));
|
||||
assert!(!bitmap.contains(14u32));
|
||||
assert!(!bitmap.contains(15u32));
|
||||
#[test]
|
||||
fn test_count_ones_before_crossing_elements() {
|
||||
let w = <$inner_type>::BITS as usize;
|
||||
let idx = |x: usize| -> $inner_type {
|
||||
x.try_into().ok().expect("bit index fits inner type")
|
||||
};
|
||||
|
||||
// 69 - 10100010
|
||||
assert!(bitmap.contains(16u32));
|
||||
assert!(!bitmap.contains(17u32));
|
||||
assert!(bitmap.contains(18u32));
|
||||
assert!(!bitmap.contains(19u32));
|
||||
assert!(!bitmap.contains(20u32));
|
||||
assert!(!bitmap.contains(21u32));
|
||||
assert!(bitmap.contains(22u32));
|
||||
assert!(!bitmap.contains(23u32));
|
||||
let mut bitmap = TestBitmap::empty(2 * w + 1);
|
||||
for i in [0usize, 1, w, w + 1, w + 2] {
|
||||
bitmap.insert(idx(i));
|
||||
}
|
||||
|
||||
let bits_per_element = core::mem::size_of::<$inner_type>() * 8;
|
||||
|
||||
assert_eq!(bitmap.count_ones_before(bits_per_element), Some(2));
|
||||
assert_eq!(bitmap.count_ones_before(bits_per_element + 1), Some(3));
|
||||
assert_eq!(bitmap.count_ones_before(bits_per_element + 3), Some(5));
|
||||
assert_eq!(bitmap.count_ones_before(2 * bits_per_element), Some(5));
|
||||
|
||||
assert_eq!(bitmap.count_ones_before(2 * bits_per_element + 1), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_count_ones_before_bounds_and_overflow() {
|
||||
let w = <$inner_type>::BITS as usize;
|
||||
let idx = |x: usize| -> $inner_type {
|
||||
x.try_into().ok().expect("bit index fits inner type")
|
||||
};
|
||||
|
||||
let mut bitmap = TestBitmap::empty(w);
|
||||
for i in 0..w {
|
||||
bitmap.insert(idx(i));
|
||||
}
|
||||
|
||||
let bits_per_element = (core::mem::size_of::<$inner_type>() * 8) as u32;
|
||||
|
||||
assert_eq!(bitmap.count_ones_before(bits_per_element - 1), Some(bits_per_element - 1));
|
||||
assert_eq!(bitmap.count_ones_before(bits_per_element), None);
|
||||
assert_eq!(bitmap.count_ones_before(10000u32), None);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ghost-traits"
|
||||
version = "0.4.9"
|
||||
version = "0.4.10"
|
||||
description = "Shared traits including `GhostHasher`, `NetworkDataBasicHandler`, `BoundedBTreeMap`, `MerkleTree` and more."
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
@ -20,6 +20,10 @@ pub trait BoundedBitmapReader {
|
||||
fn highest_bit<N>(&self) -> Option<N>
|
||||
where
|
||||
usize: UniqueSaturatedInto<N>;
|
||||
fn count_ones_before<N>(&self, index: N) -> Option<N>
|
||||
where
|
||||
N: UniqueSaturatedInto<usize>,
|
||||
usize: UniqueSaturatedInto<N>;
|
||||
fn count_ones<N>(&self) -> N
|
||||
where
|
||||
u32: UniqueSaturatedInto<N>;
|
||||
@ -40,9 +44,6 @@ pub trait BoundedBitmapGenerator {
|
||||
where
|
||||
N: UniqueSaturatedInto<usize>;
|
||||
fn empty_from(bitmap: &Self) -> Self;
|
||||
fn from_bitmask<Outer>(values: &Vec<Outer>) -> Self
|
||||
where
|
||||
Outer: AtLeast8BitUnsigned + num_traits::PrimInt;
|
||||
fn from_vec<Outer>(values: &Vec<Outer>) -> Self
|
||||
where
|
||||
Outer: AtLeast8BitUnsigned + TryInto<usize> + PartialEq + Copy;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user