Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Refactor runtime fn enter(...) to select disputes, bitfields and backing candidates to protect against over length block issues #4020

Closed
wants to merge 14 commits into from

Conversation

Lldenaurois
Copy link
Contributor

TBD

@Lldenaurois Lldenaurois changed the title First attempt at a skeleton diff Refactor runtime fn enter(...) to select disputes, bitfields and backing candidates to protect against over length block issues Oct 5, 2021
@Lldenaurois Lldenaurois added A0-please_review Pull request needs code review. B0-silent Changes should not be mentioned in any release notes C3-medium PR touches the given topic and has a medium impact on builders. I8-refactor Code needs refactoring. D2-notlive 💤 PR contains changes in a runtime directory that is not deployed to a chain that requires an audit. labels Oct 5, 2021
@@ -129,6 +129,8 @@ pub trait DisputesHandler<BlockNumber> {
included_in: BlockNumber,
);

fn get_included(session: SessionIndex, candidate_hash: CandidateHash) -> Option<BlockNumber>;
Copy link
Contributor

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_.

Copy link
Contributor

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 +
Copy link
Contributor

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,
Copy link
Contributor

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);
Copy link
Contributor

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.

Copy link
Contributor

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

Copy link
Contributor

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;
Copy link
Contributor

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,
Copy link
Contributor

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.
Copy link
Contributor

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 {
Copy link
Contributor

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| {
Copy link
Contributor

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 {
Copy link
Contributor

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() }
Copy link
Contributor

@rphmeier rphmeier Oct 11, 2021

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.

@rphmeier
Copy link
Contributor

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.

Copy link
Member

@eskimor eskimor left a 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;
Copy link
Member

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.

Copy link
Member

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.

Copy link
Contributor Author

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,
Copy link
Member

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.

Copy link
Contributor Author

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

@@ -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;
Copy link
Contributor

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.
Copy link
Contributor

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,
Copy link
Contributor

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.

Copy link
Contributor Author

@Lldenaurois Lldenaurois Oct 18, 2021

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.

Comment on lines +442 to +445
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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_ => 0 as Weight,
None => 0 as Weight,

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A0-please_review Pull request needs code review. B0-silent Changes should not be mentioned in any release notes C3-medium PR touches the given topic and has a medium impact on builders. D2-notlive 💤 PR contains changes in a runtime directory that is not deployed to a chain that requires an audit. I8-refactor Code needs refactoring.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants