@@ -3,6 +3,7 @@ package lf3
33import (
44 "context"
55 "errors"
6+ "fmt"
67 "path/filepath"
78
89 "github.com/ipfs/go-datastore"
@@ -18,9 +19,11 @@ import (
1819 "github.com/filecoin-project/go-f3/certs"
1920 "github.com/filecoin-project/go-f3/gpbft"
2021 "github.com/filecoin-project/go-f3/manifest"
22+ "github.com/filecoin-project/go-state-types/abi"
2123
2224 "github.com/filecoin-project/lotus/api"
2325 "github.com/filecoin-project/lotus/chain"
26+ "github.com/filecoin-project/lotus/chain/actors/policy"
2427 "github.com/filecoin-project/lotus/chain/stmgr"
2528 "github.com/filecoin-project/lotus/chain/store"
2629 "github.com/filecoin-project/lotus/chain/types"
@@ -82,6 +85,7 @@ func New(mctx helpers.MetricsCtx, lc fx.Lifecycle, params F3Params) (*F3, error)
8285 status := func () (* manifest.Manifest , gpbft.Instant ) {
8386 return module .Manifest (), module .Progress ()
8487 }
88+
8589 fff := & F3 {
8690 inner : module ,
8791 ec : ec ,
@@ -228,3 +232,143 @@ func (fff *F3) ListParticipants() []api.F3Participant {
228232 }
229233 return participants
230234}
235+
236+ // ResolveTipSetKeySelector resolves a TipSetSelector into a concrete TipSetKey.
237+ //
238+ // For TipSetKey selectors, returns the key directly.
239+ // For epoch descriptors:
240+ // - "latest" returns the current heaviest tipset
241+ // - "finalized" returns the most recently finalized tipset, using either F3
242+ // or EC finality depending on whether F3 is enabled and running.
243+ //
244+ // Returns:
245+ // - TipSetKey: The resolved tipset key (this may be EmptyTSK if that was
246+ // the selector)
247+ // - error: On invalid selectors or resolution failures
248+ func ResolveTipSetKeySelector (ctx context.Context , fff * F3 , chain * store.ChainStore , tss types.TipSetSelector ) (types.TipSetKey , error ) {
249+ if tss .TipSetKey != nil {
250+ return * tss .TipSetKey , nil
251+ }
252+ if tss .EpochDescriptor == nil {
253+ return types .EmptyTSK , errors .New ("invalid tipset selector" )
254+ }
255+ switch * tss .EpochDescriptor {
256+ case types .EpochLatest :
257+ return chain .GetHeaviestTipSet ().Key (), nil
258+ case types .EpochFinalized :
259+ if fff != nil { // F3 is enabled
260+ cert , err := fff .inner .GetLatestCert (ctx )
261+ if err == nil { // F3 is running
262+ tsk , err := types .TipSetKeyFromBytes (cert .ECChain .Head ().Key )
263+ if err != nil {
264+ return types .EmptyTSK , xerrors .Errorf ("decoding tipset key reported by F3: %w" , err )
265+ }
266+ return tsk , nil
267+ }
268+ }
269+ // either F3 isn't enabled, or isn't running
270+ head := chain .GetHeaviestTipSet ()
271+ finalizedHeight := head .Height () - policy .ChainFinality
272+ ts , err := chain .GetTipsetByHeight (ctx , finalizedHeight , head , true )
273+ if err != nil {
274+ return types .EmptyTSK , xerrors .Errorf ("getting tipset by height: %w" , err )
275+ }
276+ return ts .Key (), nil
277+ }
278+ return types .EmptyTSK , fmt .Errorf ("invalid epoch descriptor: %s" , * tss .EpochDescriptor )
279+ }
280+
281+ // ResolveTipSetSelector resolves a TipSetSelector into a concrete TipSet.
282+ //
283+ // For TipSetKey selectors, loads the tipset directly.
284+ // For epoch descriptors:
285+ // - "latest" returns the current heaviest tipset
286+ // - "finalized" returns the most recently finalized tipset, using either F3
287+ // or EC finality depending on whether F3 is enabled and running.
288+ //
289+ // Returns:
290+ // - TipSet: The resolved tipset
291+ // - error: On invalid selectors or resolution failures
292+ func ResolveTipSetSelector (ctx context.Context , fff * F3 , chain * store.ChainStore , tss types.TipSetSelector ) (* types.TipSet , error ) {
293+ if tss .TipSetKey != nil {
294+ ts , err := chain .GetTipSetFromKey (ctx , * tss .TipSetKey )
295+ if err != nil {
296+ return nil , xerrors .Errorf ("loading tipset %s: %w" , * tss .TipSetKey , err )
297+ }
298+ return ts , nil
299+ }
300+ if tss .EpochDescriptor == nil {
301+ return nil , errors .New ("invalid tipset selector" )
302+ }
303+
304+ return ResolveEpochDescriptorTipSet (ctx , fff , chain , * tss .EpochDescriptor )
305+ }
306+
307+ // ResolveEpochSelector resolves an EpochSelector into a concrete ChainEpoch.
308+ //
309+ // For ChainEpoch selectors, returns the epoch directly.
310+ // For epoch descriptors:
311+ // - "latest" returns the current epoch
312+ // - "finalized" returns the most recently finalized epoch, using either F3
313+ // or EC finality depending on whether F3 is enabled and running.
314+ //
315+ // Returns:
316+ // - ChainEpoch: The resolved epoch
317+ // - error: On invalid selectors or resolution failures
318+ func ResolveEpochSelector (ctx context.Context , fff * F3 , chain * store.ChainStore , es types.EpochSelector ) (abi.ChainEpoch , error ) {
319+ if es .ChainEpoch != nil {
320+ return * es .ChainEpoch , nil
321+ } else if es .EpochDescriptor == nil {
322+ return 0 , errors .New ("invalid epoch selector" )
323+ }
324+
325+ if ts , err := ResolveEpochDescriptorTipSet (ctx , fff , chain , * es .EpochDescriptor ); err != nil {
326+ return 0 , xerrors .Errorf ("resolving epoch descriptor: %w" , err )
327+ } else {
328+ return ts .Height (), nil
329+ }
330+ }
331+
332+ // ResolveEpochDescriptorTipSet resolves an EpochDescriptor into a concrete TipSet.
333+ //
334+ // For "latest", returns the current heaviest tipset.
335+ // For "finalized", returns the most recently finalized tipset, using either F3
336+ // or EC finality depending on whether F3 is enabled and running.
337+ //
338+ // Returns:
339+ // - TipSet: The resolved tipset
340+ // - error: On invalid descriptors or resolution failures
341+ func ResolveEpochDescriptorTipSet (ctx context.Context , fff * F3 , chain * store.ChainStore , ed types.EpochDescriptor ) (* types.TipSet , error ) {
342+ switch ed {
343+ case types .EpochLatest :
344+ return chain .GetHeaviestTipSet (), nil
345+
346+ case types .EpochFinalized :
347+ if fff != nil { // F3 is enabled
348+ cert , err := fff .inner .GetLatestCert (ctx )
349+ if err == nil { // F3 is running
350+ tsk , err := types .TipSetKeyFromBytes (cert .ECChain .Head ().Key )
351+ if err != nil {
352+ return nil , xerrors .Errorf ("decoding tipset key reported by F3: %w" , err )
353+ }
354+ if ts , err := chain .LoadTipSet (ctx , tsk ); err != nil {
355+ return nil , xerrors .Errorf ("loading tipset %s: %w" , tsk , err )
356+ } else {
357+ return ts , nil
358+ }
359+ }
360+ }
361+
362+ // either F3 isn't enabled, or isn't running
363+ head := chain .GetHeaviestTipSet ()
364+ finalizedHeight := head .Height () - policy .ChainFinality
365+ if ts , err := chain .GetTipsetByHeight (ctx , finalizedHeight , head , true ); err != nil {
366+ return nil , xerrors .Errorf ("getting tipset by height: %w" , err )
367+ } else {
368+ return ts , nil
369+ }
370+
371+ default :
372+ return nil , fmt .Errorf ("invalid epoch descriptor: %s" , ed )
373+ }
374+ }
0 commit comments