1
1
package cmd
2
2
3
3
import (
4
+ "errors"
4
5
"fmt"
5
6
6
7
"github.com/rs/zerolog/log"
@@ -13,10 +14,11 @@ import (
13
14
)
14
15
15
16
var (
16
- flagHeight uint64
17
- flagBlockID string
18
- flagFinal bool
19
- flagSealed bool
17
+ flagHeight uint64
18
+ flagBlockID string
19
+ flagFinal bool
20
+ flagSealed bool
21
+ flagExecuted bool
20
22
)
21
23
22
24
var Cmd = & cobra.Command {
@@ -39,17 +41,22 @@ func init() {
39
41
40
42
Cmd .Flags ().BoolVar (& flagSealed , "sealed" , false ,
41
43
"get sealed block" )
44
+
45
+ Cmd .Flags ().BoolVar (& flagExecuted , "executed" , false ,
46
+ "get last executed and sealed block (execution node only)" )
42
47
}
43
48
44
49
type Reader struct {
45
- state protocol.State
46
- blocks storage.Blocks
50
+ state protocol.State
51
+ blocks storage.Blocks
52
+ commits storage.Commits
47
53
}
48
54
49
55
func NewReader (state protocol.State , storages * storage.All ) * Reader {
50
56
return & Reader {
51
- state : state ,
52
- blocks : storages .Blocks ,
57
+ state : state ,
58
+ blocks : storages .Blocks ,
59
+ commits : storages .Commits ,
53
60
}
54
61
}
55
62
@@ -101,6 +108,16 @@ func (r *Reader) GetSealed() (*flow.Block, error) {
101
108
return block , nil
102
109
}
103
110
111
+ func (r * Reader ) GetRoot () (* flow.Block , error ) {
112
+ header := r .state .Params ().SealedRoot ()
113
+
114
+ block , err := r .getBlockByHeader (header )
115
+ if err != nil {
116
+ return nil , fmt .Errorf ("could not get block by header: %w" , err )
117
+ }
118
+ return block , nil
119
+ }
120
+
104
121
func (r * Reader ) GetBlockByID (blockID flow.Identifier ) (* flow.Block , error ) {
105
122
header , err := r .state .AtBlockID (blockID ).Head ()
106
123
if err != nil {
@@ -114,6 +131,22 @@ func (r *Reader) GetBlockByID(blockID flow.Identifier) (*flow.Block, error) {
114
131
return block , nil
115
132
}
116
133
134
+ // IsExecuted returns true if the block is executed
135
+ // this only works for execution node.
136
+ func (r * Reader ) IsExecuted (blockID flow.Identifier ) (bool , error ) {
137
+ _ , err := r .commits .ByBlockID (blockID )
138
+ if err == nil {
139
+ return true , nil
140
+ }
141
+
142
+ // statecommitment not exists means the block hasn't been executed yet
143
+ if errors .Is (err , storage .ErrNotFound ) {
144
+ return false , nil
145
+ }
146
+
147
+ return false , err
148
+ }
149
+
117
150
func run (* cobra.Command , []string ) {
118
151
db := common .InitStorage (flagDatadir )
119
152
defer db .Close ()
@@ -126,6 +159,12 @@ func run(*cobra.Command, []string) {
126
159
127
160
reader := NewReader (state , storages )
128
161
162
+ // making sure only one flag is being used
163
+ err = checkOnlyOneFlagIsUsed (flagHeight , flagBlockID , flagFinal , flagSealed , flagExecuted )
164
+ if err != nil {
165
+ log .Fatal ().Err (err ).Msg ("could not get block" )
166
+ }
167
+
129
168
if flagHeight > 0 {
130
169
log .Info ().Msgf ("get block by height: %v" , flagHeight )
131
170
block , err := reader .GetBlockByHeight (flagHeight )
@@ -171,5 +210,63 @@ func run(*cobra.Command, []string) {
171
210
return
172
211
}
173
212
174
- log .Fatal ().Msgf ("missing flag, try --final or --sealed or --height or --block-id" )
213
+ if flagExecuted {
214
+ log .Info ().Msgf ("get last executed and sealed block" )
215
+ sealed , err := reader .GetSealed ()
216
+ if err != nil {
217
+ log .Fatal ().Err (err ).Msg ("could not get sealed block" )
218
+ }
219
+
220
+ root , err := reader .GetRoot ()
221
+ if err != nil {
222
+ log .Fatal ().Err (err ).Msg ("could not get root block" )
223
+ }
224
+
225
+ // find the last executed and sealed block
226
+ for h := sealed .Header .Height ; h >= root .Header .Height ; h -- {
227
+ block , err := reader .GetBlockByHeight (h )
228
+ if err != nil {
229
+ log .Fatal ().Err (err ).Msgf ("could not get block by height: %v" , h )
230
+ }
231
+
232
+ executed , err := reader .IsExecuted (block .ID ())
233
+ if err != nil {
234
+ log .Fatal ().Err (err ).Msgf ("could not check block executed or not: %v" , h )
235
+ }
236
+
237
+ if executed {
238
+ common .PrettyPrintEntity (block )
239
+ return
240
+ }
241
+ }
242
+
243
+ log .Fatal ().Msg ("could not find executed block" )
244
+ }
245
+
246
+ log .Fatal ().Msgf ("missing flag, try --final or --sealed or --height or --executed or --block-id, note that only one flag can be used at a time" )
247
+ }
248
+
249
+ func checkOnlyOneFlagIsUsed (height uint64 , blockID string , final , sealed , executed bool ) error {
250
+ flags := make ([]string , 0 , 5 )
251
+ if height > 0 {
252
+ flags = append (flags , "height" )
253
+ }
254
+ if blockID != "" {
255
+ flags = append (flags , "blockID" )
256
+ }
257
+ if final {
258
+ flags = append (flags , "final" )
259
+ }
260
+ if sealed {
261
+ flags = append (flags , "sealed" )
262
+ }
263
+ if executed {
264
+ flags = append (flags , "executed" )
265
+ }
266
+
267
+ if len (flags ) != 1 {
268
+ return fmt .Errorf ("only one flag can be used at a time, used flags: %v" , flags )
269
+ }
270
+
271
+ return nil
175
272
}
0 commit comments