Skip to content

Commit 0a135c4

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 0a135c4

File tree

5 files changed

+99
-75
lines changed

5 files changed

+99
-75
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-60
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,54 @@ 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+
2329
type ecWrapper struct {
2430
ChainStore *store.ChainStore
31+
Syncer *chain.Syncer
2532
StateManager *stmgr.StateManager
2633
}
2734

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)
35+
type f3TipSet struct {
36+
*types.TipSet
3437
}
3538

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

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

4450
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+
switch header := ts.FirstBlockHeader(); {
52+
case header == nil, len(header.BeaconEntries) == 0:
53+
// This should never happen in practice, but set beacon to a non-nil 32byte slice
54+
// to force the message builder to generate a ticket. Otherwise, messages that
55+
// require ticket, i.e. CONVERGE will fail validation due to the absence of
56+
// ticket. This is a convoluted way of doing it.
57+
58+
// TODO: investigate if this is still necessary, or how message builder can be
59+
// adapted to behave correctly regardless of beacon value, e.g. fail fast
60+
// instead of building CONVERGE with empty beacon.
5161
return make([]byte, 32)
62+
default:
63+
return header.BeaconEntries[len(header.BeaconEntries)-1].Data
5264
}
53-
return entries[len(entries)-1].Data
54-
}
55-
56-
func (ts *f3TipSet) Epoch() int64 {
57-
return int64(ts.cast().Height())
5865
}
5966

6067
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
68+
if header := ts.FirstBlockHeader(); header != nil {
69+
return time.Unix(int64(header.Timestamp), 0)
6770
}
68-
return (*f3TipSet)(ts)
71+
return time.Time{}
6972
}
7073

7174
// GetTipsetByEpoch should return a tipset before the one requested if the requested
@@ -75,57 +78,42 @@ func (ec *ecWrapper) GetTipsetByEpoch(ctx context.Context, epoch int64) (ec.TipS
7578
if err != nil {
7679
return nil, xerrors.Errorf("getting tipset by height: %w", err)
7780
}
78-
return wrapTS(ts), nil
81+
return &f3TipSet{TipSet: ts}, nil
7982
}
8083

8184
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)
85+
ts, err := ec.getTipSetFromF3TSK(ctx, tsk)
8886
if err != nil {
8987
return nil, xerrors.Errorf("getting tipset by key: %w", err)
9088
}
9189

92-
return wrapTS(ts), nil
90+
return &f3TipSet{TipSet: ts}, nil
9391
}
9492

95-
func (ec *ecWrapper) GetHead(_ context.Context) (ec.TipSet, error) {
96-
return wrapTS(ec.ChainStore.GetHeaviestTipSet()), nil
93+
func (ec *ecWrapper) GetHead(context.Context) (ec.TipSet, error) {
94+
head := ec.ChainStore.GetHeaviestTipSet()
95+
if head == nil {
96+
return nil, xerrors.New("no heaviest tipset")
97+
}
98+
return &f3TipSet{TipSet: head}, nil
9799
}
98100

99101
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-
}
102+
ts, err := ec.toLotusTipSet(ctx, tsF3)
103+
if err != nil {
104+
return nil, err
117105
}
118106
parentTs, err := ec.ChainStore.GetTipSetFromKey(ctx, ts.Parents())
119107
if err != nil {
120108
return nil, xerrors.Errorf("getting parent tipset: %w", err)
121109
}
122-
return wrapTS(parentTs), nil
110+
return &f3TipSet{TipSet: parentTs}, nil
123111
}
124112

125113
func (ec *ecWrapper) GetPowerTable(ctx context.Context, tskF3 gpbft.TipSetKey) (gpbft.PowerEntries, error) {
126-
tsk, err := types.TipSetKeyFromBytes(tskF3)
114+
tsk, err := toLotusTipSetKey(tskF3)
127115
if err != nil {
128-
return nil, xerrors.Errorf("decoding tsk: %w", err)
116+
return nil, err
129117
}
130118
return ec.getPowerTableLotusTSK(ctx, tsk)
131119
}
@@ -208,7 +196,7 @@ func (ec *ecWrapper) getPowerTableLotusTSK(ctx context.Context, tsk types.TipSet
208196
if waddr.Protocol() != address.BLS {
209197
return xerrors.Errorf("wrong type of worker address")
210198
}
211-
pe.PubKey = gpbft.PubKey(waddr.Payload())
199+
pe.PubKey = waddr.Payload()
212200
powerEntries = append(powerEntries, pe)
213201
return nil
214202
})
@@ -219,3 +207,44 @@ func (ec *ecWrapper) getPowerTableLotusTSK(ctx context.Context, tsk types.TipSet
219207
sort.Sort(powerEntries)
220208
return powerEntries, nil
221209
}
210+
211+
func (ec *ecWrapper) Finalize(ctx context.Context, key gpbft.TipSetKey) error {
212+
tsk, err := toLotusTipSetKey(key)
213+
if err != nil {
214+
return err
215+
}
216+
if err = ec.Syncer.SyncCheckpoint(ctx, tsk); err != nil {
217+
return xerrors.Errorf("checkpointing finalized tipset: %w", err)
218+
}
219+
return nil
220+
}
221+
222+
func (ec *ecWrapper) toLotusTipSet(ctx context.Context, ts ec.TipSet) (*types.TipSet, error) {
223+
switch tst := ts.(type) {
224+
case *f3TipSet:
225+
return tst.TipSet, nil
226+
default:
227+
// Fall back on getting the tipset by key. This path is executed only in testing.
228+
return ec.getTipSetFromF3TSK(ctx, ts.Key())
229+
}
230+
}
231+
232+
func (ec *ecWrapper) getTipSetFromF3TSK(ctx context.Context, key gpbft.TipSetKey) (*types.TipSet, error) {
233+
tsk, err := toLotusTipSetKey(key)
234+
if err != nil {
235+
return nil, err
236+
}
237+
ts, err := ec.ChainStore.GetTipSetFromKey(ctx, tsk)
238+
if err != nil {
239+
return nil, xerrors.Errorf("getting tipset from key: %w", err)
240+
}
241+
return ts, nil
242+
}
243+
244+
func toLotusTipSetKey(key gpbft.TipSetKey) (types.TipSetKey, error) {
245+
tsk, err := types.TipSetKeyFromBytes(key)
246+
if err != nil {
247+
return types.TipSetKey{}, xerrors.Errorf("decoding tpiset key: %w", err)
248+
}
249+
return tsk, nil
250+
}

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)