@@ -12,6 +12,7 @@ import (
12
12
"github.com/filecoin-project/go-f3/gpbft"
13
13
"github.com/filecoin-project/go-state-types/abi"
14
14
15
+ "github.com/filecoin-project/lotus/chain"
15
16
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
16
17
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
17
18
"github.com/filecoin-project/lotus/chain/stmgr"
@@ -20,52 +21,57 @@ import (
20
21
"github.com/filecoin-project/lotus/chain/vm"
21
22
)
22
23
24
+ var (
25
+ _ ec.Backend = (* ecWrapper )(nil )
26
+ _ ec.TipSet = (* f3TipSet )(nil )
27
+ )
28
+
23
29
type ecWrapper struct {
24
30
ChainStore * store.ChainStore
31
+ Syncer * chain.Syncer
25
32
StateManager * stmgr.StateManager
26
- }
27
-
28
- var _ ec.TipSet = (* f3TipSet )(nil )
29
-
30
- type f3TipSet types.TipSet
31
33
32
- func ( ts * f3TipSet ) cast () * types. TipSet {
33
- return ( * types . TipSet )( ts )
34
+ // Checkpoint sets whether to checkpoint tipsets finalized by F3 in ChainStore.
35
+ Checkpoint bool
34
36
}
35
37
36
- func ( ts * f3TipSet ) String () string {
37
- return ts . cast (). String ()
38
+ type f3TipSet struct {
39
+ * types. TipSet
38
40
}
39
41
40
- func (ts * f3TipSet ) Key () gpbft.TipSetKey {
41
- return ts .cast ().Key ().Bytes ()
42
+ func (ts * f3TipSet ) String () string { return ts .TipSet .String () }
43
+ func (ts * f3TipSet ) Key () gpbft.TipSetKey { return ts .TipSet .Key ().Bytes () }
44
+ func (ts * f3TipSet ) Epoch () int64 { return int64 (ts .TipSet .Height ()) }
45
+
46
+ func (ts * f3TipSet ) FirstBlockHeader () * types.BlockHeader {
47
+ if ts .TipSet == nil || len (ts .TipSet .Blocks ()) == 0 {
48
+ return nil
49
+ }
50
+ return ts .TipSet .Blocks ()[0 ]
42
51
}
43
52
44
53
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.
54
+ switch header := ts .FirstBlockHeader (); {
55
+ case header == nil , len (header .BeaconEntries ) == 0 :
56
+ // This should never happen in practice, but set beacon to a non-nil 32byte slice
57
+ // to force the message builder to generate a ticket. Otherwise, messages that
58
+ // require ticket, i.e. CONVERGE will fail validation due to the absence of
59
+ // ticket. This is a convoluted way of doing it.
60
+
61
+ // TODO: investigate if this is still necessary, or how message builder can be
62
+ // adapted to behave correctly regardless of beacon value, e.g. fail fast
63
+ // instead of building CONVERGE with empty beacon.
51
64
return make ([]byte , 32 )
65
+ default :
66
+ return header .BeaconEntries [len (header .BeaconEntries )- 1 ].Data
52
67
}
53
- return entries [len (entries )- 1 ].Data
54
- }
55
-
56
- func (ts * f3TipSet ) Epoch () int64 {
57
- return int64 (ts .cast ().Height ())
58
68
}
59
69
60
70
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
71
+ if header := ts .FirstBlockHeader (); header != nil {
72
+ return time .Unix (int64 (header .Timestamp ), 0 )
67
73
}
68
- return ( * f3TipSet )( ts )
74
+ return time. Time {}
69
75
}
70
76
71
77
// GetTipsetByEpoch should return a tipset before the one requested if the requested
@@ -75,57 +81,42 @@ func (ec *ecWrapper) GetTipsetByEpoch(ctx context.Context, epoch int64) (ec.TipS
75
81
if err != nil {
76
82
return nil , xerrors .Errorf ("getting tipset by height: %w" , err )
77
83
}
78
- return wrapTS ( ts ) , nil
84
+ return & f3TipSet { TipSet : ts } , nil
79
85
}
80
86
81
87
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 )
88
+ ts , err := ec .getTipSetFromF3TSK (ctx , tsk )
88
89
if err != nil {
89
90
return nil , xerrors .Errorf ("getting tipset by key: %w" , err )
90
91
}
91
92
92
- return wrapTS ( ts ) , nil
93
+ return & f3TipSet { TipSet : ts } , nil
93
94
}
94
95
95
- func (ec * ecWrapper ) GetHead (_ context.Context ) (ec.TipSet , error ) {
96
- return wrapTS (ec .ChainStore .GetHeaviestTipSet ()), nil
96
+ func (ec * ecWrapper ) GetHead (context.Context ) (ec.TipSet , error ) {
97
+ head := ec .ChainStore .GetHeaviestTipSet ()
98
+ if head == nil {
99
+ return nil , xerrors .New ("no heaviest tipset" )
100
+ }
101
+ return & f3TipSet {TipSet : head }, nil
97
102
}
98
103
99
104
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
- }
105
+ ts , err := ec .toLotusTipSet (ctx , tsF3 )
106
+ if err != nil {
107
+ return nil , err
117
108
}
118
109
parentTs , err := ec .ChainStore .GetTipSetFromKey (ctx , ts .Parents ())
119
110
if err != nil {
120
111
return nil , xerrors .Errorf ("getting parent tipset: %w" , err )
121
112
}
122
- return wrapTS ( parentTs ) , nil
113
+ return & f3TipSet { TipSet : parentTs } , nil
123
114
}
124
115
125
116
func (ec * ecWrapper ) GetPowerTable (ctx context.Context , tskF3 gpbft.TipSetKey ) (gpbft.PowerEntries , error ) {
126
- tsk , err := types . TipSetKeyFromBytes (tskF3 )
117
+ tsk , err := toLotusTipSetKey (tskF3 )
127
118
if err != nil {
128
- return nil , xerrors . Errorf ( "decoding tsk: %w" , err )
119
+ return nil , err
129
120
}
130
121
return ec .getPowerTableLotusTSK (ctx , tsk )
131
122
}
@@ -208,7 +199,7 @@ func (ec *ecWrapper) getPowerTableLotusTSK(ctx context.Context, tsk types.TipSet
208
199
if waddr .Protocol () != address .BLS {
209
200
return xerrors .Errorf ("wrong type of worker address" )
210
201
}
211
- pe .PubKey = gpbft . PubKey ( waddr .Payload () )
202
+ pe .PubKey = waddr .Payload ()
212
203
powerEntries = append (powerEntries , pe )
213
204
return nil
214
205
})
@@ -219,3 +210,43 @@ func (ec *ecWrapper) getPowerTableLotusTSK(ctx context.Context, tsk types.TipSet
219
210
sort .Sort (powerEntries )
220
211
return powerEntries , nil
221
212
}
213
+
214
+ func (ec * ecWrapper ) Finalize (ctx context.Context , key gpbft.TipSetKey ) error {
215
+ if ! ec .Checkpoint {
216
+ return nil // Nothing to do; checkpointing is not enabled.
217
+ }
218
+ tsk , err := toLotusTipSetKey (key )
219
+ if err != nil {
220
+ return err
221
+ }
222
+ if err = ec .Syncer .SyncCheckpoint (ctx , tsk ); err != nil {
223
+ return xerrors .Errorf ("checkpointing finalized tipset: %w" , err )
224
+ }
225
+ return nil
226
+ }
227
+
228
+ func (ec * ecWrapper ) toLotusTipSet (ctx context.Context , ts ec.TipSet ) (* types.TipSet , error ) {
229
+ switch tst := ts .(type ) {
230
+ case * f3TipSet :
231
+ return tst .TipSet , nil
232
+ default :
233
+ // Fall back on getting the tipset by key. This path is executed only in testing.
234
+ return ec .getTipSetFromF3TSK (ctx , ts .Key ())
235
+ }
236
+ }
237
+
238
+ func (ec * ecWrapper ) getTipSetFromF3TSK (ctx context.Context , key gpbft.TipSetKey ) (* types.TipSet , error ) {
239
+ tsk , err := toLotusTipSetKey (key )
240
+ if err != nil {
241
+ return nil , err
242
+ }
243
+ ts , err := ec .ChainStore .GetTipSetFromKey (ctx , tsk )
244
+ if err != nil {
245
+ return nil , xerrors .Errorf ("getting tipset from key: %w" , err )
246
+ }
247
+ return ts , nil
248
+ }
249
+
250
+ func toLotusTipSetKey (key gpbft.TipSetKey ) (types.TipSetKey , error ) {
251
+ return types .TipSetKeyFromBytes (key )
252
+ }
0 commit comments