Make sure to preserve backing votes (#6382)

* Guide updates

* Consider more dead forks.

* Ensure backing votes don't get overridden.

* Fix spelling.

* Fix comments.

* Update node/primitives/src/lib.rs

Co-authored-by: Tsvetomir Dimitrov <tsvetomir@parity.io>

Co-authored-by: eskimor <eskimor@no-such-url.com>
Co-authored-by: Tsvetomir Dimitrov <tsvetomir@parity.io>
This commit is contained in:
eskimor
2022-12-07 18:14:13 +01:00
committed by GitHub
parent 1b9f62cd73
commit c748250748
11 changed files with 301 additions and 183 deletions
+98 -2
View File
@@ -14,7 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use std::collections::{BTreeMap, BTreeSet};
use std::collections::{
btree_map::{Entry as Bentry, Keys as Bkeys},
BTreeMap, BTreeSet,
};
use parity_scale_codec::{Decode, Encode};
@@ -49,7 +52,7 @@ pub struct CandidateVotes {
/// The receipt of the candidate itself.
pub candidate_receipt: CandidateReceipt,
/// Votes of validity, sorted by validator index.
pub valid: BTreeMap<ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature)>,
pub valid: ValidCandidateVotes,
/// Votes of invalidity, sorted by validator index.
pub invalid: BTreeMap<ValidatorIndex, (InvalidDisputeStatementKind, ValidatorSignature)>,
}
@@ -69,6 +72,99 @@ impl CandidateVotes {
}
}
#[derive(Debug, Clone)]
/// Valid candidate votes.
///
/// Prefere backing votes over other votes.
pub struct ValidCandidateVotes {
votes: BTreeMap<ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature)>,
}
impl ValidCandidateVotes {
/// Create new empty `ValidCandidateVotes`
pub fn new() -> Self {
Self { votes: BTreeMap::new() }
}
/// Insert a vote, replacing any already existing vote.
///
/// Except, for backing votes: Backing votes are always kept, and will never get overridden.
/// Import of other king of `valid` votes, will be ignored if a backing vote is already
/// present. Any already existing `valid` vote, will be overridden by any given backing vote.
///
/// Returns: true, if the insert had any effect.
pub fn insert_vote(
&mut self,
validator_index: ValidatorIndex,
kind: ValidDisputeStatementKind,
sig: ValidatorSignature,
) -> bool {
match self.votes.entry(validator_index) {
Bentry::Vacant(vacant) => {
vacant.insert((kind, sig));
true
},
Bentry::Occupied(mut occupied) => match occupied.get().0 {
ValidDisputeStatementKind::BackingValid(_) |
ValidDisputeStatementKind::BackingSeconded(_) => false,
ValidDisputeStatementKind::Explicit |
ValidDisputeStatementKind::ApprovalChecking => {
occupied.insert((kind, sig));
kind != occupied.get().0
},
},
}
}
/// Retain any votes that match the given criteria.
pub fn retain<F>(&mut self, f: F)
where
F: FnMut(&ValidatorIndex, &mut (ValidDisputeStatementKind, ValidatorSignature)) -> bool,
{
self.votes.retain(f)
}
/// Get all the validator indeces we have votes for.
pub fn keys(
&self,
) -> Bkeys<'_, ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature)> {
self.votes.keys()
}
/// Get read only direct access to underlying map.
pub fn raw(
&self,
) -> &BTreeMap<ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature)> {
&self.votes
}
}
impl FromIterator<(ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature))>
for ValidCandidateVotes
{
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = (ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature))>,
{
Self { votes: BTreeMap::from_iter(iter) }
}
}
impl From<ValidCandidateVotes>
for BTreeMap<ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature)>
{
fn from(wrapped: ValidCandidateVotes) -> Self {
wrapped.votes
}
}
impl IntoIterator for ValidCandidateVotes {
type Item = (ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature));
type IntoIter = <BTreeMap<ValidatorIndex, (ValidDisputeStatementKind, ValidatorSignature)> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.votes.into_iter()
}
}
impl SignedDisputeStatement {
/// Create a new `SignedDisputeStatement` from information
/// that is available on-chain, and hence already can be trusted.
+24 -4
View File
@@ -30,10 +30,10 @@ use parity_scale_codec::{Decode, Encode, Error as CodecError, Input};
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use polkadot_primitives::v2::{
BlakeTwo256, CandidateCommitments, CandidateHash, CollatorPair, CommittedCandidateReceipt,
CompactStatement, EncodeAs, Hash, HashT, HeadData, Id as ParaId, OutboundHrmpMessage,
PersistedValidationData, SessionIndex, Signed, UncheckedSigned, UpwardMessage, ValidationCode,
ValidatorIndex, MAX_CODE_SIZE, MAX_POV_SIZE,
BlakeTwo256, BlockNumber, CandidateCommitments, CandidateHash, CollatorPair,
CommittedCandidateReceipt, CompactStatement, EncodeAs, Hash, HashT, HeadData, Id as ParaId,
OutboundHrmpMessage, PersistedValidationData, SessionIndex, Signed, UncheckedSigned,
UpwardMessage, ValidationCode, ValidatorIndex, MAX_CODE_SIZE, MAX_POV_SIZE,
};
pub use sp_consensus_babe::{
AllowedSlots as BabeAllowedSlots, BabeEpochConfiguration, Epoch as BabeEpoch,
@@ -73,8 +73,28 @@ pub const BACKING_EXECUTION_TIMEOUT: Duration = Duration::from_secs(2);
/// ensure that in the absence of extremely large disparities between hardware,
/// blocks that pass backing are considered executable by approval checkers or
/// dispute participants.
///
/// NOTE: If this value is increased significantly, also check the dispute coordinator to consider
/// candidates longer into finalization: `DISPUTE_CANDIDATE_LIFETIME_AFTER_FINALIZATION`.
pub const APPROVAL_EXECUTION_TIMEOUT: Duration = Duration::from_secs(12);
/// How many blocks after finalization an information about backed/included candidate should be
/// kept.
///
/// We don't want to remove scraped candidates on finalization because we want to
/// be sure that disputes will conclude on abandoned forks.
/// Removing the candidate on finalization creates a possibility for an attacker to
/// avoid slashing. If a bad fork is abandoned too quickly because another
/// better one gets finalized the entries for the bad fork will be pruned and we
/// might never participate in a dispute for it.
///
/// This value should consider the timeout we allow for participation in approval-voting. In
/// particular, the following condition should hold:
///
/// slot time * `DISPUTE_CANDIDATE_LIFETIME_AFTER_FINALIZATION` > `APPROVAL_EXECUTION_TIMEOUT`
/// + slot time
pub const DISPUTE_CANDIDATE_LIFETIME_AFTER_FINALIZATION: BlockNumber = 10;
/// Linked to `MAX_FINALITY_LAG` in relay chain selection,
/// `MAX_HEADS_LOOK_BACK` in `approval-voting` and
/// `MAX_BATCH_SCRAPE_ANCESTORS` in `dispute-coordinator`