Skip to content

Commit 300c240

Browse files
committed
Checkpoint tipsets that are finalized by F3
Once a decision is received from F3, checkpoint it in chain-store. As part of checkpointing, refactor the F3 tipset wrapper to reduce type casting. Note that go-f3 module now uses Go 1.22, which requires Lotus go module to be updated accordingly. Part of filecoin-project/go-f3#603
1 parent 9c266bf commit 300c240

File tree

5 files changed

+99
-77
lines changed

5 files changed

+99
-77
lines changed

build/openrpc/full.json

+4-12
Original file line numberDiff line numberDiff line change
@@ -6485,9 +6485,7 @@
64856485
"type": "string"
64866486
},
64876487
"PowerTable": {
6488-
"media": {
6489-
"binaryEncoding": "base64"
6490-
},
6488+
"title": "Content Identifier",
64916489
"type": "string"
64926490
}
64936491
},
@@ -6548,9 +6546,7 @@
65486546
"type": "array"
65496547
},
65506548
"PowerTable": {
6551-
"media": {
6552-
"binaryEncoding": "base64"
6553-
},
6549+
"title": "Content Identifier",
65546550
"type": "string"
65556551
}
65566552
},
@@ -6822,9 +6818,7 @@
68226818
"type": "string"
68236819
},
68246820
"PowerTable": {
6825-
"media": {
6826-
"binaryEncoding": "base64"
6827-
},
6821+
"title": "Content Identifier",
68286822
"type": "string"
68296823
}
68306824
},
@@ -6885,9 +6879,7 @@
68856879
"type": "array"
68866880
},
68876881
"PowerTable": {
6888-
"media": {
6889-
"binaryEncoding": "base64"
6890-
},
6882+
"title": "Content Identifier",
68916883
"type": "string"
68926884
}
68936885
},

chain/lf3/ec.go

+89-62
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/filecoin-project/go-f3/gpbft"
1313
"github.com/filecoin-project/go-state-types/abi"
1414

15+
"github.com/filecoin-project/lotus/chain"
1516
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
1617
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
1718
"github.com/filecoin-project/lotus/chain/stmgr"
@@ -20,52 +21,56 @@ import (
2021
"github.com/filecoin-project/lotus/chain/vm"
2122
)
2223

24+
var (
25+
_ ec.Backend = (*ecWrapper)(nil)
26+
_ ec.TipSet = (*f3TipSet)(nil)
27+
28+
emptyBeacon = make([]byte, 32)
29+
)
30+
2331
type ecWrapper struct {
2432
ChainStore *store.ChainStore
33+
Syncer *chain.Syncer
2534
StateManager *stmgr.StateManager
2635
}
2736

28-
var _ ec.TipSet = (*f3TipSet)(nil)
29-
30-
type f3TipSet types.TipSet
31-
32-
func (ts *f3TipSet) cast() *types.TipSet {
33-
return (*types.TipSet)(ts)
37+
type f3TipSet struct {
38+
*types.TipSet
3439
}
3540

36-
func (ts *f3TipSet) String() string {
37-
return ts.cast().String()
38-
}
41+
func (ts *f3TipSet) String() string { return ts.TipSet.String() }
42+
func (ts *f3TipSet) Key() gpbft.TipSetKey { return ts.TipSet.Key().Bytes() }
43+
func (ts *f3TipSet) Epoch() int64 { return int64(ts.TipSet.Height()) }
3944

40-
func (ts *f3TipSet) Key() gpbft.TipSetKey {
41-
return ts.cast().Key().Bytes()
45+
func (ts *f3TipSet) FirstBlockHeader() *types.BlockHeader {
46+
if ts.TipSet == nil || len(ts.TipSet.Blocks()) == 0 {
47+
return nil
48+
}
49+
return ts.TipSet.Blocks()[0]
4250
}
4351

4452
func (ts *f3TipSet) Beacon() []byte {
45-
entries := ts.cast().Blocks()[0].BeaconEntries
46-
if len(entries) == 0 {
47-
// This should never happen in practice, but set beacon to a non-nil
48-
// 32byte slice to force the message builder to generate a
49-
// ticket. Otherwise, messages that require ticket, i.e. CONVERGE will fail
50-
// validation due to the absence of ticket. This is a convoluted way of doing it.
51-
return make([]byte, 32)
52-
}
53-
return entries[len(entries)-1].Data
54-
}
55-
56-
func (ts *f3TipSet) Epoch() int64 {
57-
return int64(ts.cast().Height())
53+
switch header := ts.FirstBlockHeader(); {
54+
case header == nil, len(header.BeaconEntries) == 0:
55+
// This should never happen in practice, but set beacon to a non-nil 32byte slice
56+
// to force the message builder to generate a ticket. Otherwise, messages that
57+
// require ticket, i.e. CONVERGE will fail validation due to the absence of
58+
// ticket. This is a convoluted way of doing it.
59+
60+
// TODO: investigate if this is still necessary, or how message builder can be
61+
// adapted to behave correctly regardless of beacon value, e.g. fail fast
62+
// instead of building CONVERGE with empty beacon.
63+
return emptyBeacon
64+
default:
65+
return header.BeaconEntries[len(header.BeaconEntries)-1].Data
66+
}
5867
}
5968

6069
func (ts *f3TipSet) Timestamp() time.Time {
61-
return time.Unix(int64(ts.cast().Blocks()[0].Timestamp), 0)
62-
}
63-
64-
func wrapTS(ts *types.TipSet) ec.TipSet {
65-
if ts == nil {
66-
return nil
70+
if header := ts.FirstBlockHeader(); header != nil {
71+
return time.Unix(int64(header.Timestamp), 0)
6772
}
68-
return (*f3TipSet)(ts)
73+
return time.Time{}
6974
}
7075

7176
// GetTipsetByEpoch should return a tipset before the one requested if the requested
@@ -75,57 +80,38 @@ func (ec *ecWrapper) GetTipsetByEpoch(ctx context.Context, epoch int64) (ec.TipS
7580
if err != nil {
7681
return nil, xerrors.Errorf("getting tipset by height: %w", err)
7782
}
78-
return wrapTS(ts), nil
83+
return &f3TipSet{TipSet: ts}, nil
7984
}
8085

8186
func (ec *ecWrapper) GetTipset(ctx context.Context, tsk gpbft.TipSetKey) (ec.TipSet, error) {
82-
tskLotus, err := types.TipSetKeyFromBytes(tsk)
83-
if err != nil {
84-
return nil, xerrors.Errorf("decoding tsk: %w", err)
85-
}
86-
87-
ts, err := ec.ChainStore.GetTipSetFromKey(ctx, tskLotus)
87+
ts, err := ec.getTipSetFromF3TSK(ctx, tsk)
8888
if err != nil {
8989
return nil, xerrors.Errorf("getting tipset by key: %w", err)
9090
}
9191

92-
return wrapTS(ts), nil
92+
return &f3TipSet{TipSet: ts}, nil
9393
}
9494

95-
func (ec *ecWrapper) GetHead(_ context.Context) (ec.TipSet, error) {
96-
return wrapTS(ec.ChainStore.GetHeaviestTipSet()), nil
95+
func (ec *ecWrapper) GetHead(context.Context) (ec.TipSet, error) {
96+
return &f3TipSet{TipSet: ec.ChainStore.GetHeaviestTipSet()}, nil
9797
}
9898

9999
func (ec *ecWrapper) GetParent(ctx context.Context, tsF3 ec.TipSet) (ec.TipSet, error) {
100-
var ts *types.TipSet
101-
if tsW, ok := tsF3.(*f3TipSet); ok {
102-
ts = tsW.cast()
103-
} else {
104-
// There are only two implementations of ec.TipSet: f3TipSet, and one in fake EC
105-
// backend.
106-
//
107-
// TODO: Revisit the type check here and remove as needed once testing
108-
// is over and fake EC backend goes away.
109-
tskLotus, err := types.TipSetKeyFromBytes(tsF3.Key())
110-
if err != nil {
111-
return nil, xerrors.Errorf("decoding tsk: %w", err)
112-
}
113-
ts, err = ec.ChainStore.GetTipSetFromKey(ctx, tskLotus)
114-
if err != nil {
115-
return nil, xerrors.Errorf("getting tipset by key for get parent: %w", err)
116-
}
100+
ts, err := ec.toLotusTipSet(ctx, tsF3)
101+
if err != nil {
102+
return nil, err
117103
}
118104
parentTs, err := ec.ChainStore.GetTipSetFromKey(ctx, ts.Parents())
119105
if err != nil {
120106
return nil, xerrors.Errorf("getting parent tipset: %w", err)
121107
}
122-
return wrapTS(parentTs), nil
108+
return &f3TipSet{TipSet: parentTs}, nil
123109
}
124110

125111
func (ec *ecWrapper) GetPowerTable(ctx context.Context, tskF3 gpbft.TipSetKey) (gpbft.PowerEntries, error) {
126-
tsk, err := types.TipSetKeyFromBytes(tskF3)
112+
tsk, err := toLotusTipSetKey(tskF3)
127113
if err != nil {
128-
return nil, xerrors.Errorf("decoding tsk: %w", err)
114+
return nil, err
129115
}
130116
return ec.getPowerTableLotusTSK(ctx, tsk)
131117
}
@@ -208,7 +194,7 @@ func (ec *ecWrapper) getPowerTableLotusTSK(ctx context.Context, tsk types.TipSet
208194
if waddr.Protocol() != address.BLS {
209195
return xerrors.Errorf("wrong type of worker address")
210196
}
211-
pe.PubKey = gpbft.PubKey(waddr.Payload())
197+
pe.PubKey = waddr.Payload()
212198
powerEntries = append(powerEntries, pe)
213199
return nil
214200
})
@@ -219,3 +205,44 @@ func (ec *ecWrapper) getPowerTableLotusTSK(ctx context.Context, tsk types.TipSet
219205
sort.Sort(powerEntries)
220206
return powerEntries, nil
221207
}
208+
209+
func (ec *ecWrapper) Finalize(ctx context.Context, key gpbft.TipSetKey) error {
210+
tsk, err := toLotusTipSetKey(key)
211+
if err != nil {
212+
return err
213+
}
214+
if err = ec.Syncer.SyncCheckpoint(ctx, tsk); err != nil {
215+
return xerrors.Errorf("checkpointing finalized tipset: %w", err)
216+
}
217+
return nil
218+
}
219+
220+
func (ec *ecWrapper) toLotusTipSet(ctx context.Context, ts ec.TipSet) (*types.TipSet, error) {
221+
switch tst := ts.(type) {
222+
case *f3TipSet:
223+
return tst.TipSet, nil
224+
default:
225+
// Fall back on getting the tipset by key. This path is executed only in testing.
226+
return ec.getTipSetFromF3TSK(ctx, ts.Key())
227+
}
228+
}
229+
230+
func (ec *ecWrapper) getTipSetFromF3TSK(ctx context.Context, key gpbft.TipSetKey) (*types.TipSet, error) {
231+
tsk, err := toLotusTipSetKey(key)
232+
if err != nil {
233+
return nil, err
234+
}
235+
ts, err := ec.ChainStore.GetTipSetFromKey(ctx, tsk)
236+
if err != nil {
237+
return nil, xerrors.Errorf("getting tipset from key: %w", err)
238+
}
239+
return ts, nil
240+
}
241+
242+
func toLotusTipSetKey(key gpbft.TipSetKey) (types.TipSetKey, error) {
243+
tsk, err := types.TipSetKeyFromBytes(key)
244+
if err != nil {
245+
return types.TipSetKey{}, xerrors.Errorf("decoding tpiset key: %w", err)
246+
}
247+
return tsk, nil
248+
}

chain/lf3/f3.go

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/filecoin-project/go-f3/manifest"
2121

2222
"github.com/filecoin-project/lotus/api"
23+
"github.com/filecoin-project/lotus/chain"
2324
"github.com/filecoin-project/lotus/chain/stmgr"
2425
"github.com/filecoin-project/lotus/chain/store"
2526
"github.com/filecoin-project/lotus/chain/types"
@@ -43,6 +44,7 @@ type F3Params struct {
4344
PubSub *pubsub.PubSub
4445
Host host.Host
4546
ChainStore *store.ChainStore
47+
Syncer *chain.Syncer
4648
StateManager *stmgr.StateManager
4749
Datastore dtypes.MetadataDS
4850
Wallet api.Wallet
@@ -56,6 +58,7 @@ func New(mctx helpers.MetricsCtx, lc fx.Lifecycle, params F3Params) (*F3, error)
5658
ec := &ecWrapper{
5759
ChainStore: params.ChainStore,
5860
StateManager: params.StateManager,
61+
Syncer: params.Syncer,
5962
}
6063
verif := blssig.VerifierWithKeyOnG1()
6164

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ require (
4444
github.com/filecoin-project/go-cbor-util v0.0.1
4545
github.com/filecoin-project/go-commp-utils/v2 v2.1.0
4646
github.com/filecoin-project/go-crypto v0.1.0
47-
github.com/filecoin-project/go-f3 v0.2.0
47+
github.com/filecoin-project/go-f3 v0.2.1-0.20240913140337-3f4036020113
4848
github.com/filecoin-project/go-fil-commcid v0.2.0
4949
github.com/filecoin-project/go-hamt-ipld/v3 v3.4.0
5050
github.com/filecoin-project/go-jsonrpc v0.6.0

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,8 @@ github.com/filecoin-project/go-commp-utils/v2 v2.1.0/go.mod h1:NbxJYlhxtWaNhlVCj
264264
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ=
265265
github.com/filecoin-project/go-crypto v0.1.0 h1:Pob2MphoipMbe/ksxZOMcQvmBHAd3sI/WEqcbpIsGI0=
266266
github.com/filecoin-project/go-crypto v0.1.0/go.mod h1:K9UFXvvoyAVvB+0Le7oGlKiT9mgA5FHOJdYQXEE8IhI=
267-
github.com/filecoin-project/go-f3 v0.2.0 h1:Gis44+hOrDjSUEw3IDmU7CudNILi5e+bb1pgZgp680k=
268-
github.com/filecoin-project/go-f3 v0.2.0/go.mod h1:43fBLX0iX0+Nnw4Z91wSrdfDYAd6YEDexy7GcLnIJtk=
267+
github.com/filecoin-project/go-f3 v0.2.1-0.20240913140337-3f4036020113 h1:Qbb6IhbkjEqf5LbZU3p8z2FbFFQE3TDjamu+xGYMLRw=
268+
github.com/filecoin-project/go-f3 v0.2.1-0.20240913140337-3f4036020113/go.mod h1:5Gg9h133OU0GMp+oJwOOnJ84p9MO9RJfMKt4Xrgt070=
269269
github.com/filecoin-project/go-fil-commcid v0.2.0 h1:B+5UX8XGgdg/XsdUpST4pEBviKkFOw+Fvl2bLhSKGpI=
270270
github.com/filecoin-project/go-fil-commcid v0.2.0/go.mod h1:8yigf3JDIil+/WpqR5zoKyP0jBPCOGtEqq/K1CcMy9Q=
271271
github.com/filecoin-project/go-fil-commp-hashhash v0.2.0 h1:HYIUugzjq78YvV3vC6rL95+SfC/aSTVSnZSZiDV5pCk=

0 commit comments

Comments
 (0)