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