-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Refactor runtime fn enter(...)
to select disputes, bitfields and backing candidates to protect against over length block issues
#4020
Conversation
a2d157c
to
77a5b7f
Compare
fn enter(...)
to select disputes, bitfields and backing candidates to protect against over length block issues
runtime/parachains/src/disputes.rs
Outdated
@@ -129,6 +129,8 @@ pub trait DisputesHandler<BlockNumber> { | |||
included_in: BlockNumber, | |||
); | |||
|
|||
fn get_included(session: SessionIndex, candidate_hash: CandidateHash) -> Option<BlockNumber>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note #4054 does the same, and it's an antipattern in Rust to call a getter get_
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing docs
MINIMAL_INCLUSION_INHERENT_WEIGHT + data.backed_candidates.len() as Weight * BACKED_CANDIDATE_WEIGHT, | ||
MINIMAL_INCLUSION_INHERENT_WEIGHT + | ||
count_dispute_statements(&data.disputes) as Weight * DISPUTE_PER_STATEMENT_WEIGHT + | ||
count_bitfield_bits(&data.bitfields) as Weight * BITFIELD_WEIGHT + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it really should be based on the number of bitfields, so the amount of signatures checked. That should be the dominant factor
MINIMAL_INCLUSION_INHERENT_WEIGHT + | ||
count_dispute_statements(&data.disputes) as Weight * DISPUTE_PER_STATEMENT_WEIGHT + | ||
count_bitfield_bits(&data.bitfields) as Weight * BITFIELD_WEIGHT + | ||
data.backed_candidates.len() as Weight * BACKED_CANDIDATE_WEIGHT, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could see this being based on the number of signatures checked as well plus a constant for any candidates that do code upgrades.
@@ -177,6 +185,11 @@ pub mod pallet { | |||
Error::<T>::InvalidParentHeader, | |||
); | |||
|
|||
limit_paras_inherent::<T>(&mut disputes, &mut signed_bitfields, &mut backed_candidates); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
limiting shouldn't be done within enter
, but instead outside of it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@drahnr - we had further discussions that imply both should do limiting, but the on-chain limiting should be a no-op
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No-op as in, filter(filter(x))
should be equal to filter(x)
so if on-chain filtering catches anything it was probably not produced by the usual block authoring code or there's a bug.
limit_paras_inherent::<T>(&mut disputes, &mut signed_bitfields, &mut backed_candidates); | ||
let num_dispute_statements = count_dispute_statements(&disputes) as Weight; | ||
let num_bitfield_bits = count_bitfield_bits(&signed_bitfields) as Weight; | ||
let backed_candidates_len = backed_candidates.len() as Weight; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this already done above?
let b_local_block = T::DisputesHandler::get_included(b.session, b.candidate_hash); | ||
match (a_local_block, b_local_block) { | ||
// Prioritize local disputes over remote disputes. | ||
(None, Some(_)) => Ordering::Greater, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this do the opposite? This says a > b if a not local and b local
// Prioritize local disputes over remote disputes. | ||
(None, Some(_)) => Ordering::Greater, | ||
(Some(_), None) => Ordering::Less, | ||
// For local disputes, prioritize those with votes for invalidity. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disputes have to have 1 vote on either side. I don't get it.
return | ||
} | ||
|
||
if disputes_weight + bitfields_weight > max_non_inclusion_weight { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should recalculate disputes weight after filtering.
|
||
if disputes_weight + bitfields_weight > max_non_inclusion_weight { | ||
let mut total_weight = disputes_weight; | ||
bitfields.retain(|b| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we assume that the bitfield weight is constant, this whole block could be replaced by a simple division to check how many bitfields we need to cut off.
return | ||
} | ||
|
||
if total_non_inclusion_weight > max_non_inclusion_weight { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again, new bitfields weight needs to be recalculated.
&signing_context, | ||
)); | ||
|
||
}: { Pallet::<T>::process_bitfields(expected_bits(), vec![signed.into()], core_lookup).unwrap() } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't going to be very useful unless we actually set some occupied cores i.e. CandidatePendingAvailability
in the inclusion
pallet.
This looks very incomplete and not at the state I'd expect after this time. Still, the direction looks ~ok, although it is extremely minimal. It might help to map out in english, not in code, what you intend for this PR to do. It's also worth noting that filtering things based on weight will affect the validity of other things that are included. i.e. disputes affect occupied cores, which affects bitfields, which affects backing. This ties in heavily with #3989 and #4028. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quick first review. Another more in depth one to come.
let backed_candidates_weight = | ||
count_backed_candidate_signatures::<T>(&backed_candidates) as Weight * | ||
BACKED_CANDIDATE_WEIGHT + | ||
count_code_upgrades::<T>(&backed_candidates) as Weight * CODE_UPGRADE_WEIGHT; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CODE_UPGRADE_WEIGHT
can hardly be a constant, as it will depend on the size of the runtime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also I am missing taking into account messages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that the messages are accounted for by ensuring we only consider available weight, i.e. other messages in the block are already deducted from the max_block weight.
.saturating_sub(num_code_upgrades as u64 * CODE_UPGRADE_WEIGHT); | ||
let num_candidates_to_retain = min( | ||
num_backed_candidate_signatures as u64, | ||
block_weight_available / BACKED_CANDIDATE_WEIGHT, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how this can work with a constant, with runtime upgrades + messages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have modified the calculation shortly after you performed the review.
For Code upgrades, I was unaware that this would depend on the size of the runtime, but that makes perfect sense. I assume that will mean that we must unpack the Option on the candidate and determine the weight via NUM_BYTES * PER_BYTE_WEIGHT
9af6191
to
b1818ef
Compare
@@ -21,6 +21,8 @@ | |||
//! as it has no initialization logic and its finalization logic depends only on the details of | |||
//! this module. | |||
|
|||
use no_std_compat::cmp::Ordering; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
usually it's imported as sp_std::cmp
// Sort the dispute statements according to the following prioritization: | ||
// 1. Prioritize local disputes over remote disputes. | ||
// 2. Prioritize older disputes over newer disputes. | ||
// 3. Prioritize local disputes withan invalid vote over valid votes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this comment outdated?
match (a_local_block, b_local_block) { | ||
// Prioritize local disputes over remote disputes. | ||
(None, Some(_)) => Ordering::Less, | ||
(Some(_), None) => Ordering::Greater, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this and the above backwards? local dispute > remote dispute so sort ascending puts remote before local.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was quite certain it was correct before but then you added a comment saying it's backward, so I changed it.
Going to write a test to confirm which of the two is desired.
seed[31] = (index % (1 << 8)) as u8; | ||
seed[30] = ((index >> 8) % (1 << 8)) as u8; | ||
seed[29] = ((index >> 16) % (1 << 8)) as u8; | ||
seed[29] = ((index >> 24) % (1 << 8)) as u8; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seed[31] = (index % (1 << 8)) as u8; | |
seed[30] = ((index >> 8) % (1 << 8)) as u8; | |
seed[29] = ((index >> 16) % (1 << 8)) as u8; | |
seed[29] = ((index >> 24) % (1 << 8)) as u8; | |
seed[31] = (index & 0xFF) as u8; | |
seed[30] = ((index >> 8) & 0xFF) as u8; | |
seed[29] = ((index >> 16) & 0xFF) as u8; | |
seed[29] = ((index >> 24) & 0xFF) as u8; |
candidate.validity_votes.len() as Weight * BACKED_CANDIDATE_WEIGHT + | ||
match &candidate.candidate.commitments.new_validation_code { | ||
Some(v) => v.0.len() as u64 * CODE_UPGRADE_WEIGHT_VARIABLE + CODE_UPGRADE_WEIGHT_FIXED, | ||
_ => 0 as Weight, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_ => 0 as Weight, | |
None => 0 as Weight, |
TBD