Skip to content

Commit e0cd362

Browse files
trial
1 parent 5b9e6e8 commit e0cd362

File tree

7 files changed

+296
-1
lines changed

7 files changed

+296
-1
lines changed

dash/src/sml/llmq_type/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
mod network;
2+
// pub mod rotation;
3+
// pub mod snapshot;
4+
// pub mod quorum_snapshot_skip_mode;
25

36
use std::fmt::{Display, Formatter};
47
use std::io;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use std::fmt::Formatter;
2+
3+
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Hash)]
4+
pub enum LLMQSnapshotSkipMode {
5+
// No skipping. The skip list is empty.
6+
NoSkipping = 0,
7+
// Skip the first entry of the list.
8+
// The following entries contain the relative position of subsequent skips.
9+
// For example, if during the initialization phase you skip entries x, y and z of the models
10+
// list, the skip list will contain x, y-x and z-y in this mode.
11+
SkipFirst = 1,
12+
// Contains the entries which were not skipped.
13+
// This is better when there are many skips.
14+
// Mode 2 is more efficient and should be used when 3/4*quorumSize ≥ 1/2*masternodeNb or
15+
// quorumsize ≥ 2/3*masternodeNb
16+
SkipExcept = 2,
17+
// Every node was skipped. The skip list is empty. DKG sessions were not attempted.
18+
SkipAll = 3,
19+
}
20+
impl std::fmt::Display for LLMQSnapshotSkipMode {
21+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
22+
write!(f, "{}", match self {
23+
LLMQSnapshotSkipMode::NoSkipping => "0_NoSkipping",
24+
LLMQSnapshotSkipMode::SkipFirst => "1_SkipFirst",
25+
LLMQSnapshotSkipMode::SkipExcept => "2_SkipExcept",
26+
LLMQSnapshotSkipMode::SkipAll => "3_SkipAll"
27+
})
28+
}
29+
}
30+
impl From<u32> for LLMQSnapshotSkipMode {
31+
fn from(orig: u32) -> Self {
32+
match orig {
33+
0 => LLMQSnapshotSkipMode::NoSkipping,
34+
1 => LLMQSnapshotSkipMode::SkipFirst,
35+
2 => LLMQSnapshotSkipMode::SkipExcept,
36+
3 => LLMQSnapshotSkipMode::SkipAll,
37+
_ => LLMQSnapshotSkipMode::NoSkipping,
38+
}
39+
}
40+
}
41+
impl From<LLMQSnapshotSkipMode> for u32 {
42+
fn from(orig: LLMQSnapshotSkipMode) -> Self {
43+
match orig {
44+
LLMQSnapshotSkipMode::NoSkipping => 0,
45+
LLMQSnapshotSkipMode::SkipFirst => 1,
46+
LLMQSnapshotSkipMode::SkipExcept => 2,
47+
LLMQSnapshotSkipMode::SkipAll => 3,
48+
}
49+
}
50+
}
51+
impl LLMQSnapshotSkipMode {
52+
pub fn index(&self) -> u32 {
53+
u32::from(self.clone())
54+
}
55+
}
56+
57+
58+
pub fn from_index(index: u32) -> LLMQSnapshotSkipMode {
59+
LLMQSnapshotSkipMode::from(index)
60+
}

dash/src/sml/llmq_type/rotation.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use crate::sml::llmq_type::snapshot::LLMQSnapshot;
2+
use crate::sml::masternode_list_entry::MasternodeListEntry;
3+
4+
pub enum LLMQQuarterType {
5+
AtHeightMinus3Cycles,
6+
AtHeightMinus2Cycles,
7+
AtHeightMinusCycle,
8+
New,
9+
}
10+
11+
#[derive(Clone, Copy)]
12+
pub enum LLMQQuarterReconstructionType<'a> {
13+
Snapshot,
14+
New {
15+
previous_quarters: [&'a Vec<Vec<MasternodeListEntry>>; 3],
16+
skip_removed_masternodes: bool,
17+
}
18+
}
19+
20+
pub enum LLMQQuarterUsageType {
21+
Snapshot(LLMQSnapshot),
22+
New(Vec<Vec<MasternodeListEntry>>)
23+
}

dash/src/sml/llmq_type/snapshot.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use std::fmt::{Display, Formatter};
2+
use crate::sml::llmq_type::quorum_snapshot_skip_mode::LLMQSnapshotSkipMode;
3+
4+
#[derive(Clone, Debug)]
5+
pub struct LLMQSnapshot {
6+
// The bitset of nodes already in quarters at the start of cycle at height n
7+
// (masternodeListSize + 7)/8
8+
pub member_list: Vec<u8>,
9+
// Skiplist at height n
10+
pub skip_list: Vec<i32>,
11+
// Mode of the skip list
12+
pub skip_list_mode: LLMQSnapshotSkipMode,
13+
}
14+
impl Default for LLMQSnapshot {
15+
fn default() -> Self {
16+
Self {
17+
member_list: vec![],
18+
skip_list: vec![],
19+
skip_list_mode: LLMQSnapshotSkipMode::NoSkipping,
20+
}
21+
}
22+
}
23+
24+
impl Display for LLMQSnapshot {
25+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
26+
let skip_list = self.skip_list.iter().fold(String::new(), |mut acc, i| {
27+
acc.push_str(format!("{},", *i).as_str());
28+
acc
29+
});
30+
write!(f, "members: {} {} {}", hex::encode(&self.member_list), self.skip_list_mode, skip_list)
31+
}
32+
}
33+
34+
impl<'a> std::fmt::Debug for LLMQSnapshot {
35+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36+
let member_list = hex::encode(&self.member_list);
37+
f.debug_struct("LLMQSnapshot")
38+
.field("member_list", &member_list)
39+
.field("skip_list", &self.skip_list.iter())
40+
.field("skip_list_mode", &self.skip_list_mode)
41+
.finish()
42+
}
43+
}
44+
45+
impl LLMQSnapshot {
46+
pub fn new(member_list: Vec<u8>, skip_list: Vec<i32>, skip_list_mode: LLMQSnapshotSkipMode) -> Self {
47+
LLMQSnapshot {
48+
member_list,
49+
skip_list,
50+
skip_list_mode
51+
}
52+
}
53+
54+
pub fn length(&self) -> usize {
55+
self.member_list.len() + 1 + 2 + self.skip_list.len() * 2
56+
}
57+
58+
pub fn member_is_true_at_index(&self, i: u32) -> bool {
59+
self.member_list.as_slice().bit_is_true_at_le_index(i)
60+
}
61+
}
62+

dash/src/sml/masternode_list/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod peer_addresses;
88
pub mod from_diff;
99
mod apply_diff;
1010
mod scores_for_quorum;
11+
// mod rotation;
1112

1213
use std::collections::BTreeMap;
1314
use crate::hash_types::{MerkleRootMasternodeList, MerkleRootQuorums};
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
use std::collections::BTreeMap;
2+
use crate::sml::llmq_type::{LLMQParams, LLMQType};
3+
use crate::sml::llmq_type::rotation::LLMQQuarterReconstructionType;
4+
use crate::sml::masternode_list::MasternodeList;
5+
use crate::sml::masternode_list_entry::MasternodeListEntry;
6+
use crate::sml::quorum_validation_error::QuorumValidationError;
7+
8+
impl MasternodeList {
9+
fn quorum_quarter_members_by_reconstruction_type(
10+
&self,
11+
reconstruction_type: LLMQQuarterReconstructionType,
12+
llmq_params: &LLMQParams,
13+
work_block_height: u32,
14+
) -> Result<Vec<Vec<MasternodeListEntry>>, QuorumValidationError> {
15+
let work_block_hash = self.provider.lookup_block_hash_by_height(work_block_height);
16+
if work_block_hash.is_zero() {
17+
warn!("quorum_quarter_members_by_reconstruction_type: empty work block hash for {work_block_height}")
18+
}
19+
let masternode_list = self.masternode_list_for_block_hash(work_block_hash)
20+
.ok_or(QuorumValidationError::RequiredMasternodeListNotPresent(work_block_hash))?;
21+
let llmq_type = llmq_params.r#type.clone();
22+
let quorum_count = llmq_params.signing_active_quorum_count as usize;
23+
let quorum_size = llmq_params.size as usize;
24+
let quarter_size = quorum_size / 4;
25+
let quorum_modifier_type = self.llmq_modifier_type_for(llmq_type, work_block_hash, work_block_height);
26+
let quorum_modifier = quorum_modifier_type.build_llmq_hash();
27+
match reconstruction_type {
28+
LLMQQuarterReconstructionType::New { previous_quarters, skip_removed_masternodes } => {
29+
let (used_at_h_masternodes, unused_at_h_masternodes, used_at_h_indexed_masternodes) =
30+
masternode_list.usage_info(previous_quarters, skip_removed_masternodes, quorum_count);
31+
Ok(apply_skip_strategy_of_type(LLMQQuarterUsageType::New(used_at_h_indexed_masternodes), used_at_h_masternodes, unused_at_h_masternodes, work_block_height, quorum_modifier, quorum_count, quarter_size))
32+
},
33+
LLMQQuarterReconstructionType::Snapshot => {
34+
if let Some(snapshot) = self.cache.maybe_snapshot(work_block_hash) {
35+
let (used_at_h_masternodes, unused_at_h_masternodes) =
36+
usage_info_from_snapshot(&masternode_list.masternodes, &snapshot, quorum_modifier, work_block_height);
37+
Ok(apply_skip_strategy_of_type(LLMQQuarterUsageType::Snapshot(snapshot), used_at_h_masternodes, unused_at_h_masternodes, work_block_height, quorum_modifier, quorum_count, quarter_size))
38+
} else {
39+
Err(CoreProviderError::NoSnapshot)
40+
}
41+
42+
// self.provider.find_snapshot(work_block_hash, &self.cache)
43+
// .map(|snapshot| {
44+
// let (used_at_h_masternodes, unused_at_h_masternodes) =
45+
// usage_info_from_snapshot(masternode_list, &snapshot, quorum_modifier, work_block_height);
46+
// apply_skip_strategy_of_type(LLMQQuarterUsageType::Snapshot(snapshot), used_at_h_masternodes, unused_at_h_masternodes, work_block_height, quorum_modifier, quorum_count, quarter_size)
47+
// })
48+
}
49+
}
50+
}
51+
52+
53+
fn rotate_members(
54+
&self,
55+
cycle_base_height: u32,
56+
llmq_params: LLMQParams,
57+
skip_removed_masternodes: bool,
58+
// cache: &Arc<RwLock<MasternodeProcessorCache>>
59+
// cached_mn_lists: &BTreeMap<UInt256, MasternodeList>,
60+
// cached_llmq_snapshots: &BTreeMap<UInt256, LLMQSnapshot>,
61+
// cached_cl_signatures: &BTreeMap<UInt256, UInt768>,
62+
// unknown_mn_lists: &mut Vec<UInt256>,
63+
) -> Result<Vec<Vec<MasternodeListEntry>>, QuorumValidationError> {
64+
let num_quorums = llmq_params.signing_active_quorum_count as usize;
65+
let cycle_length = llmq_params.dkg_params.interval;
66+
let work_block_height_for_index = |index: u32| (cycle_base_height - index * cycle_length) - 8;
67+
// Reconstruct quorum members at h - 3c from snapshot
68+
let q_h_m_3c = self.quorum_quarter_members_by_reconstruction_type(LLMQQuarterReconstructionType::Snapshot, &llmq_params, work_block_height_for_index(3))?;
69+
// Reconstruct quorum members at h - 2c from snapshot
70+
let q_h_m_2c = self.quorum_quarter_members_by_reconstruction_type(LLMQQuarterReconstructionType::Snapshot, &llmq_params, work_block_height_for_index(2))?;
71+
// Reconstruct quorum members at h - c from snapshot
72+
let q_h_m_c = self.quorum_quarter_members_by_reconstruction_type(LLMQQuarterReconstructionType::Snapshot, &llmq_params, work_block_height_for_index(1))?;
73+
// Determine quorum members at new index
74+
let reconstruction_type = LLMQQuarterReconstructionType::New { previous_quarters: [&q_h_m_c, &q_h_m_2c, &q_h_m_3c], skip_removed_masternodes };
75+
let quarter_new = self.quorum_quarter_members_by_reconstruction_type(reconstruction_type, &llmq_params, work_block_height_for_index(0))?;
76+
let mut quorum_members =
77+
Vec::<Vec<MasternodeListEntry>>::with_capacity(num_quorums);
78+
(0..num_quorums).for_each(|index| {
79+
add_quorum_members_from_quarter(&mut quorum_members, &q_h_m_3c, index);
80+
add_quorum_members_from_quarter(&mut quorum_members, &q_h_m_2c, index);
81+
add_quorum_members_from_quarter(&mut quorum_members, &q_h_m_c, index);
82+
add_quorum_members_from_quarter(&mut quorum_members, &quarter_new, index);
83+
});
84+
Ok(quorum_members)
85+
}
86+
87+
/// Determine masternodes which is responsible for signing at this quorum index
88+
#[allow(clippy::too_many_arguments)]
89+
pub fn get_rotated_masternodes_for_quorum(
90+
&self,
91+
llmq_type: LLMQType,
92+
block_hash: [u8; 32],
93+
block_height: u32,
94+
skip_removed_masternodes: bool,
95+
) -> Result<Vec<MasternodeListEntry>, CoreProviderError> {
96+
let mut llmq_members_lock = self.cache.llmq_members.write().unwrap();
97+
let cached_members_of_llmq_type_opt = llmq_members_lock.get_mut(&llmq_type);
98+
if cached_members_of_llmq_type_opt.is_some() {
99+
if let Some(cached_members) = cached_members_of_llmq_type_opt.as_ref().unwrap().get(&block_hash).cloned() {
100+
drop(llmq_members_lock);
101+
return Ok(cached_members);
102+
}
103+
} else {
104+
llmq_members_lock.insert(llmq_type.clone(), BTreeMap::new());
105+
}
106+
107+
let cached_members_of_llmq_type = llmq_members_lock.get_mut(&llmq_type).unwrap();
108+
let llmq_params = llmq_type.params();
109+
let quorum_index = block_height % llmq_params.dkg_params.interval;
110+
let cycle_base_height = block_height - quorum_index;
111+
let cycle_base_hash = self.provider.lookup_block_hash_by_height(cycle_base_height);
112+
let mut llmq_indexed_members_lock = self.cache.llmq_indexed_members.write().unwrap();
113+
if let Some(map_by_type_indexed) = llmq_indexed_members_lock.get(&llmq_type) {
114+
let indexed_hash = LLMQIndexedHash::from((cycle_base_hash, quorum_index));
115+
if let Some(cached_members) = map_by_type_indexed.get(&indexed_hash).cloned() {
116+
cached_members_of_llmq_type.insert(block_hash, cached_members.clone());
117+
drop(llmq_members_lock);
118+
drop(llmq_indexed_members_lock);
119+
return Ok(cached_members);
120+
}
121+
} else {
122+
llmq_indexed_members_lock.insert(llmq_type.clone(), BTreeMap::new());
123+
}
124+
drop(llmq_indexed_members_lock);
125+
let rotated_members = self.rotate_members(cycle_base_height, llmq_params, skip_removed_masternodes)?;
126+
let result = if let Some(rotated_members_at_index) = rotated_members.get(quorum_index as usize) {
127+
cached_members_of_llmq_type.insert(block_hash, rotated_members_at_index.clone());
128+
Ok(rotated_members_at_index.clone())
129+
} else {
130+
Err(CoreProviderError::NullResult(format!("No rotated_members for llmq index {} ({})", quorum_index, block_hash.to_hex())))
131+
};
132+
drop(llmq_members_lock);
133+
134+
self.cache.write_llmq_indexed_members(|lock| {
135+
lock.get_mut(&llmq_type)
136+
.unwrap()
137+
.extend(rotated_members.into_iter()
138+
.enumerate()
139+
.map(|(index, members)|
140+
(LLMQIndexedHash::from((cycle_base_hash, index)), members)));
141+
});
142+
result
143+
}
144+
}

dash/src/sml/quorum_entry/validation.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ impl QualifiedQuorumEntry {
1010
where
1111
I: IntoIterator<Item = &'a BLSPublicKey>,
1212
{
13-
let message = self.commitment_hash.as_byte_array().as_slice();
13+
let mut message = self.commitment_hash.as_byte_array();
14+
message.reverse();
15+
let message = message.as_slice();
1416
let public_keys : Vec<(blsful::PublicKey<Bls12381G2Impl>)> = operator_keys
1517
.into_iter()
1618
.map(|key| key.try_into())

0 commit comments

Comments
 (0)