Source file seed_storage.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
type seed_computation_status =
| Nonce_revelation_stage
| Vdf_revelation_stage of {
seed_discriminant : Seed_repr.seed;
seed_challenge : Seed_repr.seed;
}
| Computation_finished
type error +=
|
Unknown of {
oldest : Cycle_repr.t;
cycle : Cycle_repr.t;
latest : Cycle_repr.t;
}
| Already_accepted
| Unverified_vdf
| Too_early_revelation
let () =
register_error_kind
`Permanent
~id:"seed.unknown_seed"
~title:"Unknown seed"
~description:"The requested seed is not available"
~pp:(fun ppf (oldest, cycle, latest) ->
if Cycle_repr.(cycle < oldest) then
Format.fprintf
ppf
"The seed for cycle %a has been cleared from the context (oldest \
known seed is for cycle %a)"
Cycle_repr.pp
cycle
Cycle_repr.pp
oldest
else
Format.fprintf
ppf
"The seed for cycle %a has not been computed yet (latest known seed \
is for cycle %a)"
Cycle_repr.pp
cycle
Cycle_repr.pp
latest)
Data_encoding.(
obj3
(req "oldest" Cycle_repr.encoding)
(req "requested" Cycle_repr.encoding)
(req "latest" Cycle_repr.encoding))
(function
| Unknown {oldest; cycle; latest} -> Some (oldest, cycle, latest)
| _ -> None)
(fun (oldest, cycle, latest) -> Unknown {oldest; cycle; latest}) ;
register_error_kind
`Temporary
~id:"vdf.too_early_revelation"
~title:"Too early VDF revelation"
~description:"VDF revelation before the end of the nonce revelation period"
Data_encoding.unit
(function Too_early_revelation -> Some () | _ -> None)
(fun () -> Too_early_revelation) ;
register_error_kind
`Branch
~id:"vdf.unverified_result"
~title:"Unverified VDF"
~description:"VDF verification failed"
~pp:(fun ppf () ->
Format.fprintf
ppf
"A correct VDF result and Wesolowski's proof are expected")
Data_encoding.unit
(function Unverified_vdf -> Some () | _ -> None)
(fun () -> Unverified_vdf) ;
register_error_kind
`Branch
~id:"vdf.previously_revealed"
~title:"Previously revealed VDF"
~description:"Duplicate VDF revelation in cycle"
Data_encoding.unit
(function Already_accepted -> Some () | _ -> None)
(fun () -> Already_accepted)
let purge_nonces_and_get_unrevealed ctxt ~cycle =
let open Lwt_result_syntax in
let levels = Level_storage.levels_with_commitments_in_cycle ctxt cycle in
let combine (c, unrevealed) level =
let* seed = Storage.Seed.Nonce.get c level in
match seed with
| Revealed _ ->
let+ c = Storage.Seed.Nonce.remove_existing c level in
(c, unrevealed)
| Unrevealed u ->
let+ c = Storage.Seed.Nonce.remove_existing c level in
(c, u :: unrevealed)
in
List.fold_left_es combine (ctxt, []) levels
let compute_randao ctxt =
let open Lwt_result_syntax in
let current_cycle = (Level_storage.current ctxt).cycle in
let preserved = Constants_storage.preserved_cycles ctxt in
let cycle_computed = Cycle_repr.add current_cycle (preserved + 1) in
let*! seed_computed = Storage.Seed.For_cycle.mem ctxt cycle_computed in
match Cycle_repr.(pred current_cycle, pred cycle_computed) with
| Some prev_cycle, Some prev_cycle_computed when not seed_computed ->
let levels =
Level_storage.levels_with_commitments_in_cycle ctxt prev_cycle
in
let* prev_seed = Storage.Seed.For_cycle.get ctxt prev_cycle_computed in
let combine (c, random_seed) level =
let* seed = Storage.Seed.Nonce.get c level in
match seed with
| Revealed nonce -> return (c, Seed_repr.update_seed random_seed nonce)
| Unrevealed _ -> return (c, random_seed)
in
let seed = Seed_repr.deterministic_seed prev_seed in
let* c, seed = List.fold_left_es combine (ctxt, seed) levels in
Storage.Seed.For_cycle.init c cycle_computed seed
| _, _ -> return ctxt
let get_seed_computation_status ctxt =
let open Lwt_result_syntax in
let current_level = Level_storage.current ctxt in
let current_cycle = current_level.cycle in
let nonce_revelation_threshold =
Constants_storage.nonce_revelation_threshold ctxt
in
if Compare.Int32.(current_level.cycle_position < nonce_revelation_threshold)
then return Nonce_revelation_stage
else
let* status = Storage.Seed.get_status ctxt in
match status with
| RANDAO_seed ->
let preserved = Constants_storage.preserved_cycles ctxt in
let cycle_computed = Cycle_repr.add current_cycle (preserved + 1) in
let previous_cycle = Cycle_repr.add current_cycle preserved in
let* seed_discriminant =
Storage.Seed.For_cycle.get ctxt previous_cycle
in
let* seed_challenge = Storage.Seed.For_cycle.get ctxt cycle_computed in
return (Vdf_revelation_stage {seed_discriminant; seed_challenge})
| VDF_seed -> return Computation_finished
let check_vdf ctxt vdf_solution =
let open Lwt_result_syntax in
let* r = get_seed_computation_status ctxt in
match r with
| Computation_finished -> tzfail Already_accepted
| Nonce_revelation_stage -> tzfail Too_early_revelation
| Vdf_revelation_stage {seed_discriminant; seed_challenge} ->
let* stored = Storage.Seed.VDF_setup.find ctxt in
let* ctxt, setup =
match stored with
| None ->
let setup =
Seed_repr.generate_vdf_setup ~seed_discriminant ~seed_challenge
in
let*! ctxt = Storage.Seed.VDF_setup.add ctxt setup in
return (ctxt, setup)
| Some setup -> return (ctxt, setup)
in
let*? () =
error_unless
(Option.value
~default:false
(Seed_repr.verify
setup
(Constants_storage.vdf_difficulty ctxt)
vdf_solution))
Unverified_vdf
in
return_unit
let update_seed ctxt vdf_solution =
let open Lwt_result_syntax in
let current_cycle = (Level_storage.current ctxt).cycle in
let preserved = Constants_storage.preserved_cycles ctxt in
let cycle_computed = Cycle_repr.add current_cycle (preserved + 1) in
let* seed_challenge = Storage.Seed.For_cycle.get ctxt cycle_computed in
let new_seed = Seed_repr.vdf_to_seed seed_challenge vdf_solution in
Storage.Seed.For_cycle.update ctxt cycle_computed new_seed Seed_repr.VDF_seed
let raw_for_cycle = Storage.Seed.For_cycle.get
let for_cycle ctxt cycle =
let open Lwt_result_syntax in
let preserved = Constants_storage.preserved_cycles ctxt in
let max_slashing_period = Constants_repr.max_slashing_period in
let current_cycle = (Level_storage.current ctxt).cycle in
let latest =
if Cycle_repr.(current_cycle = root) then
Cycle_repr.add current_cycle (preserved + 1)
else Cycle_repr.add current_cycle preserved
in
let oldest =
match Cycle_repr.sub current_cycle (max_slashing_period - 1) with
| None -> Cycle_repr.root
| Some oldest -> oldest
in
let*? () =
error_unless
Cycle_repr.(oldest <= cycle && cycle <= latest)
(Unknown {oldest; cycle; latest})
in
Storage.Seed.For_cycle.get ctxt cycle
let init ?initial_seed ctxt =
let open Lwt_result_syntax in
let preserved = Constants_storage.preserved_cycles ctxt in
let* ctxt = Storage.Seed_status.init ctxt Seed_repr.RANDAO_seed in
let+ (_ : int), ctxt =
List.fold_left_es
(fun (c, ctxt) seed ->
let cycle = Cycle_repr.of_int32_exn (Int32.of_int c) in
let+ ctxt = Storage.Seed.For_cycle.init ctxt cycle seed in
(c + 1, ctxt))
(0, ctxt)
(Seed_repr.initial_seeds ?initial_seed (preserved + 2))
in
ctxt
let cycle_end ctxt last_cycle =
let open Lwt_result_syntax in
let*! ctxt = Storage.Seed.VDF_setup.remove ctxt in
match Cycle_repr.pred last_cycle with
| None -> return (ctxt, [])
| Some previous_cycle ->
purge_nonces_and_get_unrevealed ctxt ~cycle:previous_cycle
let remove_for_cycle = Storage.Seed.For_cycle.remove_existing