Elastic scaling: use an assumed CoreIndex in candidate-backing (#3229)

First step in implementing
https://github.com/paritytech/polkadot-sdk/issues/3144

### Summary of changes
- switch statement `Table` candidate mapping from `ParaId` to
`CoreIndex`
- introduce experimental `InjectCoreIndex`  node feature.
- determine and assume a `CoreIndex` for a candidate based on statement
validator index. If the signature is valid it means validator controls
the validator that index and we can easily map it to a validator
group/core.
- introduce a temporary provisioner fix until we fully enable elastic
scaling in the subystem. The fix ensures we don't fetch the same
backable candidate when calling `get_backable_candidate` for each core.

TODO:
- [x] fix backing tests
- [x] fix statement table tests
- [x] add new test

---------

Signed-off-by: Andrei Sandu <andrei-mihail@parity.io>
Signed-off-by: alindima <alin@parity.io>
Co-authored-by: alindima <alin@parity.io>
This commit is contained in:
Andrei Sandu
2024-02-22 15:22:31 +07:00
committed by GitHub
parent e76b244853
commit 60e537b95f
13 changed files with 462 additions and 96 deletions
+23 -27
View File
@@ -36,6 +36,7 @@ use primitives::{
};
use parity_scale_codec::{Decode, Encode};
const LOG_TARGET: &str = "parachain::statement-table";
/// Context for the statement table.
pub trait Context {
@@ -53,9 +54,6 @@ pub trait Context {
/// get the digest of a candidate.
fn candidate_digest(candidate: &Self::Candidate) -> Self::Digest;
/// get the group of a candidate.
fn candidate_group(candidate: &Self::Candidate) -> Self::GroupId;
/// Whether a authority is a member of a group.
/// Members are meant to submit candidates and vote on validity.
fn is_member_of(&self, authority: &Self::AuthorityId, group: &Self::GroupId) -> bool;
@@ -342,13 +340,13 @@ impl<Ctx: Context> Table<Ctx> {
pub fn import_statement(
&mut self,
context: &Ctx,
group_id: Ctx::GroupId,
statement: SignedStatement<Ctx::Candidate, Ctx::Digest, Ctx::AuthorityId, Ctx::Signature>,
) -> Option<Summary<Ctx::Digest, Ctx::GroupId>> {
let SignedStatement { statement, signature, sender: signer } = statement;
let res = match statement {
Statement::Seconded(candidate) =>
self.import_candidate(context, signer.clone(), candidate, signature),
self.import_candidate(context, signer.clone(), candidate, signature, group_id),
Statement::Valid(digest) =>
self.validity_vote(context, signer.clone(), digest, ValidityVote::Valid(signature)),
};
@@ -387,9 +385,10 @@ impl<Ctx: Context> Table<Ctx> {
authority: Ctx::AuthorityId,
candidate: Ctx::Candidate,
signature: Ctx::Signature,
group: Ctx::GroupId,
) -> ImportResult<Ctx> {
let group = Ctx::candidate_group(&candidate);
if !context.is_member_of(&authority, &group) {
gum::debug!(target: LOG_TARGET, authority = ?authority, group = ?group, "New `Misbehavior::UnauthorizedStatement`, candidate backed by validator that doesn't belong to expected group" );
return Err(Misbehavior::UnauthorizedStatement(UnauthorizedStatement {
statement: SignedStatement {
signature,
@@ -634,10 +633,6 @@ mod tests {
Digest(candidate.1)
}
fn candidate_group(candidate: &Candidate) -> GroupId {
GroupId(candidate.0)
}
fn is_member_of(&self, authority: &AuthorityId, group: &GroupId) -> bool {
self.authorities.get(authority).map(|v| v == group).unwrap_or(false)
}
@@ -675,10 +670,10 @@ mod tests {
sender: AuthorityId(1),
};
table.import_statement(&context, statement_a);
table.import_statement(&context, GroupId(2), statement_a);
assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1)));
table.import_statement(&context, statement_b);
table.import_statement(&context, GroupId(2), statement_b);
assert_eq!(
table.detected_misbehavior[&AuthorityId(1)][0],
Misbehavior::MultipleCandidates(MultipleCandidates {
@@ -711,10 +706,10 @@ mod tests {
sender: AuthorityId(1),
};
table.import_statement(&context, statement_a);
table.import_statement(&context, GroupId(2), statement_a);
assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1)));
table.import_statement(&context, statement_b);
table.import_statement(&context, GroupId(2), statement_b);
assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1)));
}
@@ -735,7 +730,7 @@ mod tests {
sender: AuthorityId(1),
};
table.import_statement(&context, statement);
table.import_statement(&context, GroupId(2), statement);
assert_eq!(
table.detected_misbehavior[&AuthorityId(1)][0],
@@ -769,7 +764,7 @@ mod tests {
};
let candidate_a_digest = Digest(100);
table.import_statement(&context, candidate_a);
table.import_statement(&context, GroupId(2), candidate_a);
assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1)));
assert!(!table.detected_misbehavior.contains_key(&AuthorityId(2)));
@@ -779,7 +774,7 @@ mod tests {
signature: Signature(2),
sender: AuthorityId(2),
};
table.import_statement(&context, bad_validity_vote);
table.import_statement(&context, GroupId(3), bad_validity_vote);
assert_eq!(
table.detected_misbehavior[&AuthorityId(2)][0],
@@ -811,7 +806,7 @@ mod tests {
sender: AuthorityId(1),
};
table.import_statement(&context, statement);
table.import_statement(&context, GroupId(2), statement);
assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1)));
let invalid_statement = SignedStatement {
@@ -820,7 +815,7 @@ mod tests {
sender: AuthorityId(1),
};
table.import_statement(&context, invalid_statement);
table.import_statement(&context, GroupId(2), invalid_statement);
assert!(table.detected_misbehavior.contains_key(&AuthorityId(1)));
}
@@ -842,7 +837,7 @@ mod tests {
};
let candidate_digest = Digest(100);
table.import_statement(&context, statement);
table.import_statement(&context, GroupId(2), statement);
assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1)));
let extra_vote = SignedStatement {
@@ -851,7 +846,7 @@ mod tests {
sender: AuthorityId(1),
};
table.import_statement(&context, extra_vote);
table.import_statement(&context, GroupId(2), extra_vote);
assert_eq!(
table.detected_misbehavior[&AuthorityId(1)][0],
Misbehavior::ValidityDoubleVote(ValidityDoubleVote::IssuedAndValidity(
@@ -910,7 +905,7 @@ mod tests {
};
let candidate_digest = Digest(100);
table.import_statement(&context, statement);
table.import_statement(&context, GroupId(2), statement);
assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1)));
assert!(table.attested_candidate(&candidate_digest, &context, 2).is_none());
@@ -921,7 +916,7 @@ mod tests {
sender: AuthorityId(2),
};
table.import_statement(&context, vote);
table.import_statement(&context, GroupId(2), vote);
assert!(!table.detected_misbehavior.contains_key(&AuthorityId(2)));
assert!(table.attested_candidate(&candidate_digest, &context, 2).is_some());
}
@@ -944,7 +939,7 @@ mod tests {
};
let summary = table
.import_statement(&context, statement)
.import_statement(&context, GroupId(2), statement)
.expect("candidate import to give summary");
assert_eq!(summary.candidate, Digest(100));
@@ -971,7 +966,7 @@ mod tests {
};
let candidate_digest = Digest(100);
table.import_statement(&context, statement);
table.import_statement(&context, GroupId(2), statement);
assert!(!table.detected_misbehavior.contains_key(&AuthorityId(1)));
let vote = SignedStatement {
@@ -980,8 +975,9 @@ mod tests {
sender: AuthorityId(2),
};
let summary =
table.import_statement(&context, vote).expect("candidate vote to give summary");
let summary = table
.import_statement(&context, GroupId(2), vote)
.expect("candidate vote to give summary");
assert!(!table.detected_misbehavior.contains_key(&AuthorityId(2)));
+3 -3
View File
@@ -35,8 +35,8 @@ pub use generic::{Config, Context, Table};
pub mod v2 {
use crate::generic;
use primitives::{
CandidateHash, CommittedCandidateReceipt, CompactStatement as PrimitiveStatement, Id,
ValidatorIndex, ValidatorSignature,
CandidateHash, CommittedCandidateReceipt, CompactStatement as PrimitiveStatement,
CoreIndex, ValidatorIndex, ValidatorSignature,
};
/// Statements about candidates on the network.
@@ -59,7 +59,7 @@ pub mod v2 {
>;
/// A summary of import of a statement.
pub type Summary = generic::Summary<CandidateHash, Id>;
pub type Summary = generic::Summary<CandidateHash, CoreIndex>;
impl<'a> From<&'a Statement> for PrimitiveStatement {
fn from(s: &'a Statement) -> PrimitiveStatement {