Conversation
| /// Expected committee duties per epoch that are due to only sync committee beacon duties | ||
| fn expected_single_sc_committee_duties_per_epoch( | ||
| num_validators: usize, | ||
| slots_per_epoch: u32, | ||
| sync_committee_size: f64, | ||
| ) -> f64 { | ||
| if num_validators == 0 { | ||
| return 0.0; | ||
| } | ||
|
|
||
| // If the committee has more validators than our limit, return the limit value | ||
| if num_validators >= MAX_VALIDATORS_PER_COMMITTEE { | ||
| return SINGLE_SC_DUTIES_LIMIT; | ||
| } | ||
|
|
||
| // Probability that a validator is not in sync committee | ||
| let sync_committee_probability = sync_committee_size / ETHEREUM_VALIDATORS; | ||
| let chance_of_not_being_in_sync_committee = 1.0 - sync_committee_probability; | ||
|
|
||
| // Probability that all validators are not in sync committee | ||
| let chance_that_all_validators_are_not_in_sync_committee = | ||
| chance_of_not_being_in_sync_committee.powf(num_validators as f64); | ||
|
|
||
| // Probability that at least one validator is in sync committee | ||
| let chance_of_at_least_one_validator_being_in_sync_committee = | ||
| 1.0 - chance_that_all_validators_are_not_in_sync_committee; | ||
|
|
||
| // Expected number of slots with no attestation duty | ||
| let expected_slots_with_no_duty = slots_per_epoch as f64 | ||
| - expected_committee_duties_per_epoch_due_to_attestation(num_validators, slots_per_epoch); | ||
|
|
||
| // Expected number of committee duties per epoch created due to only sync committee duties | ||
| chance_of_at_least_one_validator_being_in_sync_committee * expected_slots_with_no_duty | ||
| } |
There was a problem hiding this comment.
This is comment is not a critique of your implementation, but of the approach used by Go-SSV.
This does not make sense to me, especially for small committees. We calculate a miniscule chance here and multiply it by slots. The result might be accurate if we are considering the message rate over YEARS, but as soon as a validator in a small committee hits a sync committee duty, we easily exceed the calculated rate for several hours. So, if we get unlucky, the calculated rate is far off the actual rate.
There was a problem hiding this comment.
We calculate a miniscule chance here
Could you please elaborate more?
There was a problem hiding this comment.
You might be right. That's a graph generated with AI, and based on some test runs, it seems correct. I'm not sure if I understand your point, but the calculated value is always very small.
There was a problem hiding this comment.
Ok, I get it now
The helper that returns an epoch-level mean is the wrong tool for sizing a live gossipsub topic. Sync-committee traffic is a bursty, low-frequency, high-magnitude phenomenon: for most epochs it is zero, but the instant one of your validators hits the 27-hour sync-committee “lottery,” you get 32 extra messages every slot (8 192 messages in the period) — several orders of magnitude above the average.
There was a problem hiding this comment.
I see your point, but this discrepancy is somewhat irrelevant. Also, the operator sending more messages than expected presents no harm. Let me walk through why I think that:
So, the total GossipSub score for a peer is
Above, I abstracted how each counter is used, but more precisely:
$P_1 = min(P_{1c}, Cap)$ $P_2 = min(P_{2c}, Cap)$ $P_3 = (Threshold - P_{3c})^2$ $P_4 = P_{4c}^2$
Anyway, the only two positive subscores are P1 and P2, which may contribute to a maximum positive score of ~32.
Any negative score, when contributing, will decrease the final score dramatically. E.g., in case of a malicious message,
The P2 subscore increases the score of the peer based on "first seen deliveries", and that's where the function expected_single_sc_committee_duties_per_epoch will be used to help compute the topic's expected msg rate.
More precisely, letting:
-
$d$ be the decay -
$S$ the target mesh size -
$r$ the expected message rate
We compute the P2 cap by
Then, the weight is computed as
where 80 is the maximum contribution of P2.
Alright, going back to expected_single_sc_committee_duties_per_epoch, the influence of it on
Sorry for the long text 😅
I tried to explain as clearly as possible why, from my perspective, this discrepancy is a bit irrelevant.
Note
Expected message rate may also be used for P3, which penalises the score, but it's currently unused.
There was a problem hiding this comment.
Thanks a lot for the clear explanation! I don't mind the long text at all ;)
If expected_single_sc_committee_duties_per_epoch is irrelevant in practical terms, do you think it'd be better to remove it and have less code to understand and maintain?
| const ESTIMATED_ATTESTATION_COMMITTEE_SIZE: f64 = ETHEREUM_VALIDATORS / 2048.0; | ||
| const AGGREGATOR_PROBABILITY: f64 = 16.0 / ESTIMATED_ATTESTATION_COMMITTEE_SIZE; |
There was a problem hiding this comment.
2048.0 is hardcoding E::MaxValidatorsPerCommittee, 16.0 is hardcoding ChainSpec::target_aggregators_per_committee.
There was a problem hiding this comment.
@nkryuchkov these values are still hardcoded in stage, is it an issue?
There was a problem hiding this comment.
@diegomrsantos thanks for noticing this. It's not critical and we will unlikely ever change it in test networks, but probably we'll need to consider moving this to network config
There was a problem hiding this comment.
Pull Request Overview
This PR integrates a new message rate calculation into topic scoring by introducing a message_rate module and updating topic scoring configuration to use it with ChainSpec and an EthSpec generic.
- Add
message_rate.rsto compute expected message rates for gossipsub topics based on committee configurations. - Update
TopicScoringOptions::newandtopic_score_params_for_subnetto accept aChainSpecand usecalculate_message_rate_for_topic. - Expose the new
message_ratemodule inscoring/mod.rs.
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| anchor/network/src/scoring/topic_score_config.rs | Updated TopicScoringOptions::new and topic_score_params_for_subnet to call into the new message rate calculation with a ChainSpec and EthSpec generic |
| anchor/network/src/scoring/mod.rs | Added mod message_rate; to include the new module |
| anchor/network/src/scoring/message_rate.rs | New module implementing calculate_message_rate_for_topic, helper functions, and tests |
Comments suppressed due to low confidence (1)
anchor/network/src/scoring/topic_score_config.rs:314
- The signature for
topic_score_params_for_subnetnow takes achain_specparameter; the doc comment should be updated to mentionchain_spec(and remove any outdated reference toone_epoch_duration).
/// Generate topic score parameters for a specific subnet
dknopik
left a comment
There was a problem hiding this comment.
LGTM, thanks a lot for this! Feel free to merge if there is no outstanding TODO from your point of view.
Not tested on a node yet as we'll wire it up with the next PR.
| // TODO: It depends on duties per epoch, 32 duties per epoch maps to | ||
| // 560. If the value of duties per epoch changes, this value needs | ||
| // to be adjusted (need to run Monte Carlo simulation for that number). |
There was a problem hiding this comment.
Good catch! Need to keep this is mind especially if the number of slots increases (though this is unlikely IMO).

Issue Addressed
#371
Proposed Changes
This PR integrates a new message rate calculation into topic scoring by introducing a
message_ratemodule and updating topic scoring configuration to use it withChainSpecand anEthSpecgeneric.message_rate.rsto compute expected message rates for gossipsub topics based on committee configurations.TopicScoringOptions::newandtopic_score_params_for_subnetto accept aChainSpecand usecalculate_message_rate_for_topic.message_ratemodule inscoring/mod.rs.Additional Info
Please provide any additional information. For example, future considerations
or information useful for reviewers.