Skip to content

Commit 1ac1011

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 6c63df6 commit 1ac1011

File tree

5 files changed

+90
-77
lines changed

5 files changed

+90
-77
lines changed

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#####################################
2-
FROM golang:1.21.7-bullseye AS lotus-builder
2+
FROM golang:1.22.7-bullseye AS lotus-builder
33
MAINTAINER Lotus Development Team
44

55
RUN apt-get update && apt-get install -y ca-certificates build-essential clang ocl-icd-opencl-dev ocl-icd-libopencl1 jq libhwloc-dev

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

+77-60
Original file line numberDiff line numberDiff line change
@@ -20,52 +20,55 @@ import (
2020
"github.com/filecoin-project/lotus/chain/vm"
2121
)
2222

23+
var (
24+
_ ec.Backend = (*ecWrapper)(nil)
25+
_ ec.TipSet = (*f3TipSet)(nil)
26+
27+
emptyBeacon = make([]byte, 32)
28+
)
29+
2330
type ecWrapper struct {
2431
ChainStore *store.ChainStore
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-
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())
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.
61+
return emptyBeacon
62+
default:
63+
return header.BeaconEntries[len(header.BeaconEntries)-1].Data
64+
}
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,51 +78,32 @@ 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+
return &f3TipSet{TipSet: ec.ChainStore.GetHeaviestTipSet()}, nil
9795
}
9896

9997
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-
}
98+
ts, err := ec.toLotusTipset(ctx, tsF3)
99+
if err != nil {
100+
return nil, err
117101
}
118102
parentTs, err := ec.ChainStore.GetTipSetFromKey(ctx, ts.Parents())
119103
if err != nil {
120104
return nil, xerrors.Errorf("getting parent tipset: %w", err)
121105
}
122-
return wrapTS(parentTs), nil
106+
return &f3TipSet{TipSet: parentTs}, nil
123107
}
124108

125109
func (ec *ecWrapper) GetPowerTable(ctx context.Context, tskF3 gpbft.TipSetKey) (gpbft.PowerEntries, error) {
@@ -208,7 +192,7 @@ func (ec *ecWrapper) getPowerTableLotusTSK(ctx context.Context, tsk types.TipSet
208192
if waddr.Protocol() != address.BLS {
209193
return xerrors.Errorf("wrong type of worker address")
210194
}
211-
pe.PubKey = gpbft.PubKey(waddr.Payload())
195+
pe.PubKey = waddr.Payload()
212196
powerEntries = append(powerEntries, pe)
213197
return nil
214198
})
@@ -219,3 +203,36 @@ func (ec *ecWrapper) getPowerTableLotusTSK(ctx context.Context, tsk types.TipSet
219203
sort.Sort(powerEntries)
220204
return powerEntries, nil
221205
}
206+
207+
func (ec *ecWrapper) Finalize(ctx context.Context, key gpbft.TipSetKey) error {
208+
ts, err := ec.getTipSetFromF3TSK(ctx, key)
209+
if err != nil {
210+
return err
211+
}
212+
if err = ec.ChainStore.SetCheckpoint(ctx, ts); err != nil {
213+
return xerrors.Errorf("checkpointing finalized tipset: %w", err)
214+
}
215+
return nil
216+
}
217+
218+
func (ec *ecWrapper) toLotusTipset(ctx context.Context, ts ec.TipSet) (*types.TipSet, error) {
219+
switch tst := ts.(type) {
220+
case *f3TipSet:
221+
return tst.TipSet, nil
222+
default:
223+
// Fall back on getting the tipset by key. This path is executed only in testing.
224+
return ec.getTipSetFromF3TSK(ctx, ts.Key())
225+
}
226+
}
227+
228+
func (ec *ecWrapper) getTipSetFromF3TSK(ctx context.Context, key gpbft.TipSetKey) (*types.TipSet, error) {
229+
tsk, err := types.TipSetKeyFromBytes(key)
230+
if err != nil {
231+
return nil, xerrors.Errorf("decoding tpiset key: %w", err)
232+
}
233+
ts, err := ec.ChainStore.GetTipSetFromKey(ctx, tsk)
234+
if err != nil {
235+
return nil, xerrors.Errorf("getting tipset from key: %w", err)
236+
}
237+
return ts, nil
238+
}

go.mod

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
module github.com/filecoin-project/lotus
22

3-
go 1.21
3+
go 1.22
4+
5+
toolchain go1.22.6
46

57
retract v1.14.0 // Accidentally force-pushed tag, use v1.14.1+ instead.
68

@@ -42,7 +44,7 @@ require (
4244
github.com/filecoin-project/go-cbor-util v0.0.1
4345
github.com/filecoin-project/go-commp-utils/v2 v2.1.0
4446
github.com/filecoin-project/go-crypto v0.1.0
45-
github.com/filecoin-project/go-f3 v0.2.0
47+
github.com/filecoin-project/go-f3 v0.2.1-0.20240913140337-3f4036020113
4648
github.com/filecoin-project/go-fil-commcid v0.2.0
4749
github.com/filecoin-project/go-hamt-ipld/v3 v3.4.0
4850
github.com/filecoin-project/go-jsonrpc v0.6.0

go.sum

+4-2
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,10 @@ github.com/filecoin-project/go-commp-utils/v2 v2.1.0/go.mod h1:NbxJYlhxtWaNhlVCj
268268
github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03/go.mod h1:+viYnvGtUTgJRdy6oaeF4MTFKAfatX071MPDPBL11EQ=
269269
github.com/filecoin-project/go-crypto v0.1.0 h1:Pob2MphoipMbe/ksxZOMcQvmBHAd3sI/WEqcbpIsGI0=
270270
github.com/filecoin-project/go-crypto v0.1.0/go.mod h1:K9UFXvvoyAVvB+0Le7oGlKiT9mgA5FHOJdYQXEE8IhI=
271-
github.com/filecoin-project/go-f3 v0.2.0 h1:Gis44+hOrDjSUEw3IDmU7CudNILi5e+bb1pgZgp680k=
272-
github.com/filecoin-project/go-f3 v0.2.0/go.mod h1:43fBLX0iX0+Nnw4Z91wSrdfDYAd6YEDexy7GcLnIJtk=
271+
github.com/filecoin-project/go-f3 v0.2.1-0.20240913104949-a47f60fb7a51 h1:x1S+tB2diPXBKxDh6uUERdx/Foy/jU5df9d8Kbb7ICQ=
272+
github.com/filecoin-project/go-f3 v0.2.1-0.20240913104949-a47f60fb7a51/go.mod h1:5Gg9h133OU0GMp+oJwOOnJ84p9MO9RJfMKt4Xrgt070=
273+
github.com/filecoin-project/go-f3 v0.2.1-0.20240913140337-3f4036020113 h1:Qbb6IhbkjEqf5LbZU3p8z2FbFFQE3TDjamu+xGYMLRw=
274+
github.com/filecoin-project/go-f3 v0.2.1-0.20240913140337-3f4036020113/go.mod h1:5Gg9h133OU0GMp+oJwOOnJ84p9MO9RJfMKt4Xrgt070=
273275
github.com/filecoin-project/go-fil-commcid v0.2.0 h1:B+5UX8XGgdg/XsdUpST4pEBviKkFOw+Fvl2bLhSKGpI=
274276
github.com/filecoin-project/go-fil-commcid v0.2.0/go.mod h1:8yigf3JDIil+/WpqR5zoKyP0jBPCOGtEqq/K1CcMy9Q=
275277
github.com/filecoin-project/go-fil-commp-hashhash v0.2.0 h1:HYIUugzjq78YvV3vC6rL95+SfC/aSTVSnZSZiDV5pCk=

0 commit comments

Comments
 (0)