testclock.gno
3.57 Kb · 148 lines
1package kourtv3
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("kourtv3: only the deployer may drive the test clock")
35 }
36}
37func EnableTestClock(cur realm) {
38 mustDeployer(cur)
39 if tcSealed {
40 panic("kourtv3: the test clock is sealed; this realm is not a test chain")
41 }
42 if tcArmed {
43 panic("kourtv3: the test clock is already armed")
44 }
45 if CourtCount() > 1 {
46 panic("kourtv3: 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("kourtv3: this realm has been used; the test clock cannot be armed")
51 }
52 tcArmed = true
53 tcEverArmed = true
54 tcBase = time.Now().Unix()
55 if tcBase > maxBase {
56 panic("kourtv3: this chain's own clock reads past 2100; check its genesis time")
57 }
58 tcFloor = tcBase
59 tcHeightBase = runtime.ChainHeight()
60 tcHeightFloor = tcHeightBase
61}
62func EnableTestClockAt(cur realm, base int64) {
63 mustDeployer(cur)
64 if base <= 0 {
65 panic("kourtv3: the test clock's base must be a positive instant")
66 }
67 if base > maxBase {
68 panic("kourtv3: the test clock's base must be an instant before 2100")
69 }
70 EnableTestClock(cur)
71 tcBase = base
72 if base > tcFloor {
73 tcFloor = base
74 }
75}
76func AdvanceTestClock(cur realm, secs int64) {
77 mustDeployer(cur)
78 if !tcArmed {
79 panic("kourtv3: the test clock is not armed")
80 }
81 if secs <= 0 || secs > maxAdvanceStep {
82 panic("kourtv3: the test clock moves forward, by at most ten years a step")
83 }
84 if tcSkew > maxAdvanceTotal-secs {
85 panic("kourtv3: the test clock has advanced as far as it may")
86 }
87 tcSkew += secs
88 if tcSkew > tcPeakSkew {
89 tcPeakSkew = tcSkew
90 }
91 if n := tcBase + tcSkew; n > tcFloor {
92 tcFloor = n
93 }
94}
95func AdvanceTestHeight(cur realm, n int64) {
96 mustDeployer(cur)
97 if !tcArmed {
98 panic("kourtv3: the test clock is not armed")
99 }
100 if n <= 0 || n > maxHeightStep {
101 panic("kourtv3: the test height moves forward, by at most two emission periods a step")
102 }
103 if tcHeightSkew > maxHeightTotal-n {
104 panic("kourtv3: the test height has advanced as far as it may")
105 }
106 tcHeightSkew += n
107 if tcHeightSkew > tcHeightPeak {
108 tcHeightPeak = tcHeightSkew
109 }
110 if h := tcHeightBase + tcHeightSkew; h > tcHeightFloor {
111 tcHeightFloor = h
112 }
113}
114func TestHeightSkew() int64 { return tcHeightSkew }
115func TestHeightPeakSkew() int64 { return tcHeightPeak }
116func SealTestClock(cur realm) {
117 mustDeployer(cur)
118 if !tcArmed {
119 panic("kourtv3: the test clock was never armed; there is nothing to seal")
120 }
121 sealTestClock()
122}
123func sealTestClock() {
124 tcSealed = true
125 tcArmed = false
126 tcSkew = 0
127 tcBase = 0
128 tcHeightSkew = 0
129}
130func TestClockActive() bool { return tcArmed }
131func TestClockFabricated() bool { return tcEverArmed }
132func TestClockSkew() int64 { return tcSkew }
133func TestClockPeakSkew() int64 { return tcPeakSkew }
134func tcNow() int64 {
135 var n int64
136 if tcArmed {
137 n = tcBase + tcSkew
138 } else {
139 n = time.Now().Unix()
140 if n < tcFloor {
141 n = tcFloor
142 }
143 }
144 if n <= 0 {
145 panic("kourtv3: the clock read a non-positive instant")
146 }
147 return n
148}