timelock.gno
4.33 Kb ยท 206 lines
1package timelock
2
3import (
4 "chain"
5 "chain/runtime"
6 "strconv"
7 "time"
8
9 "gno.land/p/nt/avl"
10 "gno.land/p/nt/seqid"
11 "gno.land/p/nt/ufmt"
12 "gno.land/p/thox/accesscontrol"
13)
14
15// Represents the status of a planned operation
16type OperationState int
17
18const (
19 Unset OperationState = iota
20 Pending
21 Ready
22 Done
23)
24
25func (os OperationState) StateToString() string {
26 switch os {
27 case Unset:
28 return "Unset"
29 case Pending:
30 return "Pending"
31 case Ready:
32 return "Ready"
33 case Done:
34 return "Done"
35 default:
36 return "Unknown"
37 }
38}
39
40// OperationStatus represents the status of an operation
41type OperationStatus struct {
42 sheduleTime int64
43 isDone bool
44}
45
46// TimeLock stores the necessary parameters for the timelock operations
47type TimeLock struct {
48 timestamps *avl.Tree // id -> time.Time
49 accessControl *accesscontrol.Role
50 minDelay uint64
51}
52
53// New instance of TimeLock
54func NewTimeLock(timestamps *avl.Tree, accessControl *accesscontrol.Role, minDelay uint64) (*TimeLock, error) {
55 if timestamps == nil || accessControl == nil {
56 return nil, ErrNilTimestampsOrAccessControl
57 }
58
59 return &TimeLock{
60 timestamps: timestamps,
61 accessControl: accessControl,
62 minDelay: minDelay,
63 }, nil
64}
65
66// Schedules an operation to be carried out after a minimum delay
67func (tl *TimeLock) Schedule(id seqid.ID, delay uint64) error {
68 if delay < tl.minDelay {
69 return ErrInsufficientDelay
70 }
71
72 if tl.timestamps.Has(id.Binary()) {
73 return ErrOperationAlreadyScheduled
74 }
75
76 timestamp := time.Now().Unix() + int64(delay)
77 status := OperationStatus{sheduleTime: timestamp, isDone: false}
78 tl.timestamps.Set(id.Binary(), status)
79
80 chain.Emit(
81 "TimeLockScheduled",
82 "id", id.String(),
83 "delay", strconv.FormatInt(int64(delay), 10),
84 )
85
86 return nil
87}
88
89// Remove operation
90func (tl *TimeLock) Remove(id seqid.ID) {
91 tl.timestamps.Remove(id.Binary())
92
93 chain.Emit(
94 "TimeLockRemoved",
95 "id", id.String(),
96 )
97}
98
99// Cancels a planned operation
100func (tl *TimeLock) Cancel(id seqid.ID) error {
101 if !tl.IsPending(id) {
102 return ErrOperationCancelNotPending
103 }
104
105 tl.timestamps.Remove(id.Binary())
106
107 chain.Emit(
108 "TimeLockCancelled",
109 "id", id.String(),
110 )
111 return nil
112}
113
114// Executes a pending operation
115func (tl *TimeLock) Execute(id seqid.ID) error {
116 if !tl.IsPending(id) {
117 return ErrOperationExecuteNotPending
118 }
119
120 status, err := tl.GetOperationStatus(id)
121 if err != nil {
122 return err
123 }
124 status.isDone = true
125 tl.timestamps.Set(id.Binary(), status)
126
127 chain.Emit(
128 "TimeLockExecuted",
129 "id", id.String(),
130 )
131
132 return nil
133}
134
135// Update the minimum lead time for future operations
136func (tl *TimeLock) UpdateDelay(newDelay uint64) error {
137 if runtime.PreviousRealm().Address() != tl.accessControl.Ownable.Owner() {
138 return ErrUpadateDelay
139 }
140
141 chain.Emit(
142 "TimeLockMinDelayChanged",
143 "oldDelay", strconv.FormatInt(int64(tl.minDelay), 10),
144 "newDelay", strconv.FormatInt(int64(newDelay), 10),
145 )
146
147 tl.minDelay = newDelay
148
149 return nil
150}
151
152// Checks if an operation is pending
153func (tl *TimeLock) IsPending(id seqid.ID) bool {
154 state, err := tl.GetOperationState(id)
155 if err != nil {
156 // Handle the error appropriately; for now, we assume the operation is not pending if there's an error
157 ufmt.Errorf("Error retrieving operation state: %v", err)
158 return false
159 }
160
161 return state == Pending
162}
163
164// Checks if an operation is ready
165func (tl *TimeLock) IsReady(id seqid.ID) bool {
166 state, err := tl.GetOperationState(id)
167 if err != nil {
168 // Handle the error appropriately; for now, we assume the operation is not pending if there's an error
169 ufmt.Errorf("Error retrieving operation state: %v", err)
170 return false
171 }
172
173 return state == Ready
174}
175
176// Returns the status of an operation
177func (tl *TimeLock) GetOperationState(id seqid.ID) (OperationState, error) {
178 status, err := tl.GetOperationStatus(id)
179 if err != nil {
180 return Unset, err
181 }
182 if status.isDone {
183 return Done, nil
184 }
185 if status.sheduleTime == 0 {
186 return Unset, nil
187 }
188 if status.sheduleTime > time.Now().Unix() {
189 return Pending, nil
190 }
191 return Ready, nil
192}
193
194// Returns the status of an operation
195func (tl *TimeLock) GetOperationStatus(id seqid.ID) (OperationStatus, error) {
196 value, ok := tl.timestamps.Get(id.Binary())
197
198 if !ok {
199 return OperationStatus{}, nil // Return an empty status if the operation is not found
200 }
201 if status, ok := value.(OperationStatus); ok {
202 return status, nil
203 } else {
204 return OperationStatus{}, ErrUnexpectedType
205 }
206}