Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
- fix(eth): handle nil address in trace_filter for failed contract creates ([filecoin-project/lotus#13549](https://github.com/filecoin-project/lotus/pull/13549))
- fix(gas): stricter bounds for GasEstimateGasPremium lookback ([filecoin-project/lotus#13555](https://github.com/filecoin-project/lotus/pull/13555))
- fix(api): `StateSearchMsg` should respect `lookbackLimit` [filecoin-project/lotus#13562](https://github.com/filecoin-project/lotus/pull/13562)
- fix(ecfinality): account for null rounds in EC finality calculator chain walk, aligning with FRC-0089 theoretical model and fixing depth-to-height conversion ([filecoin-project/lotus#13565](https://github.com/filecoin-project/lotus/pull/13565))

# Node and Miner v1.35.0 / 2026-02-19

Expand Down
16 changes: 13 additions & 3 deletions chain/ecfinality/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,18 +116,28 @@ func (c *ECFinalityCache) GetFinalizedTipSet(ctx context.Context) (*types.TipSet
return s.FinalizedTipSet, nil
}

// walkChain walks back from head collecting block counts for the calculator.
// Each LoadTipSet call typically hits the ChainStore's ARC cache.
// walkChain walks back from head collecting block counts per epoch for the
// calculator. Null rounds (epochs with no blocks) are included as 0 entries
// so that the calculator sees the real timeline and the returned array depth
// corresponds directly to epoch height differences. Each LoadTipSet call
// typically hits the ChainStore's ARC cache.
func (c *ECFinalityCache) walkChain(ctx context.Context, head *types.TipSet) ([]int, error) {
needed := c.windowSize
chain := make([]int, 0, needed)
ts := head
for len(chain) < needed {
for {
chain = append(chain, len(ts.Cids()))
if len(chain) >= needed {
break
}
parent, err := c.cs.LoadTipSet(ctx, ts.Parents())
if err != nil {
return nil, err
}
// Insert 0 entries for null rounds between this tipset and its parent.
for nulls := int(ts.Height()-parent.Height()) - 1; nulls > 0 && len(chain) < needed; nulls-- {
chain = append(chain, 0)
}
ts = parent
}
// Reverse to chronological order (oldest first).
Expand Down
4 changes: 2 additions & 2 deletions chain/ecfinality/calculator.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ const (
// BisectLow and BisectHigh define the search range for the bisect algorithm
// that finds the epoch depth at which the finality guarantee is met. A low
// bound of 3 avoids evaluating trivially shallow depths; a high bound of
// 200 accommodates degraded chains that take longer to finalize.
// 450 accommodates degraded chains that take longer to finalize.
BisectLow = 3
BisectHigh = 200
BisectHigh = 450
Comment thread
rvagg marked this conversation as resolved.

// DefaultBlocksPerEpoch is the Filecoin mainnet expected block production rate.
DefaultBlocksPerEpoch = 5.0
Expand Down
6 changes: 3 additions & 3 deletions chain/ecfinality/calculator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,11 @@ func TestFindThresholdDepth_HealthyChain(t *testing.T) {
func TestFindThresholdDepth_DegradedChain(t *testing.T) {
req := require.New(t)

// All-2s chain is too degraded to achieve 2^-30 within the bisect
// search range (BisectHigh=200), so threshold is not found
// All-1s chain is too degraded to achieve 2^-30 within the bisect
// search range (BisectHigh=450), so threshold is not found
chain := make([]int, 905)
for i := range chain {
chain[i] = 2
chain[i] = 1
}
guarantee := math.Pow(2, -30)

Expand Down
13 changes: 10 additions & 3 deletions cmd/lotus-shed/finality.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,20 @@ machine-readable output of all 900 epochs.`,
headEpoch = int(head.Height())
readLength := int(policy.ChainFinality) + 5
chain = append(chain, len(head.Cids()))
for range readLength - 1 {
head, err = api.ChainGetTipSet(ctx, head.Parents())
for len(chain) < readLength {
parent, err := api.ChainGetTipSet(ctx, head.Parents())
if err != nil {
return err
}
chain = append(chain, len(head.Cids()))
// Insert 0 entries for null rounds between this tipset and its parent.
for nulls := int(head.Height()-parent.Height()) - 1; nulls > 0 && len(chain) < readLength; nulls-- {
chain = append(chain, 0)
}
chain = append(chain, len(parent.Cids()))
head = parent
}
// Trim to exact length in case null round insertion overshot.
chain = chain[:readLength]
// API walk produces most-recent-first; reverse to match the
// expected ordering (index 0 = earliest epoch).
slices.Reverse(chain)
Expand Down
Loading