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
| included_in: BlockNumber, | ||
| ); | ||
|
|
||
| fn get_included(session: SessionIndex, candidate_hash: CandidateHash) -> Option<BlockNumber>; |
There was a problem hiding this comment.
Note #4054 does the same, and it's an antipattern in Rust to call a getter get_.
| 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.
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.
I could see this being based on the number of signatures checked as well plus a constant for any candidates that do code upgrades.
| Error::<T>::InvalidParentHeader, | ||
| ); | ||
|
|
||
| limit_paras_inherent::<T>(&mut disputes, &mut signed_bitfields, &mut backed_candidates); |
There was a problem hiding this comment.
limiting shouldn't be done within enter, but instead outside of it.
There was a problem hiding this comment.
@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.
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.
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.
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.
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.
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.
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.
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.
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. |
eskimor
left a comment
There was a problem hiding this comment.
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.
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.
Also I am missing taking into account messages.
There was a problem hiding this comment.
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.
Not sure how this can work with a constant, with runtime upgrades + messages.
There was a problem hiding this comment.
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
| //! 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.
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.
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.
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.
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.
| 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.
| _ => 0 as Weight, | |
| None => 0 as Weight, |
TBD