1
1
use crate :: {
2
+ genesis_values:: GenesisValues ,
2
3
rational_number:: { ChameleonFraction , RationalNumber } ,
3
- BlockVersionData , Committee , Constitution , CostModel , DRepVotingThresholds , ExUnitPrices ,
4
- ExUnits , PoolVotingThresholds , ProtocolConsts ,
4
+ BlockVersionData , Committee , Constitution , CostModel , DRepVotingThresholds , Era , ExUnitPrices ,
5
+ ExUnits , NetworkId , PoolVotingThresholds , ProtocolConsts ,
5
6
} ;
7
+ use anyhow:: Result ;
8
+ use blake2:: { digest:: consts:: U32 , Blake2b , Digest } ;
6
9
use chrono:: { DateTime , Utc } ;
7
10
use serde_with:: serde_as;
8
11
@@ -95,12 +98,6 @@ pub struct ShelleyProtocolParams {
95
98
pub pool_pledge_influence : RationalNumber ,
96
99
}
97
100
98
- #[ derive( Debug , Clone , PartialEq , Eq , serde:: Serialize , serde:: Deserialize ) ]
99
- pub enum NetworkId {
100
- Testnet ,
101
- Mainnet ,
102
- }
103
-
104
101
#[ serde_as]
105
102
#[ derive( Debug , Clone , PartialEq , serde:: Serialize , serde:: Deserialize ) ]
106
103
#[ serde( rename_all = "camelCase" ) ]
@@ -127,6 +124,72 @@ pub struct ShelleyParams {
127
124
pub update_quorum : u32 ,
128
125
}
129
126
127
+ #[ serde_as]
128
+ #[ derive( Debug , Clone , PartialEq , serde:: Serialize , serde:: Deserialize ) ]
129
+ #[ serde( rename_all = "camelCase" ) ]
130
+ pub struct PraosParams {
131
+ pub security_param : u32 ,
132
+ #[ serde_as( as = "ChameleonFraction" ) ]
133
+ pub active_slots_coeff : RationalNumber ,
134
+ pub epoch_length : u32 ,
135
+ pub max_kes_evolutions : u32 ,
136
+ pub max_lovelace_supply : u64 ,
137
+ pub network_id : NetworkId ,
138
+ pub slot_length : u32 ,
139
+ pub slots_per_kes_period : u32 ,
140
+
141
+ /// Relative slot from which data of the previous epoch can be considered stable.
142
+ /// This value is used for all TPraos eras AND Babbage Era from Praos
143
+ pub stability_window : u64 ,
144
+
145
+ /// Number of slots at the end of each epoch which do NOT contribute randomness to the candidate
146
+ /// nonce of the following epoch.
147
+ /// This value is used for all Praos eras except Babbage
148
+ pub randomness_stabilization_window : u64 ,
149
+ }
150
+
151
+ impl PraosParams {
152
+ pub fn mainnet ( ) -> Self {
153
+ PraosParams {
154
+ security_param : 2160 ,
155
+ active_slots_coeff : RationalNumber :: new ( 1 , 20 ) ,
156
+ epoch_length : 432000 ,
157
+ max_kes_evolutions : 62 ,
158
+ max_lovelace_supply : 45_000_000_000_000_000 ,
159
+ network_id : NetworkId :: Mainnet ,
160
+ slot_length : 1 ,
161
+ slots_per_kes_period : 129600 ,
162
+ stability_window : 129600 ,
163
+ randomness_stabilization_window : 172800 ,
164
+ }
165
+ }
166
+ }
167
+
168
+ impl From < & ShelleyParams > for PraosParams {
169
+ fn from ( params : & ShelleyParams ) -> Self {
170
+ let active_slots_coeff = params. active_slots_coeff ;
171
+ let security_param = params. security_param ;
172
+ let stability_window =
173
+ ( security_param as u64 ) * active_slots_coeff. denom ( ) / active_slots_coeff. numer ( ) * 3 ;
174
+ let randomness_stabilization_window =
175
+ ( security_param as u64 ) * active_slots_coeff. denom ( ) / active_slots_coeff. numer ( ) * 4 ;
176
+
177
+ Self {
178
+ security_param : security_param,
179
+ active_slots_coeff : active_slots_coeff,
180
+ epoch_length : params. epoch_length ,
181
+ max_kes_evolutions : params. max_kes_evolutions ,
182
+ max_lovelace_supply : params. max_lovelace_supply ,
183
+ network_id : params. network_id . clone ( ) ,
184
+ slot_length : params. slot_length ,
185
+ slots_per_kes_period : params. slots_per_kes_period ,
186
+
187
+ stability_window : stability_window,
188
+ randomness_stabilization_window : randomness_stabilization_window,
189
+ }
190
+ }
191
+ }
192
+
130
193
//
131
194
// Babbage protocol parameters
132
195
//
@@ -164,16 +227,132 @@ pub struct ProtocolVersion {
164
227
pub major : u64 ,
165
228
}
166
229
167
- #[ derive( Debug , Clone , PartialEq , Eq , serde:: Serialize , serde:: Deserialize ) ]
230
+ #[ derive(
231
+ Default , Debug , Clone , PartialEq , Eq , PartialOrd , Ord , serde:: Serialize , serde:: Deserialize ,
232
+ ) ]
168
233
#[ serde( rename_all = "PascalCase" ) ]
169
234
pub enum NonceVariant {
235
+ #[ default]
170
236
NeutralNonce ,
171
237
Nonce ,
172
238
}
173
239
174
- #[ derive( Debug , Clone , PartialEq , Eq , serde:: Serialize , serde:: Deserialize ) ]
240
+ pub type NonceHash = [ u8 ; 32 ] ;
241
+
242
+ #[ derive( Debug , Clone , PartialEq , Eq , PartialOrd , Ord , serde:: Serialize , serde:: Deserialize ) ]
175
243
#[ serde( rename_all = "camelCase" ) ]
176
244
pub struct Nonce {
177
245
pub tag : NonceVariant ,
178
- pub hash : Option < Vec < u8 > > ,
246
+ pub hash : Option < NonceHash > ,
247
+ }
248
+
249
+ impl Default for Nonce {
250
+ fn default ( ) -> Self {
251
+ Self {
252
+ tag : NonceVariant :: NeutralNonce ,
253
+ hash : None ,
254
+ }
255
+ }
256
+ }
257
+
258
+ impl From < NonceHash > for Nonce {
259
+ fn from ( hash : NonceHash ) -> Self {
260
+ Self {
261
+ tag : NonceVariant :: Nonce ,
262
+ hash : Some ( hash) ,
263
+ }
264
+ }
265
+ }
266
+
267
+ #[ derive(
268
+ Default , Debug , PartialEq , Eq , PartialOrd , Ord , Clone , serde:: Serialize , serde:: Deserialize ,
269
+ ) ]
270
+ pub struct Nonces {
271
+ pub epoch : u64 ,
272
+ pub active : Nonce ,
273
+ pub evolving : Nonce ,
274
+ pub candidate : Nonce ,
275
+ // Nonce constructed from the hash of the Last Applied Block
276
+ pub lab : Nonce ,
277
+ // Nonce corresponding to the LAB nonce of the last block of the previous epoch
278
+ pub prev_lab : Nonce ,
279
+ }
280
+
281
+ impl Nonces {
282
+ pub fn shelley_genesis_nonces ( genesis : & GenesisValues ) -> Nonces {
283
+ Nonces {
284
+ epoch : genesis. shelley_epoch ,
285
+ active : genesis. shelley_genesis_hash . into ( ) ,
286
+ evolving : genesis. shelley_genesis_hash . into ( ) ,
287
+ candidate : genesis. shelley_genesis_hash . into ( ) ,
288
+ lab : Nonce :: default ( ) ,
289
+ prev_lab : Nonce :: default ( ) ,
290
+ }
291
+ }
292
+
293
+ pub fn from_candidate ( candidate : & Nonce , prev_lab : & Nonce ) -> Result < Nonce > {
294
+ let Some ( candidate_hash) = candidate. hash . as_ref ( ) else {
295
+ return Err ( anyhow:: anyhow!( "Candidate hash is not set" ) ) ;
296
+ } ;
297
+
298
+ // if prev_lab is Neutral then just return candidate
299
+ // this is for second shelley epoch boundary (from 208 to 209 in mainnet)
300
+ match prev_lab. tag {
301
+ NonceVariant :: NeutralNonce => {
302
+ return Ok ( candidate. clone ( ) ) ;
303
+ }
304
+ NonceVariant :: Nonce => {
305
+ let Some ( prev_lab_hash) = prev_lab. hash . as_ref ( ) else {
306
+ return Err ( anyhow:: anyhow!( "Prev lab hash is not set" ) ) ;
307
+ } ;
308
+ let mut hasher = Blake2b :: < U32 > :: new ( ) ;
309
+ hasher. update ( & [ & candidate_hash. clone ( ) [ ..] , & prev_lab_hash. clone ( ) [ ..] ] . concat ( ) ) ;
310
+ let hash: NonceHash = hasher. finalize ( ) . into ( ) ;
311
+ Ok ( Nonce :: from ( hash) )
312
+ }
313
+ }
314
+ }
315
+
316
+ /// Evolve the current nonce by combining it with the current rolling nonce and the
317
+ /// range-extended tagged leader VRF output.
318
+ ///
319
+ /// Specifically, we combine it with `η` (a.k.a eta), which is a blake2b-256 hash of the
320
+ /// tagged leader VRF output after a range extension. The range extension is, yet another
321
+ /// blake2b-256 hash.
322
+ pub fn evolve ( current : & Nonce , nonce_vrf_output : & Vec < u8 > ) -> Result < Nonce > {
323
+ // first hash nonce_vrf_output
324
+ let mut hasher = Blake2b :: < U32 > :: new ( ) ;
325
+ hasher. update ( nonce_vrf_output. as_slice ( ) ) ;
326
+ let nonce_vrf_output_hash: [ u8 ; 32 ] = hasher. finalize ( ) . into ( ) ;
327
+
328
+ match current. hash . as_ref ( ) {
329
+ Some ( nonce) => {
330
+ let mut hasher = Blake2b :: < U32 > :: new ( ) ;
331
+ hasher. update ( & [ & nonce. clone ( ) [ ..] , & nonce_vrf_output_hash[ ..] ] . concat ( ) ) ;
332
+ let hash: NonceHash = hasher. finalize ( ) . into ( ) ;
333
+ Ok ( Nonce :: from ( hash) )
334
+ }
335
+ _ => Err ( anyhow:: anyhow!( "Current nonce is not set" ) ) ,
336
+ }
337
+ }
338
+
339
+ pub fn randomness_stability_window (
340
+ era : Era ,
341
+ slot : u64 ,
342
+ genesis : & GenesisValues ,
343
+ params : & PraosParams ,
344
+ ) -> bool {
345
+ let ( epoch, _) = genesis. slot_to_epoch ( slot) ;
346
+ let next_epoch_first_slot = genesis. epoch_to_first_slot ( epoch + 1 ) ;
347
+
348
+ // For Praos in Babbage (just as in all TPraos eras) we use the
349
+ // smaller (3k/f vs 4k/f slots) stability window here for
350
+ // backwards-compatibility.
351
+ let window = match era {
352
+ Era :: Conway => params. randomness_stabilization_window ,
353
+ _ => params. stability_window ,
354
+ } ;
355
+
356
+ slot + window < next_epoch_first_slot
357
+ }
179
358
}
0 commit comments