Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}