testclock.gno
3.55 Kb · 147 lines
1package kourt
2import (
3 "time"
4 runtime "chain/runtime"
5 unsafe "chain/runtime/unsafe"
6)
7const (
8 maxAdvanceStep = int64(10 * 365 * 86400)
9 maxAdvanceTotal = int64(100 * 365 * 86400)
10 maxHeightStep = 2 * periodBlocks
11 maxHeightTotal = 10 * periodBlocks
12 maxBase = int64(4102444800)
13)
14var (
15 tcDeployer address
16 tcArmed bool
17 tcSealed bool
18 tcSkew int64
19 tcBase int64
20 tcFloor int64
21 tcEverArmed bool
22 tcPeakSkew int64
23 tcHeightSkew int64
24 tcHeightPeak int64
25 tcHeightBase int64
26 tcHeightFloor int64
27)
28func init() { tcDeployer = unsafe.OriginCaller() }
29func mustDeployer(cur realm) {
30 if !cur.IsCurrent() {
31 panic(errStaleRealm)
32 }
33 if cur.Previous().Address() != tcDeployer {
34 panic("kourtv2: only the deployer may drive the test clock")
35 }
36}
37func EnableTestClock(cur realm) {
38 mustDeployer(cur)
39 if tcSealed {
40 panic("kourtv2: the test clock is sealed; this realm is not a test chain")
41 }
42 if tcArmed {
43 panic("kourtv2: the test clock is already armed")
44 }
45 if CourtCount() > 2 {
46 panic("kourtv2: this realm already has courts; the test clock cannot be armed")
47 }
48 m := mustCourt(metaSlug)
49 if m.nextID > 1 || m.coin.TotalSupply() > 0 {
50 panic("kourtv2: this realm has been used; the test clock cannot be armed")
51 }
52 tcArmed = true
53 tcBase = time.Now().Unix()
54 if tcBase > maxBase {
55 panic("kourtv2: this chain's own clock reads past 2100; check its genesis time")
56 }
57 tcFloor = tcBase
58 tcHeightBase = runtime.ChainHeight()
59 tcHeightFloor = tcHeightBase
60}
61func EnableTestClockAt(cur realm, base int64) {
62 mustDeployer(cur)
63 if base <= 0 {
64 panic("kourtv2: the test clock's base must be a positive instant")
65 }
66 if base > maxBase {
67 panic("kourtv2: the test clock's base must be an instant before 2100")
68 }
69 EnableTestClock(cur)
70 tcBase = base
71 if base > tcFloor {
72 tcFloor = base
73 }
74}
75func AdvanceTestClock(cur realm, secs int64) {
76 mustDeployer(cur)
77 if !tcArmed {
78 panic("kourtv2: the test clock is not armed")
79 }
80 if secs <= 0 || secs > maxAdvanceStep {
81 panic("kourtv2: the test clock moves forward, by at most ten years a step")
82 }
83 if tcSkew > maxAdvanceTotal-secs {
84 panic("kourtv2: the test clock has advanced as far as it may")
85 }
86 tcSkew += secs
87 if tcSkew > tcPeakSkew {
88 tcPeakSkew = tcSkew
89 }
90 if n := tcBase + tcSkew; n > tcFloor {
91 tcFloor = n
92 }
93}
94func AdvanceTestHeight(cur realm, n int64) {
95 mustDeployer(cur)
96 if !tcArmed {
97 panic("kourtv2: the test clock is not armed")
98 }
99 if n <= 0 || n > maxHeightStep {
100 panic("kourtv2: the test height moves forward, by at most two emission periods a step")
101 }
102 if tcHeightSkew > maxHeightTotal-n {
103 panic("kourtv2: the test height has advanced as far as it may")
104 }
105 tcHeightSkew += n
106 if tcHeightSkew > tcHeightPeak {
107 tcHeightPeak = tcHeightSkew
108 }
109 if h := tcHeightBase + tcHeightSkew; h > tcHeightFloor {
110 tcHeightFloor = h
111 }
112}
113func TestHeightSkew() int64 { return tcHeightSkew }
114func TestHeightPeakSkew() int64 { return tcHeightPeak }
115func SealTestClock(cur realm) {
116 mustDeployer(cur)
117 if !tcArmed {
118 panic("kourtv2: the test clock was never armed; there is nothing to seal")
119 }
120 sealTestClock()
121}
122func sealTestClock() {
123 tcSealed = true
124 tcArmed = false
125 tcSkew = 0
126 tcBase = 0
127 tcHeightSkew = 0
128}
129func TestClockActive() bool { return tcArmed }
130func TestClockFabricated() bool { return tcEverArmed }
131func TestClockSkew() int64 { return tcSkew }
132func TestClockPeakSkew() int64 { return tcPeakSkew }
133func tcNow() int64 {
134 var n int64
135 if tcArmed {
136 n = tcBase + tcSkew
137 } else {
138 n = time.Now().Unix()
139 if n < tcFloor {
140 n = tcFloor
141 }
142 }
143 if n <= 0 {
144 panic("kourtv2: the clock read a non-positive instant")
145 }
146 return n
147}