make BFT function name more self-descriptive and align implementation with paper

Signed-off-by: Uncle Stinky <uncle.stinky@ghostchain.io>
This commit is contained in:
Uncle Stinky 2026-09-08 14:17:01 +03:00
parent 66bf42cbd3
commit 3cf96b1820
Signed by: st1nky
GPG Key ID: 016064BD97603B40
10 changed files with 80 additions and 42 deletions

6
Cargo.lock generated
View File

@ -3684,7 +3684,7 @@ dependencies = [
[[package]]
name = "ghost-exodus"
version = "0.0.7"
version = "0.0.8"
dependencies = [
"chacha20poly1305",
"frame-benchmarking",
@ -3757,7 +3757,7 @@ dependencies = [
[[package]]
name = "ghost-helpers"
version = "0.0.6"
version = "0.0.7"
dependencies = [
"frame-support",
"ghost-traits",
@ -4109,7 +4109,7 @@ dependencies = [
[[package]]
name = "ghost-weaver"
version = "0.0.4"
version = "0.0.5"
dependencies = [
"frame-benchmarking",
"frame-support",

View File

@ -1,6 +1,6 @@
[package]
name = "ghost-exodus"
version = "0.0.7"
version = "0.0.8"
description = "Threshold signature generation with DKG included"
license.workspace = true
authors.workspace = true

View File

@ -74,7 +74,7 @@ mod benchmarks {
c: Linear<4, { T::MaxAuthorities::get() }>
) -> Result<(), BenchmarkError> {
let max_signers = c as AuthIndex;
let min_signers = get_byzantium_threshold(max_signers);
let min_signers = get_byzantium_quorum_threshold(max_signers);
let (mut dkg_state, network_curve, authority, authority_index) =
prepare_pallet::<T>(max_signers as usize);
@ -637,7 +637,7 @@ mod benchmarks {
#[benchmark]
fn register_signature_share() -> Result<(), BenchmarkError> {
let max_signers = T::MaxAuthorities::get();
let threshold = get_byzantium_threshold(max_signers);
let threshold = get_byzantium_quorum_threshold(max_signers);
let network_id: NetworkIdOf<T> = 69_420u64.unique_saturated_into();
let (mut dkg_state, network_curve, authority, authority_index) =

View File

@ -29,7 +29,7 @@ use sp_runtime::traits::BlockNumberProvider;
use sp_io::hashing::blake2_256;
use ghost_helpers::{
get_byzantium_threshold, SubstrateBlake2Hasher,
get_byzantium_quorum_threshold, SubstrateBlake2Hasher,
hash_chain::sequential_hash,
networks::{NetworkData, NetworkCurve, NetworkType},
bounded_bitmap::{validate_bitmap_sizes, BoundedBitmap},
@ -263,7 +263,7 @@ pub struct Round1MaxBytes<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> Get<u32> for Round1MaxBytes<T> {
fn get() -> u32 {
let max_authorities = T::MaxAuthorities::get();
let min_authorities = get_byzantium_threshold(max_authorities);
let min_authorities = get_byzantium_quorum_threshold(max_authorities);
round1_package_size(
min_authorities as usize,
@ -324,7 +324,7 @@ pub struct BindingFactorsProof<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> Get<u32> for BindingFactorsProof<T> {
fn get() -> u32 {
let max_authorities = T::MaxAuthorities::get();
get_byzantium_threshold(max_authorities)
get_byzantium_quorum_threshold(max_authorities)
.next_power_of_two()
.trailing_zeros()
}
@ -1422,7 +1422,7 @@ pub mod pallet {
};
let threshold = ActiveAuthorities::<T>::decode_len(&network_curve)
.map(|max_participants| get_byzantium_threshold(max_participants))
.map(|max_participants| get_byzantium_quorum_threshold(max_participants))
.ok_or(Error::<T>::DkgAuthoritiesNotInitialized)?;
let exodus_session = package.session;
@ -1515,7 +1515,7 @@ pub mod pallet {
let max_authorities = ActiveAuthorities::<T>::decode_len(&network_curve)
.ok_or(Error::<T>::DkgAuthoritiesNotInitialized)?;
let threshold = get_byzantium_threshold(max_authorities);
let threshold = get_byzantium_quorum_threshold(max_authorities);
let exodus_session = package.session;
ensure!(
@ -1633,7 +1633,7 @@ pub mod pallet {
};
let threshold = ActiveAuthorities::<T>::decode_len(&network_curve)
.map(|max_participants| get_byzantium_threshold(max_participants))
.map(|max_participants| get_byzantium_quorum_threshold(max_participants))
.ok_or(Error::<T>::DkgAuthoritiesNotInitialized)?;
let exodus_session = package.session;
@ -1898,7 +1898,7 @@ pub mod pallet {
None => return weight,
};
let min_threshold = get_byzantium_threshold(qualification_len);
let min_threshold = get_byzantium_quorum_threshold(qualification_len);
weight.saturating_accrue(T::DbWeight::get().reads(1));
let maybe_cursor = DkgMaybeCursor::<T>::get(&network_curve);
@ -2711,7 +2711,7 @@ impl<T: Config> Pallet<T> {
}
let authorities = QualificationAuthorities::<T>::get(&network_curve);
let expected_count = get_byzantium_threshold(authorities.len());
let expected_count = get_byzantium_quorum_threshold(authorities.len());
let expected_package_len = round1_package_size(
expected_count,
@ -3283,7 +3283,7 @@ impl<T: Config> Pallet<T> {
let max_authorities = ActiveAuthorities::<T>::decode_len(&network_curve)
.ok_or(ExodusError::NoActiveAuthorities)?;
let threshold = get_byzantium_threshold(max_authorities);
let threshold = get_byzantium_quorum_threshold(max_authorities);
let mut exodus_requests = ExodusRequests::<T>::iter_prefix(&network_curve)
.collect::<Vec<(ExodusSession, ExodusRequest<_, _>)>>();
@ -3732,7 +3732,7 @@ impl<T: Config> Pallet<T> {
.map(|max_signers| max_signers as AuthIndex)
.ok_or(ExodusError::InvalidMaxSigners(0))?;
let min_signers = get_byzantium_threshold(max_signers);
let min_signers = get_byzantium_quorum_threshold(max_signers);
if qualification_state.contains_index(authority_index) {
return Ok(ExodusOk::DkgRoundAlreadyPassed(

View File

@ -841,7 +841,7 @@ fn run_dkg_session(
let threshold = ext.execute_with(|| {
let len = ActiveAuthorities::<Runtime>::decode_len(&curve)
.unwrap_or(0);
get_byzantium_threshold(len) as u16
get_byzantium_quorum_threshold(len) as u16
});
let mut exodus_steps_outer = BTreeMap::new();
@ -1259,7 +1259,7 @@ fn test_exodus_dkg_robustness() {
assert_eq!(state.get_dkg_index(), dkg_index);
assert_eq!(state.get_phase(), DkgPhase::Finalized);
let bft_threshold = get_byzantium_threshold(authorities_len);
let bft_threshold = get_byzantium_quorum_threshold(authorities_len);
assert!(qualification_count >= bft_threshold);
assert!(state.count_ones::<usize>() >= bft_threshold);
assert_eq!(state.get_indexes().active_bits_count(), authorities_len as u32);
@ -1299,7 +1299,7 @@ fn test_prepare_round4_abort_on_byzantine_attack() {
}
}
let bft_threshold = get_byzantium_threshold(authorities_len);
let bft_threshold = get_byzantium_quorum_threshold(authorities_len);
ext.execute_with(|| {
let mut complaints_map = BitmapByAuthority::<Runtime>::default();
for authority in 0..=bft_threshold {
@ -1344,7 +1344,7 @@ fn test_prepare_round5_abort_on_byzantine_attack() {
}
}
let bft_threshold = get_byzantium_threshold(authorities_len);
let bft_threshold = get_byzantium_quorum_threshold(authorities_len);
ext.execute_with(|| {
let mut justifications_map = BitmapByAuthority::<Runtime>::default();
for authority in 0..=bft_threshold {

View File

@ -1,6 +1,6 @@
[package]
name = "ghost-helpers"
version = "0.0.6"
version = "0.0.7"
description = "Cryptographic utility suite for custom runtimes: optimized Bitmaps, UTXO parsing, Merkle Tree proofs, and Hash Chain components."
license.workspace = true
authors.workspace = true

View File

@ -34,20 +34,18 @@ pub type NetworkIdU32 = u32;
pub type NetworkIdU64 = u64;
pub type NetworkIdU128 = u128;
pub fn get_byzantium_threshold<I>(value: I) -> I
pub fn get_byzantium_quorum_threshold<I>(value: I) -> I
where
I: num_traits::PrimInt,
{
let zero = I::zero();
let one = I::one();
let two = one + one;
let three = two + one;
let three = one + one + one;
if value == zero {
return zero;
}
if value == zero { return zero; }
(value - one) / three * two + one
let f = (value - one) / three;
value - f
}
pub struct SubstrateBlake2Hasher;
@ -97,3 +95,43 @@ impl GhostHasher for UtxoSha2Hasher {
H256::zero()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bft_threshold_standard_paper_cases() {
// Lower boundaries (f increases here)
assert_eq!(get_byzantium_quorum_threshold(1u32), 1);
assert_eq!(get_byzantium_quorum_threshold(4u32), 3);
assert_eq!(get_byzantium_quorum_threshold(7u32), 5);
assert_eq!(get_byzantium_quorum_threshold(10u32), 7);
// Upper boundaries (max nodes for a given 'f')
assert_eq!(get_byzantium_quorum_threshold(3u32), 3);
assert_eq!(get_byzantium_quorum_threshold(6u32), 5);
assert_eq!(get_byzantium_quorum_threshold(9u32), 7);
}
#[test]
fn test_bft_threshold_zero_nodes() {
assert_eq!(get_byzantium_quorum_threshold(0u32), 0);
}
#[test]
fn test_bft_threshold_integer_boundaries() {
let max_val = usize::MAX;
let expected_f = (max_val - 1) / 3;
let expected_honest = max_val - expected_f;
assert_eq!(get_byzantium_quorum_threshold(max_val), expected_honest);
}
#[test]
fn test_bft_threshold_different_types() {
assert_eq!(get_byzantium_quorum_threshold(4u8), 3u8);
assert_eq!(get_byzantium_quorum_threshold(4u64), 3u64);
assert_eq!(get_byzantium_quorum_threshold(4usize), 3usize);
}
}

View File

@ -1,6 +1,6 @@
[package]
name = "ghost-weaver"
version = "0.0.4"
version = "0.0.5"
description = "Weaving a secure cryptographic tapestry across different external chains."
license.workspace = true
authors.workspace = true

View File

@ -25,7 +25,7 @@ use sp_runtime::{
use ghost_helpers::{
bounded_bitmap::{validate_bitmap_sizes, BoundedBitmap},
get_byzantium_threshold,
get_byzantium_quorum_threshold,
networks::{NetworkData, NetworkType},
};
use ghost_traits::{
@ -487,7 +487,7 @@ pub mod pallet {
weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 1));
let len = Authorities::<T>::decode_len().unwrap_or_default();
let weaving_threshold = get_byzantium_threshold(len);
let weaving_threshold = get_byzantium_quorum_threshold(len);
let maybe_tapestry_drafts = TapestryDrafts::<T>::get(&network_id);
@ -850,7 +850,7 @@ impl<T: Config> Pallet<T> {
.parse_block_requests(pending_requests, deadline, request_id);
let total_blocks = block_numbers.len();
let weaving_threshold = get_byzantium_threshold(total_blocks);
let weaving_threshold = get_byzantium_quorum_threshold(total_blocks);
let median_block = Self::validate_block_numbers_consensus(
&mut block_numbers,

View File

@ -65,7 +65,9 @@ fn should_successfully_compute_median_and_start_weaving_round() {
let pair3 = AuthorityPair::from_string("//Carol", None).expect("Should be valid seed");
let network_id = 1;
let network_data = NetworkDataBuilder::default().build();
let network_data = NetworkDataBuilder::default()
.with_block_deviation(5)
.build();
let authorities = vec![pair1.clone(), pair2.clone(), pair3.clone()];
let networks = vec![(network_id, network_data, Default::default())];
@ -122,8 +124,8 @@ fn should_apply_cooldown_delay_on_consensus_failure() {
.with_network_id(network_id);
let att1 = context.with_authority_index(0).build_block_attestation(100);
let att2 = context.with_authority_index(1).build_block_attestation(500);
let att3 = context.with_authority_index(2).build_block_attestation(900);
let att2 = context.with_authority_index(1).build_block_attestation(102);
let att3 = context.with_authority_index(2).build_block_attestation(104);
let sig1 = pair1.sign(&att1.encode());
let sig2 = pair2.sign(&att2.encode());
@ -933,7 +935,7 @@ fn should_successfully_pull_utxo_thread_and_mint_currency() {
}
#[test]
fn on_initialize_should_finalize_session_when_threshold_or_timeout_is_reached() {
fn on_initialize_should_finalize_session_when_timeout_is_reached() {
let pair1 = AuthorityPair::from_string("//Alice", None).expect("Should be valid seed");
let pair2 = AuthorityPair::from_string("//Bob", None).expect("Should be valid seed");
let pair3 = AuthorityPair::from_string("//Charlie", None).expect("Should be valid seed");
@ -995,14 +997,12 @@ fn on_initialize_should_finalize_session_when_threshold_or_timeout_is_reached()
let current_session = CurrentWeavingSession::<TestRuntime>::get(&network_id);
GhostWeaver::on_initialize(current_substrate_block);
let saved_loot = LoomStates::<TestRuntime>::get(&network_id, current_session);
assert_eq!(saved_loot, target_hash);
let saved_loom = LoomStates::<TestRuntime>::get(&network_id, current_session);
assert_ne!(saved_loom, target_hash);
assert_eq!(saved_loom, saved_loom);
let updated_session = CurrentWeavingSession::<TestRuntime>::get(&network_id);
assert_eq!(updated_session, current_session + 1);
System::set_block_number(15);
GhostWeaver::on_initialize(15);
assert_eq!(updated_session, current_session);
});
}