1
1
/** @typedef {any } Gov - TODO */
2
2
3
+ /**
4
+ * @typedef Snapshot
5
+ * @prop {Uint53 } block - the block to be used for calculation
6
+ * @prop {Uint53 } ms - the time of that block in ms
7
+ */
8
+
9
+ /**
10
+ * @typedef Estimate
11
+ * @prop {Uint53 } secondsPerBlock
12
+ * @prop {Uint53 } voteHeight
13
+ * @prop {Uint53 } voteDelta
14
+ * @prop {String } voteIso - date in ISO format
15
+ * @prop {Uint53 } voteMs
16
+ * @prop {Uint53 } voteDeltaMs
17
+ * @prop {Uint53 } superblockHeight
18
+ * @prop {Uint53 } superblockDelta
19
+ * @prop {String } superblockIso - date in ISO format
20
+ * @prop {Uint53 } superblockMs
21
+ * @prop {Uint53 } superblockDeltaMs
22
+ */
23
+
24
+ /**
25
+ * @typedef Estimates
26
+ * @prop {Estimate } last - the most recent superblock
27
+ * @prop {Estimate? } lameduck - the current voting period, if close to deadline
28
+ * @prop {Array<Estimate> } upcoming - future voting periods
29
+ */
30
+
3
31
/** @type {Gov } */
4
32
//@ts -ignore
5
33
var DashGov = ( "object" === typeof module && exports ) || { } ;
@@ -182,131 +210,127 @@ var DashGov = ("object" === typeof module && exports) || {};
182
210
} ;
183
211
184
212
// TODO move to a nice place
185
- const SUPERBLOCK_INTERVAL = 16616 ; // actual
213
+ const SUPERBLOCK_INTERVAL = 16616 ;
214
+ const VOTE_LEAD_BLOCKS = 1662 ;
215
+ const PROPOSAL_LEAD_MS = 6 * 24 * 60 * 60 * 1000 ;
186
216
DashGov . SUPERBLOCK_INTERVAL = SUPERBLOCK_INTERVAL ;
187
- const SECONDS_PER_BLOCK_ESTIMATE = 155 ; // estimate (measured as ~157.64)
188
217
189
- const VOTE_OFFSET_BLOCKS = 1662 ; // actual
190
- const VOTE_OFFSET_MS = VOTE_OFFSET_BLOCKS * SECONDS_PER_BLOCK_ESTIMATE * 1000 ; // estimate
218
+ // not used because the actual average at any time is always closer to 157.5
219
+ //const SECONDS_PER_BLOCK_ESTIMATE = 155;
220
+ DashGov . _AVG_SECS_PER_BLOCK = 157.58166827154548 ;
191
221
222
+ // used to calculate ~5 year (~60 month) averages
192
223
const MONTHLY_SUPERBLOCK_01_DATE = "2017-03-05T20:16:00Z" ;
193
224
const MONTHLY_SUPERBLOCK_01 = 631408 ;
194
225
const MONTHLY_SUPERBLOCK_61_DATE = "2022-02-26T03:53:00Z" ;
195
226
const MONTHLY_SUPERBLOCK_61 = 1628368 ;
196
- const ERR_INCOMPLETE_BLOCK =
197
- "both block and time must be given for snapshot calculations" ;
198
227
199
228
/**
200
- * @typedef Snapshot
201
- * @prop {Uint53 } block - the block to be used for calculation
202
- * @prop {Uint53 } ms - the time of that block in ms
203
- */
204
-
205
- /**
206
- * @param {Snapshot } [snapshot]
229
+ * @param {Snapshot } snapshot
207
230
* @returns {Float64 } - fractional seconds
208
231
*/
209
232
GObj . measureSecondsPerBlock = function ( snapshot ) {
210
- if ( ! snapshot ) {
211
- snapshot = { ms : 0 , block : 0 } ;
212
- }
213
-
214
- if ( ! snapshot . ms ) {
215
- if ( snapshot . block ) {
216
- throw new Error ( ERR_INCOMPLETE_BLOCK ) ;
217
- }
218
- let d = new Date ( MONTHLY_SUPERBLOCK_61_DATE ) ;
219
- snapshot . ms = d . valueOf ( ) ;
220
- snapshot . block = MONTHLY_SUPERBLOCK_61 ;
221
- } else {
222
- if ( ! snapshot . block ) {
223
- throw new Error ( ERR_INCOMPLETE_BLOCK ) ;
224
- }
225
- }
226
-
227
- let firstSuperblockDate = new Date ( MONTHLY_SUPERBLOCK_01_DATE ) ;
228
233
let blockDelta = snapshot . block - MONTHLY_SUPERBLOCK_01 ;
229
- let timeDelta = snapshot . ms - firstSuperblockDate . valueOf ( ) ;
234
+ let timeDelta = snapshot . ms - Date . parse ( MONTHLY_SUPERBLOCK_01_DATE ) ;
230
235
let msPerBlock = timeDelta / blockDelta ;
231
236
let sPerBlock = msPerBlock / 1000 ;
232
237
233
- // let sPerBlockF = sPerBlock.toFixed(1);
234
- // sPerBlock = parseFloat(sPerBlockF);
235
-
236
238
return sPerBlock ;
237
239
} ;
238
240
239
- GObj . estimateFutureBlocks = function (
240
- now = Date . now ( ) ,
241
- currentBlock = 0 ,
242
- secondsPerBlock = 0 ,
243
- cycles = 3 ,
244
- ) {
245
- if ( ! currentBlock ) {
246
- let d = new Date ( MONTHLY_SUPERBLOCK_61_DATE ) ;
247
- let then = d . valueOf ( ) ;
248
- let spb = GObj . measureSecondsPerBlock ( {
241
+ /**
242
+ * @param {Snapshot } [snapshot] - defaults to monthly superblock 61
243
+ */
244
+ GObj . estimateSecondsPerBlock = function ( snapshot ) {
245
+ if ( ! snapshot ) {
246
+ snapshot = {
249
247
block : MONTHLY_SUPERBLOCK_61 ,
250
- ms : then ,
251
- } ) ;
252
- let delta = now - then ;
253
- let deltaS = delta / 1000 ;
254
- let blocks = deltaS / spb ;
255
- currentBlock = MONTHLY_SUPERBLOCK_61 + blocks ;
256
- if ( ! secondsPerBlock ) {
257
- secondsPerBlock = spb ;
258
- }
248
+ ms : Date . parse ( MONTHLY_SUPERBLOCK_61_DATE ) ,
249
+ } ;
259
250
}
260
251
252
+ let spb = GObj . measureSecondsPerBlock ( snapshot ) ;
253
+ return spb ;
254
+ } ;
255
+
256
+ /**
257
+ * @param {Uint53 } ms - the current time
258
+ * @param {Float64 } secondsPerBlock
259
+ */
260
+ GObj . estimateBlockHeight = function ( ms , secondsPerBlock ) {
261
+ let then = Date . parse ( MONTHLY_SUPERBLOCK_61_DATE ) ;
262
+ let delta = ms - then ;
263
+ let deltaS = delta / 1000 ;
264
+ let blocks = deltaS / secondsPerBlock ;
265
+ blocks = Math . round ( blocks ) ;
266
+
267
+ let height = MONTHLY_SUPERBLOCK_61 + blocks ;
268
+ return height ;
269
+ } ;
270
+
271
+ /**
272
+ * Note: since we're dealing with estimates that are typically reliable
273
+ * within an hour (and often even within 15 minutes), this may
274
+ * generate more results than it presents.
275
+ * @param {Uint8 } [cycles] - 3 by default
276
+ * @param {Snapshot? } [snapshot]
277
+ * @param {Uint32 } [proposalLeadtime] - default 3 days in ms
278
+ * @param {Float64 } [secondsPerBlock] - typically close to 157.6
279
+ * @returns {Estimates } - the last, due, and upcoming proposal cycles
280
+ */
281
+ GObj . estimateProposalCycles = function (
282
+ cycles = 3 ,
283
+ snapshot = null ,
284
+ proposalLeadtime = PROPOSAL_LEAD_MS ,
285
+ secondsPerBlock = 0 ,
286
+ ) {
287
+ let now = snapshot ?. ms || Date . now ( ) ;
288
+ let currentBlock = snapshot ?. block ;
261
289
if ( ! secondsPerBlock ) {
262
- secondsPerBlock = GObj . measureSecondsPerBlock ( {
263
- block : currentBlock ,
264
- ms : now ,
265
- } ) ;
290
+ if ( currentBlock ) {
291
+ snapshot = { block : currentBlock , ms : now } ;
292
+ }
293
+ secondsPerBlock = GObj . measureSecondsPerBlock ( snapshot ) ;
294
+ }
295
+ if ( ! currentBlock ) {
296
+ currentBlock = GObj . estimateBlockHeight ( now , secondsPerBlock ) ;
266
297
}
267
298
268
299
/** @type {Array<Estimate> } */
269
300
let estimates = [ ] ;
270
- for ( let i = - 1 ; i < cycles ; i += 1 ) {
271
- let estimate = GObj . estimateNthGovCycle (
301
+ for ( let i = 0 ; i <= cycles + 1 ; i += 1 ) {
302
+ let estimate = GObj . estimateNthNextGovCycle (
272
303
{ block : currentBlock , ms : now } ,
273
304
secondsPerBlock ,
274
305
i ,
275
306
) ;
276
307
estimates . push ( estimate ) ;
277
308
}
278
309
279
- return estimates ;
280
- } ;
281
-
282
- /**
283
- * @typedef Estimate
284
- * @prop {Uint53 } secondsPerBlock
285
- * @prop {Uint53 } voteHeight
286
- * @prop {Uint53 } voteDelta
287
- * @prop {String } voteIso - date in ISO format
288
- * @prop {Uint53 } voteMs
289
- * @prop {Uint53 } voteDeltaMs
290
- * @prop {Uint53 } superblockHeight
291
- * @prop {Uint53 } superblockDelta
292
- * @prop {String } superblockIso - date in ISO format
293
- * @prop {Uint53 } superblockMs
294
- * @prop {Uint53 } superblockDeltaMs
295
- */
296
-
297
- /**
298
- * @param {Uint53 } height
299
- * @param {Uint53 } offset - 0 (current / previous), 1 (next), 2, 3, nth
300
- * @returns {Uint53 } - the superblock after the given height
301
- */
302
- GObj . getNthNextSuperblock = function ( height , offset ) {
303
- let superblockCount = height / SUPERBLOCK_INTERVAL ;
304
- superblockCount = Math . floor ( superblockCount ) ;
305
-
306
- superblockCount += offset ;
307
- let superblockHeight = superblockCount * SUPERBLOCK_INTERVAL ;
310
+ {
311
+ /** @type {Estimate } */
312
+ //@ts -ignore - we know there is at least one (past) estimate
313
+ let last = estimates . shift ( ) ;
314
+
315
+ /** @type {Estimate? } */
316
+ let lameduck = null ;
317
+ if ( estimates . length ) {
318
+ if ( estimates [ 0 ] . voteDeltaMs < proposalLeadtime ) {
319
+ //@ts -ignore - we just checked the length
320
+ lameduck = estimates . shift ( ) ;
321
+ } else {
322
+ // lose the extra cycle
323
+ void estimates . pop ( ) ;
324
+ }
325
+ }
326
+ let upcoming = estimates ;
308
327
309
- return superblockHeight ;
328
+ return {
329
+ last,
330
+ lameduck,
331
+ upcoming,
332
+ } ;
333
+ }
310
334
} ;
311
335
312
336
/**
@@ -315,38 +339,39 @@ var DashGov = ("object" === typeof module && exports) || {};
315
339
* @param {Uint53 } offset - how many superblocks in the future
316
340
* @returns {Estimate } - details about the current governance cycle
317
341
*/
318
- GObj . estimateNthGovCycle = function (
319
- { block , ms } ,
342
+ GObj . estimateNthNextGovCycle = function (
343
+ snapshot ,
320
344
secondsPerBlock ,
321
345
offset = 0 ,
322
346
) {
323
- let blockOffset = offset * SUPERBLOCK_INTERVAL ;
324
- let blockTarget = block + blockOffset ;
325
- let superblockHeight = GObj . getNthNextSuperblock ( blockTarget , offset ) ;
347
+ if ( ! secondsPerBlock ) {
348
+ secondsPerBlock = GObj . estimateSecondsPerBlock ( snapshot ) ;
349
+ }
350
+
351
+ let superblockHeight = GObj . getNthNextSuperblock ( snapshot . block , offset ) ;
326
352
327
- let superblockDelta = superblockHeight - block ;
328
- // let superblockDeltaMs = superblockDelta * SECONDS_PER_BLOCK_ESTIMATE * 1000;
353
+ let superblockDelta = superblockHeight - snapshot . block ;
329
354
let superblockDeltaMs = superblockDelta * secondsPerBlock * 1000 ;
355
+ let voteDeltaMs = VOTE_LEAD_BLOCKS * secondsPerBlock * 1000 ;
330
356
331
- console . log ( ms , superblockDeltaMs ) ;
332
- let d = new Date ( ms ) ;
357
+ let d = new Date ( snapshot . ms ) ;
333
358
d . setUTCMilliseconds ( 0 ) ;
334
359
335
360
d . setUTCMilliseconds ( superblockDeltaMs ) ;
336
361
let sbms = d . valueOf ( ) ;
337
362
let sbts = d . toISOString ( ) ;
338
363
339
- d . setUTCMilliseconds ( - VOTE_OFFSET_MS ) ;
364
+ d . setUTCMilliseconds ( - voteDeltaMs ) ;
340
365
let vtms = d . valueOf ( ) ;
341
366
let vtts = d . toISOString ( ) ;
342
367
343
368
return {
344
- secondsPerBlock : secondsPerBlock ,
345
- voteHeight : superblockHeight - VOTE_OFFSET_BLOCKS ,
369
+ // TODO split into objects
370
+ voteHeight : superblockHeight - VOTE_LEAD_BLOCKS ,
346
371
voteIso : vtts ,
347
372
voteMs : vtms ,
348
- voteDelta : superblockDelta - VOTE_OFFSET_BLOCKS ,
349
- voteDeltaMs : superblockDeltaMs - VOTE_OFFSET_MS ,
373
+ voteDelta : superblockDelta - VOTE_LEAD_BLOCKS ,
374
+ voteDeltaMs : superblockDeltaMs - voteDeltaMs ,
350
375
superblockHeight : superblockHeight ,
351
376
superblockDelta : superblockDelta ,
352
377
superblockIso : sbts ,
@@ -355,6 +380,21 @@ var DashGov = ("object" === typeof module && exports) || {};
355
380
} ;
356
381
} ;
357
382
383
+ /**
384
+ * @param {Uint53 } height
385
+ * @param {Uint53 } offset - 0 (current / previous), 1 (next), 2, 3, nth
386
+ * @returns {Uint53 } - the superblock after the given height
387
+ */
388
+ GObj . getNthNextSuperblock = function ( height , offset ) {
389
+ let superblockCount = height / SUPERBLOCK_INTERVAL ;
390
+ superblockCount = Math . floor ( superblockCount ) ;
391
+
392
+ superblockCount += offset ;
393
+ let superblockHeight = superblockCount * SUPERBLOCK_INTERVAL ;
394
+
395
+ return superblockHeight ;
396
+ } ;
397
+
358
398
//@ts -ignore
359
399
window . DashGov = GObj ;
360
400
} ) ( globalThis . window || { } , DashGov ) ;
0 commit comments