mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 23:57:56 +00:00
cargo +nightly fmt (#3540)
* cargo +nightly fmt * add cargo-fmt check to ci * update ci * fmt * fmt * skip macro * ignore bridges
This commit is contained in:
@@ -16,13 +16,14 @@
|
||||
|
||||
//! Utilities for checking whether a candidate has been approved under a given block.
|
||||
|
||||
use bitvec::{order::Lsb0 as BitOrderLsb0, slice::BitSlice};
|
||||
use polkadot_node_primitives::approval::DelayTranche;
|
||||
use polkadot_primitives::v1::ValidatorIndex;
|
||||
use bitvec::slice::BitSlice;
|
||||
use bitvec::order::Lsb0 as BitOrderLsb0;
|
||||
|
||||
use crate::persisted_entries::{TrancheEntry, ApprovalEntry, CandidateEntry};
|
||||
use crate::time::Tick;
|
||||
use crate::{
|
||||
persisted_entries::{ApprovalEntry, CandidateEntry, TrancheEntry},
|
||||
time::Tick,
|
||||
};
|
||||
|
||||
/// The required tranches of assignments needed to determine whether a candidate is approved.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@@ -57,7 +58,7 @@ pub enum RequiredTranches {
|
||||
/// event that there are some assignments that don't have corresponding approval votes. If this
|
||||
/// is `None`, all assignments have approvals.
|
||||
next_no_show: Option<Tick>,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/// The result of a check.
|
||||
@@ -96,12 +97,11 @@ pub fn check_approval(
|
||||
approval: &ApprovalEntry,
|
||||
required: RequiredTranches,
|
||||
) -> Check {
|
||||
|
||||
// any set of size f+1 contains at least one honest node. If at least one
|
||||
// honest node approves, the candidate should be approved.
|
||||
let approvals = candidate.approvals();
|
||||
if 3 * approvals.count_ones() > approvals.len() {
|
||||
return Check::ApprovedOneThird;
|
||||
return Check::ApprovedOneThird
|
||||
}
|
||||
|
||||
match required {
|
||||
@@ -134,7 +134,7 @@ pub fn check_approval(
|
||||
} else {
|
||||
Check::Unapproved
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ impl State {
|
||||
) -> RequiredTranches {
|
||||
let covering = if self.depth == 0 { 0 } else { self.covering };
|
||||
if self.depth != 0 && self.assignments + covering + self.uncovered >= n_validators {
|
||||
return RequiredTranches::All;
|
||||
return RequiredTranches::All
|
||||
}
|
||||
|
||||
// If we have enough assignments and all no-shows are covered, we have reached the number
|
||||
@@ -192,7 +192,7 @@ impl State {
|
||||
needed: tranche,
|
||||
tolerated_missing: self.covered,
|
||||
next_no_show: self.next_no_show,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// We're pending more assignments and should look at more tranches.
|
||||
@@ -245,10 +245,7 @@ impl State {
|
||||
self.covered + new_covered
|
||||
};
|
||||
let uncovered = self.uncovered + new_no_shows;
|
||||
let next_no_show = super::min_prefer_some(
|
||||
self.next_no_show,
|
||||
next_no_show,
|
||||
);
|
||||
let next_no_show = super::min_prefer_some(self.next_no_show, next_no_show);
|
||||
|
||||
let (depth, covering, uncovered) = if covering == 0 {
|
||||
if uncovered == 0 {
|
||||
@@ -271,27 +268,25 @@ fn filled_tranche_iterator<'a>(
|
||||
) -> impl Iterator<Item = (DelayTranche, &[(ValidatorIndex, Tick)])> {
|
||||
let mut gap_end = None;
|
||||
|
||||
let approval_entries_filled = tranches
|
||||
.iter()
|
||||
.flat_map(move |tranche_entry| {
|
||||
let tranche = tranche_entry.tranche();
|
||||
let assignments = tranche_entry.assignments();
|
||||
let approval_entries_filled = tranches.iter().flat_map(move |tranche_entry| {
|
||||
let tranche = tranche_entry.tranche();
|
||||
let assignments = tranche_entry.assignments();
|
||||
|
||||
// The new gap_start immediately follows the prior gap_end, if one exists.
|
||||
// Otherwise, on the first pass, the new gap_start is set to the first
|
||||
// tranche so that the range below will be empty.
|
||||
let gap_start = gap_end.map(|end| end + 1).unwrap_or(tranche);
|
||||
gap_end = Some(tranche);
|
||||
// The new gap_start immediately follows the prior gap_end, if one exists.
|
||||
// Otherwise, on the first pass, the new gap_start is set to the first
|
||||
// tranche so that the range below will be empty.
|
||||
let gap_start = gap_end.map(|end| end + 1).unwrap_or(tranche);
|
||||
gap_end = Some(tranche);
|
||||
|
||||
(gap_start..tranche).map(|i| (i, &[] as &[_]))
|
||||
.chain(std::iter::once((tranche, assignments)))
|
||||
});
|
||||
(gap_start..tranche)
|
||||
.map(|i| (i, &[] as &[_]))
|
||||
.chain(std::iter::once((tranche, assignments)))
|
||||
});
|
||||
|
||||
let pre_end = tranches.first().map(|t| t.tranche());
|
||||
let post_start = tranches.last().map_or(0, |t| t.tranche() + 1);
|
||||
|
||||
let pre = pre_end.into_iter()
|
||||
.flat_map(|pre_end| (0..pre_end).map(|i| (i, &[] as &[_])));
|
||||
let pre = pre_end.into_iter().flat_map(|pre_end| (0..pre_end).map(|i| (i, &[] as &[_])));
|
||||
let post = (post_start..).map(|i| (i, &[] as &[_]));
|
||||
|
||||
pre.chain(approval_entries_filled).chain(post)
|
||||
@@ -313,13 +308,14 @@ fn count_no_shows(
|
||||
drifted_tick_now: Tick,
|
||||
) -> (usize, Option<u64>) {
|
||||
let mut next_no_show = None;
|
||||
let no_shows = assignments.iter()
|
||||
let no_shows = assignments
|
||||
.iter()
|
||||
.map(|(v_index, tick)| (v_index, tick.saturating_sub(clock_drift) + no_show_duration))
|
||||
.filter(|&(v_index, no_show_at)| {
|
||||
let has_approved = if let Some(approved) = approvals.get(v_index.0 as usize) {
|
||||
*approved
|
||||
} else {
|
||||
return false;
|
||||
return false
|
||||
};
|
||||
|
||||
let is_no_show = !has_approved && no_show_at <= drifted_tick_now;
|
||||
@@ -331,14 +327,12 @@ fn count_no_shows(
|
||||
// *this node* should observe whether or not the validator is a no show. Recall
|
||||
// that when the when drifted_tick_now is computed during that subsequent wake up,
|
||||
// the clock drift will be removed again to do the comparison above.
|
||||
next_no_show = super::min_prefer_some(
|
||||
next_no_show,
|
||||
Some(no_show_at + clock_drift),
|
||||
);
|
||||
next_no_show = super::min_prefer_some(next_no_show, Some(no_show_at + clock_drift));
|
||||
}
|
||||
|
||||
is_no_show
|
||||
}).count();
|
||||
})
|
||||
.count();
|
||||
|
||||
(no_shows, next_no_show)
|
||||
}
|
||||
@@ -430,9 +424,8 @@ pub fn tranches_to_approve(
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use bitvec::{bitvec, order::Lsb0 as BitOrderLsb0};
|
||||
use polkadot_primitives::v1::GroupIndex;
|
||||
use bitvec::bitvec;
|
||||
use bitvec::order::Lsb0 as BitOrderLsb0;
|
||||
|
||||
use crate::approval_db;
|
||||
|
||||
@@ -443,7 +436,8 @@ mod tests {
|
||||
session: 0,
|
||||
block_assignments: Default::default(),
|
||||
approvals: Default::default(),
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
let approval_entry = approval_db::v1::ApprovalEntry {
|
||||
tranches: Vec::new(),
|
||||
@@ -452,7 +446,8 @@ mod tests {
|
||||
our_approval_sig: None,
|
||||
backing_group: GroupIndex(0),
|
||||
approved: false,
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
assert!(!check_approval(
|
||||
&candidate,
|
||||
@@ -463,7 +458,8 @@ mod tests {
|
||||
maximum_broadcast: 0,
|
||||
clock_drift: 0,
|
||||
},
|
||||
).is_approved());
|
||||
)
|
||||
.is_approved());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -473,7 +469,8 @@ mod tests {
|
||||
session: 0,
|
||||
block_assignments: Default::default(),
|
||||
approvals: bitvec![BitOrderLsb0, u8; 0; 10],
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
for i in 0..3 {
|
||||
candidate.mark_approval(ValidatorIndex(i));
|
||||
@@ -499,35 +496,27 @@ mod tests {
|
||||
our_approval_sig: None,
|
||||
backing_group: GroupIndex(0),
|
||||
approved: false,
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
assert!(check_approval(
|
||||
&candidate,
|
||||
&approval_entry,
|
||||
RequiredTranches::Exact {
|
||||
needed: 0,
|
||||
tolerated_missing: 0,
|
||||
next_no_show: None,
|
||||
},
|
||||
).is_approved());
|
||||
RequiredTranches::Exact { needed: 0, tolerated_missing: 0, next_no_show: None },
|
||||
)
|
||||
.is_approved());
|
||||
assert!(!check_approval(
|
||||
&candidate,
|
||||
&approval_entry,
|
||||
RequiredTranches::Exact {
|
||||
needed: 1,
|
||||
tolerated_missing: 0,
|
||||
next_no_show: None,
|
||||
},
|
||||
).is_approved());
|
||||
RequiredTranches::Exact { needed: 1, tolerated_missing: 0, next_no_show: None },
|
||||
)
|
||||
.is_approved());
|
||||
assert!(check_approval(
|
||||
&candidate,
|
||||
&approval_entry,
|
||||
RequiredTranches::Exact {
|
||||
needed: 1,
|
||||
tolerated_missing: 2,
|
||||
next_no_show: None,
|
||||
},
|
||||
).is_approved());
|
||||
RequiredTranches::Exact { needed: 1, tolerated_missing: 2, next_no_show: None },
|
||||
)
|
||||
.is_approved());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -537,7 +526,8 @@ mod tests {
|
||||
session: 0,
|
||||
block_assignments: Default::default(),
|
||||
approvals: bitvec![BitOrderLsb0, u8; 0; 10],
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
for i in 0..3 {
|
||||
candidate.mark_approval(ValidatorIndex(i));
|
||||
@@ -563,13 +553,11 @@ mod tests {
|
||||
our_approval_sig: None,
|
||||
backing_group: GroupIndex(0),
|
||||
approved: false,
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
let exact_all = RequiredTranches::Exact {
|
||||
needed: 10,
|
||||
tolerated_missing: 0,
|
||||
next_no_show: None,
|
||||
};
|
||||
let exact_all =
|
||||
RequiredTranches::Exact { needed: 10, tolerated_missing: 0, next_no_show: None };
|
||||
|
||||
let pending_all = RequiredTranches::Pending {
|
||||
considered: 5,
|
||||
@@ -578,44 +566,20 @@ mod tests {
|
||||
clock_drift: 12,
|
||||
};
|
||||
|
||||
assert!(!check_approval(
|
||||
&candidate,
|
||||
&approval_entry,
|
||||
RequiredTranches::All,
|
||||
).is_approved());
|
||||
assert!(!check_approval(&candidate, &approval_entry, RequiredTranches::All,).is_approved());
|
||||
|
||||
assert!(!check_approval(
|
||||
&candidate,
|
||||
&approval_entry,
|
||||
exact_all.clone(),
|
||||
).is_approved());
|
||||
assert!(!check_approval(&candidate, &approval_entry, exact_all.clone(),).is_approved());
|
||||
|
||||
assert!(!check_approval(
|
||||
&candidate,
|
||||
&approval_entry,
|
||||
pending_all.clone(),
|
||||
).is_approved());
|
||||
assert!(!check_approval(&candidate, &approval_entry, pending_all.clone(),).is_approved());
|
||||
|
||||
// This creates a set of 4/10 approvals, which is always an approval.
|
||||
candidate.mark_approval(ValidatorIndex(3));
|
||||
|
||||
assert!(check_approval(
|
||||
&candidate,
|
||||
&approval_entry,
|
||||
RequiredTranches::All,
|
||||
).is_approved());
|
||||
assert!(check_approval(&candidate, &approval_entry, RequiredTranches::All,).is_approved());
|
||||
|
||||
assert!(check_approval(
|
||||
&candidate,
|
||||
&approval_entry,
|
||||
exact_all,
|
||||
).is_approved());
|
||||
assert!(check_approval(&candidate, &approval_entry, exact_all,).is_approved());
|
||||
|
||||
assert!(check_approval(
|
||||
&candidate,
|
||||
&approval_entry,
|
||||
pending_all,
|
||||
).is_approved());
|
||||
assert!(check_approval(&candidate, &approval_entry, pending_all,).is_approved());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -631,15 +595,16 @@ mod tests {
|
||||
our_approval_sig: None,
|
||||
backing_group: GroupIndex(0),
|
||||
approved: false,
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
approval_entry.import_assignment(0,ValidatorIndex(0), block_tick);
|
||||
approval_entry.import_assignment(0,ValidatorIndex(1), block_tick);
|
||||
approval_entry.import_assignment(0, ValidatorIndex(0), block_tick);
|
||||
approval_entry.import_assignment(0, ValidatorIndex(1), block_tick);
|
||||
|
||||
approval_entry.import_assignment(1,ValidatorIndex(2), block_tick + 1);
|
||||
approval_entry.import_assignment(1,ValidatorIndex(3), block_tick + 1);
|
||||
approval_entry.import_assignment(1, ValidatorIndex(2), block_tick + 1);
|
||||
approval_entry.import_assignment(1, ValidatorIndex(3), block_tick + 1);
|
||||
|
||||
approval_entry.import_assignment(2,ValidatorIndex(4), block_tick + 2);
|
||||
approval_entry.import_assignment(2, ValidatorIndex(4), block_tick + 2);
|
||||
|
||||
let approvals = bitvec![BitOrderLsb0, u8; 1; 5];
|
||||
|
||||
@@ -669,7 +634,8 @@ mod tests {
|
||||
our_approval_sig: None,
|
||||
backing_group: GroupIndex(0),
|
||||
approved: false,
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
approval_entry.import_assignment(0, ValidatorIndex(0), block_tick);
|
||||
approval_entry.import_assignment(1, ValidatorIndex(2), block_tick);
|
||||
@@ -708,7 +674,8 @@ mod tests {
|
||||
our_approval_sig: None,
|
||||
backing_group: GroupIndex(0),
|
||||
approved: false,
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
approval_entry.import_assignment(0, ValidatorIndex(0), block_tick);
|
||||
approval_entry.import_assignment(0, ValidatorIndex(1), block_tick);
|
||||
@@ -752,7 +719,8 @@ mod tests {
|
||||
our_approval_sig: None,
|
||||
backing_group: GroupIndex(0),
|
||||
approved: false,
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
approval_entry.import_assignment(0, ValidatorIndex(0), block_tick);
|
||||
approval_entry.import_assignment(0, ValidatorIndex(1), block_tick);
|
||||
@@ -818,7 +786,8 @@ mod tests {
|
||||
our_approval_sig: None,
|
||||
backing_group: GroupIndex(0),
|
||||
approved: false,
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
approval_entry.import_assignment(0, ValidatorIndex(0), block_tick);
|
||||
approval_entry.import_assignment(0, ValidatorIndex(1), block_tick);
|
||||
@@ -868,7 +837,7 @@ mod tests {
|
||||
RequiredTranches::Exact {
|
||||
needed: 2,
|
||||
tolerated_missing: 1,
|
||||
next_no_show: Some(block_tick + 2*no_show_duration + 2),
|
||||
next_no_show: Some(block_tick + 2 * no_show_duration + 2),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -906,7 +875,8 @@ mod tests {
|
||||
our_approval_sig: None,
|
||||
backing_group: GroupIndex(0),
|
||||
approved: false,
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
approval_entry.import_assignment(0, ValidatorIndex(0), block_tick);
|
||||
approval_entry.import_assignment(0, ValidatorIndex(1), block_tick);
|
||||
@@ -935,11 +905,7 @@ mod tests {
|
||||
no_show_duration,
|
||||
needed_approvals,
|
||||
),
|
||||
RequiredTranches::Exact {
|
||||
needed: 2,
|
||||
tolerated_missing: 1,
|
||||
next_no_show: None,
|
||||
},
|
||||
RequiredTranches::Exact { needed: 2, tolerated_missing: 1, next_no_show: None },
|
||||
);
|
||||
|
||||
// Even though tranche 2 has 2 validators, it only covers 1 no-show.
|
||||
@@ -977,11 +943,7 @@ mod tests {
|
||||
no_show_duration,
|
||||
needed_approvals,
|
||||
),
|
||||
RequiredTranches::Exact {
|
||||
needed: 3,
|
||||
tolerated_missing: 2,
|
||||
next_no_show: None,
|
||||
},
|
||||
RequiredTranches::Exact { needed: 3, tolerated_missing: 2, next_no_show: None },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -996,7 +958,8 @@ mod tests {
|
||||
session: 0,
|
||||
block_assignments: Default::default(),
|
||||
approvals: bitvec![BitOrderLsb0, u8; 0; 3],
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
for i in 0..3 {
|
||||
candidate.mark_approval(ValidatorIndex(i));
|
||||
@@ -1015,7 +978,8 @@ mod tests {
|
||||
our_approval_sig: None,
|
||||
backing_group: GroupIndex(0),
|
||||
approved: false,
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
let approvals = bitvec![BitOrderLsb0, u8; 0; 3];
|
||||
|
||||
@@ -1043,14 +1007,14 @@ mod tests {
|
||||
const PREFIX: u32 = 10;
|
||||
|
||||
let test_tranches = vec![
|
||||
vec![], // empty set
|
||||
vec![0], // zero start
|
||||
vec![0, 3], // zero start with gap
|
||||
vec![2], // non-zero start
|
||||
vec![2, 4], // non-zero start with gap
|
||||
vec![0, 1, 2], // zero start with run and no gap
|
||||
vec![2, 3, 4, 8], // non-zero start with run and gap
|
||||
vec![0, 1, 2, 5, 6, 7], // zero start with runs and gap
|
||||
vec![], // empty set
|
||||
vec![0], // zero start
|
||||
vec![0, 3], // zero start with gap
|
||||
vec![2], // non-zero start
|
||||
vec![2, 4], // non-zero start with gap
|
||||
vec![0, 1, 2], // zero start with run and no gap
|
||||
vec![2, 3, 4, 8], // non-zero start with run and gap
|
||||
vec![0, 1, 2, 5, 6, 7], // zero start with runs and gap
|
||||
];
|
||||
|
||||
for test_tranche in test_tranches {
|
||||
@@ -1061,7 +1025,8 @@ mod tests {
|
||||
our_approval_sig: None,
|
||||
assignments: bitvec![BitOrderLsb0, u8; 0; 3],
|
||||
approved: false,
|
||||
}.into();
|
||||
}
|
||||
.into();
|
||||
|
||||
// Populate the requested tranches. The assignemnts aren't inspected in
|
||||
// this test.
|
||||
@@ -1072,10 +1037,8 @@ mod tests {
|
||||
let filled_tranches = filled_tranche_iterator(approval_entry.tranches());
|
||||
|
||||
// Take the first PREFIX entries and map them to their tranche.
|
||||
let tranches: Vec<DelayTranche> = filled_tranches
|
||||
.take(PREFIX as usize)
|
||||
.map(|e| e.0)
|
||||
.collect();
|
||||
let tranches: Vec<DelayTranche> =
|
||||
filled_tranches.take(PREFIX as usize).map(|e| e.0).collect();
|
||||
|
||||
// We expect this sequence to be sequential.
|
||||
let exp_tranches: Vec<DelayTranche> = (0..PREFIX).collect();
|
||||
@@ -1194,7 +1157,11 @@ mod tests {
|
||||
#[test]
|
||||
fn count_no_shows_three_validators_one_almost_late_one_no_show_one_approving() {
|
||||
test_count_no_shows(NoShowTest {
|
||||
assignments: vec![(ValidatorIndex(1), 21), (ValidatorIndex(2), 20), (ValidatorIndex(3), 20)],
|
||||
assignments: vec![
|
||||
(ValidatorIndex(1), 21),
|
||||
(ValidatorIndex(2), 20),
|
||||
(ValidatorIndex(3), 20),
|
||||
],
|
||||
approvals: vec![3],
|
||||
clock_drift: 10,
|
||||
no_show_duration: 10,
|
||||
@@ -1207,7 +1174,11 @@ mod tests {
|
||||
#[test]
|
||||
fn count_no_shows_three_no_show_validators() {
|
||||
test_count_no_shows(NoShowTest {
|
||||
assignments: vec![(ValidatorIndex(1), 20), (ValidatorIndex(2), 20), (ValidatorIndex(3), 20)],
|
||||
assignments: vec![
|
||||
(ValidatorIndex(1), 20),
|
||||
(ValidatorIndex(2), 20),
|
||||
(ValidatorIndex(3), 20),
|
||||
],
|
||||
approvals: vec![],
|
||||
clock_drift: 10,
|
||||
no_show_duration: 10,
|
||||
@@ -1220,7 +1191,11 @@ mod tests {
|
||||
#[test]
|
||||
fn count_no_shows_three_approving_validators() {
|
||||
test_count_no_shows(NoShowTest {
|
||||
assignments: vec![(ValidatorIndex(1), 20), (ValidatorIndex(2), 20), (ValidatorIndex(3), 20)],
|
||||
assignments: vec![
|
||||
(ValidatorIndex(1), 20),
|
||||
(ValidatorIndex(2), 20),
|
||||
(ValidatorIndex(3), 20),
|
||||
],
|
||||
approvals: vec![1, 2, 3],
|
||||
clock_drift: 10,
|
||||
no_show_duration: 10,
|
||||
@@ -1270,17 +1245,17 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_no_shows_validator_index_out_of_approvals_range_is_ignored_as_next_no_show() {
|
||||
test_count_no_shows(NoShowTest {
|
||||
assignments: vec![(ValidatorIndex(1000), 21)],
|
||||
approvals: vec![],
|
||||
clock_drift: 10,
|
||||
no_show_duration: 10,
|
||||
drifted_tick_now: 20,
|
||||
exp_no_shows: 0,
|
||||
exp_next_no_show: None,
|
||||
})
|
||||
}
|
||||
fn count_no_shows_validator_index_out_of_approvals_range_is_ignored_as_next_no_show() {
|
||||
test_count_no_shows(NoShowTest {
|
||||
assignments: vec![(ValidatorIndex(1000), 21)],
|
||||
approvals: vec![],
|
||||
clock_drift: 10,
|
||||
no_show_duration: 10,
|
||||
drifted_tick_now: 20,
|
||||
exp_no_shows: 0,
|
||||
exp_next_no_show: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1318,10 +1293,6 @@ fn depth_0_issued_as_exact_even_when_all() {
|
||||
|
||||
assert_eq!(
|
||||
state.output(0, 10, 10, 20),
|
||||
RequiredTranches::Exact {
|
||||
needed: 0,
|
||||
tolerated_missing: 0,
|
||||
next_no_show: None,
|
||||
},
|
||||
RequiredTranches::Exact { needed: 0, tolerated_missing: 0, next_no_show: None },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,21 +17,22 @@
|
||||
//! Version 1 of the DB schema.
|
||||
|
||||
use kvdb::{DBTransaction, KeyValueDB};
|
||||
use polkadot_node_subsystem::{SubsystemResult, SubsystemError};
|
||||
use polkadot_node_primitives::approval::{DelayTranche, AssignmentCert};
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
use polkadot_node_primitives::approval::{AssignmentCert, DelayTranche};
|
||||
use polkadot_node_subsystem::{SubsystemError, SubsystemResult};
|
||||
use polkadot_primitives::v1::{
|
||||
ValidatorIndex, GroupIndex, CandidateReceipt, SessionIndex, CoreIndex,
|
||||
BlockNumber, Hash, CandidateHash, ValidatorSignature,
|
||||
BlockNumber, CandidateHash, CandidateReceipt, CoreIndex, GroupIndex, Hash, SessionIndex,
|
||||
ValidatorIndex, ValidatorSignature,
|
||||
};
|
||||
use sp_consensus_slots::Slot;
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
use bitvec::{vec::BitVec, order::Lsb0 as BitOrderLsb0};
|
||||
use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec};
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
|
||||
use crate::backend::{Backend, BackendWriteOp};
|
||||
use crate::persisted_entries;
|
||||
use crate::{
|
||||
backend::{Backend, BackendWriteOp},
|
||||
persisted_entries,
|
||||
};
|
||||
|
||||
const STORED_BLOCKS_KEY: &[u8] = b"Approvals_StoredBlocks";
|
||||
|
||||
@@ -48,10 +49,7 @@ impl DbBackend {
|
||||
/// Create a new [`DbBackend`] with the supplied key-value store and
|
||||
/// config.
|
||||
pub fn new(db: Arc<dyn KeyValueDB>, config: Config) -> Self {
|
||||
DbBackend {
|
||||
inner: db,
|
||||
config,
|
||||
}
|
||||
DbBackend { inner: db, config }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,22 +58,17 @@ impl Backend for DbBackend {
|
||||
&self,
|
||||
block_hash: &Hash,
|
||||
) -> SubsystemResult<Option<persisted_entries::BlockEntry>> {
|
||||
load_block_entry(&*self.inner, &self.config, block_hash)
|
||||
.map(|e| e.map(Into::into))
|
||||
load_block_entry(&*self.inner, &self.config, block_hash).map(|e| e.map(Into::into))
|
||||
}
|
||||
|
||||
fn load_candidate_entry(
|
||||
&self,
|
||||
candidate_hash: &CandidateHash,
|
||||
) -> SubsystemResult<Option<persisted_entries::CandidateEntry>> {
|
||||
load_candidate_entry(&*self.inner, &self.config, candidate_hash)
|
||||
.map(|e| e.map(Into::into))
|
||||
load_candidate_entry(&*self.inner, &self.config, candidate_hash).map(|e| e.map(Into::into))
|
||||
}
|
||||
|
||||
fn load_blocks_at_height(
|
||||
&self,
|
||||
block_height: &BlockNumber
|
||||
) -> SubsystemResult<Vec<Hash>> {
|
||||
fn load_blocks_at_height(&self, block_height: &BlockNumber) -> SubsystemResult<Vec<Hash>> {
|
||||
load_blocks_at_height(&*self.inner, &self.config, block_height)
|
||||
}
|
||||
|
||||
@@ -89,7 +82,8 @@ impl Backend for DbBackend {
|
||||
|
||||
/// Atomically write the list of operations, with later operations taking precedence over prior.
|
||||
fn write<I>(&mut self, ops: I) -> SubsystemResult<()>
|
||||
where I: IntoIterator<Item = BackendWriteOp>
|
||||
where
|
||||
I: IntoIterator<Item = BackendWriteOp>,
|
||||
{
|
||||
let mut tx = DBTransaction::new();
|
||||
for op in ops {
|
||||
@@ -100,20 +94,13 @@ impl Backend for DbBackend {
|
||||
&STORED_BLOCKS_KEY,
|
||||
stored_block_range.encode(),
|
||||
);
|
||||
}
|
||||
},
|
||||
BackendWriteOp::WriteBlocksAtHeight(h, blocks) => {
|
||||
tx.put_vec(
|
||||
self.config.col_data,
|
||||
&blocks_at_height_key(h),
|
||||
blocks.encode(),
|
||||
);
|
||||
}
|
||||
tx.put_vec(self.config.col_data, &blocks_at_height_key(h), blocks.encode());
|
||||
},
|
||||
BackendWriteOp::DeleteBlocksAtHeight(h) => {
|
||||
tx.delete(
|
||||
self.config.col_data,
|
||||
&blocks_at_height_key(h),
|
||||
);
|
||||
}
|
||||
tx.delete(self.config.col_data, &blocks_at_height_key(h));
|
||||
},
|
||||
BackendWriteOp::WriteBlockEntry(block_entry) => {
|
||||
let block_entry: BlockEntry = block_entry.into();
|
||||
tx.put_vec(
|
||||
@@ -121,13 +108,10 @@ impl Backend for DbBackend {
|
||||
&block_entry_key(&block_entry.block_hash),
|
||||
block_entry.encode(),
|
||||
);
|
||||
}
|
||||
},
|
||||
BackendWriteOp::DeleteBlockEntry(hash) => {
|
||||
tx.delete(
|
||||
self.config.col_data,
|
||||
&block_entry_key(&hash),
|
||||
);
|
||||
}
|
||||
tx.delete(self.config.col_data, &block_entry_key(&hash));
|
||||
},
|
||||
BackendWriteOp::WriteCandidateEntry(candidate_entry) => {
|
||||
let candidate_entry: CandidateEntry = candidate_entry.into();
|
||||
tx.put_vec(
|
||||
@@ -135,13 +119,10 @@ impl Backend for DbBackend {
|
||||
&candidate_entry_key(&candidate_entry.candidate.hash()),
|
||||
candidate_entry.encode(),
|
||||
);
|
||||
}
|
||||
},
|
||||
BackendWriteOp::DeleteCandidateEntry(candidate_hash) => {
|
||||
tx.delete(
|
||||
self.config.col_data,
|
||||
&candidate_entry_key(&candidate_hash),
|
||||
);
|
||||
}
|
||||
tx.delete(self.config.col_data, &candidate_entry_key(&candidate_hash));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,13 +238,14 @@ impl std::error::Error for Error {}
|
||||
/// Result alias for DB errors.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub(crate) fn load_decode<D: Decode>(store: &dyn KeyValueDB, col_data: u32, key: &[u8]) -> Result<Option<D>>
|
||||
{
|
||||
pub(crate) fn load_decode<D: Decode>(
|
||||
store: &dyn KeyValueDB,
|
||||
col_data: u32,
|
||||
key: &[u8],
|
||||
) -> Result<Option<D>> {
|
||||
match store.get(col_data, key)? {
|
||||
None => Ok(None),
|
||||
Some(raw) => D::decode(&mut &raw[..])
|
||||
.map(Some)
|
||||
.map_err(Into::into),
|
||||
Some(raw) => D::decode(&mut &raw[..]).map(Some).map_err(Into::into),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,7 +290,6 @@ pub fn load_all_blocks(store: &dyn KeyValueDB, config: &Config) -> SubsystemResu
|
||||
let blocks = load_blocks_at_height(store, config, &height)?;
|
||||
hashes.extend(blocks);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Ok(hashes)
|
||||
|
||||
@@ -16,21 +16,19 @@
|
||||
|
||||
//! Tests for the aux-schema of approval voting.
|
||||
|
||||
use super::*;
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
use polkadot_primitives::v1::Id as ParaId;
|
||||
use super::{DbBackend, StoredBlockRange, *};
|
||||
use crate::{
|
||||
backend::{Backend, OverlayedBackend},
|
||||
ops::{add_block_entry, canonicalize, force_approve, NewCandidateInfo},
|
||||
};
|
||||
use kvdb::KeyValueDB;
|
||||
use super::{DbBackend, StoredBlockRange};
|
||||
use crate::backend::{Backend, OverlayedBackend};
|
||||
use crate::ops::{NewCandidateInfo, add_block_entry, force_approve, canonicalize};
|
||||
use polkadot_primitives::v1::Id as ParaId;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
const DATA_COL: u32 = 0;
|
||||
const NUM_COLUMNS: u32 = 1;
|
||||
|
||||
const TEST_CONFIG: Config = Config {
|
||||
col_data: DATA_COL,
|
||||
};
|
||||
const TEST_CONFIG: Config = Config { col_data: DATA_COL };
|
||||
|
||||
fn make_db() -> (DbBackend, Arc<dyn KeyValueDB>) {
|
||||
let db_writer: Arc<dyn KeyValueDB> = Arc::new(kvdb_memorydb::create(NUM_COLUMNS));
|
||||
@@ -80,26 +78,25 @@ fn read_write() {
|
||||
let range = StoredBlockRange(10, 20);
|
||||
let at_height = vec![hash_a, hash_b];
|
||||
|
||||
let block_entry = make_block_entry(
|
||||
hash_a,
|
||||
Default::default(),
|
||||
1,
|
||||
vec![(CoreIndex(0), candidate_hash)],
|
||||
);
|
||||
let block_entry =
|
||||
make_block_entry(hash_a, Default::default(), 1, vec![(CoreIndex(0), candidate_hash)]);
|
||||
|
||||
let candidate_entry = CandidateEntry {
|
||||
candidate: Default::default(),
|
||||
session: 5,
|
||||
block_assignments: vec![
|
||||
(hash_a, ApprovalEntry {
|
||||
block_assignments: vec![(
|
||||
hash_a,
|
||||
ApprovalEntry {
|
||||
tranches: Vec::new(),
|
||||
backing_group: GroupIndex(1),
|
||||
our_assignment: None,
|
||||
our_approval_sig: None,
|
||||
assignments: Default::default(),
|
||||
approved: false,
|
||||
})
|
||||
].into_iter().collect(),
|
||||
},
|
||||
)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
approvals: Default::default(),
|
||||
};
|
||||
|
||||
@@ -114,7 +111,10 @@ fn read_write() {
|
||||
|
||||
assert_eq!(load_stored_blocks(store.as_ref(), &TEST_CONFIG).unwrap(), Some(range));
|
||||
assert_eq!(load_blocks_at_height(store.as_ref(), &TEST_CONFIG, &1).unwrap(), at_height);
|
||||
assert_eq!(load_block_entry(store.as_ref(), &TEST_CONFIG, &hash_a).unwrap(), Some(block_entry.into()));
|
||||
assert_eq!(
|
||||
load_block_entry(store.as_ref(), &TEST_CONFIG, &hash_a).unwrap(),
|
||||
Some(block_entry.into())
|
||||
);
|
||||
assert_eq!(
|
||||
load_candidate_entry(store.as_ref(), &TEST_CONFIG, &candidate_hash).unwrap(),
|
||||
Some(candidate_entry.into()),
|
||||
@@ -129,7 +129,9 @@ fn read_write() {
|
||||
|
||||
assert!(load_blocks_at_height(store.as_ref(), &TEST_CONFIG, &1).unwrap().is_empty());
|
||||
assert!(load_block_entry(store.as_ref(), &TEST_CONFIG, &hash_a).unwrap().is_none());
|
||||
assert!(load_candidate_entry(store.as_ref(), &TEST_CONFIG, &candidate_hash).unwrap().is_none());
|
||||
assert!(load_candidate_entry(store.as_ref(), &TEST_CONFIG, &candidate_hash)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -165,47 +167,48 @@ fn add_block_entry_works() {
|
||||
let n_validators = 10;
|
||||
|
||||
let mut new_candidate_info = HashMap::new();
|
||||
new_candidate_info.insert(candidate_hash_a, NewCandidateInfo::new(
|
||||
candidate_receipt_a,
|
||||
GroupIndex(0),
|
||||
None,
|
||||
));
|
||||
new_candidate_info
|
||||
.insert(candidate_hash_a, NewCandidateInfo::new(candidate_receipt_a, GroupIndex(0), None));
|
||||
|
||||
let mut overlay_db = OverlayedBackend::new(&db);
|
||||
add_block_entry(
|
||||
&mut overlay_db,
|
||||
block_entry_a.clone().into(),
|
||||
n_validators,
|
||||
|h| new_candidate_info.get(h).map(|x| x.clone()),
|
||||
).unwrap();
|
||||
add_block_entry(&mut overlay_db, block_entry_a.clone().into(), n_validators, |h| {
|
||||
new_candidate_info.get(h).map(|x| x.clone())
|
||||
})
|
||||
.unwrap();
|
||||
let write_ops = overlay_db.into_write_ops();
|
||||
db.write(write_ops).unwrap();
|
||||
|
||||
new_candidate_info.insert(candidate_hash_b, NewCandidateInfo::new(
|
||||
candidate_receipt_b,
|
||||
GroupIndex(1),
|
||||
None,
|
||||
));
|
||||
new_candidate_info
|
||||
.insert(candidate_hash_b, NewCandidateInfo::new(candidate_receipt_b, GroupIndex(1), None));
|
||||
|
||||
let mut overlay_db = OverlayedBackend::new(&db);
|
||||
add_block_entry(
|
||||
&mut overlay_db,
|
||||
block_entry_b.clone().into(),
|
||||
n_validators,
|
||||
|h| new_candidate_info.get(h).map(|x| x.clone()),
|
||||
).unwrap();
|
||||
add_block_entry(&mut overlay_db, block_entry_b.clone().into(), n_validators, |h| {
|
||||
new_candidate_info.get(h).map(|x| x.clone())
|
||||
})
|
||||
.unwrap();
|
||||
let write_ops = overlay_db.into_write_ops();
|
||||
db.write(write_ops).unwrap();
|
||||
|
||||
assert_eq!(load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_a).unwrap(), Some(block_entry_a.into()));
|
||||
assert_eq!(load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_b).unwrap(), Some(block_entry_b.into()));
|
||||
assert_eq!(
|
||||
load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_a).unwrap(),
|
||||
Some(block_entry_a.into())
|
||||
);
|
||||
assert_eq!(
|
||||
load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_b).unwrap(),
|
||||
Some(block_entry_b.into())
|
||||
);
|
||||
|
||||
let candidate_entry_a = load_candidate_entry(store.as_ref(), &TEST_CONFIG, &candidate_hash_a)
|
||||
.unwrap().unwrap();
|
||||
assert_eq!(candidate_entry_a.block_assignments.keys().collect::<Vec<_>>(), vec![&block_hash_a, &block_hash_b]);
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
candidate_entry_a.block_assignments.keys().collect::<Vec<_>>(),
|
||||
vec![&block_hash_a, &block_hash_b]
|
||||
);
|
||||
|
||||
let candidate_entry_b = load_candidate_entry(store.as_ref(), &TEST_CONFIG, &candidate_hash_b)
|
||||
.unwrap().unwrap();
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(candidate_entry_b.block_assignments.keys().collect::<Vec<_>>(), vec![&block_hash_b]);
|
||||
}
|
||||
|
||||
@@ -217,44 +220,30 @@ fn add_block_entry_adds_child() {
|
||||
let block_hash_a = Hash::repeat_byte(2);
|
||||
let block_hash_b = Hash::repeat_byte(69);
|
||||
|
||||
let mut block_entry_a = make_block_entry(
|
||||
block_hash_a,
|
||||
parent_hash,
|
||||
1,
|
||||
Vec::new(),
|
||||
);
|
||||
let mut block_entry_a = make_block_entry(block_hash_a, parent_hash, 1, Vec::new());
|
||||
|
||||
let block_entry_b = make_block_entry(
|
||||
block_hash_b,
|
||||
block_hash_a,
|
||||
2,
|
||||
Vec::new(),
|
||||
);
|
||||
let block_entry_b = make_block_entry(block_hash_b, block_hash_a, 2, Vec::new());
|
||||
|
||||
let n_validators = 10;
|
||||
|
||||
let mut overlay_db = OverlayedBackend::new(&db);
|
||||
add_block_entry(
|
||||
&mut overlay_db,
|
||||
block_entry_a.clone().into(),
|
||||
n_validators,
|
||||
|_| None,
|
||||
).unwrap();
|
||||
add_block_entry(&mut overlay_db, block_entry_a.clone().into(), n_validators, |_| None).unwrap();
|
||||
|
||||
add_block_entry(
|
||||
&mut overlay_db,
|
||||
block_entry_b.clone().into(),
|
||||
n_validators,
|
||||
|_| None,
|
||||
).unwrap();
|
||||
add_block_entry(&mut overlay_db, block_entry_b.clone().into(), n_validators, |_| None).unwrap();
|
||||
|
||||
let write_ops = overlay_db.into_write_ops();
|
||||
db.write(write_ops).unwrap();
|
||||
|
||||
block_entry_a.children.push(block_hash_b);
|
||||
|
||||
assert_eq!(load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_a).unwrap(), Some(block_entry_a.into()));
|
||||
assert_eq!(load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_b).unwrap(), Some(block_entry_b.into()));
|
||||
assert_eq!(
|
||||
load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_a).unwrap(),
|
||||
Some(block_entry_a.into())
|
||||
);
|
||||
assert_eq!(
|
||||
load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_b).unwrap(),
|
||||
Some(block_entry_b.into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -305,12 +294,8 @@ fn canonicalize_works() {
|
||||
|
||||
let block_entry_a = make_block_entry(block_hash_a, genesis, 1, Vec::new());
|
||||
let block_entry_b1 = make_block_entry(block_hash_b1, block_hash_a, 2, Vec::new());
|
||||
let block_entry_b2 = make_block_entry(
|
||||
block_hash_b2,
|
||||
block_hash_a,
|
||||
2,
|
||||
vec![(CoreIndex(0), cand_hash_1)],
|
||||
);
|
||||
let block_entry_b2 =
|
||||
make_block_entry(block_hash_b2, block_hash_a, 2, vec![(CoreIndex(0), cand_hash_1)]);
|
||||
let block_entry_c1 = make_block_entry(block_hash_c1, block_hash_b1, 3, Vec::new());
|
||||
let block_entry_c2 = make_block_entry(
|
||||
block_hash_c2,
|
||||
@@ -324,44 +309,27 @@ fn canonicalize_works() {
|
||||
4,
|
||||
vec![(CoreIndex(0), cand_hash_3), (CoreIndex(1), cand_hash_4)],
|
||||
);
|
||||
let block_entry_d2 = make_block_entry(
|
||||
block_hash_d2,
|
||||
block_hash_c2,
|
||||
4,
|
||||
vec![(CoreIndex(0), cand_hash_5)],
|
||||
);
|
||||
let block_entry_d2 =
|
||||
make_block_entry(block_hash_d2, block_hash_c2, 4, vec![(CoreIndex(0), cand_hash_5)]);
|
||||
|
||||
let candidate_info = {
|
||||
let mut candidate_info = HashMap::new();
|
||||
candidate_info.insert(cand_hash_1, NewCandidateInfo::new(
|
||||
candidate_receipt_genesis,
|
||||
GroupIndex(1),
|
||||
None,
|
||||
));
|
||||
candidate_info.insert(
|
||||
cand_hash_1,
|
||||
NewCandidateInfo::new(candidate_receipt_genesis, GroupIndex(1), None),
|
||||
);
|
||||
|
||||
candidate_info.insert(cand_hash_2, NewCandidateInfo::new(
|
||||
candidate_receipt_a,
|
||||
GroupIndex(2),
|
||||
None,
|
||||
));
|
||||
candidate_info
|
||||
.insert(cand_hash_2, NewCandidateInfo::new(candidate_receipt_a, GroupIndex(2), None));
|
||||
|
||||
candidate_info.insert(cand_hash_3, NewCandidateInfo::new(
|
||||
candidate_receipt_b,
|
||||
GroupIndex(3),
|
||||
None,
|
||||
));
|
||||
candidate_info
|
||||
.insert(cand_hash_3, NewCandidateInfo::new(candidate_receipt_b, GroupIndex(3), None));
|
||||
|
||||
candidate_info.insert(cand_hash_4, NewCandidateInfo::new(
|
||||
candidate_receipt_b1,
|
||||
GroupIndex(4),
|
||||
None,
|
||||
));
|
||||
candidate_info
|
||||
.insert(cand_hash_4, NewCandidateInfo::new(candidate_receipt_b1, GroupIndex(4), None));
|
||||
|
||||
candidate_info.insert(cand_hash_5, NewCandidateInfo::new(
|
||||
candidate_receipt_c1,
|
||||
GroupIndex(5),
|
||||
None,
|
||||
));
|
||||
candidate_info
|
||||
.insert(cand_hash_5, NewCandidateInfo::new(candidate_receipt_c1, GroupIndex(5), None));
|
||||
|
||||
candidate_info
|
||||
};
|
||||
@@ -379,12 +347,10 @@ fn canonicalize_works() {
|
||||
|
||||
let mut overlay_db = OverlayedBackend::new(&db);
|
||||
for block_entry in blocks {
|
||||
add_block_entry(
|
||||
&mut overlay_db,
|
||||
block_entry.into(),
|
||||
n_validators,
|
||||
|h| candidate_info.get(h).map(|x| x.clone()),
|
||||
).unwrap();
|
||||
add_block_entry(&mut overlay_db, block_entry.into(), n_validators, |h| {
|
||||
candidate_info.get(h).map(|x| x.clone())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
let write_ops = overlay_db.into_write_ops();
|
||||
db.write(write_ops).unwrap();
|
||||
@@ -393,9 +359,11 @@ fn canonicalize_works() {
|
||||
for (c_hash, in_blocks) in expected {
|
||||
let (entry, in_blocks) = match in_blocks {
|
||||
None => {
|
||||
assert!(load_candidate_entry(store.as_ref(), &TEST_CONFIG, &c_hash).unwrap().is_none());
|
||||
assert!(load_candidate_entry(store.as_ref(), &TEST_CONFIG, &c_hash)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
continue
|
||||
}
|
||||
},
|
||||
Some(i) => (
|
||||
load_candidate_entry(store.as_ref(), &TEST_CONFIG, &c_hash).unwrap().unwrap(),
|
||||
i,
|
||||
@@ -414,13 +382,13 @@ fn canonicalize_works() {
|
||||
for (hash, with_candidates) in expected {
|
||||
let (entry, with_candidates) = match with_candidates {
|
||||
None => {
|
||||
assert!(load_block_entry(store.as_ref(), &TEST_CONFIG, &hash).unwrap().is_none());
|
||||
assert!(load_block_entry(store.as_ref(), &TEST_CONFIG, &hash)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
continue
|
||||
}
|
||||
Some(i) => (
|
||||
load_block_entry(store.as_ref(), &TEST_CONFIG, &hash).unwrap().unwrap(),
|
||||
i,
|
||||
),
|
||||
},
|
||||
Some(i) =>
|
||||
(load_block_entry(store.as_ref(), &TEST_CONFIG, &hash).unwrap().unwrap(), i),
|
||||
};
|
||||
|
||||
assert_eq!(entry.candidates.len(), with_candidates.len());
|
||||
@@ -454,7 +422,10 @@ fn canonicalize_works() {
|
||||
let write_ops = overlay_db.into_write_ops();
|
||||
db.write(write_ops).unwrap();
|
||||
|
||||
assert_eq!(load_stored_blocks(store.as_ref(), &TEST_CONFIG).unwrap().unwrap(), StoredBlockRange(4, 5));
|
||||
assert_eq!(
|
||||
load_stored_blocks(store.as_ref(), &TEST_CONFIG).unwrap().unwrap(),
|
||||
StoredBlockRange(4, 5)
|
||||
);
|
||||
|
||||
check_candidates_in_store(vec![
|
||||
(cand_hash_1, None),
|
||||
@@ -489,25 +460,31 @@ fn force_approve_works() {
|
||||
let single_candidate_vec = vec![(CoreIndex(0), candidate_hash)];
|
||||
let candidate_info = {
|
||||
let mut candidate_info = HashMap::new();
|
||||
candidate_info.insert(candidate_hash, NewCandidateInfo::new(
|
||||
make_candidate(1.into(), Default::default()),
|
||||
GroupIndex(1),
|
||||
None,
|
||||
));
|
||||
candidate_info.insert(
|
||||
candidate_hash,
|
||||
NewCandidateInfo::new(
|
||||
make_candidate(1.into(), Default::default()),
|
||||
GroupIndex(1),
|
||||
None,
|
||||
),
|
||||
);
|
||||
|
||||
candidate_info
|
||||
};
|
||||
|
||||
|
||||
let block_hash_a = Hash::repeat_byte(1); // 1
|
||||
let block_hash_b = Hash::repeat_byte(2);
|
||||
let block_hash_c = Hash::repeat_byte(3);
|
||||
let block_hash_d = Hash::repeat_byte(4); // 4
|
||||
|
||||
let block_entry_a = make_block_entry(block_hash_a, Default::default(), 1, single_candidate_vec.clone());
|
||||
let block_entry_b = make_block_entry(block_hash_b, block_hash_a, 2, single_candidate_vec.clone());
|
||||
let block_entry_c = make_block_entry(block_hash_c, block_hash_b, 3, single_candidate_vec.clone());
|
||||
let block_entry_d = make_block_entry(block_hash_d, block_hash_c, 4, single_candidate_vec.clone());
|
||||
let block_entry_a =
|
||||
make_block_entry(block_hash_a, Default::default(), 1, single_candidate_vec.clone());
|
||||
let block_entry_b =
|
||||
make_block_entry(block_hash_b, block_hash_a, 2, single_candidate_vec.clone());
|
||||
let block_entry_c =
|
||||
make_block_entry(block_hash_c, block_hash_b, 3, single_candidate_vec.clone());
|
||||
let block_entry_d =
|
||||
make_block_entry(block_hash_d, block_hash_c, 4, single_candidate_vec.clone());
|
||||
|
||||
let blocks = vec![
|
||||
block_entry_a.clone(),
|
||||
@@ -518,41 +495,36 @@ fn force_approve_works() {
|
||||
|
||||
let mut overlay_db = OverlayedBackend::new(&db);
|
||||
for block_entry in blocks {
|
||||
add_block_entry(
|
||||
&mut overlay_db,
|
||||
block_entry.into(),
|
||||
n_validators,
|
||||
|h| candidate_info.get(h).map(|x| x.clone()),
|
||||
).unwrap();
|
||||
add_block_entry(&mut overlay_db, block_entry.into(), n_validators, |h| {
|
||||
candidate_info.get(h).map(|x| x.clone())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
let approved_hashes = force_approve(&mut overlay_db, block_hash_d, 2).unwrap();
|
||||
let approved_hashes = force_approve(&mut overlay_db, block_hash_d, 2).unwrap();
|
||||
let write_ops = overlay_db.into_write_ops();
|
||||
db.write(write_ops).unwrap();
|
||||
|
||||
assert!(load_block_entry(
|
||||
store.as_ref(),
|
||||
&TEST_CONFIG,
|
||||
&block_hash_a,
|
||||
).unwrap().unwrap().approved_bitfield.all());
|
||||
assert!(load_block_entry(
|
||||
store.as_ref(),
|
||||
&TEST_CONFIG,
|
||||
&block_hash_b,
|
||||
).unwrap().unwrap().approved_bitfield.all());
|
||||
assert!(load_block_entry(
|
||||
store.as_ref(),
|
||||
&TEST_CONFIG,
|
||||
&block_hash_c,
|
||||
).unwrap().unwrap().approved_bitfield.not_any());
|
||||
assert!(load_block_entry(
|
||||
store.as_ref(),
|
||||
&TEST_CONFIG,
|
||||
&block_hash_d,
|
||||
).unwrap().unwrap().approved_bitfield.not_any());
|
||||
assert_eq!(
|
||||
approved_hashes,
|
||||
vec![block_hash_b, block_hash_a],
|
||||
);
|
||||
assert!(load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_a,)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.approved_bitfield
|
||||
.all());
|
||||
assert!(load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_b,)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.approved_bitfield
|
||||
.all());
|
||||
assert!(load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_c,)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.approved_bitfield
|
||||
.not_any());
|
||||
assert!(load_block_entry(store.as_ref(), &TEST_CONFIG, &block_hash_d,)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.approved_bitfield
|
||||
.not_any());
|
||||
assert_eq!(approved_hashes, vec![block_hash_b, block_hash_a],);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -566,60 +538,27 @@ fn load_all_blocks_works() {
|
||||
|
||||
let block_number = 10;
|
||||
|
||||
let block_entry_a = make_block_entry(
|
||||
block_hash_a,
|
||||
parent_hash,
|
||||
block_number,
|
||||
vec![],
|
||||
);
|
||||
let block_entry_a = make_block_entry(block_hash_a, parent_hash, block_number, vec![]);
|
||||
|
||||
let block_entry_b = make_block_entry(
|
||||
block_hash_b,
|
||||
parent_hash,
|
||||
block_number,
|
||||
vec![],
|
||||
);
|
||||
let block_entry_b = make_block_entry(block_hash_b, parent_hash, block_number, vec![]);
|
||||
|
||||
let block_entry_c = make_block_entry(
|
||||
block_hash_c,
|
||||
block_hash_a,
|
||||
block_number + 1,
|
||||
vec![],
|
||||
);
|
||||
let block_entry_c = make_block_entry(block_hash_c, block_hash_a, block_number + 1, vec![]);
|
||||
|
||||
let n_validators = 10;
|
||||
|
||||
let mut overlay_db = OverlayedBackend::new(&db);
|
||||
add_block_entry(
|
||||
&mut overlay_db,
|
||||
block_entry_a.clone().into(),
|
||||
n_validators,
|
||||
|_| None
|
||||
).unwrap();
|
||||
add_block_entry(&mut overlay_db, block_entry_a.clone().into(), n_validators, |_| None).unwrap();
|
||||
|
||||
// add C before B to test sorting.
|
||||
add_block_entry(
|
||||
&mut overlay_db,
|
||||
block_entry_c.clone().into(),
|
||||
n_validators,
|
||||
|_| None
|
||||
).unwrap();
|
||||
add_block_entry(&mut overlay_db, block_entry_c.clone().into(), n_validators, |_| None).unwrap();
|
||||
|
||||
add_block_entry(
|
||||
&mut overlay_db,
|
||||
block_entry_b.clone().into(),
|
||||
n_validators,
|
||||
|_| None
|
||||
).unwrap();
|
||||
add_block_entry(&mut overlay_db, block_entry_b.clone().into(), n_validators, |_| None).unwrap();
|
||||
|
||||
let write_ops = overlay_db.into_write_ops();
|
||||
db.write(write_ops).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
load_all_blocks(
|
||||
store.as_ref(),
|
||||
&TEST_CONFIG
|
||||
).unwrap(),
|
||||
load_all_blocks(store.as_ref(), &TEST_CONFIG).unwrap(),
|
||||
vec![block_hash_a, block_hash_b, block_hash_c],
|
||||
)
|
||||
}
|
||||
|
||||
@@ -21,13 +21,15 @@
|
||||
//! [`Backend`], maintaining consistency between queries and temporary writes,
|
||||
//! before any commit to the underlying storage is made.
|
||||
|
||||
use polkadot_node_subsystem::{SubsystemResult};
|
||||
use polkadot_node_subsystem::SubsystemResult;
|
||||
use polkadot_primitives::v1::{BlockNumber, CandidateHash, Hash};
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::approval_db::v1::StoredBlockRange;
|
||||
use super::persisted_entries::{BlockEntry, CandidateEntry};
|
||||
use super::{
|
||||
approval_db::v1::StoredBlockRange,
|
||||
persisted_entries::{BlockEntry, CandidateEntry},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BackendWriteOp {
|
||||
@@ -45,7 +47,10 @@ pub trait Backend {
|
||||
/// Load a block entry from the DB.
|
||||
fn load_block_entry(&self, hash: &Hash) -> SubsystemResult<Option<BlockEntry>>;
|
||||
/// Load a candidate entry from the DB.
|
||||
fn load_candidate_entry(&self, candidate_hash: &CandidateHash) -> SubsystemResult<Option<CandidateEntry>>;
|
||||
fn load_candidate_entry(
|
||||
&self,
|
||||
candidate_hash: &CandidateHash,
|
||||
) -> SubsystemResult<Option<CandidateEntry>>;
|
||||
/// Load all blocks at a specific height.
|
||||
fn load_blocks_at_height(&self, height: &BlockNumber) -> SubsystemResult<Vec<Hash>>;
|
||||
/// Load all block from the DB.
|
||||
@@ -54,7 +59,8 @@ pub trait Backend {
|
||||
fn load_stored_blocks(&self) -> SubsystemResult<Option<StoredBlockRange>>;
|
||||
/// Atomically write the list of operations, with later operations taking precedence over prior.
|
||||
fn write<I>(&mut self, ops: I) -> SubsystemResult<()>
|
||||
where I: IntoIterator<Item = BackendWriteOp>;
|
||||
where
|
||||
I: IntoIterator<Item = BackendWriteOp>;
|
||||
}
|
||||
|
||||
/// An in-memory overlay over the backend.
|
||||
@@ -128,7 +134,10 @@ impl<'a, B: 'a + Backend> OverlayedBackend<'a, B> {
|
||||
self.inner.load_block_entry(hash)
|
||||
}
|
||||
|
||||
pub fn load_candidate_entry(&self, candidate_hash: &CandidateHash) -> SubsystemResult<Option<CandidateEntry>> {
|
||||
pub fn load_candidate_entry(
|
||||
&self,
|
||||
candidate_hash: &CandidateHash,
|
||||
) -> SubsystemResult<Option<CandidateEntry>> {
|
||||
if let Some(val) = self.candidate_entries.get(&candidate_hash) {
|
||||
return Ok(val.clone())
|
||||
}
|
||||
|
||||
@@ -16,21 +16,20 @@
|
||||
|
||||
//! Assignment criteria VRF generation and checking.
|
||||
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
use polkadot_node_primitives::approval::{
|
||||
self as approval_types, AssignmentCert, AssignmentCertKind, DelayTranche, RelayVRFStory,
|
||||
};
|
||||
use polkadot_primitives::v1::{
|
||||
CoreIndex, ValidatorIndex, SessionInfo, AssignmentPair, AssignmentId, GroupIndex, CandidateHash,
|
||||
AssignmentId, AssignmentPair, CandidateHash, CoreIndex, GroupIndex, SessionInfo, ValidatorIndex,
|
||||
};
|
||||
use sc_keystore::LocalKeystore;
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
use sp_application_crypto::Public;
|
||||
|
||||
use merlin::Transcript;
|
||||
use schnorrkel::vrf::VRFInOut;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::{hash_map::Entry, HashMap};
|
||||
|
||||
use super::LOG_TARGET;
|
||||
|
||||
@@ -88,10 +87,7 @@ impl From<OurAssignment> for crate::approval_db::v1::OurAssignment {
|
||||
}
|
||||
}
|
||||
|
||||
fn relay_vrf_modulo_transcript(
|
||||
relay_vrf_story: RelayVRFStory,
|
||||
sample: u32,
|
||||
) -> Transcript {
|
||||
fn relay_vrf_modulo_transcript(relay_vrf_story: RelayVRFStory, sample: u32) -> Transcript {
|
||||
// combine the relay VRF story with a sample number.
|
||||
let mut t = Transcript::new(approval_types::RELAY_VRF_MODULO_CONTEXT);
|
||||
t.append_message(b"RC-VRF", &relay_vrf_story.0);
|
||||
@@ -100,10 +96,7 @@ fn relay_vrf_modulo_transcript(
|
||||
t
|
||||
}
|
||||
|
||||
fn relay_vrf_modulo_core(
|
||||
vrf_in_out: &VRFInOut,
|
||||
n_cores: u32,
|
||||
) -> CoreIndex {
|
||||
fn relay_vrf_modulo_core(vrf_in_out: &VRFInOut, n_cores: u32) -> CoreIndex {
|
||||
let bytes: [u8; 4] = vrf_in_out.make_bytes(approval_types::CORE_RANDOMNESS_CONTEXT);
|
||||
|
||||
// interpret as little-endian u32.
|
||||
@@ -111,10 +104,7 @@ fn relay_vrf_modulo_core(
|
||||
CoreIndex(random_core)
|
||||
}
|
||||
|
||||
fn relay_vrf_delay_transcript(
|
||||
relay_vrf_story: RelayVRFStory,
|
||||
core_index: CoreIndex,
|
||||
) -> Transcript {
|
||||
fn relay_vrf_delay_transcript(relay_vrf_story: RelayVRFStory, core_index: CoreIndex) -> Transcript {
|
||||
let mut t = Transcript::new(approval_types::RELAY_VRF_DELAY_CONTEXT);
|
||||
t.append_message(b"RC-VRF", &relay_vrf_story.0);
|
||||
core_index.0.using_encoded(|s| t.append_message(b"core", s));
|
||||
@@ -129,7 +119,8 @@ fn relay_vrf_delay_tranche(
|
||||
let bytes: [u8; 4] = vrf_in_out.make_bytes(approval_types::TRANCHE_RANDOMNESS_CONTEXT);
|
||||
|
||||
// interpret as little-endian u32 and reduce by the number of tranches.
|
||||
let wide_tranche = u32::from_le_bytes(bytes) % (num_delay_tranches + zeroth_delay_tranche_width);
|
||||
let wide_tranche =
|
||||
u32::from_le_bytes(bytes) % (num_delay_tranches + zeroth_delay_tranche_width);
|
||||
|
||||
// Consolidate early results to tranche zero so tranche zero is extra wide.
|
||||
wide_tranche.saturating_sub(zeroth_delay_tranche_width)
|
||||
@@ -202,12 +193,7 @@ impl AssignmentCriteria for RealAssignmentCriteria {
|
||||
config: &Config,
|
||||
leaving_cores: Vec<(CandidateHash, CoreIndex, GroupIndex)>,
|
||||
) -> HashMap<CoreIndex, OurAssignment> {
|
||||
compute_assignments(
|
||||
keystore,
|
||||
relay_vrf_story,
|
||||
config,
|
||||
leaving_cores,
|
||||
)
|
||||
compute_assignments(keystore, relay_vrf_story, config, leaving_cores)
|
||||
}
|
||||
|
||||
fn check_assignment_cert(
|
||||
@@ -246,13 +232,16 @@ pub(crate) fn compute_assignments(
|
||||
config: &Config,
|
||||
leaving_cores: impl IntoIterator<Item = (CandidateHash, CoreIndex, GroupIndex)> + Clone,
|
||||
) -> HashMap<CoreIndex, OurAssignment> {
|
||||
if config.n_cores == 0 || config.assignment_keys.is_empty() || config.validator_groups.is_empty() {
|
||||
if config.n_cores == 0 ||
|
||||
config.assignment_keys.is_empty() ||
|
||||
config.validator_groups.is_empty()
|
||||
{
|
||||
return HashMap::new()
|
||||
}
|
||||
|
||||
let (index, assignments_key): (ValidatorIndex, AssignmentPair) = {
|
||||
let key = config.assignment_keys.iter().enumerate()
|
||||
.find_map(|(i, p)| match keystore.key_pair(p) {
|
||||
let key = config.assignment_keys.iter().enumerate().find_map(|(i, p)| {
|
||||
match keystore.key_pair(p) {
|
||||
Ok(Some(pair)) => Some((ValidatorIndex(i as _), pair)),
|
||||
Ok(None) => None,
|
||||
Err(sc_keystore::Error::Unavailable) => None,
|
||||
@@ -260,8 +249,9 @@ pub(crate) fn compute_assignments(
|
||||
Err(e) => {
|
||||
tracing::warn!(target: LOG_TARGET, "Encountered keystore error: {:?}", e);
|
||||
None
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
match key {
|
||||
None => return Default::default(),
|
||||
@@ -270,7 +260,8 @@ pub(crate) fn compute_assignments(
|
||||
};
|
||||
|
||||
// Ignore any cores where the assigned group is our own.
|
||||
let leaving_cores = leaving_cores.into_iter()
|
||||
let leaving_cores = leaving_cores
|
||||
.into_iter()
|
||||
.filter(|&(_, _, ref g)| !is_in_backing_group(&config.validator_groups, index, *g))
|
||||
.map(|(c_hash, core, _)| (c_hash, core))
|
||||
.collect::<Vec<_>>();
|
||||
@@ -322,8 +313,8 @@ fn compute_relay_vrf_modulo_assignments(
|
||||
relay_vrf_modulo_transcript(relay_vrf_story.clone(), rvm_sample),
|
||||
|vrf_in_out| {
|
||||
*core = relay_vrf_modulo_core(&vrf_in_out, config.n_cores);
|
||||
if let Some((candidate_hash, _))
|
||||
= leaving_cores.clone().into_iter().find(|(_, c)| c == core)
|
||||
if let Some((candidate_hash, _)) =
|
||||
leaving_cores.clone().into_iter().find(|(_, c)| c == core)
|
||||
{
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
@@ -338,7 +329,7 @@ fn compute_relay_vrf_modulo_assignments(
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
};
|
||||
|
||||
@@ -347,7 +338,10 @@ fn compute_relay_vrf_modulo_assignments(
|
||||
// has been executed.
|
||||
let cert = AssignmentCert {
|
||||
kind: AssignmentCertKind::RelayVRFModulo { sample: rvm_sample },
|
||||
vrf: (approval_types::VRFOutput(vrf_in_out.to_output()), approval_types::VRFProof(vrf_proof)),
|
||||
vrf: (
|
||||
approval_types::VRFOutput(vrf_in_out.to_output()),
|
||||
approval_types::VRFProof(vrf_proof),
|
||||
),
|
||||
};
|
||||
|
||||
// All assignments of type RelayVRFModulo have tranche 0.
|
||||
@@ -370,9 +364,8 @@ fn compute_relay_vrf_delay_assignments(
|
||||
assignments: &mut HashMap<CoreIndex, OurAssignment>,
|
||||
) {
|
||||
for (candidate_hash, core) in leaving_cores {
|
||||
let (vrf_in_out, vrf_proof, _) = assignments_key.vrf_sign(
|
||||
relay_vrf_delay_transcript(relay_vrf_story.clone(), core),
|
||||
);
|
||||
let (vrf_in_out, vrf_proof, _) =
|
||||
assignments_key.vrf_sign(relay_vrf_delay_transcript(relay_vrf_story.clone(), core));
|
||||
|
||||
let tranche = relay_vrf_delay_tranche(
|
||||
&vrf_in_out,
|
||||
@@ -382,24 +375,26 @@ fn compute_relay_vrf_delay_assignments(
|
||||
|
||||
let cert = AssignmentCert {
|
||||
kind: AssignmentCertKind::RelayVRFDelay { core_index: core },
|
||||
vrf: (approval_types::VRFOutput(vrf_in_out.to_output()), approval_types::VRFProof(vrf_proof)),
|
||||
vrf: (
|
||||
approval_types::VRFOutput(vrf_in_out.to_output()),
|
||||
approval_types::VRFProof(vrf_proof),
|
||||
),
|
||||
};
|
||||
|
||||
let our_assignment = OurAssignment {
|
||||
cert,
|
||||
tranche,
|
||||
validator_index,
|
||||
triggered: false,
|
||||
};
|
||||
let our_assignment = OurAssignment { cert, tranche, validator_index, triggered: false };
|
||||
|
||||
let used = match assignments.entry(core) {
|
||||
Entry::Vacant(e) => { let _ = e.insert(our_assignment); true }
|
||||
Entry::Occupied(mut e) => if e.get().tranche > our_assignment.tranche {
|
||||
e.insert(our_assignment);
|
||||
Entry::Vacant(e) => {
|
||||
let _ = e.insert(our_assignment);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
},
|
||||
Entry::Occupied(mut e) =>
|
||||
if e.get().tranche > our_assignment.tranche {
|
||||
e.insert(our_assignment);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
},
|
||||
};
|
||||
|
||||
if used {
|
||||
@@ -425,7 +420,7 @@ impl std::fmt::Display for InvalidAssignment {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for InvalidAssignment { }
|
||||
impl std::error::Error for InvalidAssignment {}
|
||||
|
||||
/// Checks the crypto of an assignment cert. Failure conditions:
|
||||
/// * Validator index out of bounds
|
||||
@@ -446,7 +441,8 @@ pub(crate) fn check_assignment_cert(
|
||||
assignment: &AssignmentCert,
|
||||
backing_group: GroupIndex,
|
||||
) -> Result<DelayTranche, InvalidAssignment> {
|
||||
let validator_public = config.assignment_keys
|
||||
let validator_public = config
|
||||
.assignment_keys
|
||||
.get(validator_index.0 as usize)
|
||||
.ok_or(InvalidAssignment)?;
|
||||
|
||||
@@ -454,34 +450,33 @@ pub(crate) fn check_assignment_cert(
|
||||
.map_err(|_| InvalidAssignment)?;
|
||||
|
||||
if claimed_core_index.0 >= config.n_cores {
|
||||
return Err(InvalidAssignment);
|
||||
return Err(InvalidAssignment)
|
||||
}
|
||||
|
||||
// Check that the validator was not part of the backing group
|
||||
// and not already assigned.
|
||||
let is_in_backing = is_in_backing_group(
|
||||
&config.validator_groups,
|
||||
validator_index,
|
||||
backing_group,
|
||||
);
|
||||
let is_in_backing =
|
||||
is_in_backing_group(&config.validator_groups, validator_index, backing_group);
|
||||
|
||||
if is_in_backing {
|
||||
return Err(InvalidAssignment);
|
||||
return Err(InvalidAssignment)
|
||||
}
|
||||
|
||||
let &(ref vrf_output, ref vrf_proof) = &assignment.vrf;
|
||||
match assignment.kind {
|
||||
AssignmentCertKind::RelayVRFModulo { sample } => {
|
||||
if sample >= config.relay_vrf_modulo_samples {
|
||||
return Err(InvalidAssignment);
|
||||
return Err(InvalidAssignment)
|
||||
}
|
||||
|
||||
let (vrf_in_out, _) = public.vrf_verify_extra(
|
||||
relay_vrf_modulo_transcript(relay_vrf_story, sample),
|
||||
&vrf_output.0,
|
||||
&vrf_proof.0,
|
||||
assigned_core_transcript(claimed_core_index),
|
||||
).map_err(|_| InvalidAssignment)?;
|
||||
let (vrf_in_out, _) = public
|
||||
.vrf_verify_extra(
|
||||
relay_vrf_modulo_transcript(relay_vrf_story, sample),
|
||||
&vrf_output.0,
|
||||
&vrf_proof.0,
|
||||
assigned_core_transcript(claimed_core_index),
|
||||
)
|
||||
.map_err(|_| InvalidAssignment)?;
|
||||
|
||||
// ensure that the `vrf_in_out` actually gives us the claimed core.
|
||||
if relay_vrf_modulo_core(&vrf_in_out, config.n_cores) == claimed_core_index {
|
||||
@@ -489,24 +484,26 @@ pub(crate) fn check_assignment_cert(
|
||||
} else {
|
||||
Err(InvalidAssignment)
|
||||
}
|
||||
}
|
||||
},
|
||||
AssignmentCertKind::RelayVRFDelay { core_index } => {
|
||||
if core_index != claimed_core_index {
|
||||
return Err(InvalidAssignment);
|
||||
return Err(InvalidAssignment)
|
||||
}
|
||||
|
||||
let (vrf_in_out, _) = public.vrf_verify(
|
||||
relay_vrf_delay_transcript(relay_vrf_story, core_index),
|
||||
&vrf_output.0,
|
||||
&vrf_proof.0,
|
||||
).map_err(|_| InvalidAssignment)?;
|
||||
let (vrf_in_out, _) = public
|
||||
.vrf_verify(
|
||||
relay_vrf_delay_transcript(relay_vrf_story, core_index),
|
||||
&vrf_output.0,
|
||||
&vrf_proof.0,
|
||||
)
|
||||
.map_err(|_| InvalidAssignment)?;
|
||||
|
||||
Ok(relay_vrf_delay_tranche(
|
||||
&vrf_in_out,
|
||||
config.n_delay_tranches,
|
||||
config.zeroth_delay_tranche_width,
|
||||
))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,22 +518,22 @@ fn is_in_backing_group(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use sp_keystore::CryptoStore;
|
||||
use sp_keyring::sr25519::Keyring as Sr25519Keyring;
|
||||
use polkadot_node_primitives::approval::{VRFOutput, VRFProof};
|
||||
use polkadot_primitives::v1::{Hash, ASSIGNMENT_KEY_TYPE_ID};
|
||||
use sp_application_crypto::sr25519;
|
||||
use sp_core::crypto::Pair as PairT;
|
||||
use polkadot_primitives::v1::{ASSIGNMENT_KEY_TYPE_ID, Hash};
|
||||
use polkadot_node_primitives::approval::{VRFOutput, VRFProof};
|
||||
use sp_keyring::sr25519::Keyring as Sr25519Keyring;
|
||||
use sp_keystore::CryptoStore;
|
||||
|
||||
// sets up a keystore with the given keyring accounts.
|
||||
async fn make_keystore(accounts: &[Sr25519Keyring]) -> LocalKeystore {
|
||||
let store = LocalKeystore::in_memory();
|
||||
|
||||
for s in accounts.iter().copied().map(|k| k.to_seed()) {
|
||||
store.sr25519_generate_new(
|
||||
ASSIGNMENT_KEY_TYPE_ID,
|
||||
Some(s.as_str()),
|
||||
).await.unwrap();
|
||||
store
|
||||
.sr25519_generate_new(ASSIGNMENT_KEY_TYPE_ID, Some(s.as_str()))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
store
|
||||
@@ -546,12 +543,15 @@ mod tests {
|
||||
assignment_keys_plus_random(accounts, 0)
|
||||
}
|
||||
|
||||
fn assignment_keys_plus_random(accounts: &[Sr25519Keyring], random: usize) -> Vec<AssignmentId> {
|
||||
let gen_random = (0..random).map(|_|
|
||||
AssignmentId::from(sr25519::Pair::generate().0.public())
|
||||
);
|
||||
fn assignment_keys_plus_random(
|
||||
accounts: &[Sr25519Keyring],
|
||||
random: usize,
|
||||
) -> Vec<AssignmentId> {
|
||||
let gen_random =
|
||||
(0..random).map(|_| AssignmentId::from(sr25519::Pair::generate().0.public()));
|
||||
|
||||
accounts.iter()
|
||||
accounts
|
||||
.iter()
|
||||
.map(|k| AssignmentId::from(k.public()))
|
||||
.chain(gen_random)
|
||||
.collect()
|
||||
@@ -562,12 +562,14 @@ mod tests {
|
||||
let big_groups = n_validators % n_groups;
|
||||
let scraps = n_groups * size;
|
||||
|
||||
(0..n_groups).map(|i| {
|
||||
(i * size .. (i + 1) *size)
|
||||
.chain(if i < big_groups { Some(scraps + i) } else { None })
|
||||
.map(|j| ValidatorIndex(j as _))
|
||||
.collect::<Vec<_>>()
|
||||
}).collect()
|
||||
(0..n_groups)
|
||||
.map(|i| {
|
||||
(i * size..(i + 1) * size)
|
||||
.chain(if i < big_groups { Some(scraps + i) } else { None })
|
||||
.map(|j| ValidatorIndex(j as _))
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
// used for generating assignments where the validity of the VRF doesn't matter.
|
||||
@@ -581,9 +583,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn assignments_produced_for_non_backing() {
|
||||
let keystore = futures::executor::block_on(
|
||||
make_keystore(&[Sr25519Keyring::Alice])
|
||||
);
|
||||
let keystore = futures::executor::block_on(make_keystore(&[Sr25519Keyring::Alice]));
|
||||
|
||||
let c_a = CandidateHash(Hash::repeat_byte(0));
|
||||
let c_b = CandidateHash(Hash::repeat_byte(1));
|
||||
@@ -598,7 +598,10 @@ mod tests {
|
||||
Sr25519Keyring::Bob,
|
||||
Sr25519Keyring::Charlie,
|
||||
]),
|
||||
validator_groups: vec![vec![ValidatorIndex(0)], vec![ValidatorIndex(1), ValidatorIndex(2)]],
|
||||
validator_groups: vec![
|
||||
vec![ValidatorIndex(0)],
|
||||
vec![ValidatorIndex(1), ValidatorIndex(2)],
|
||||
],
|
||||
n_cores: 2,
|
||||
zeroth_delay_tranche_width: 10,
|
||||
relay_vrf_modulo_samples: 3,
|
||||
@@ -615,9 +618,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn assign_to_nonzero_core() {
|
||||
let keystore = futures::executor::block_on(
|
||||
make_keystore(&[Sr25519Keyring::Alice])
|
||||
);
|
||||
let keystore = futures::executor::block_on(make_keystore(&[Sr25519Keyring::Alice]));
|
||||
|
||||
let c_a = CandidateHash(Hash::repeat_byte(0));
|
||||
let c_b = CandidateHash(Hash::repeat_byte(1));
|
||||
@@ -632,7 +633,10 @@ mod tests {
|
||||
Sr25519Keyring::Bob,
|
||||
Sr25519Keyring::Charlie,
|
||||
]),
|
||||
validator_groups: vec![vec![ValidatorIndex(0)], vec![ValidatorIndex(1), ValidatorIndex(2)]],
|
||||
validator_groups: vec![
|
||||
vec![ValidatorIndex(0)],
|
||||
vec![ValidatorIndex(1), ValidatorIndex(2)],
|
||||
],
|
||||
n_cores: 2,
|
||||
zeroth_delay_tranche_width: 10,
|
||||
relay_vrf_modulo_samples: 3,
|
||||
@@ -647,9 +651,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn succeeds_empty_for_0_cores() {
|
||||
let keystore = futures::executor::block_on(
|
||||
make_keystore(&[Sr25519Keyring::Alice])
|
||||
);
|
||||
let keystore = futures::executor::block_on(make_keystore(&[Sr25519Keyring::Alice]));
|
||||
|
||||
let relay_vrf_story = RelayVRFStory([42u8; 32]);
|
||||
let assignments = compute_assignments(
|
||||
@@ -689,14 +691,15 @@ mod tests {
|
||||
rotation_offset: usize,
|
||||
f: impl Fn(&mut MutatedAssignment) -> Option<bool>, // None = skip
|
||||
) {
|
||||
let keystore = futures::executor::block_on(
|
||||
make_keystore(&[Sr25519Keyring::Alice])
|
||||
);
|
||||
let keystore = futures::executor::block_on(make_keystore(&[Sr25519Keyring::Alice]));
|
||||
|
||||
let group_for_core = |i| GroupIndex(((i + rotation_offset) % n_cores) as _);
|
||||
|
||||
let config = Config {
|
||||
assignment_keys: assignment_keys_plus_random(&[Sr25519Keyring::Alice], n_validators - 1),
|
||||
assignment_keys: assignment_keys_plus_random(
|
||||
&[Sr25519Keyring::Alice],
|
||||
n_validators - 1,
|
||||
),
|
||||
validator_groups: basic_groups(n_validators, n_cores),
|
||||
n_cores: n_cores as u32,
|
||||
zeroth_delay_tranche_width: 10,
|
||||
@@ -710,11 +713,13 @@ mod tests {
|
||||
relay_vrf_story.clone(),
|
||||
&config,
|
||||
(0..n_cores)
|
||||
.map(|i| (
|
||||
CandidateHash(Hash::repeat_byte(i as u8)),
|
||||
CoreIndex(i as u32),
|
||||
group_for_core(i),
|
||||
))
|
||||
.map(|i| {
|
||||
(
|
||||
CandidateHash(Hash::repeat_byte(i as u8)),
|
||||
CoreIndex(i as u32),
|
||||
group_for_core(i),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
@@ -743,7 +748,8 @@ mod tests {
|
||||
relay_vrf_story.clone(),
|
||||
&mutated.cert,
|
||||
mutated.group,
|
||||
).is_ok();
|
||||
)
|
||||
.is_ok();
|
||||
|
||||
assert_eq!(expected, is_good)
|
||||
}
|
||||
@@ -787,7 +793,7 @@ mod tests {
|
||||
AssignmentCertKind::RelayVRFDelay { .. } => {
|
||||
m.cert.vrf = garbage_vrf();
|
||||
Some(false)
|
||||
}
|
||||
},
|
||||
_ => None, // skip everything else.
|
||||
}
|
||||
});
|
||||
@@ -800,7 +806,7 @@ mod tests {
|
||||
AssignmentCertKind::RelayVRFModulo { .. } => {
|
||||
m.cert.vrf = garbage_vrf();
|
||||
Some(false)
|
||||
}
|
||||
},
|
||||
_ => None, // skip everything else.
|
||||
}
|
||||
});
|
||||
@@ -813,7 +819,7 @@ mod tests {
|
||||
AssignmentCertKind::RelayVRFModulo { sample } => {
|
||||
m.config.relay_vrf_modulo_samples = sample;
|
||||
Some(false)
|
||||
}
|
||||
},
|
||||
_ => None, // skip everything else.
|
||||
}
|
||||
});
|
||||
@@ -826,7 +832,7 @@ mod tests {
|
||||
AssignmentCertKind::RelayVRFDelay { .. } => {
|
||||
m.core = CoreIndex((m.core.0 + 1) % 100);
|
||||
Some(false)
|
||||
}
|
||||
},
|
||||
_ => None, // skip everything else.
|
||||
}
|
||||
});
|
||||
@@ -839,7 +845,7 @@ mod tests {
|
||||
AssignmentCertKind::RelayVRFModulo { .. } => {
|
||||
m.core = CoreIndex((m.core.0 + 1) % 100);
|
||||
Some(false)
|
||||
}
|
||||
},
|
||||
_ => None, // skip everything else.
|
||||
}
|
||||
});
|
||||
|
||||
@@ -28,43 +28,42 @@
|
||||
//!
|
||||
//! We maintain a rolling window of session indices. This starts as empty
|
||||
|
||||
use polkadot_node_subsystem::{
|
||||
overseer,
|
||||
messages::{
|
||||
RuntimeApiMessage, RuntimeApiRequest, ChainApiMessage, ApprovalDistributionMessage,
|
||||
ChainSelectionMessage,
|
||||
},
|
||||
SubsystemContext, SubsystemError, SubsystemResult,
|
||||
};
|
||||
use polkadot_node_subsystem_util::determine_new_blocks;
|
||||
use polkadot_node_subsystem_util::rolling_session_window::{
|
||||
RollingSessionWindow, SessionWindowUpdate,
|
||||
};
|
||||
use polkadot_primitives::v1::{
|
||||
Hash, SessionIndex, CandidateEvent, Header, CandidateHash,
|
||||
CandidateReceipt, CoreIndex, GroupIndex, BlockNumber, ConsensusLog,
|
||||
};
|
||||
use polkadot_node_jaeger as jaeger;
|
||||
use polkadot_node_primitives::approval::{
|
||||
self as approval_types, BlockApprovalMeta, RelayVRFStory,
|
||||
};
|
||||
use polkadot_node_jaeger as jaeger;
|
||||
use polkadot_node_subsystem::{
|
||||
messages::{
|
||||
ApprovalDistributionMessage, ChainApiMessage, ChainSelectionMessage, RuntimeApiMessage,
|
||||
RuntimeApiRequest,
|
||||
},
|
||||
overseer, SubsystemContext, SubsystemError, SubsystemResult,
|
||||
};
|
||||
use polkadot_node_subsystem_util::{
|
||||
determine_new_blocks,
|
||||
rolling_session_window::{RollingSessionWindow, SessionWindowUpdate},
|
||||
};
|
||||
use polkadot_primitives::v1::{
|
||||
BlockNumber, CandidateEvent, CandidateHash, CandidateReceipt, ConsensusLog, CoreIndex,
|
||||
GroupIndex, Hash, Header, SessionIndex,
|
||||
};
|
||||
use sc_keystore::LocalKeystore;
|
||||
use sp_consensus_slots::Slot;
|
||||
|
||||
use futures::prelude::*;
|
||||
use futures::channel::oneshot;
|
||||
use bitvec::order::Lsb0 as BitOrderLsb0;
|
||||
use futures::{channel::oneshot, prelude::*};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::{collections::HashMap, convert::TryFrom};
|
||||
|
||||
use super::approval_db::v1;
|
||||
use crate::backend::{Backend, OverlayedBackend};
|
||||
use crate::persisted_entries::CandidateEntry;
|
||||
use crate::criteria::{AssignmentCriteria, OurAssignment};
|
||||
use crate::time::{slot_number_to_tick, Tick};
|
||||
use crate::{
|
||||
backend::{Backend, OverlayedBackend},
|
||||
criteria::{AssignmentCriteria, OurAssignment},
|
||||
persisted_entries::CandidateEntry,
|
||||
time::{slot_number_to_tick, Tick},
|
||||
};
|
||||
|
||||
use super::{LOG_TARGET, State};
|
||||
use super::{State, LOG_TARGET};
|
||||
|
||||
struct ImportedBlockInfo {
|
||||
included_candidates: Vec<(CandidateHash, CandidateReceipt, CoreIndex, GroupIndex)>,
|
||||
@@ -99,7 +98,8 @@ async fn imported_block_info(
|
||||
ctx.send_message(RuntimeApiMessage::Request(
|
||||
block_hash,
|
||||
RuntimeApiRequest::CandidateEvents(c_tx),
|
||||
)).await;
|
||||
))
|
||||
.await;
|
||||
|
||||
let events: Vec<CandidateEvent> = match c_rx.await {
|
||||
Ok(Ok(events)) => events,
|
||||
@@ -107,11 +107,14 @@ async fn imported_block_info(
|
||||
Err(_) => return Ok(None),
|
||||
};
|
||||
|
||||
events.into_iter().filter_map(|e| match e {
|
||||
CandidateEvent::CandidateIncluded(receipt, _, core, group)
|
||||
=> Some((receipt.hash(), receipt, core, group)),
|
||||
_ => None,
|
||||
}).collect()
|
||||
events
|
||||
.into_iter()
|
||||
.filter_map(|e| match e {
|
||||
CandidateEvent::CandidateIncluded(receipt, _, core, group) =>
|
||||
Some((receipt.hash(), receipt, core, group)),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
// fetch session. ignore blocks that are too old, but unless sessions are really
|
||||
@@ -121,7 +124,8 @@ async fn imported_block_info(
|
||||
ctx.send_message(RuntimeApiMessage::Request(
|
||||
block_header.parent_hash,
|
||||
RuntimeApiRequest::SessionIndexForChild(s_tx),
|
||||
)).await;
|
||||
))
|
||||
.await;
|
||||
|
||||
let session_index = match s_rx.await {
|
||||
Ok(Ok(s)) => s,
|
||||
@@ -130,10 +134,14 @@ async fn imported_block_info(
|
||||
};
|
||||
|
||||
if env.session_window.earliest_session().map_or(true, |e| session_index < e) {
|
||||
tracing::debug!(target: LOG_TARGET, "Block {} is from ancient session {}. Skipping",
|
||||
block_hash, session_index);
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
"Block {} is from ancient session {}. Skipping",
|
||||
block_hash,
|
||||
session_index
|
||||
);
|
||||
|
||||
return Ok(None);
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
session_index
|
||||
@@ -162,7 +170,8 @@ async fn imported_block_info(
|
||||
ctx.send_message(RuntimeApiMessage::Request(
|
||||
block_hash,
|
||||
RuntimeApiRequest::CurrentBabeEpoch(s_tx),
|
||||
)).await;
|
||||
))
|
||||
.await;
|
||||
|
||||
match s_rx.await {
|
||||
Ok(Ok(s)) => s,
|
||||
@@ -180,8 +189,8 @@ async fn imported_block_info(
|
||||
block_hash,
|
||||
);
|
||||
|
||||
return Ok(None);
|
||||
}
|
||||
return Ok(None)
|
||||
},
|
||||
};
|
||||
|
||||
let (assignments, slot, relay_vrf_story) = {
|
||||
@@ -201,7 +210,8 @@ async fn imported_block_info(
|
||||
&env.keystore,
|
||||
relay_vrf.clone(),
|
||||
&crate::criteria::Config::from(session_info),
|
||||
included_candidates.iter()
|
||||
included_candidates
|
||||
.iter()
|
||||
.map(|(c_hash, _, core, group)| (*c_hash, *core, *group))
|
||||
.collect(),
|
||||
);
|
||||
@@ -210,7 +220,7 @@ async fn imported_block_info(
|
||||
},
|
||||
Err(_) => return Ok(None),
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
@@ -218,16 +228,12 @@ async fn imported_block_info(
|
||||
block_hash,
|
||||
);
|
||||
|
||||
return Ok(None);
|
||||
}
|
||||
return Ok(None)
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
n_assignments = assignments.len(),
|
||||
"Produced assignments"
|
||||
);
|
||||
tracing::trace!(target: LOG_TARGET, n_assignments = assignments.len(), "Produced assignments");
|
||||
|
||||
let force_approve =
|
||||
block_header.digest.convert_first(|l| match ConsensusLog::from_digest_item(l) {
|
||||
@@ -241,7 +247,7 @@ async fn imported_block_info(
|
||||
);
|
||||
|
||||
Some(num)
|
||||
}
|
||||
},
|
||||
Ok(Some(_)) => None,
|
||||
Ok(None) => None,
|
||||
Err(err) => {
|
||||
@@ -253,7 +259,7 @@ async fn imported_block_info(
|
||||
);
|
||||
|
||||
None
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Ok(Some(ImportedBlockInfo {
|
||||
@@ -291,8 +297,7 @@ pub(crate) async fn handle_new_head(
|
||||
db: &mut OverlayedBackend<'_, impl Backend>,
|
||||
head: Hash,
|
||||
finalized_number: &Option<BlockNumber>,
|
||||
) -> SubsystemResult<Vec<BlockImportedCandidates>>
|
||||
{
|
||||
) -> SubsystemResult<Vec<BlockImportedCandidates>> {
|
||||
// Update session info based on most recent head.
|
||||
|
||||
let mut span = jaeger::Span::new(head, "approval-checking-import");
|
||||
@@ -309,13 +314,13 @@ pub(crate) async fn handle_new_head(
|
||||
e,
|
||||
);
|
||||
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
return Ok(Vec::new())
|
||||
},
|
||||
Ok(None) => {
|
||||
tracing::warn!(target: LOG_TARGET, "Missing header for new head {}", head);
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
Ok(Some(h)) => h
|
||||
return Ok(Vec::new())
|
||||
},
|
||||
Ok(Some(h)) => h,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -329,15 +334,15 @@ pub(crate) async fn handle_new_head(
|
||||
);
|
||||
|
||||
return Ok(Vec::new())
|
||||
}
|
||||
},
|
||||
Ok(a @ SessionWindowUpdate::Advanced { .. }) => {
|
||||
tracing::info!(
|
||||
target: LOG_TARGET,
|
||||
update = ?a,
|
||||
"Advanced session window for approvals",
|
||||
);
|
||||
}
|
||||
Ok(_) => {}
|
||||
},
|
||||
Ok(_) => {},
|
||||
}
|
||||
|
||||
// If we've just started the node and haven't yet received any finality notifications,
|
||||
@@ -351,12 +356,14 @@ pub(crate) async fn handle_new_head(
|
||||
&header,
|
||||
lower_bound_number,
|
||||
)
|
||||
.map_err(|e| SubsystemError::with_origin("approval-voting", e))
|
||||
.await?;
|
||||
.map_err(|e| SubsystemError::with_origin("approval-voting", e))
|
||||
.await?;
|
||||
|
||||
span.add_uint_tag("new-blocks", new_blocks.len() as u64);
|
||||
|
||||
if new_blocks.is_empty() { return Ok(Vec::new()) }
|
||||
if new_blocks.is_empty() {
|
||||
return Ok(Vec::new())
|
||||
}
|
||||
|
||||
let mut approval_meta: Vec<BlockApprovalMeta> = Vec::with_capacity(new_blocks.len());
|
||||
let mut imported_candidates = Vec::with_capacity(new_blocks.len());
|
||||
@@ -376,9 +383,11 @@ pub(crate) async fn handle_new_head(
|
||||
None => {
|
||||
// It's possible that we've lost a race with finality.
|
||||
let (tx, rx) = oneshot::channel();
|
||||
ctx.send_message(
|
||||
ChainApiMessage::FinalizedBlockHash(block_header.number.clone(), tx)
|
||||
).await;
|
||||
ctx.send_message(ChainApiMessage::FinalizedBlockHash(
|
||||
block_header.number.clone(),
|
||||
tx,
|
||||
))
|
||||
.await;
|
||||
|
||||
let lost_to_finality = match rx.await {
|
||||
Ok(Ok(Some(h))) if h != block_hash => true,
|
||||
@@ -395,7 +404,7 @@ pub(crate) async fn handle_new_head(
|
||||
);
|
||||
}
|
||||
|
||||
return Ok(Vec::new());
|
||||
return Ok(Vec::new())
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -420,7 +429,9 @@ pub(crate) async fn handle_new_head(
|
||||
force_approve,
|
||||
} = imported_block_info;
|
||||
|
||||
let session_info = state.session_window.session_info(session_index)
|
||||
let session_info = state
|
||||
.session_window
|
||||
.session_info(session_index)
|
||||
.expect("imported_block_info requires session to be available; qed");
|
||||
|
||||
let (block_tick, no_show_duration) = {
|
||||
@@ -432,7 +443,8 @@ pub(crate) async fn handle_new_head(
|
||||
(block_tick, no_show_duration)
|
||||
};
|
||||
let needed_approvals = session_info.needed_approvals;
|
||||
let validator_group_lens: Vec<usize> = session_info.validator_groups.iter().map(|v| v.len()).collect();
|
||||
let validator_group_lens: Vec<usize> =
|
||||
session_info.validator_groups.iter().map(|v| v.len()).collect();
|
||||
// insta-approve candidates on low-node testnets:
|
||||
// cf. https://github.com/paritytech/polkadot/issues/2411
|
||||
let num_candidates = included_candidates.len();
|
||||
@@ -447,10 +459,10 @@ pub(crate) async fn handle_new_head(
|
||||
} else {
|
||||
let mut result = bitvec::bitvec![BitOrderLsb0, u8; 0; num_candidates];
|
||||
for (i, &(_, _, _, backing_group)) in included_candidates.iter().enumerate() {
|
||||
let backing_group_size = validator_group_lens.get(backing_group.0 as usize)
|
||||
.copied()
|
||||
.unwrap_or(0);
|
||||
let needed_approvals = usize::try_from(needed_approvals).expect("usize is at least u32; qed");
|
||||
let backing_group_size =
|
||||
validator_group_lens.get(backing_group.0 as usize).copied().unwrap_or(0);
|
||||
let needed_approvals =
|
||||
usize::try_from(needed_approvals).expect("usize is at least u32; qed");
|
||||
if n_validators.saturating_sub(backing_group_size) < needed_approvals {
|
||||
result.set(i, true);
|
||||
}
|
||||
@@ -481,19 +493,16 @@ pub(crate) async fn handle_new_head(
|
||||
session: session_index,
|
||||
slot,
|
||||
relay_vrf_story: relay_vrf_story.0,
|
||||
candidates: included_candidates.iter()
|
||||
.map(|(hash, _, core, _)| (*core, *hash)).collect(),
|
||||
candidates: included_candidates
|
||||
.iter()
|
||||
.map(|(hash, _, core, _)| (*core, *hash))
|
||||
.collect(),
|
||||
approved_bitfield,
|
||||
children: Vec::new(),
|
||||
};
|
||||
|
||||
if let Some(up_to) = force_approve {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
?block_hash,
|
||||
up_to,
|
||||
"Enacting force-approve",
|
||||
);
|
||||
tracing::debug!(target: LOG_TARGET, ?block_hash, up_to, "Enacting force-approve",);
|
||||
|
||||
let approved_hashes = crate::ops::force_approve(db, block_hash, up_to)
|
||||
.map_err(|e| SubsystemError::with_origin("approval-voting", e))?;
|
||||
@@ -511,19 +520,19 @@ pub(crate) async fn handle_new_head(
|
||||
"Writing BlockEntry",
|
||||
);
|
||||
|
||||
let candidate_entries = crate::ops::add_block_entry(
|
||||
db,
|
||||
block_entry.into(),
|
||||
n_validators,
|
||||
|candidate_hash| {
|
||||
included_candidates.iter().find(|(hash, _, _, _)| candidate_hash == hash)
|
||||
.map(|(_, receipt, core, backing_group)| super::ops::NewCandidateInfo::new(
|
||||
receipt.clone(),
|
||||
*backing_group,
|
||||
assignments.get(core).map(|a| a.clone().into()),
|
||||
))
|
||||
}
|
||||
).map_err(|e| SubsystemError::with_origin("approval-voting", e))?;
|
||||
let candidate_entries =
|
||||
crate::ops::add_block_entry(db, block_entry.into(), n_validators, |candidate_hash| {
|
||||
included_candidates.iter().find(|(hash, _, _, _)| candidate_hash == hash).map(
|
||||
|(_, receipt, core, backing_group)| {
|
||||
super::ops::NewCandidateInfo::new(
|
||||
receipt.clone(),
|
||||
*backing_group,
|
||||
assignments.get(core).map(|a| a.clone().into()),
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
.map_err(|e| SubsystemError::with_origin("approval-voting", e))?;
|
||||
approval_meta.push(BlockApprovalMeta {
|
||||
hash: block_hash,
|
||||
number: block_header.number,
|
||||
@@ -532,18 +541,16 @@ pub(crate) async fn handle_new_head(
|
||||
slot,
|
||||
});
|
||||
|
||||
imported_candidates.push(
|
||||
BlockImportedCandidates {
|
||||
block_hash,
|
||||
block_number: block_header.number,
|
||||
block_tick,
|
||||
no_show_duration,
|
||||
imported_candidates: candidate_entries
|
||||
.into_iter()
|
||||
.map(|(h, e)| (h, e.into()))
|
||||
.collect(),
|
||||
}
|
||||
);
|
||||
imported_candidates.push(BlockImportedCandidates {
|
||||
block_hash,
|
||||
block_number: block_header.number,
|
||||
block_tick,
|
||||
no_show_duration,
|
||||
imported_candidates: candidate_entries
|
||||
.into_iter()
|
||||
.map(|(h, e)| (h, e.into()))
|
||||
.collect(),
|
||||
});
|
||||
}
|
||||
|
||||
tracing::trace!(
|
||||
@@ -562,33 +569,30 @@ pub(crate) async fn handle_new_head(
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::approval_db::v1::DbBackend;
|
||||
use polkadot_node_subsystem_test_helpers::make_subsystem_context;
|
||||
use polkadot_node_primitives::approval::{VRFOutput, VRFProof};
|
||||
use polkadot_primitives::v1::{SessionInfo, ValidatorIndex};
|
||||
use polkadot_node_subsystem::messages::AllMessages;
|
||||
use sp_core::testing::TaskExecutor;
|
||||
pub(crate) use sp_runtime::{Digest, DigestItem};
|
||||
pub(crate) use sp_consensus_babe::{
|
||||
Epoch as BabeEpoch, BabeEpochConfiguration, AllowedSlots,
|
||||
};
|
||||
pub(crate) use sp_consensus_babe::digests::{CompatibleDigestItem, PreDigest, SecondaryVRFPreDigest};
|
||||
use sp_keyring::sr25519::Keyring as Sr25519Keyring;
|
||||
use assert_matches::assert_matches;
|
||||
use merlin::Transcript;
|
||||
use std::{pin::Pin, sync::Arc};
|
||||
use kvdb::KeyValueDB;
|
||||
use merlin::Transcript;
|
||||
use polkadot_node_primitives::approval::{VRFOutput, VRFProof};
|
||||
use polkadot_node_subsystem::messages::AllMessages;
|
||||
use polkadot_node_subsystem_test_helpers::make_subsystem_context;
|
||||
use polkadot_primitives::v1::{SessionInfo, ValidatorIndex};
|
||||
pub(crate) use sp_consensus_babe::{
|
||||
digests::{CompatibleDigestItem, PreDigest, SecondaryVRFPreDigest},
|
||||
AllowedSlots, BabeEpochConfiguration, Epoch as BabeEpoch,
|
||||
};
|
||||
use sp_core::testing::TaskExecutor;
|
||||
use sp_keyring::sr25519::Keyring as Sr25519Keyring;
|
||||
pub(crate) use sp_runtime::{Digest, DigestItem};
|
||||
use std::{pin::Pin, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
APPROVAL_SESSIONS, criteria, BlockEntry,
|
||||
approval_db::v1::Config as DatabaseConfig,
|
||||
approval_db::v1::Config as DatabaseConfig, criteria, BlockEntry, APPROVAL_SESSIONS,
|
||||
};
|
||||
|
||||
const DATA_COL: u32 = 0;
|
||||
const NUM_COLUMNS: u32 = 1;
|
||||
|
||||
const TEST_CONFIG: DatabaseConfig = DatabaseConfig {
|
||||
col_data: DATA_COL,
|
||||
};
|
||||
const TEST_CONFIG: DatabaseConfig = DatabaseConfig { col_data: DATA_COL };
|
||||
#[derive(Default)]
|
||||
struct MockClock;
|
||||
|
||||
@@ -598,9 +602,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
fn wait(&self, _tick: Tick) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
|
||||
Box::pin(async move {
|
||||
()
|
||||
})
|
||||
Box::pin(async move { () })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -633,7 +635,11 @@ pub(crate) mod tests {
|
||||
_keystore: &LocalKeystore,
|
||||
_relay_vrf_story: polkadot_node_primitives::approval::RelayVRFStory,
|
||||
_config: &criteria::Config,
|
||||
_leaving_cores: Vec<(CandidateHash, polkadot_primitives::v1::CoreIndex, polkadot_primitives::v1::GroupIndex)>,
|
||||
_leaving_cores: Vec<(
|
||||
CandidateHash,
|
||||
polkadot_primitives::v1::CoreIndex,
|
||||
polkadot_primitives::v1::GroupIndex,
|
||||
)>,
|
||||
) -> HashMap<polkadot_primitives::v1::CoreIndex, criteria::OurAssignment> {
|
||||
HashMap::new()
|
||||
}
|
||||
@@ -675,7 +681,6 @@ pub(crate) mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn imported_block_info_is_good() {
|
||||
let pool = TaskExecutor::new();
|
||||
@@ -691,12 +696,7 @@ pub(crate) mod tests {
|
||||
let mut d = Digest::default();
|
||||
let (vrf_output, vrf_proof) = garbage_vrf();
|
||||
d.push(DigestItem::babe_pre_digest(PreDigest::SecondaryVRF(
|
||||
SecondaryVRFPreDigest {
|
||||
authority_index: 0,
|
||||
slot,
|
||||
vrf_output,
|
||||
vrf_proof,
|
||||
}
|
||||
SecondaryVRFPreDigest { authority_index: 0, slot, vrf_output, vrf_proof },
|
||||
)));
|
||||
|
||||
d
|
||||
@@ -719,13 +719,15 @@ pub(crate) mod tests {
|
||||
(make_candidate(2.into()), CoreIndex(1), GroupIndex(3)),
|
||||
];
|
||||
|
||||
|
||||
let inclusion_events = candidates.iter().cloned()
|
||||
let inclusion_events = candidates
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|(r, c, g)| CandidateEvent::CandidateIncluded(r, Vec::new().into(), c, g))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let test_fut = {
|
||||
let included_candidates = candidates.iter()
|
||||
let included_candidates = candidates
|
||||
.iter()
|
||||
.map(|(r, c, g)| (r.hash(), r.clone(), *c, *g))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@@ -743,12 +745,8 @@ pub(crate) mod tests {
|
||||
keystore: &LocalKeystore::in_memory(),
|
||||
};
|
||||
|
||||
let info = imported_block_info(
|
||||
&mut ctx,
|
||||
env,
|
||||
hash,
|
||||
&header,
|
||||
).await.unwrap().unwrap();
|
||||
let info =
|
||||
imported_block_info(&mut ctx, env, hash, &header).await.unwrap().unwrap();
|
||||
|
||||
assert_eq!(info.included_candidates, included_candidates);
|
||||
assert_eq!(info.session_index, session);
|
||||
@@ -835,7 +833,9 @@ pub(crate) mod tests {
|
||||
(make_candidate(2.into()), CoreIndex(1), GroupIndex(3)),
|
||||
];
|
||||
|
||||
let inclusion_events = candidates.iter().cloned()
|
||||
let inclusion_events = candidates
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|(r, c, g)| CandidateEvent::CandidateIncluded(r, Vec::new().into(), c, g))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@@ -854,12 +854,7 @@ pub(crate) mod tests {
|
||||
keystore: &LocalKeystore::in_memory(),
|
||||
};
|
||||
|
||||
let info = imported_block_info(
|
||||
&mut ctx,
|
||||
env,
|
||||
hash,
|
||||
&header,
|
||||
).await.unwrap();
|
||||
let info = imported_block_info(&mut ctx, env, hash, &header).await.unwrap();
|
||||
|
||||
assert!(info.is_none());
|
||||
})
|
||||
@@ -940,7 +935,9 @@ pub(crate) mod tests {
|
||||
(make_candidate(2.into()), CoreIndex(1), GroupIndex(3)),
|
||||
];
|
||||
|
||||
let inclusion_events = candidates.iter().cloned()
|
||||
let inclusion_events = candidates
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|(r, c, g)| CandidateEvent::CandidateIncluded(r, Vec::new().into(), c, g))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@@ -955,12 +952,7 @@ pub(crate) mod tests {
|
||||
keystore: &LocalKeystore::in_memory(),
|
||||
};
|
||||
|
||||
let info = imported_block_info(
|
||||
&mut ctx,
|
||||
env,
|
||||
hash,
|
||||
&header,
|
||||
).await.unwrap();
|
||||
let info = imported_block_info(&mut ctx, env, hash, &header).await.unwrap();
|
||||
|
||||
assert!(info.is_none());
|
||||
})
|
||||
@@ -1008,12 +1000,7 @@ pub(crate) mod tests {
|
||||
let mut d = Digest::default();
|
||||
let (vrf_output, vrf_proof) = garbage_vrf();
|
||||
d.push(DigestItem::babe_pre_digest(PreDigest::SecondaryVRF(
|
||||
SecondaryVRFPreDigest {
|
||||
authority_index: 0,
|
||||
slot,
|
||||
vrf_output,
|
||||
vrf_proof,
|
||||
}
|
||||
SecondaryVRFPreDigest { authority_index: 0, slot, vrf_output, vrf_proof },
|
||||
)));
|
||||
|
||||
d.push(ConsensusLog::ForceApprove(3).into());
|
||||
@@ -1038,13 +1025,15 @@ pub(crate) mod tests {
|
||||
(make_candidate(2.into()), CoreIndex(1), GroupIndex(3)),
|
||||
];
|
||||
|
||||
|
||||
let inclusion_events = candidates.iter().cloned()
|
||||
let inclusion_events = candidates
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|(r, c, g)| CandidateEvent::CandidateIncluded(r, Vec::new().into(), c, g))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let test_fut = {
|
||||
let included_candidates = candidates.iter()
|
||||
let included_candidates = candidates
|
||||
.iter()
|
||||
.map(|(r, c, g)| (r.hash(), r.clone(), *c, *g))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@@ -1062,12 +1051,8 @@ pub(crate) mod tests {
|
||||
keystore: &LocalKeystore::in_memory(),
|
||||
};
|
||||
|
||||
let info = imported_block_info(
|
||||
&mut ctx,
|
||||
env,
|
||||
hash,
|
||||
&header,
|
||||
).await.unwrap().unwrap();
|
||||
let info =
|
||||
imported_block_info(&mut ctx, env, hash, &header).await.unwrap().unwrap();
|
||||
|
||||
assert_eq!(info.included_candidates, included_candidates);
|
||||
assert_eq!(info.session_index, session);
|
||||
@@ -1159,12 +1144,7 @@ pub(crate) mod tests {
|
||||
let mut d = Digest::default();
|
||||
let (vrf_output, vrf_proof) = garbage_vrf();
|
||||
d.push(DigestItem::babe_pre_digest(PreDigest::SecondaryVRF(
|
||||
SecondaryVRFPreDigest {
|
||||
authority_index: 0,
|
||||
slot,
|
||||
vrf_output,
|
||||
vrf_proof,
|
||||
}
|
||||
SecondaryVRFPreDigest { authority_index: 0, slot, vrf_output, vrf_proof },
|
||||
)));
|
||||
|
||||
d
|
||||
@@ -1186,7 +1166,9 @@ pub(crate) mod tests {
|
||||
(make_candidate(1.into()), CoreIndex(0), GroupIndex(0)),
|
||||
(make_candidate(2.into()), CoreIndex(1), GroupIndex(1)),
|
||||
];
|
||||
let inclusion_events = candidates.iter().cloned()
|
||||
let inclusion_events = candidates
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|(r, c, g)| CandidateEvent::CandidateIncluded(r, Vec::new().into(), c, g))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@@ -1202,7 +1184,8 @@ pub(crate) mod tests {
|
||||
candidates: Vec::new(),
|
||||
approved_bitfield: Default::default(),
|
||||
children: Vec::new(),
|
||||
}.into()
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
|
||||
let write_ops = overlay_db.into_write_ops();
|
||||
@@ -1211,13 +1194,9 @@ pub(crate) mod tests {
|
||||
let test_fut = {
|
||||
Box::pin(async move {
|
||||
let mut overlay_db = OverlayedBackend::new(&db);
|
||||
let result = handle_new_head(
|
||||
&mut ctx,
|
||||
&mut state,
|
||||
&mut overlay_db,
|
||||
hash,
|
||||
&Some(1),
|
||||
).await.unwrap();
|
||||
let result = handle_new_head(&mut ctx, &mut state, &mut overlay_db, hash, &Some(1))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let write_ops = overlay_db.into_write_ops();
|
||||
db.write(write_ops).unwrap();
|
||||
@@ -1229,14 +1208,11 @@ pub(crate) mod tests {
|
||||
assert_eq!(candidates[1].1.approvals().len(), 6);
|
||||
// the first candidate should be insta-approved
|
||||
// the second should not
|
||||
let entry: BlockEntry = v1::load_block_entry(
|
||||
db_writer.as_ref(),
|
||||
&TEST_CONFIG,
|
||||
&hash,
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.into();
|
||||
let entry: BlockEntry =
|
||||
v1::load_block_entry(db_writer.as_ref(), &TEST_CONFIG, &hash)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.into();
|
||||
assert!(entry.is_candidate_approved(&candidates[0].0));
|
||||
assert!(!entry.is_candidate_approved(&candidates[1].0));
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -19,21 +19,18 @@
|
||||
|
||||
use polkadot_node_subsystem::SubsystemResult;
|
||||
|
||||
use polkadot_primitives::v1::{
|
||||
CandidateHash, CandidateReceipt, BlockNumber, GroupIndex, Hash,
|
||||
use bitvec::order::Lsb0 as BitOrderLsb0;
|
||||
use polkadot_primitives::v1::{BlockNumber, CandidateHash, CandidateReceipt, GroupIndex, Hash};
|
||||
|
||||
use std::{
|
||||
collections::{hash_map::Entry, BTreeMap, HashMap},
|
||||
convert::Into,
|
||||
};
|
||||
use bitvec::{order::Lsb0 as BitOrderLsb0};
|
||||
|
||||
use std::convert::Into;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
use super::persisted_entries::{ApprovalEntry, CandidateEntry, BlockEntry};
|
||||
use super::backend::{Backend, OverlayedBackend};
|
||||
use super::approval_db::{
|
||||
v1::{
|
||||
OurAssignment, StoredBlockRange,
|
||||
},
|
||||
use super::{
|
||||
approval_db::v1::{OurAssignment, StoredBlockRange},
|
||||
backend::{Backend, OverlayedBackend},
|
||||
persisted_entries::{ApprovalEntry, BlockEntry, CandidateEntry},
|
||||
};
|
||||
|
||||
/// Information about a new candidate necessary to instantiate the requisite
|
||||
@@ -75,7 +72,7 @@ fn visit_and_remove_block_entry(
|
||||
None => continue, // Should not happen except for corrupt DB
|
||||
Some(c) => c,
|
||||
})
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
candidate.block_assignments.remove(&block_hash);
|
||||
@@ -111,11 +108,7 @@ pub fn canonicalize(
|
||||
overlay_db.delete_blocks_at_height(i);
|
||||
|
||||
for b in at_height {
|
||||
let _ = visit_and_remove_block_entry(
|
||||
b,
|
||||
overlay_db,
|
||||
&mut visited_candidates,
|
||||
)?;
|
||||
let _ = visit_and_remove_block_entry(b, overlay_db, &mut visited_candidates)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,11 +122,7 @@ pub fn canonicalize(
|
||||
let mut pruned_branches = Vec::new();
|
||||
|
||||
for b in at_height {
|
||||
let children = visit_and_remove_block_entry(
|
||||
b,
|
||||
overlay_db,
|
||||
&mut visited_candidates,
|
||||
)?;
|
||||
let children = visit_and_remove_block_entry(b, overlay_db, &mut visited_candidates)?;
|
||||
|
||||
if b != canon_hash {
|
||||
pruned_branches.extend(children);
|
||||
@@ -145,13 +134,11 @@ pub fn canonicalize(
|
||||
|
||||
// Follow all children of non-canonicalized blocks.
|
||||
{
|
||||
let mut frontier: Vec<(BlockNumber, Hash)> = pruned_branches.into_iter().map(|h| (canon_number + 1, h)).collect();
|
||||
let mut frontier: Vec<(BlockNumber, Hash)> =
|
||||
pruned_branches.into_iter().map(|h| (canon_number + 1, h)).collect();
|
||||
while let Some((height, next_child)) = frontier.pop() {
|
||||
let children = visit_and_remove_block_entry(
|
||||
next_child,
|
||||
overlay_db,
|
||||
&mut visited_candidates,
|
||||
)?;
|
||||
let children =
|
||||
visit_and_remove_block_entry(next_child, overlay_db, &mut visited_candidates)?;
|
||||
|
||||
// extend the frontier of branches to include the given height.
|
||||
frontier.extend(children.into_iter().map(|h| (height + 1, h)));
|
||||
@@ -188,10 +175,7 @@ pub fn canonicalize(
|
||||
// due to the fork pruning, this range actually might go too far above where our actual highest block is,
|
||||
// if a relatively short fork is canonicalized.
|
||||
// TODO https://github.com/paritytech/polkadot/issues/3389
|
||||
let new_range = StoredBlockRange(
|
||||
canon_number + 1,
|
||||
std::cmp::max(range.1, canon_number + 2),
|
||||
);
|
||||
let new_range = StoredBlockRange(canon_number + 1, std::cmp::max(range.1, canon_number + 2));
|
||||
|
||||
overlay_db.write_stored_block_range(new_range);
|
||||
|
||||
@@ -246,21 +230,20 @@ pub fn add_block_entry(
|
||||
// read and write all updated entries.
|
||||
{
|
||||
for &(_, ref candidate_hash) in entry.candidates() {
|
||||
let NewCandidateInfo {
|
||||
candidate,
|
||||
backing_group,
|
||||
our_assignment,
|
||||
} = match candidate_info(candidate_hash) {
|
||||
None => return Ok(Vec::new()),
|
||||
Some(info) => info,
|
||||
};
|
||||
let NewCandidateInfo { candidate, backing_group, our_assignment } =
|
||||
match candidate_info(candidate_hash) {
|
||||
None => return Ok(Vec::new()),
|
||||
Some(info) => info,
|
||||
};
|
||||
|
||||
let mut candidate_entry = store.load_candidate_entry(&candidate_hash)?
|
||||
.unwrap_or_else(move || CandidateEntry {
|
||||
candidate,
|
||||
session,
|
||||
block_assignments: BTreeMap::new(),
|
||||
approvals: bitvec::bitvec![BitOrderLsb0, u8; 0; n_validators],
|
||||
let mut candidate_entry =
|
||||
store.load_candidate_entry(&candidate_hash)?.unwrap_or_else(move || {
|
||||
CandidateEntry {
|
||||
candidate,
|
||||
session,
|
||||
block_assignments: BTreeMap::new(),
|
||||
approvals: bitvec::bitvec![BitOrderLsb0, u8; 0; n_validators],
|
||||
}
|
||||
});
|
||||
|
||||
candidate_entry.block_assignments.insert(
|
||||
@@ -272,7 +255,7 @@ pub fn add_block_entry(
|
||||
None,
|
||||
bitvec::bitvec![BitOrderLsb0, u8; 0; n_validators],
|
||||
false,
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
store.write_candidate_entry(candidate_entry.clone());
|
||||
@@ -313,7 +296,6 @@ pub fn force_approve(
|
||||
// iterate back to the `up_to` block, and then iterate backwards until all blocks
|
||||
// are updated.
|
||||
while let Some(mut entry) = store.load_block_entry(&cur_hash)? {
|
||||
|
||||
if entry.block_number() <= up_to {
|
||||
state = State::Approving;
|
||||
}
|
||||
@@ -326,7 +308,7 @@ pub fn force_approve(
|
||||
entry.approved_bitfield.iter_mut().for_each(|mut b| *b = true);
|
||||
approved_hashes.push(entry.block_hash());
|
||||
store.write_block_entry(entry);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,18 +20,17 @@
|
||||
//! Within that context, things are plain-old-data. Within this module,
|
||||
//! data and logic are intertwined.
|
||||
|
||||
use polkadot_node_primitives::approval::{DelayTranche, RelayVRFStory, AssignmentCert};
|
||||
use polkadot_node_primitives::approval::{AssignmentCert, DelayTranche, RelayVRFStory};
|
||||
use polkadot_primitives::v1::{
|
||||
ValidatorIndex, CandidateReceipt, SessionIndex, GroupIndex, CoreIndex,
|
||||
Hash, CandidateHash, BlockNumber, ValidatorSignature,
|
||||
BlockNumber, CandidateHash, CandidateReceipt, CoreIndex, GroupIndex, Hash, SessionIndex,
|
||||
ValidatorIndex, ValidatorSignature,
|
||||
};
|
||||
use sp_consensus_slots::Slot;
|
||||
|
||||
use bitvec::{order::Lsb0 as BitOrderLsb0, slice::BitSlice, vec::BitVec};
|
||||
use std::collections::BTreeMap;
|
||||
use bitvec::{slice::BitSlice, vec::BitVec, order::Lsb0 as BitOrderLsb0};
|
||||
|
||||
use super::time::Tick;
|
||||
use super::criteria::OurAssignment;
|
||||
use super::{criteria::OurAssignment, time::Tick};
|
||||
|
||||
/// Metadata regarding a specific tranche of assignments for a specific candidate.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@@ -105,11 +104,14 @@ impl ApprovalEntry {
|
||||
}
|
||||
|
||||
// Note that our assignment is triggered. No-op if already triggered.
|
||||
pub fn trigger_our_assignment(&mut self, tick_now: Tick)
|
||||
-> Option<(AssignmentCert, ValidatorIndex, DelayTranche)>
|
||||
{
|
||||
pub fn trigger_our_assignment(
|
||||
&mut self,
|
||||
tick_now: Tick,
|
||||
) -> Option<(AssignmentCert, ValidatorIndex, DelayTranche)> {
|
||||
let our = self.our_assignment.as_mut().and_then(|a| {
|
||||
if a.triggered() { return None }
|
||||
if a.triggered() {
|
||||
return None
|
||||
}
|
||||
a.mark_triggered();
|
||||
|
||||
Some(a.clone())
|
||||
@@ -143,22 +145,16 @@ impl ApprovalEntry {
|
||||
let idx = match self.tranches.iter().position(|t| t.tranche >= tranche) {
|
||||
Some(pos) => {
|
||||
if self.tranches[pos].tranche > tranche {
|
||||
self.tranches.insert(pos, TrancheEntry {
|
||||
tranche: tranche,
|
||||
assignments: Vec::new(),
|
||||
});
|
||||
self.tranches.insert(pos, TrancheEntry { tranche, assignments: Vec::new() });
|
||||
}
|
||||
|
||||
pos
|
||||
}
|
||||
},
|
||||
None => {
|
||||
self.tranches.push(TrancheEntry {
|
||||
tranche: tranche,
|
||||
assignments: Vec::new(),
|
||||
});
|
||||
self.tranches.push(TrancheEntry { tranche, assignments: Vec::new() });
|
||||
|
||||
self.tranches.len() - 1
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
self.tranches[idx].assignments.push((validator_index, tick_now));
|
||||
@@ -168,15 +164,16 @@ impl ApprovalEntry {
|
||||
// Produce a bitvec indicating the assignments of all validators up to and
|
||||
// including `tranche`.
|
||||
pub fn assignments_up_to(&self, tranche: DelayTranche) -> BitVec<BitOrderLsb0, u8> {
|
||||
self.tranches.iter()
|
||||
.take_while(|e| e.tranche <= tranche)
|
||||
.fold(bitvec::bitvec![BitOrderLsb0, u8; 0; self.assignments.len()], |mut a, e| {
|
||||
self.tranches.iter().take_while(|e| e.tranche <= tranche).fold(
|
||||
bitvec::bitvec![BitOrderLsb0, u8; 0; self.assignments.len()],
|
||||
|mut a, e| {
|
||||
for &(v, _) in &e.assignments {
|
||||
a.set(v.0 as _, true);
|
||||
}
|
||||
|
||||
a
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Whether the approval entry is approved
|
||||
@@ -299,11 +296,7 @@ impl CandidateEntry {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn add_approval_entry(
|
||||
&mut self,
|
||||
block_hash: Hash,
|
||||
approval_entry: ApprovalEntry,
|
||||
) {
|
||||
pub fn add_approval_entry(&mut self, block_hash: Hash, approval_entry: ApprovalEntry) {
|
||||
self.block_assignments.insert(block_hash, approval_entry);
|
||||
}
|
||||
}
|
||||
@@ -313,7 +306,11 @@ impl From<crate::approval_db::v1::CandidateEntry> for CandidateEntry {
|
||||
CandidateEntry {
|
||||
candidate: entry.candidate,
|
||||
session: entry.session,
|
||||
block_assignments: entry.block_assignments.into_iter().map(|(h, ae)| (h, ae.into())).collect(),
|
||||
block_assignments: entry
|
||||
.block_assignments
|
||||
.into_iter()
|
||||
.map(|(h, ae)| (h, ae.into()))
|
||||
.collect(),
|
||||
approvals: entry.approvals,
|
||||
}
|
||||
}
|
||||
@@ -324,7 +321,11 @@ impl From<CandidateEntry> for crate::approval_db::v1::CandidateEntry {
|
||||
Self {
|
||||
candidate: entry.candidate,
|
||||
session: entry.session,
|
||||
block_assignments: entry.block_assignments.into_iter().map(|(h, ae)| (h, ae.into())).collect(),
|
||||
block_assignments: entry
|
||||
.block_assignments
|
||||
.into_iter()
|
||||
.map(|(h, ae)| (h, ae.into()))
|
||||
.collect(),
|
||||
approvals: entry.approvals,
|
||||
}
|
||||
}
|
||||
@@ -360,7 +361,9 @@ impl BlockEntry {
|
||||
|
||||
/// Whether a candidate is approved in the bitfield.
|
||||
pub fn is_candidate_approved(&self, candidate_hash: &CandidateHash) -> bool {
|
||||
self.candidates.iter().position(|(_, h)| h == candidate_hash)
|
||||
self.candidates
|
||||
.iter()
|
||||
.position(|(_, h)| h == candidate_hash)
|
||||
.and_then(|p| self.approved_bitfield.get(p).map(|b| *b))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
@@ -372,10 +375,12 @@ impl BlockEntry {
|
||||
|
||||
/// Iterate over all unapproved candidates.
|
||||
pub fn unapproved_candidates(&self) -> impl Iterator<Item = CandidateHash> + '_ {
|
||||
self.approved_bitfield.iter().enumerate().filter_map(move |(i, a)| if !*a {
|
||||
Some(self.candidates[i].1)
|
||||
} else {
|
||||
None
|
||||
self.approved_bitfield.iter().enumerate().filter_map(move |(i, a)| {
|
||||
if !*a {
|
||||
Some(self.candidates[i].1)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -385,9 +390,7 @@ impl BlockEntry {
|
||||
/// Panics if the core is already used.
|
||||
#[cfg(test)]
|
||||
pub fn add_candidate(&mut self, core: CoreIndex, candidate_hash: CandidateHash) -> usize {
|
||||
let pos = self.candidates
|
||||
.binary_search_by_key(&core, |(c, _)| *c)
|
||||
.unwrap_err();
|
||||
let pos = self.candidates.binary_search_by_key(&core, |(c, _)| *c).unwrap_err();
|
||||
|
||||
self.candidates.insert(pos, (core, candidate_hash));
|
||||
|
||||
|
||||
@@ -16,34 +16,39 @@
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
use std::time::Duration;
|
||||
use polkadot_overseer::HeadSupportsParachains;
|
||||
use polkadot_primitives::v1::{
|
||||
CoreIndex, GroupIndex, ValidatorSignature, Header, CandidateEvent,
|
||||
};
|
||||
use polkadot_node_subsystem::{ActivatedLeaf, ActiveLeavesUpdate, LeafStatus};
|
||||
use polkadot_node_primitives::approval::{
|
||||
AssignmentCert, AssignmentCertKind, VRFOutput, VRFProof,
|
||||
RELAY_VRF_MODULO_CONTEXT, DelayTranche,
|
||||
AssignmentCert, AssignmentCertKind, DelayTranche, VRFOutput, VRFProof, RELAY_VRF_MODULO_CONTEXT,
|
||||
};
|
||||
use polkadot_node_subsystem::{
|
||||
messages::{AllMessages, ApprovalVotingMessage, AssignmentCheckResult},
|
||||
ActivatedLeaf, ActiveLeavesUpdate, LeafStatus,
|
||||
};
|
||||
use polkadot_node_subsystem_test_helpers as test_helpers;
|
||||
use polkadot_node_subsystem::messages::{AllMessages, ApprovalVotingMessage, AssignmentCheckResult};
|
||||
use polkadot_node_subsystem_util::TimeoutExt;
|
||||
use polkadot_overseer::HeadSupportsParachains;
|
||||
use polkadot_primitives::v1::{CandidateEvent, CoreIndex, GroupIndex, Header, ValidatorSignature};
|
||||
use std::time::Duration;
|
||||
|
||||
use assert_matches::assert_matches;
|
||||
use parking_lot::Mutex;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use sp_keyring::sr25519::Keyring as Sr25519Keyring;
|
||||
use sp_keystore::CryptoStore;
|
||||
use assert_matches::assert_matches;
|
||||
|
||||
use super::import::tests::{
|
||||
BabeEpoch, BabeEpochConfiguration, AllowedSlots, Digest, garbage_vrf, DigestItem, PreDigest,
|
||||
SecondaryVRFPreDigest, CompatibleDigestItem,
|
||||
use std::{
|
||||
pin::Pin,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
approval_db::v1::StoredBlockRange,
|
||||
backend::BackendWriteOp,
|
||||
import::tests::{
|
||||
garbage_vrf, AllowedSlots, BabeEpoch, BabeEpochConfiguration, CompatibleDigestItem, Digest,
|
||||
DigestItem, PreDigest, SecondaryVRFPreDigest,
|
||||
},
|
||||
};
|
||||
use super::approval_db::v1::StoredBlockRange;
|
||||
use super::backend::BackendWriteOp;
|
||||
|
||||
const SLOT_DURATION_MILLIS: u64 = 5000;
|
||||
|
||||
@@ -92,14 +97,8 @@ fn make_sync_oracle(val: bool) -> (TestSyncOracle, TestSyncOracleHandle) {
|
||||
let flag = Arc::new(AtomicBool::new(val));
|
||||
|
||||
(
|
||||
TestSyncOracle {
|
||||
flag: flag.clone(),
|
||||
done_syncing_sender: Arc::new(Mutex::new(Some(tx))),
|
||||
},
|
||||
TestSyncOracleHandle {
|
||||
flag,
|
||||
done_syncing_receiver: rx,
|
||||
}
|
||||
TestSyncOracle { flag: flag.clone(), done_syncing_sender: Arc::new(Mutex::new(Some(tx))) },
|
||||
TestSyncOracleHandle { flag, done_syncing_receiver: rx },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -114,9 +113,7 @@ pub mod test_constants {
|
||||
const DATA_COL: u32 = 0;
|
||||
pub(crate) const NUM_COLUMNS: u32 = 1;
|
||||
|
||||
pub(crate) const TEST_CONFIG: DatabaseConfig = DatabaseConfig {
|
||||
col_data: DATA_COL,
|
||||
};
|
||||
pub(crate) const TEST_CONFIG: DatabaseConfig = DatabaseConfig { col_data: DATA_COL };
|
||||
}
|
||||
|
||||
struct MockSupportsParachains;
|
||||
@@ -175,10 +172,8 @@ impl MockClockInner {
|
||||
fn wakeup_all(&mut self, up_to: Tick) {
|
||||
// This finds the position of the first wakeup after
|
||||
// the given tick, or the end of the map.
|
||||
let drain_up_to = self.wakeups.binary_search_by_key(
|
||||
&(up_to + 1),
|
||||
|w| w.0,
|
||||
).unwrap_or_else(|i| i);
|
||||
let drain_up_to =
|
||||
self.wakeups.binary_search_by_key(&(up_to + 1), |w| w.0).unwrap_or_else(|i| i);
|
||||
|
||||
for (_, wakeup) in self.wakeups.drain(..drain_up_to) {
|
||||
let _ = wakeup.send(());
|
||||
@@ -201,10 +196,7 @@ impl MockClockInner {
|
||||
fn register_wakeup(&mut self, tick: Tick, pre_emptive: bool) -> oneshot::Receiver<()> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
let pos = self.wakeups.binary_search_by_key(
|
||||
&tick,
|
||||
|w| w.0,
|
||||
).unwrap_or_else(|i| i);
|
||||
let pos = self.wakeups.binary_search_by_key(&tick, |w| w.0).unwrap_or_else(|i| i);
|
||||
|
||||
self.wakeups.insert(pos, (tick, tx));
|
||||
|
||||
@@ -223,14 +215,18 @@ struct MockAssignmentCriteria<Compute, Check>(Compute, Check);
|
||||
impl<Compute, Check> AssignmentCriteria for MockAssignmentCriteria<Compute, Check>
|
||||
where
|
||||
Compute: Fn() -> HashMap<polkadot_primitives::v1::CoreIndex, criteria::OurAssignment>,
|
||||
Check: Fn() -> Result<DelayTranche, criteria::InvalidAssignment>
|
||||
Check: Fn() -> Result<DelayTranche, criteria::InvalidAssignment>,
|
||||
{
|
||||
fn compute_assignments(
|
||||
&self,
|
||||
_keystore: &LocalKeystore,
|
||||
_relay_vrf_story: polkadot_node_primitives::approval::RelayVRFStory,
|
||||
_config: &criteria::Config,
|
||||
_leaving_cores: Vec<(CandidateHash, polkadot_primitives::v1::CoreIndex, polkadot_primitives::v1::GroupIndex)>,
|
||||
_leaving_cores: Vec<(
|
||||
CandidateHash,
|
||||
polkadot_primitives::v1::CoreIndex,
|
||||
polkadot_primitives::v1::GroupIndex,
|
||||
)>,
|
||||
) -> HashMap<polkadot_primitives::v1::CoreIndex, criteria::OurAssignment> {
|
||||
self.0()
|
||||
}
|
||||
@@ -248,10 +244,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> MockAssignmentCriteria<
|
||||
fn() -> HashMap<polkadot_primitives::v1::CoreIndex, criteria::OurAssignment>,
|
||||
F,
|
||||
> {
|
||||
impl<F>
|
||||
MockAssignmentCriteria<
|
||||
fn() -> HashMap<polkadot_primitives::v1::CoreIndex, criteria::OurAssignment>,
|
||||
F,
|
||||
>
|
||||
{
|
||||
fn check_only(f: F) -> Self {
|
||||
MockAssignmentCriteria(Default::default, f)
|
||||
}
|
||||
@@ -266,10 +264,7 @@ struct TestStore {
|
||||
}
|
||||
|
||||
impl Backend for TestStore {
|
||||
fn load_block_entry(
|
||||
&self,
|
||||
block_hash: &Hash,
|
||||
) -> SubsystemResult<Option<BlockEntry>> {
|
||||
fn load_block_entry(&self, block_hash: &Hash) -> SubsystemResult<Option<BlockEntry>> {
|
||||
Ok(self.block_entries.get(block_hash).cloned())
|
||||
}
|
||||
|
||||
@@ -280,10 +275,7 @@ impl Backend for TestStore {
|
||||
Ok(self.candidate_entries.get(candidate_hash).cloned())
|
||||
}
|
||||
|
||||
fn load_blocks_at_height(
|
||||
&self,
|
||||
height: &BlockNumber,
|
||||
) -> SubsystemResult<Vec<Hash>> {
|
||||
fn load_blocks_at_height(&self, height: &BlockNumber) -> SubsystemResult<Vec<Hash>> {
|
||||
Ok(self.blocks_at_height.get(height).cloned().unwrap_or_default())
|
||||
}
|
||||
|
||||
@@ -300,31 +292,33 @@ impl Backend for TestStore {
|
||||
}
|
||||
|
||||
fn write<I>(&mut self, ops: I) -> SubsystemResult<()>
|
||||
where I: IntoIterator<Item = BackendWriteOp>
|
||||
where
|
||||
I: IntoIterator<Item = BackendWriteOp>,
|
||||
{
|
||||
for op in ops {
|
||||
match op {
|
||||
BackendWriteOp::WriteStoredBlockRange(stored_block_range) => {
|
||||
self.stored_block_range = Some(stored_block_range);
|
||||
}
|
||||
},
|
||||
BackendWriteOp::WriteBlocksAtHeight(h, blocks) => {
|
||||
self.blocks_at_height.insert(h, blocks);
|
||||
}
|
||||
},
|
||||
BackendWriteOp::DeleteBlocksAtHeight(h) => {
|
||||
let _ = self.blocks_at_height.remove(&h);
|
||||
}
|
||||
},
|
||||
BackendWriteOp::WriteBlockEntry(block_entry) => {
|
||||
self.block_entries.insert(block_entry.block_hash(), block_entry);
|
||||
}
|
||||
},
|
||||
BackendWriteOp::DeleteBlockEntry(hash) => {
|
||||
let _ = self.block_entries.remove(&hash);
|
||||
}
|
||||
},
|
||||
BackendWriteOp::WriteCandidateEntry(candidate_entry) => {
|
||||
self.candidate_entries.insert(candidate_entry.candidate_receipt().hash(), candidate_entry);
|
||||
}
|
||||
self.candidate_entries
|
||||
.insert(candidate_entry.candidate_receipt().hash(), candidate_entry);
|
||||
},
|
||||
BackendWriteOp::DeleteCandidateEntry(candidate_hash) => {
|
||||
let _ = self.candidate_entries.remove(&candidate_hash);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,10 +334,7 @@ fn garbage_assignment_cert(kind: AssignmentCertKind) -> AssignmentCert {
|
||||
let (inout, proof, _) = keypair.vrf_sign(ctx.bytes(msg));
|
||||
let out = inout.to_output();
|
||||
|
||||
AssignmentCert {
|
||||
kind,
|
||||
vrf: (VRFOutput(out), VRFProof(proof)),
|
||||
}
|
||||
AssignmentCert { kind, vrf: (VRFOutput(out), VRFProof(proof)) }
|
||||
}
|
||||
|
||||
fn sign_approval(
|
||||
@@ -383,47 +374,40 @@ fn test_harness<T: Future<Output = VirtualOverseer>>(
|
||||
|
||||
let store = TestStore::default();
|
||||
|
||||
let HarnessConfig {
|
||||
tick_start,
|
||||
assigned_tranche,
|
||||
} = config;
|
||||
let HarnessConfig { tick_start, assigned_tranche } = config;
|
||||
|
||||
let clock = Box::new(MockClock::new(tick_start));
|
||||
let subsystem = run(
|
||||
context,
|
||||
ApprovalVotingSubsystem::with_config(
|
||||
Config{
|
||||
col_data: test_constants::TEST_CONFIG.col_data,
|
||||
slot_duration_millis: 100u64,
|
||||
},
|
||||
Config { col_data: test_constants::TEST_CONFIG.col_data, slot_duration_millis: 100u64 },
|
||||
Arc::new(kvdb_memorydb::create(test_constants::NUM_COLUMNS)),
|
||||
Arc::new(keystore),
|
||||
sync_oracle,
|
||||
Metrics::default(),
|
||||
),
|
||||
clock.clone(),
|
||||
Box::new(MockAssignmentCriteria::check_only(move || { Ok(assigned_tranche) })),
|
||||
Box::new(MockAssignmentCriteria::check_only(move || Ok(assigned_tranche))),
|
||||
store,
|
||||
);
|
||||
|
||||
let test_fut = test(TestHarness {
|
||||
virtual_overseer,
|
||||
clock,
|
||||
});
|
||||
let test_fut = test(TestHarness { virtual_overseer, clock });
|
||||
|
||||
futures::pin_mut!(test_fut);
|
||||
futures::pin_mut!(subsystem);
|
||||
|
||||
futures::executor::block_on(future::join(async move {
|
||||
let mut overseer = test_fut.await;
|
||||
overseer_signal(&mut overseer, OverseerSignal::Conclude).await;
|
||||
}, subsystem)).1.unwrap();
|
||||
futures::executor::block_on(future::join(
|
||||
async move {
|
||||
let mut overseer = test_fut.await;
|
||||
overseer_signal(&mut overseer, OverseerSignal::Conclude).await;
|
||||
},
|
||||
subsystem,
|
||||
))
|
||||
.1
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
async fn overseer_send(
|
||||
overseer: &mut VirtualOverseer,
|
||||
msg: FromOverseer<ApprovalVotingMessage>,
|
||||
) {
|
||||
async fn overseer_send(overseer: &mut VirtualOverseer, msg: FromOverseer<ApprovalVotingMessage>) {
|
||||
tracing::trace!("Sending message:\n{:?}", &msg);
|
||||
overseer
|
||||
.send(msg)
|
||||
@@ -432,9 +416,7 @@ async fn overseer_send(
|
||||
.expect(&format!("{:?} is enough for sending messages.", TIMEOUT));
|
||||
}
|
||||
|
||||
async fn overseer_recv(
|
||||
overseer: &mut VirtualOverseer,
|
||||
) -> AllMessages {
|
||||
async fn overseer_recv(overseer: &mut VirtualOverseer) -> AllMessages {
|
||||
let msg = overseer_recv_with_timeout(overseer, TIMEOUT)
|
||||
.await
|
||||
.expect(&format!("{:?} is enough to receive messages.", TIMEOUT));
|
||||
@@ -449,17 +431,11 @@ async fn overseer_recv_with_timeout(
|
||||
timeout: Duration,
|
||||
) -> Option<AllMessages> {
|
||||
tracing::trace!("Waiting for message...");
|
||||
overseer
|
||||
.recv()
|
||||
.timeout(timeout)
|
||||
.await
|
||||
overseer.recv().timeout(timeout).await
|
||||
}
|
||||
|
||||
const TIMEOUT: Duration = Duration::from_millis(2000);
|
||||
async fn overseer_signal(
|
||||
overseer: &mut VirtualOverseer,
|
||||
signal: OverseerSignal,
|
||||
) {
|
||||
async fn overseer_signal(overseer: &mut VirtualOverseer, signal: OverseerSignal) {
|
||||
overseer
|
||||
.send(FromOverseer::Signal(signal))
|
||||
.timeout(TIMEOUT)
|
||||
@@ -471,10 +447,7 @@ async fn overseer_signal(
|
||||
fn blank_subsystem_act_on_bad_block() {
|
||||
let (oracle, handle) = make_sync_oracle(false);
|
||||
test_harness(Default::default(), Box::new(oracle), |test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
..
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer, .. } = test_harness;
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
@@ -484,18 +457,19 @@ fn blank_subsystem_act_on_bad_block() {
|
||||
&mut virtual_overseer,
|
||||
FromOverseer::Communication {
|
||||
msg: ApprovalVotingMessage::CheckAndImportAssignment(
|
||||
IndirectAssignmentCert{
|
||||
IndirectAssignmentCert {
|
||||
block_hash: bad_block_hash.clone(),
|
||||
validator: 0u32.into(),
|
||||
cert: garbage_assignment_cert(
|
||||
AssignmentCertKind::RelayVRFModulo { sample: 0 }
|
||||
),
|
||||
cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo {
|
||||
sample: 0,
|
||||
}),
|
||||
},
|
||||
0u32,
|
||||
tx,
|
||||
)
|
||||
}
|
||||
).await;
|
||||
),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
handle.await_mode_switch().await;
|
||||
|
||||
@@ -516,10 +490,7 @@ fn blank_subsystem_act_on_bad_block() {
|
||||
fn ss_rejects_approval_if_no_block_entry() {
|
||||
let (oracle, _handle) = make_sync_oracle(false);
|
||||
test_harness(Default::default(), Box::new(oracle), |test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
..
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer, .. } = test_harness;
|
||||
|
||||
let block_hash = Hash::repeat_byte(0x01);
|
||||
let candidate_index = 0;
|
||||
@@ -535,7 +506,8 @@ fn ss_rejects_approval_if_no_block_entry() {
|
||||
candidate_hash,
|
||||
session_index,
|
||||
false,
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_matches!(
|
||||
rx.await,
|
||||
@@ -552,10 +524,7 @@ fn ss_rejects_approval_if_no_block_entry() {
|
||||
fn ss_rejects_approval_before_assignment() {
|
||||
let (oracle, _handle) = make_sync_oracle(false);
|
||||
test_harness(Default::default(), Box::new(oracle), |test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
..
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer, .. } = test_harness;
|
||||
|
||||
let block_hash = Hash::repeat_byte(0x01);
|
||||
|
||||
@@ -584,7 +553,8 @@ fn ss_rejects_approval_before_assignment() {
|
||||
candidate_hash,
|
||||
session_index,
|
||||
false,
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_matches!(
|
||||
rx.await,
|
||||
@@ -600,59 +570,49 @@ fn ss_rejects_approval_before_assignment() {
|
||||
#[test]
|
||||
fn ss_rejects_assignment_in_future() {
|
||||
let (oracle, _handle) = make_sync_oracle(false);
|
||||
test_harness(HarnessConfig {
|
||||
tick_start: 0,
|
||||
assigned_tranche: TICK_TOO_FAR_IN_FUTURE as _,
|
||||
..Default::default()
|
||||
}, Box::new(oracle), |test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
clock,
|
||||
} = test_harness;
|
||||
test_harness(
|
||||
HarnessConfig {
|
||||
tick_start: 0,
|
||||
assigned_tranche: TICK_TOO_FAR_IN_FUTURE as _,
|
||||
..Default::default()
|
||||
},
|
||||
Box::new(oracle),
|
||||
|test_harness| async move {
|
||||
let TestHarness { mut virtual_overseer, clock } = test_harness;
|
||||
|
||||
let block_hash = Hash::repeat_byte(0x01);
|
||||
let candidate_index = 0;
|
||||
let validator = ValidatorIndex(0);
|
||||
let block_hash = Hash::repeat_byte(0x01);
|
||||
let candidate_index = 0;
|
||||
let validator = ValidatorIndex(0);
|
||||
|
||||
// Add block hash 00.
|
||||
ChainBuilder::new()
|
||||
.add_block(block_hash, ChainBuilder::GENESIS_HASH, Slot::from(1), 1)
|
||||
.build(&mut virtual_overseer)
|
||||
.await;
|
||||
// Add block hash 00.
|
||||
ChainBuilder::new()
|
||||
.add_block(block_hash, ChainBuilder::GENESIS_HASH, Slot::from(1), 1)
|
||||
.build(&mut virtual_overseer)
|
||||
.await;
|
||||
|
||||
let rx = cai_assignment(
|
||||
&mut virtual_overseer,
|
||||
block_hash,
|
||||
candidate_index,
|
||||
validator,
|
||||
).await;
|
||||
let rx =
|
||||
cai_assignment(&mut virtual_overseer, block_hash, candidate_index, validator).await;
|
||||
|
||||
assert_eq!(rx.await, Ok(AssignmentCheckResult::TooFarInFuture));
|
||||
assert_eq!(rx.await, Ok(AssignmentCheckResult::TooFarInFuture));
|
||||
|
||||
// Advance clock to make assignment reasonably near.
|
||||
clock.inner.lock().set_tick(1);
|
||||
// Advance clock to make assignment reasonably near.
|
||||
clock.inner.lock().set_tick(1);
|
||||
|
||||
let rx = cai_assignment(
|
||||
&mut virtual_overseer,
|
||||
block_hash,
|
||||
candidate_index,
|
||||
validator,
|
||||
).await;
|
||||
let rx =
|
||||
cai_assignment(&mut virtual_overseer, block_hash, candidate_index, validator).await;
|
||||
|
||||
assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted));
|
||||
assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted));
|
||||
|
||||
virtual_overseer
|
||||
});
|
||||
virtual_overseer
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ss_accepts_duplicate_assignment() {
|
||||
let (oracle, _handle) = make_sync_oracle(false);
|
||||
test_harness(Default::default(), Box::new(oracle), |test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
..
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer, .. } = test_harness;
|
||||
|
||||
let block_hash = Hash::repeat_byte(0x01);
|
||||
let candidate_index = 0;
|
||||
@@ -664,21 +624,13 @@ fn ss_accepts_duplicate_assignment() {
|
||||
.build(&mut virtual_overseer)
|
||||
.await;
|
||||
|
||||
let rx = cai_assignment(
|
||||
&mut virtual_overseer,
|
||||
block_hash,
|
||||
candidate_index,
|
||||
validator,
|
||||
).await;
|
||||
let rx =
|
||||
cai_assignment(&mut virtual_overseer, block_hash, candidate_index, validator).await;
|
||||
|
||||
assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted));
|
||||
|
||||
let rx = cai_assignment(
|
||||
&mut virtual_overseer,
|
||||
block_hash,
|
||||
candidate_index,
|
||||
validator,
|
||||
).await;
|
||||
let rx =
|
||||
cai_assignment(&mut virtual_overseer, block_hash, candidate_index, validator).await;
|
||||
|
||||
assert_eq!(rx.await, Ok(AssignmentCheckResult::AcceptedDuplicate));
|
||||
|
||||
@@ -690,10 +642,7 @@ fn ss_accepts_duplicate_assignment() {
|
||||
fn ss_rejects_assignment_with_unknown_candidate() {
|
||||
let (oracle, _handle) = make_sync_oracle(false);
|
||||
test_harness(Default::default(), Box::new(oracle), |test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
..
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer, .. } = test_harness;
|
||||
|
||||
let block_hash = Hash::repeat_byte(0x01);
|
||||
let candidate_index = 7;
|
||||
@@ -705,16 +654,14 @@ fn ss_rejects_assignment_with_unknown_candidate() {
|
||||
.build(&mut virtual_overseer)
|
||||
.await;
|
||||
|
||||
let rx = cai_assignment(
|
||||
&mut virtual_overseer,
|
||||
block_hash,
|
||||
candidate_index,
|
||||
validator,
|
||||
).await;
|
||||
let rx =
|
||||
cai_assignment(&mut virtual_overseer, block_hash, candidate_index, validator).await;
|
||||
|
||||
assert_eq!(
|
||||
rx.await,
|
||||
Ok(AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCandidateIndex(candidate_index))),
|
||||
Ok(AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCandidateIndex(
|
||||
candidate_index
|
||||
))),
|
||||
);
|
||||
|
||||
virtual_overseer
|
||||
@@ -725,10 +672,7 @@ fn ss_rejects_assignment_with_unknown_candidate() {
|
||||
fn ss_accepts_and_imports_approval_after_assignment() {
|
||||
let (oracle, _handle) = make_sync_oracle(false);
|
||||
test_harness(Default::default(), Box::new(oracle), |test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
..
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer, .. } = test_harness;
|
||||
|
||||
let block_hash = Hash::repeat_byte(0x01);
|
||||
|
||||
@@ -749,12 +693,8 @@ fn ss_accepts_and_imports_approval_after_assignment() {
|
||||
.build(&mut virtual_overseer)
|
||||
.await;
|
||||
|
||||
let rx = cai_assignment(
|
||||
&mut virtual_overseer,
|
||||
block_hash,
|
||||
candidate_index,
|
||||
validator,
|
||||
).await;
|
||||
let rx =
|
||||
cai_assignment(&mut virtual_overseer, block_hash, candidate_index, validator).await;
|
||||
|
||||
assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted));
|
||||
|
||||
@@ -766,7 +706,8 @@ fn ss_accepts_and_imports_approval_after_assignment() {
|
||||
candidate_hash,
|
||||
session_index,
|
||||
true,
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(rx.await, Ok(ApprovalCheckResult::Accepted));
|
||||
|
||||
@@ -777,10 +718,7 @@ fn ss_accepts_and_imports_approval_after_assignment() {
|
||||
fn ss_assignment_import_updates_candidate_entry_and_schedules_wakeup() {
|
||||
let (oracle, _handle) = make_sync_oracle(false);
|
||||
test_harness(Default::default(), Box::new(oracle), |test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
..
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer, .. } = test_harness;
|
||||
|
||||
let block_hash = Hash::repeat_byte(0x01);
|
||||
|
||||
@@ -800,12 +738,8 @@ fn ss_assignment_import_updates_candidate_entry_and_schedules_wakeup() {
|
||||
.build(&mut virtual_overseer)
|
||||
.await;
|
||||
|
||||
let rx = cai_assignment(
|
||||
&mut virtual_overseer,
|
||||
block_hash,
|
||||
candidate_index,
|
||||
validator,
|
||||
).await;
|
||||
let rx =
|
||||
cai_assignment(&mut virtual_overseer, block_hash, candidate_index, validator).await;
|
||||
|
||||
assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted));
|
||||
|
||||
@@ -831,16 +765,12 @@ async fn cai_approval(
|
||||
overseer,
|
||||
FromOverseer::Communication {
|
||||
msg: ApprovalVotingMessage::CheckAndImportApproval(
|
||||
IndirectSignedApprovalVote {
|
||||
block_hash,
|
||||
candidate_index,
|
||||
validator,
|
||||
signature,
|
||||
},
|
||||
IndirectSignedApprovalVote { block_hash, candidate_index, validator, signature },
|
||||
tx,
|
||||
),
|
||||
}
|
||||
).await;
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
if expect_coordinator {
|
||||
assert_matches!(
|
||||
@@ -870,17 +800,14 @@ async fn cai_assignment(
|
||||
IndirectAssignmentCert {
|
||||
block_hash,
|
||||
validator,
|
||||
cert: garbage_assignment_cert(
|
||||
AssignmentCertKind::RelayVRFModulo {
|
||||
sample: 0,
|
||||
},
|
||||
),
|
||||
cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0 }),
|
||||
},
|
||||
candidate_index,
|
||||
tx,
|
||||
),
|
||||
}
|
||||
).await;
|
||||
},
|
||||
)
|
||||
.await;
|
||||
rx
|
||||
}
|
||||
|
||||
@@ -889,16 +816,13 @@ struct ChainBuilder {
|
||||
blocks_at_height: BTreeMap<u32, Vec<Hash>>,
|
||||
}
|
||||
|
||||
|
||||
impl ChainBuilder {
|
||||
const GENESIS_HASH: Hash = Hash::repeat_byte(0xff);
|
||||
const GENESIS_PARENT_HASH: Hash = Hash::repeat_byte(0x00);
|
||||
|
||||
pub fn new() -> Self {
|
||||
let mut builder = Self {
|
||||
blocks_by_hash: HashMap::new(),
|
||||
blocks_at_height: BTreeMap::new(),
|
||||
};
|
||||
let mut builder =
|
||||
Self { blocks_by_hash: HashMap::new(), blocks_at_height: BTreeMap::new() };
|
||||
builder.add_block_inner(Self::GENESIS_HASH, Self::GENESIS_PARENT_HASH, Slot::from(0), 0);
|
||||
builder
|
||||
}
|
||||
@@ -912,7 +836,10 @@ impl ChainBuilder {
|
||||
) -> &'a mut Self {
|
||||
assert!(number != 0, "cannot add duplicate genesis block");
|
||||
assert!(hash != Self::GENESIS_HASH, "cannot add block with genesis hash");
|
||||
assert!(parent_hash != Self::GENESIS_PARENT_HASH, "cannot add block with genesis parent hash");
|
||||
assert!(
|
||||
parent_hash != Self::GENESIS_PARENT_HASH,
|
||||
"cannot add block with genesis parent hash"
|
||||
);
|
||||
assert!(self.blocks_by_hash.len() < u8::MAX.into());
|
||||
self.add_block_inner(hash, parent_hash, slot, number)
|
||||
}
|
||||
@@ -927,7 +854,8 @@ impl ChainBuilder {
|
||||
let header = ChainBuilder::make_header(parent_hash, slot, number);
|
||||
assert!(
|
||||
self.blocks_by_hash.insert(hash, header).is_none(),
|
||||
"block with hash {:?} already exists", hash,
|
||||
"block with hash {:?} already exists",
|
||||
hash,
|
||||
);
|
||||
self.blocks_at_height.entry(number).or_insert_with(Vec::new).push(hash);
|
||||
self
|
||||
@@ -939,7 +867,8 @@ impl ChainBuilder {
|
||||
let mut cur_hash = *hash;
|
||||
let mut ancestry = Vec::new();
|
||||
while cur_hash != Self::GENESIS_PARENT_HASH {
|
||||
let cur_header = self.blocks_by_hash.get(&cur_hash).expect("chain is not contiguous");
|
||||
let cur_header =
|
||||
self.blocks_by_hash.get(&cur_hash).expect("chain is not contiguous");
|
||||
ancestry.push((cur_hash, cur_header.clone()));
|
||||
cur_hash = cur_header.parent_hash;
|
||||
}
|
||||
@@ -950,21 +879,12 @@ impl ChainBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
fn make_header(
|
||||
parent_hash: Hash,
|
||||
slot: Slot,
|
||||
number: u32,
|
||||
) -> Header {
|
||||
fn make_header(parent_hash: Hash, slot: Slot, number: u32) -> Header {
|
||||
let digest = {
|
||||
let mut digest = Digest::default();
|
||||
let (vrf_output, vrf_proof) = garbage_vrf();
|
||||
digest.push(DigestItem::babe_pre_digest(PreDigest::SecondaryVRF(
|
||||
SecondaryVRFPreDigest {
|
||||
authority_index: 0,
|
||||
slot,
|
||||
vrf_output,
|
||||
vrf_proof,
|
||||
}
|
||||
SecondaryVRFPreDigest { authority_index: 0, slot, vrf_output, vrf_proof },
|
||||
)));
|
||||
digest
|
||||
};
|
||||
@@ -976,8 +896,8 @@ impl ChainBuilder {
|
||||
state_root: Default::default(),
|
||||
parent_hash,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn import_block(
|
||||
overseer: &mut VirtualOverseer,
|
||||
@@ -1003,13 +923,16 @@ async fn import_block(
|
||||
let (new_head, new_header) = &hashes[hashes.len() - 1];
|
||||
overseer_send(
|
||||
overseer,
|
||||
FromOverseer::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(ActivatedLeaf {
|
||||
hash: *new_head,
|
||||
number: session,
|
||||
status: LeafStatus::Fresh,
|
||||
span: Arc::new(jaeger::Span::Disabled),
|
||||
})),
|
||||
)).await;
|
||||
FromOverseer::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(
|
||||
ActivatedLeaf {
|
||||
hash: *new_head,
|
||||
number: session,
|
||||
status: LeafStatus::Fresh,
|
||||
span: Arc::new(jaeger::Span::Disabled),
|
||||
},
|
||||
))),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_matches!(
|
||||
overseer_recv(overseer).await,
|
||||
@@ -1070,22 +993,21 @@ async fn import_block(
|
||||
let (hash, header) = hashes[i as usize].clone();
|
||||
assert_eq!(hash, *new_head);
|
||||
h_tx.send(Ok(Some(header))).unwrap();
|
||||
}
|
||||
},
|
||||
AllMessages::ChainApi(ChainApiMessage::Ancestors {
|
||||
hash,
|
||||
k,
|
||||
response_channel,
|
||||
}) => {
|
||||
assert_eq!(hash, *new_head);
|
||||
assert_eq!(k as u32, session-1);
|
||||
assert_eq!(k as u32, session - 1);
|
||||
let history: Vec<Hash> = hashes.iter().map(|v| v.0).take(k).collect();
|
||||
response_channel.send(Ok(history)).unwrap();
|
||||
}
|
||||
_ => unreachable!{},
|
||||
},
|
||||
_ => unreachable! {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if session > 0 {
|
||||
@@ -1184,10 +1106,7 @@ fn linear_import_act_on_leaf() {
|
||||
|
||||
let (oracle, _handle) = make_sync_oracle(false);
|
||||
test_harness(Default::default(), Box::new(oracle), |test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
..
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer, .. } = test_harness;
|
||||
|
||||
let mut head: Hash = ChainBuilder::GENESIS_HASH;
|
||||
let mut builder = ChainBuilder::new();
|
||||
@@ -1197,7 +1116,7 @@ fn linear_import_act_on_leaf() {
|
||||
let hash = Hash::repeat_byte(i as u8);
|
||||
builder.add_block(hash, head, slot, i);
|
||||
head = hash;
|
||||
}
|
||||
}
|
||||
|
||||
builder.build(&mut virtual_overseer).await;
|
||||
|
||||
@@ -1207,18 +1126,19 @@ fn linear_import_act_on_leaf() {
|
||||
&mut virtual_overseer,
|
||||
FromOverseer::Communication {
|
||||
msg: ApprovalVotingMessage::CheckAndImportAssignment(
|
||||
IndirectAssignmentCert{
|
||||
IndirectAssignmentCert {
|
||||
block_hash: head,
|
||||
validator: 0u32.into(),
|
||||
cert: garbage_assignment_cert(
|
||||
AssignmentCertKind::RelayVRFModulo { sample: 0 }
|
||||
),
|
||||
cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo {
|
||||
sample: 0,
|
||||
}),
|
||||
},
|
||||
0u32,
|
||||
tx,
|
||||
)
|
||||
}
|
||||
).await;
|
||||
),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted));
|
||||
|
||||
@@ -1232,10 +1152,7 @@ fn forkful_import_at_same_height_act_on_leaf() {
|
||||
|
||||
let (oracle, _handle) = make_sync_oracle(false);
|
||||
test_harness(Default::default(), Box::new(oracle), |test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
..
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer, .. } = test_harness;
|
||||
|
||||
let mut head: Hash = ChainBuilder::GENESIS_HASH;
|
||||
let mut builder = ChainBuilder::new();
|
||||
@@ -1252,7 +1169,7 @@ fn forkful_import_at_same_height_act_on_leaf() {
|
||||
let slot = Slot::from(session as u64);
|
||||
let hash = Hash::repeat_byte(session as u8 + i);
|
||||
builder.add_block(hash, head, slot, session);
|
||||
}
|
||||
}
|
||||
builder.build(&mut virtual_overseer).await;
|
||||
|
||||
for head in forks.into_iter() {
|
||||
@@ -1262,18 +1179,19 @@ fn forkful_import_at_same_height_act_on_leaf() {
|
||||
&mut virtual_overseer,
|
||||
FromOverseer::Communication {
|
||||
msg: ApprovalVotingMessage::CheckAndImportAssignment(
|
||||
IndirectAssignmentCert{
|
||||
IndirectAssignmentCert {
|
||||
block_hash: head,
|
||||
validator: 0u32.into(),
|
||||
cert: garbage_assignment_cert(
|
||||
AssignmentCertKind::RelayVRFModulo { sample: 0 }
|
||||
),
|
||||
cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo {
|
||||
sample: 0,
|
||||
}),
|
||||
},
|
||||
0u32,
|
||||
tx,
|
||||
)
|
||||
}
|
||||
).await;
|
||||
),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted));
|
||||
}
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
|
||||
//! Time utilities for approval voting.
|
||||
|
||||
use futures::prelude::*;
|
||||
use polkadot_node_primitives::approval::DelayTranche;
|
||||
use sp_consensus_slots::Slot;
|
||||
use futures::prelude::*;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use std::pin::Pin;
|
||||
use std::{
|
||||
pin::Pin,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
const TICK_DURATION_MILLIS: u64 = 500;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user