mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-19 13:25:44 +00:00
approval votes checking logs (#3233)
* approval-voting: logs for invalid votes * proper errors for assignment checks * proper errors for approval checks
This commit is contained in:
@@ -23,9 +23,10 @@
|
|||||||
|
|
||||||
use polkadot_node_subsystem::{
|
use polkadot_node_subsystem::{
|
||||||
messages::{
|
messages::{
|
||||||
AssignmentCheckResult, ApprovalCheckResult, ApprovalVotingMessage,
|
AssignmentCheckError, AssignmentCheckResult, ApprovalCheckError, ApprovalCheckResult,
|
||||||
RuntimeApiMessage, RuntimeApiRequest, ChainApiMessage, ApprovalDistributionMessage,
|
ApprovalVotingMessage, RuntimeApiMessage, RuntimeApiRequest, ChainApiMessage,
|
||||||
ValidationFailed, CandidateValidationMessage, AvailabilityRecoveryMessage,
|
ApprovalDistributionMessage, ValidationFailed, CandidateValidationMessage,
|
||||||
|
AvailabilityRecoveryMessage,
|
||||||
},
|
},
|
||||||
errors::RecoveryError,
|
errors::RecoveryError,
|
||||||
Subsystem, SubsystemContext, SubsystemError, SubsystemResult, SpawnedSubsystem,
|
Subsystem, SubsystemContext, SubsystemError, SubsystemResult, SpawnedSubsystem,
|
||||||
@@ -1334,14 +1335,17 @@ fn check_and_import_assignment(
|
|||||||
let tick_now = state.clock.tick_now();
|
let tick_now = state.clock.tick_now();
|
||||||
let block_entry = match state.db.load_block_entry(&assignment.block_hash)? {
|
let block_entry = match state.db.load_block_entry(&assignment.block_hash)? {
|
||||||
Some(b) => b,
|
Some(b) => b,
|
||||||
None => return Ok((AssignmentCheckResult::Bad, Vec::new())),
|
None => return Ok((AssignmentCheckResult::Bad(
|
||||||
|
AssignmentCheckError::UnknownBlock(assignment.block_hash),
|
||||||
|
), Vec::new())),
|
||||||
};
|
};
|
||||||
|
|
||||||
let session_info = match state.session_info(block_entry.session()) {
|
let session_info = match state.session_info(block_entry.session()) {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
None => {
|
None => {
|
||||||
tracing::warn!(target: LOG_TARGET, "Unknown session info for {}", block_entry.session());
|
return Ok((AssignmentCheckResult::Bad(
|
||||||
return Ok((AssignmentCheckResult::Bad, Vec::new()));
|
AssignmentCheckError::UnknownSessionIndex(block_entry.session()),
|
||||||
|
), Vec::new()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1349,20 +1353,17 @@ fn check_and_import_assignment(
|
|||||||
= match block_entry.candidate(candidate_index as usize)
|
= match block_entry.candidate(candidate_index as usize)
|
||||||
{
|
{
|
||||||
Some((c, h)) => (*c, *h),
|
Some((c, h)) => (*c, *h),
|
||||||
None => return Ok((AssignmentCheckResult::Bad, Vec::new())), // no candidate at core.
|
None => return Ok((AssignmentCheckResult::Bad(
|
||||||
|
AssignmentCheckError::InvalidCandidateIndex(candidate_index),
|
||||||
|
), Vec::new())), // no candidate at core.
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut candidate_entry = match state.db.load_candidate_entry(&assigned_candidate_hash)? {
|
let mut candidate_entry = match state.db.load_candidate_entry(&assigned_candidate_hash)? {
|
||||||
Some(c) => c,
|
Some(c) => c,
|
||||||
None => {
|
None => {
|
||||||
tracing::warn!(
|
return Ok((AssignmentCheckResult::Bad(
|
||||||
target: LOG_TARGET,
|
AssignmentCheckError::InvalidCandidate(candidate_index, assigned_candidate_hash),
|
||||||
"Missing candidate entry {} referenced in live block {}",
|
), Vec::new()));
|
||||||
assigned_candidate_hash,
|
|
||||||
assignment.block_hash,
|
|
||||||
);
|
|
||||||
|
|
||||||
return Ok((AssignmentCheckResult::Bad, Vec::new()));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1372,7 +1373,9 @@ fn check_and_import_assignment(
|
|||||||
candidate_entry.approval_entry_mut(&assignment.block_hash)
|
candidate_entry.approval_entry_mut(&assignment.block_hash)
|
||||||
{
|
{
|
||||||
Some(a) => a,
|
Some(a) => a,
|
||||||
None => return Ok((AssignmentCheckResult::Bad, Vec::new())),
|
None => return Ok((AssignmentCheckResult::Bad(
|
||||||
|
AssignmentCheckError::Internal(assignment.block_hash, assigned_candidate_hash),
|
||||||
|
), Vec::new())),
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = state.assignment_criteria.check_assignment_cert(
|
let res = state.assignment_criteria.check_assignment_cert(
|
||||||
@@ -1385,7 +1388,9 @@ fn check_and_import_assignment(
|
|||||||
);
|
);
|
||||||
|
|
||||||
let tranche = match res {
|
let tranche = match res {
|
||||||
Err(crate::criteria::InvalidAssignment) => return Ok((AssignmentCheckResult::Bad, Vec::new())),
|
Err(crate::criteria::InvalidAssignment) => return Ok((AssignmentCheckResult::Bad(
|
||||||
|
AssignmentCheckError::InvalidCert(assignment.validator),
|
||||||
|
), Vec::new())),
|
||||||
Ok(tranche) => {
|
Ok(tranche) => {
|
||||||
let current_tranche = state.clock.tranche_now(
|
let current_tranche = state.clock.tranche_now(
|
||||||
state.slot_duration_millis,
|
state.slot_duration_millis,
|
||||||
@@ -1455,20 +1460,27 @@ fn check_and_import_approval<T>(
|
|||||||
|
|
||||||
let block_entry = match state.db.load_block_entry(&approval.block_hash)? {
|
let block_entry = match state.db.load_block_entry(&approval.block_hash)? {
|
||||||
Some(b) => b,
|
Some(b) => b,
|
||||||
None => respond_early!(ApprovalCheckResult::Bad)
|
None => {
|
||||||
|
respond_early!(ApprovalCheckResult::Bad(
|
||||||
|
ApprovalCheckError::UnknownBlock(approval.block_hash),
|
||||||
|
))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let session_info = match state.session_info(block_entry.session()) {
|
let session_info = match state.session_info(block_entry.session()) {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
None => {
|
None => {
|
||||||
tracing::warn!(target: LOG_TARGET, "Unknown session info for {}", block_entry.session());
|
respond_early!(ApprovalCheckResult::Bad(
|
||||||
respond_early!(ApprovalCheckResult::Bad)
|
ApprovalCheckError::UnknownSessionIndex(block_entry.session()),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let approved_candidate_hash = match block_entry.candidate(approval.candidate_index as usize) {
|
let approved_candidate_hash = match block_entry.candidate(approval.candidate_index as usize) {
|
||||||
Some((_, h)) => *h,
|
Some((_, h)) => *h,
|
||||||
None => respond_early!(ApprovalCheckResult::Bad)
|
None => respond_early!(ApprovalCheckResult::Bad(
|
||||||
|
ApprovalCheckError::InvalidCandidateIndex(approval.candidate_index),
|
||||||
|
))
|
||||||
};
|
};
|
||||||
|
|
||||||
let approval_payload = ApprovalVote(approved_candidate_hash)
|
let approval_payload = ApprovalVote(approved_candidate_hash)
|
||||||
@@ -1476,33 +1488,41 @@ fn check_and_import_approval<T>(
|
|||||||
|
|
||||||
let pubkey = match session_info.validators.get(approval.validator.0 as usize) {
|
let pubkey = match session_info.validators.get(approval.validator.0 as usize) {
|
||||||
Some(k) => k,
|
Some(k) => k,
|
||||||
None => respond_early!(ApprovalCheckResult::Bad)
|
None => respond_early!(ApprovalCheckResult::Bad(
|
||||||
|
ApprovalCheckError::InvalidValidatorIndex(approval.validator),
|
||||||
|
))
|
||||||
};
|
};
|
||||||
|
|
||||||
let approval_sig_valid = approval.signature.verify(approval_payload.as_slice(), pubkey);
|
let approval_sig_valid = approval.signature.verify(approval_payload.as_slice(), pubkey);
|
||||||
|
|
||||||
if !approval_sig_valid {
|
if !approval_sig_valid {
|
||||||
respond_early!(ApprovalCheckResult::Bad)
|
respond_early!(ApprovalCheckResult::Bad(
|
||||||
|
ApprovalCheckError::InvalidSignature(approval.validator),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
let candidate_entry = match state.db.load_candidate_entry(&approved_candidate_hash)? {
|
let candidate_entry = match state.db.load_candidate_entry(&approved_candidate_hash)? {
|
||||||
Some(c) => c,
|
Some(c) => c,
|
||||||
None => {
|
None => {
|
||||||
tracing::warn!(
|
respond_early!(ApprovalCheckResult::Bad(
|
||||||
target: LOG_TARGET,
|
ApprovalCheckError::InvalidCandidate(approval.candidate_index, approved_candidate_hash),
|
||||||
"Unknown candidate entry for {}",
|
))
|
||||||
approved_candidate_hash,
|
|
||||||
);
|
|
||||||
|
|
||||||
respond_early!(ApprovalCheckResult::Bad)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Don't accept approvals until assignment.
|
// Don't accept approvals until assignment.
|
||||||
if candidate_entry.approval_entry(&approval.block_hash)
|
match candidate_entry.approval_entry(&approval.block_hash) {
|
||||||
.map_or(true, |e| !e.is_assigned(approval.validator))
|
None => {
|
||||||
{
|
respond_early!(ApprovalCheckResult::Bad(
|
||||||
respond_early!(ApprovalCheckResult::Bad)
|
ApprovalCheckError::Internal(approval.block_hash, approved_candidate_hash),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Some(e) if !e.is_assigned(approval.validator) => {
|
||||||
|
respond_early!(ApprovalCheckResult::Bad(
|
||||||
|
ApprovalCheckError::NoAssignment(approval.validator),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
// importing the approval can be heavy as it may trigger acceptance for a series of blocks.
|
// importing the approval can be heavy as it may trigger acceptance for a series of blocks.
|
||||||
|
|||||||
@@ -397,8 +397,9 @@ fn rejects_bad_assignment() {
|
|||||||
assert!(res.1.iter().any(|action| matches!(action, Action::WriteCandidateEntry(..))));
|
assert!(res.1.iter().any(|action| matches!(action, Action::WriteCandidateEntry(..))));
|
||||||
|
|
||||||
// unknown hash
|
// unknown hash
|
||||||
|
let unknown_hash = Hash::repeat_byte(0x02);
|
||||||
let assignment = IndirectAssignmentCert {
|
let assignment = IndirectAssignmentCert {
|
||||||
block_hash: Hash::repeat_byte(0x02),
|
block_hash: unknown_hash,
|
||||||
validator: ValidatorIndex(0),
|
validator: ValidatorIndex(0),
|
||||||
cert: garbage_assignment_cert(
|
cert: garbage_assignment_cert(
|
||||||
AssignmentCertKind::RelayVRFModulo {
|
AssignmentCertKind::RelayVRFModulo {
|
||||||
@@ -412,7 +413,7 @@ fn rejects_bad_assignment() {
|
|||||||
assignment,
|
assignment,
|
||||||
candidate_index,
|
candidate_index,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
assert_eq!(res.0, AssignmentCheckResult::Bad);
|
assert_eq!(res.0, AssignmentCheckResult::Bad(AssignmentCheckError::UnknownBlock(unknown_hash)));
|
||||||
|
|
||||||
let mut state = State {
|
let mut state = State {
|
||||||
assignment_criteria: Box::new(MockAssignmentCriteria::check_only(|| {
|
assignment_criteria: Box::new(MockAssignmentCriteria::check_only(|| {
|
||||||
@@ -427,7 +428,7 @@ fn rejects_bad_assignment() {
|
|||||||
assignment_good,
|
assignment_good,
|
||||||
candidate_index,
|
candidate_index,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
assert_eq!(res.0, AssignmentCheckResult::Bad);
|
assert_eq!(res.0, AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCert(ValidatorIndex(0))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -495,7 +496,7 @@ fn rejects_assignment_with_unknown_candidate() {
|
|||||||
assignment.clone(),
|
assignment.clone(),
|
||||||
candidate_index,
|
candidate_index,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
assert_eq!(res.0, AssignmentCheckResult::Bad);
|
assert_eq!(res.0, AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCandidateIndex(candidate_index)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -579,7 +580,7 @@ fn rejects_approval_before_assignment() {
|
|||||||
|r| r
|
|r| r
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
assert_eq!(res, ApprovalCheckResult::Bad);
|
assert_eq!(res, ApprovalCheckResult::Bad(ApprovalCheckError::NoAssignment(ValidatorIndex(0))));
|
||||||
assert!(actions.is_empty());
|
assert!(actions.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,7 +612,7 @@ fn rejects_approval_if_no_candidate_entry() {
|
|||||||
|r| r
|
|r| r
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
assert_eq!(res, ApprovalCheckResult::Bad);
|
assert_eq!(res, ApprovalCheckResult::Bad(ApprovalCheckError::InvalidCandidate(0, candidate_hash)));
|
||||||
assert!(actions.is_empty());
|
assert!(actions.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -649,7 +650,7 @@ fn rejects_approval_if_no_block_entry() {
|
|||||||
|r| r
|
|r| r
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
assert_eq!(res, ApprovalCheckResult::Bad);
|
assert_eq!(res, ApprovalCheckResult::Bad(ApprovalCheckError::UnknownBlock(block_hash)));
|
||||||
assert!(actions.is_empty());
|
assert!(actions.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -624,13 +624,29 @@ impl State {
|
|||||||
if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) {
|
if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) {
|
||||||
peer_knowledge.received.insert(fingerprint);
|
peer_knowledge.received.insert(fingerprint);
|
||||||
}
|
}
|
||||||
|
tracing::debug!(
|
||||||
|
target: LOG_TARGET,
|
||||||
|
?peer_id,
|
||||||
|
"Got an `AcceptedDuplicate` assignment",
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AssignmentCheckResult::TooFarInFuture => {
|
AssignmentCheckResult::TooFarInFuture => {
|
||||||
|
tracing::debug!(
|
||||||
|
target: LOG_TARGET,
|
||||||
|
?peer_id,
|
||||||
|
"Got an assignment too far in the future",
|
||||||
|
);
|
||||||
modify_reputation(ctx, peer_id, COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE).await;
|
modify_reputation(ctx, peer_id, COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE).await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AssignmentCheckResult::Bad => {
|
AssignmentCheckResult::Bad(error) => {
|
||||||
|
tracing::info!(
|
||||||
|
target: LOG_TARGET,
|
||||||
|
?peer_id,
|
||||||
|
%error,
|
||||||
|
"Got a bad assignment from peer",
|
||||||
|
);
|
||||||
modify_reputation(ctx, peer_id, COST_INVALID_MESSAGE).await;
|
modify_reputation(ctx, peer_id, COST_INVALID_MESSAGE).await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -844,11 +860,12 @@ impl State {
|
|||||||
peer_knowledge.received.insert(fingerprint.clone());
|
peer_knowledge.received.insert(fingerprint.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ApprovalCheckResult::Bad => {
|
ApprovalCheckResult::Bad(error) => {
|
||||||
modify_reputation(ctx, peer_id, COST_INVALID_MESSAGE).await;
|
modify_reputation(ctx, peer_id, COST_INVALID_MESSAGE).await;
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
target: LOG_TARGET,
|
target: LOG_TARGET,
|
||||||
?peer_id,
|
?peer_id,
|
||||||
|
%error,
|
||||||
"Got a bad approval from peer",
|
"Got a bad approval from peer",
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use futures::{future, Future, executor};
|
use futures::{future, Future, executor};
|
||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
|
use polkadot_node_subsystem::messages::ApprovalCheckError;
|
||||||
use polkadot_node_subsystem_test_helpers as test_helpers;
|
use polkadot_node_subsystem_test_helpers as test_helpers;
|
||||||
use polkadot_node_subsystem_util::TimeoutExt as _;
|
use polkadot_node_subsystem_util::TimeoutExt as _;
|
||||||
use polkadot_node_network_protocol::{view, ObservedRole};
|
use polkadot_node_network_protocol::{view, ObservedRole};
|
||||||
@@ -594,7 +595,7 @@ fn import_approval_bad() {
|
|||||||
tx,
|
tx,
|
||||||
)) => {
|
)) => {
|
||||||
assert_eq!(vote, approval);
|
assert_eq!(vote, approval);
|
||||||
tx.send(ApprovalCheckResult::Bad).unwrap();
|
tx.send(ApprovalCheckResult::Bad(ApprovalCheckError::UnknownBlock(hash))).unwrap();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -646,7 +646,7 @@ impl CollationGenerationMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The result type of [`ApprovalVotingMessage::CheckAndImportAssignment`] request.
|
/// The result type of [`ApprovalVotingMessage::CheckAndImportAssignment`] request.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum AssignmentCheckResult {
|
pub enum AssignmentCheckResult {
|
||||||
/// The vote was accepted and should be propagated onwards.
|
/// The vote was accepted and should be propagated onwards.
|
||||||
Accepted,
|
Accepted,
|
||||||
@@ -655,16 +655,56 @@ pub enum AssignmentCheckResult {
|
|||||||
/// The vote was valid but too far in the future to accept right now.
|
/// The vote was valid but too far in the future to accept right now.
|
||||||
TooFarInFuture,
|
TooFarInFuture,
|
||||||
/// The vote was bad and should be ignored, reporting the peer who propagated it.
|
/// The vote was bad and should be ignored, reporting the peer who propagated it.
|
||||||
Bad,
|
Bad(AssignmentCheckError),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The error result type of [`ApprovalVotingMessage::CheckAndImportAssignment`] request.
|
||||||
|
#[derive(Error, Debug, Clone, PartialEq, Eq)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub enum AssignmentCheckError {
|
||||||
|
#[error("Unknown block: {0:?}")]
|
||||||
|
UnknownBlock(Hash),
|
||||||
|
#[error("Unknown session index: {0}")]
|
||||||
|
UnknownSessionIndex(SessionIndex),
|
||||||
|
#[error("Invalid candidate index: {0}")]
|
||||||
|
InvalidCandidateIndex(CandidateIndex),
|
||||||
|
#[error("Invalid candidate {0}: {1:?}")]
|
||||||
|
InvalidCandidate(CandidateIndex, CandidateHash),
|
||||||
|
#[error("Invalid cert: {0:?}")]
|
||||||
|
InvalidCert(ValidatorIndex),
|
||||||
|
#[error("Internal state mismatch: {0:?}, {1:?}")]
|
||||||
|
Internal(Hash, CandidateHash),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result type of [`ApprovalVotingMessage::CheckAndImportApproval`] request.
|
/// The result type of [`ApprovalVotingMessage::CheckAndImportApproval`] request.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum ApprovalCheckResult {
|
pub enum ApprovalCheckResult {
|
||||||
/// The vote was accepted and should be propagated onwards.
|
/// The vote was accepted and should be propagated onwards.
|
||||||
Accepted,
|
Accepted,
|
||||||
/// The vote was bad and should be ignored, reporting the peer who propagated it.
|
/// The vote was bad and should be ignored, reporting the peer who propagated it.
|
||||||
Bad,
|
Bad(ApprovalCheckError)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The error result type of [`ApprovalVotingMessage::CheckAndImportApproval`] request.
|
||||||
|
#[derive(Error, Debug, Clone, PartialEq, Eq)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub enum ApprovalCheckError {
|
||||||
|
#[error("Unknown block: {0:?}")]
|
||||||
|
UnknownBlock(Hash),
|
||||||
|
#[error("Unknown session index: {0}")]
|
||||||
|
UnknownSessionIndex(SessionIndex),
|
||||||
|
#[error("Invalid candidate index: {0}")]
|
||||||
|
InvalidCandidateIndex(CandidateIndex),
|
||||||
|
#[error("Invalid validator index: {0:?}")]
|
||||||
|
InvalidValidatorIndex(ValidatorIndex),
|
||||||
|
#[error("Invalid candidate {0}: {1:?}")]
|
||||||
|
InvalidCandidate(CandidateIndex, CandidateHash),
|
||||||
|
#[error("Invalid signature: {0:?}")]
|
||||||
|
InvalidSignature(ValidatorIndex),
|
||||||
|
#[error("No assignment for {0:?}")]
|
||||||
|
NoAssignment(ValidatorIndex),
|
||||||
|
#[error("Internal state mismatch: {0:?}, {1:?}")]
|
||||||
|
Internal(Hash, CandidateHash),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Message to the Approval Voting subsystem.
|
/// Message to the Approval Voting subsystem.
|
||||||
|
|||||||
@@ -58,14 +58,34 @@ enum AssignmentCheckResult {
|
|||||||
// The vote was valid but too far in the future to accept right now.
|
// The vote was valid but too far in the future to accept right now.
|
||||||
TooFarInFuture,
|
TooFarInFuture,
|
||||||
// The vote was bad and should be ignored, reporting the peer who propagated it.
|
// The vote was bad and should be ignored, reporting the peer who propagated it.
|
||||||
Bad,
|
Bad(AssignmentCheckError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum AssignmentCheckError {
|
||||||
|
UnknownBlock(Hash),
|
||||||
|
UnknownSessionIndex(SessionIndex),
|
||||||
|
InvalidCandidateIndex(CandidateIndex),
|
||||||
|
InvalidCandidate(CandidateIndex, CandidateHash),
|
||||||
|
InvalidCert(ValidatorIndex),
|
||||||
|
Internal(Hash, CandidateHash),
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ApprovalCheckResult {
|
enum ApprovalCheckResult {
|
||||||
// The vote was accepted and should be propagated onwards.
|
// The vote was accepted and should be propagated onwards.
|
||||||
Accepted,
|
Accepted,
|
||||||
// The vote was bad and should be ignored, reporting the peer who propagated it.
|
// The vote was bad and should be ignored, reporting the peer who propagated it.
|
||||||
Bad,
|
Bad(ApprovalCheckError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ApprovalCheckError {
|
||||||
|
UnknownBlock(Hash),
|
||||||
|
UnknownSessionIndex(SessionIndex),
|
||||||
|
InvalidCandidateIndex(CandidateIndex),
|
||||||
|
InvalidValidatorIndex(ValidatorIndex),
|
||||||
|
InvalidCandidate(CandidateIndex, CandidateHash),
|
||||||
|
InvalidSignature(ValidatorIndex),
|
||||||
|
NoAssignment(ValidatorIndex),
|
||||||
|
Internal(Hash, CandidateHash),
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ApprovalVotingMessage {
|
enum ApprovalVotingMessage {
|
||||||
|
|||||||
Reference in New Issue
Block a user