Chain Selection: Follow-ups (#3328)

* DB skeleton

* key formats

* lexicographic test

* custom types for DB

* implement backend for db-v1

* remove VoidBackend and integrate with real DbBackend

* detect stagnant blocks on in interval

* fix tests

* add tests for stagnant

* send ChainSelectionMessage::Approved

* tests for DB backend

* unused import

* upgrade kvdb-memorydb

Co-authored-by: Andronik Ordian <write@reusable.software>
This commit is contained in:
Robert Habermeier
2021-07-06 16:00:52 -05:00
committed by GitHub
parent 5ba0de035e
commit f69c175119
12 changed files with 1353 additions and 144 deletions
@@ -423,17 +423,21 @@ pub(crate) fn add_block_entry(
/// Forcibly approve all candidates included at up to the given relay-chain height in the indicated
/// chain.
///
/// Returns a list of block hashes that were not approved and are now.
pub fn force_approve(
store: &dyn KeyValueDB,
db_config: Config,
chain_head: Hash,
up_to: BlockNumber,
) -> Result<()> {
) -> Result<Vec<Hash>> {
enum State {
WalkTo,
Approving,
}
let mut approved_hashes = Vec::new();
let mut cur_hash = chain_head;
let mut state = State::WalkTo;
@@ -452,13 +456,20 @@ pub fn force_approve(
match state {
State::WalkTo => {},
State::Approving => {
entry.approved_bitfield.iter_mut().for_each(|mut b| *b = true);
tx.put_block_entry(entry);
let is_approved = entry.approved_bitfield.count_ones()
== entry.approved_bitfield.len();
if !is_approved {
entry.approved_bitfield.iter_mut().for_each(|mut b| *b = true);
approved_hashes.push(entry.block_hash);
tx.put_block_entry(entry);
}
}
}
}
tx.write(store)
tx.write(store)?;
Ok(approved_hashes)
}
/// Return all blocks which have entries in the DB, ascending, by height.
@@ -534,7 +534,7 @@ fn force_approve_works() {
).unwrap();
}
force_approve(&store, TEST_CONFIG, block_hash_d, 2).unwrap();
let approved_hashes = force_approve(&store, TEST_CONFIG, block_hash_d, 2).unwrap();
assert!(load_block_entry(
&store,
@@ -556,6 +556,10 @@ fn force_approve_works() {
&TEST_CONFIG,
&block_hash_d,
).unwrap().unwrap().approved_bitfield.not_any());
assert_eq!(
approved_hashes,
vec![block_hash_b, block_hash_a],
);
}
#[test]
@@ -31,6 +31,7 @@
use polkadot_node_subsystem::{
messages::{
RuntimeApiMessage, RuntimeApiRequest, ChainApiMessage, ApprovalDistributionMessage,
ChainSelectionMessage,
},
SubsystemContext, SubsystemError, SubsystemResult,
};
@@ -462,10 +463,16 @@ pub(crate) async fn handle_new_head(
result.len(),
);
}
result
}
};
// If all bits are already set, then send an approve message.
if approved_bitfield.count_ones() == approved_bitfield.len() {
ctx.send_message(ChainSelectionMessage::Approved(block_hash).into()).await;
}
let block_entry = approval_db::v1::BlockEntry {
block_hash,
parent_hash: block_header.parent_hash,
@@ -487,8 +494,18 @@ pub(crate) async fn handle_new_head(
"Enacting force-approve",
);
approval_db::v1::force_approve(db_writer, db_config, block_hash, up_to)
let approved_hashes = approval_db::v1::force_approve(
db_writer,
db_config,
block_hash,
up_to,
)
.map_err(|e| SubsystemError::with_origin("approval-voting", e))?;
// Notify chain-selection of all approved hashes.
for hash in approved_hashes {
ctx.send_message(ChainSelectionMessage::Approved(hash).into()).await;
}
}
tracing::trace!(
@@ -26,7 +26,7 @@ use polkadot_node_subsystem::{
AssignmentCheckError, AssignmentCheckResult, ApprovalCheckError, ApprovalCheckResult,
ApprovalVotingMessage, RuntimeApiMessage, RuntimeApiRequest, ChainApiMessage,
ApprovalDistributionMessage, CandidateValidationMessage,
AvailabilityRecoveryMessage,
AvailabilityRecoveryMessage, ChainSelectionMessage,
},
errors::RecoveryError,
Subsystem, SubsystemContext, SubsystemError, SubsystemResult, SpawnedSubsystem,
@@ -717,6 +717,7 @@ enum Action {
candidate: CandidateReceipt,
backing_group: GroupIndex,
},
NoteApprovedInChainSelection(Hash),
IssueApproval(CandidateHash, ApprovalVoteRequest),
BecomeActive,
Conclude,
@@ -962,6 +963,9 @@ async fn handle_actions(
Some(_) => {},
}
}
Action::NoteApprovedInChainSelection(block_hash) => {
ctx.send_message(ChainSelectionMessage::Approved(block_hash).into()).await;
}
Action::BecomeActive => {
*mode = Mode::Active;
@@ -1805,6 +1809,7 @@ fn import_checked_approval(
if is_block_approved && !was_block_approved {
metrics.on_block_approved(status.tranche_now as _);
actions.push(Action::NoteApprovedInChainSelection(block_hash));
}
actions.push(Action::WriteBlockEntry(block_entry));
@@ -850,6 +850,14 @@ fn import_checked_approval_updates_entries_and_schedules() {
assert_matches!(
actions.get(0).unwrap(),
Action::NoteApprovedInChainSelection(h) => {
assert_eq!(h, &block_hash);
}
);
assert_matches!(
actions.get(1).unwrap(),
Action::WriteBlockEntry(b_entry) => {
assert_eq!(b_entry.block_hash(), block_hash);
assert!(b_entry.is_fully_approved());
@@ -857,7 +865,7 @@ fn import_checked_approval_updates_entries_and_schedules() {
}
);
assert_matches!(
actions.get_mut(1).unwrap(),
actions.get_mut(2).unwrap(),
Action::WriteCandidateEntry(c_hash, ref mut c_entry) => {
assert_eq!(c_hash, &candidate_hash);
assert!(c_entry.approval_entry(&block_hash).unwrap().is_approved());
@@ -1391,9 +1399,16 @@ fn import_checked_approval_sets_one_block_bit_at_a_time() {
ApprovalSource::Remote(validator_index_b),
);
assert_eq!(actions.len(), 2);
assert_eq!(actions.len(), 3);
assert_matches!(
actions.get(0).unwrap(),
Action::NoteApprovedInChainSelection(h) => {
assert_eq!(h, &block_hash);
}
);
assert_matches!(
actions.get(1).unwrap(),
Action::WriteBlockEntry(b_entry) => {
assert_eq!(b_entry.block_hash(), block_hash);
assert!(b_entry.is_fully_approved());
@@ -1403,7 +1418,7 @@ fn import_checked_approval_sets_one_block_bit_at_a_time() {
);
assert_matches!(
actions.get(1).unwrap(),
actions.get(2).unwrap(),
Action::WriteCandidateEntry(c_h, c_entry) => {
assert_eq!(c_h, &candidate_hash_2);
assert!(c_entry.approval_entry(&block_hash).unwrap().is_approved());