Uh oh!
There was an error while loading. Please reload this page.
feat: finality calculator - #6785
Conversation
WalkthroughAdds the FRC-0089 EC finality calculator: new calculator module (Poisson + Skellam math), Skellam PMF helper, comprehensive tests, Cargo dependency additions, and integration into the Changes
Sequence DiagramsequenceDiagram
participant Client as Client
participant Calc as "calc_validator_prob"
participant Poisson as "Poisson Helpers"
participant Skellam as "Skellam PMF"
participant Normalize as "Truncate & Normalize"
participant Aggregate as "Cumulative Aggregation"
Client->>Calc: call(chain, finality, blocks_per_epoch, byzantine_fraction, current_epoch, target_epoch)
activate Calc
Calc->>Poisson: compute L (adversarial lead) and B (settlement window) distributions
Poisson-->>Calc: L, B arrays
Calc->>Skellam: compute M (future advantage) via Skellam PMF
Skellam-->>Calc: M array
Calc->>Normalize: truncate & renormalize L, B, M distributions
Normalize-->>Calc: normalized distributions
Calc->>Aggregate: nested cumulative sums / convolution-like aggregation
Aggregate-->>Calc: upper-bound reorg probability (clamped to ≤1.0)
Calc-->>Client: Result<f64> (probability)
deactivate Calc
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Suggested reviewers
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/chain/ec_finality/calculator/mod.rs`:
- Around line 223-269: The bisection using (mut low, mut high) with
calc_validator_prob is unsound because calc_validator_prob(depth) is not
strictly monotone; replace the binary search in this function with a forward
linear scan from low = BISECT_LOW (clamped by current_epoch) up to high =
BISECT_HIGH.min(current_epoch), inclusive, calling calc_validator_prob for each
depth (using current_epoch - depth) and return the first depth where prob <
guarantee, otherwise return -1; also remove the early return on low >= high and
ensure the single-depth case (low == high) is checked rather than rejected.
- Around line 62-64: The code truncates blocks_per_epoch by using an integer
cast when computing max_k_b, shrinking support for non-integer block rates;
update the computation to perform the arithmetic in f64 and use ceil() before
casting to usize (e.g., compute ((current_epoch - target_epoch) as f64 *
blocks_per_epoch).ceil() as usize) and apply the same ceil-based fix at the
other identical location where blocks_per_epoch is cast to an integer; keep the
variable names (max_k_b, current_epoch, target_epoch, blocks_per_epoch) to
locate and replace the truncating cast.
In `@src/chain/ec_finality/calculator/skellam.rs`:
- Around line 20-25: The guard in skellam_pmf incorrectly treats mu1==0 or
mu2==0 as invalid; instead handle degenerate Poisson branches explicitly in
skellam_pmf (and the similar logic around lines 95-100) so only negative or
non-finite mus return NaN. Concretely: keep the NaN checks for mu1 or mu2 being
negative or non-finite, but add branches: if mu1==0.0 && mu2==0.0 return 1.0 if
k==0.0 else 0.0; if mu1==0.0 compute the Poisson PMF for N2 with n = -k (integer
check) and return that probability; if mu2==0.0 compute the Poisson PMF for N1
with n = k (integer check) and return that probability. Use or add a helper
poisson_pmf(n: u64, mu: f64) to avoid duplicating formulas and ensure
integer-ness of k before mapping to n.
In `@src/chain/ec_finality/mod.rs`:
- Line 4: The calculator submodule (calculator) is declared but its API isn't
exposed; update this module to publicly re-export the calculator API (e.g.,
expose the module and/or its public symbols from ec_finality by adding a pub use
or pub mod re-export for calculator and selectively pub use the
functions/constants you need such as the calculator types/constants and
functions referenced) and also export the ec_finality module from the parent
chain module (add a public re-export of ec_finality or its key symbols in the
parent module so external code can reach calculator via
ec_finality::calculator); ensure you reference the existing module name
calculator and the enclosing module ec_finality when adding the re-exports.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 2b369bdb-6006-4463-9220-15b20ac58dc3
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (7)
.config/forest.dicCargo.tomlsrc/chain/ec_finality/calculator/mod.rssrc/chain/ec_finality/calculator/skellam.rssrc/chain/ec_finality/calculator/tests.rssrc/chain/ec_finality/mod.rssrc/chain/mod.rs
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Codecov Report❌ Patch coverage is
Additional details and impacted files
... and 4 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/chain/ec_finality/calculator/mod.rs`:
- Line 60: Rename the constant NELIGIBLE_THRESHOLD to NEGLIGIBLE_THRESHOLD in
the declaration and update every usage in this module to the new name (replace
all occurrences of NELIGIBLE_THRESHOLD with NEGLIGIBLE_THRESHOLD) so the
identifier spelling is correct and references compile; ensure any unit tests or
internal matches that reference the old name are updated as well.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: f3556281-c34d-4dd8-b25f-9e60b6bd5189
📒 Files selected for processing (1)
src/chain/ec_finality/calculator/mod.rs
Uh oh!
There was an error while loading. Please reload this page.
LesnyRumcajs
commented
Mar 25, 2026
@hanabi1224 I think it should be relatively easy to cover the remaining LoC with tests; those are some edge cases, but easily constructible. |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/chain/ec_finality/calculator/tests.rs (1)
223-227: Add zero-length chain case to complete short-input coverage.The short-chain parameterization currently starts at length 1. Adding length 0 would guard against accidental empty-slice regressions in
find_threshold_depth.Proposed test tweak
#[rstest] +#[case(0)] #[case(4)] #[case(3)] #[case(2)] #[case(1)] fn test_find_threshold_depth_too_short_chain(#[case] chain_len: usize) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/chain/ec_finality/calculator/tests.rs` around lines 223 - 227, Add a zero-length case to the parameterized short-chain test by adding #[case(0)] to the test_find_threshold_depth_too_short_chain parameterization so the test invokes find_threshold_depth with chain_len == 0; update any test setup inside test_find_threshold_depth_too_short_chain that constructs the slice/vector for the chain (used by find_threshold_depth) to correctly create an empty slice when chain_len is 0 so the test asserts the expected behavior for empty input.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/chain/ec_finality/calculator/tests.rs`:
- Around line 223-227: Add a zero-length case to the parameterized short-chain
test by adding #[case(0)] to the test_find_threshold_depth_too_short_chain
parameterization so the test invokes find_threshold_depth with chain_len == 0;
update any test setup inside test_find_threshold_depth_too_short_chain that
constructs the slice/vector for the chain (used by find_threshold_depth) to
correctly create an empty slice when chain_len is 0 so the test asserts the
expected behavior for empty input.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 1def13c3-df20-416d-990b-96afa74494ce
📒 Files selected for processing (2)
src/chain/ec_finality/calculator/skellam.rssrc/chain/ec_finality/calculator/tests.rs
🚧 Files skipped from review as they are similar to previous changes (1)
- src/chain/ec_finality/calculator/skellam.rs
LesnyRumcajs
left a comment
There was a problem hiding this comment.
I believe in this math and do not believe in God. In both cases, I might be wrong.
@LesnyRumcajs all edge cases are now covered. 4 left missing lines are workaround for slice indexing clippy warnings, which cannot be tested as they are infallible |
Uh oh!
There was an error while loading. Please reload this page.
Summary of changes
Changes introduced in this pull request:
Reference issue to close (if applicable)
Closes#6767
Other information and links
Change checklist
Outside contributions
Summary by CodeRabbit
New Features
Tests
Chores