@agoric/time 0.3.2 → 0.3.3-calypso-dev-84eb287.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +2 -0
- package/package.json +24 -11
- package/src/timeMath.js +267 -0
- package/src/typeGuards.js +23 -0
- package/src/types.d.ts +360 -0
- package/src/types.js +2 -0
- package/CHANGELOG.md +0 -37
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/time",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3-calypso-dev-84eb287.0+84eb287",
|
|
4
4
|
"description": "Timestamps, time math, timer service API definition",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
|
+
"types": "index.js",
|
|
7
8
|
"engines": {
|
|
8
|
-
"node": "
|
|
9
|
+
"node": "^18.12 || ^20.9"
|
|
9
10
|
},
|
|
10
11
|
"scripts": {
|
|
11
12
|
"build": "exit 0",
|
|
12
13
|
"test": "ava",
|
|
13
14
|
"test:xs": "exit 0",
|
|
14
15
|
"lint": "run-s --continue-on-error lint:*",
|
|
15
|
-
"lint:types": "tsc
|
|
16
|
+
"lint:types": "tsc",
|
|
16
17
|
"lint:eslint": "eslint .",
|
|
17
18
|
"lint-fix": "yarn lint:eslint --fix"
|
|
18
19
|
},
|
|
@@ -30,21 +31,33 @@
|
|
|
30
31
|
},
|
|
31
32
|
"homepage": "https://github.com/Agoric/agoric-sdk#readme",
|
|
32
33
|
"dependencies": {
|
|
33
|
-
"@agoric/assert": "
|
|
34
|
-
"@
|
|
35
|
-
"@endo/
|
|
34
|
+
"@agoric/assert": "0.6.1-calypso-dev-84eb287.0+84eb287",
|
|
35
|
+
"@endo/nat": "^5.0.7",
|
|
36
|
+
"@endo/patterns": "^1.4.0"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
38
|
-
"@endo/far": "^
|
|
39
|
-
"@endo/init": "^
|
|
40
|
-
"ava": "^5.
|
|
39
|
+
"@endo/far": "^1.1.2",
|
|
40
|
+
"@endo/init": "^1.1.2",
|
|
41
|
+
"ava": "^5.3.0"
|
|
42
|
+
},
|
|
43
|
+
"ava": {
|
|
44
|
+
"require": [
|
|
45
|
+
"@endo/init/debug.js"
|
|
46
|
+
],
|
|
47
|
+
"files": [
|
|
48
|
+
"test/**/*.test.*"
|
|
49
|
+
]
|
|
41
50
|
},
|
|
42
51
|
"files": [
|
|
43
52
|
"*.js",
|
|
44
|
-
"NEWS.md"
|
|
53
|
+
"NEWS.md",
|
|
54
|
+
"src"
|
|
45
55
|
],
|
|
46
56
|
"publishConfig": {
|
|
47
57
|
"access": "public"
|
|
48
58
|
},
|
|
49
|
-
"
|
|
59
|
+
"typeCoverage": {
|
|
60
|
+
"atLeast": 87.29
|
|
61
|
+
},
|
|
62
|
+
"gitHead": "84eb287915c8255d68e845a9b6e10c790f0d54cb"
|
|
50
63
|
}
|
package/src/timeMath.js
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { Nat } from '@endo/nat';
|
|
2
|
+
import { mustMatch } from '@endo/patterns';
|
|
3
|
+
import { RelativeTimeRecordShape, TimestampRecordShape } from './typeGuards.js';
|
|
4
|
+
|
|
5
|
+
/** @import {RelativeTime, RelativeTimeValue, TimerBrand, TimeMathType, Timestamp, TimestampRecord, TimestampValue} from './types.js' */
|
|
6
|
+
|
|
7
|
+
const { Fail, quote: q } = assert;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* `agreedTimerBrand` is internal to this module.
|
|
11
|
+
*
|
|
12
|
+
* @param {TimerBrand | undefined} leftBrand
|
|
13
|
+
* @param {TimerBrand | undefined} rightBrand
|
|
14
|
+
* @returns {TimerBrand | undefined}
|
|
15
|
+
*/
|
|
16
|
+
const agreedTimerBrand = (leftBrand, rightBrand) => {
|
|
17
|
+
if (leftBrand === undefined) {
|
|
18
|
+
if (rightBrand === undefined) {
|
|
19
|
+
return undefined;
|
|
20
|
+
} else {
|
|
21
|
+
return rightBrand;
|
|
22
|
+
}
|
|
23
|
+
} else if (rightBrand === undefined) {
|
|
24
|
+
return leftBrand;
|
|
25
|
+
} else {
|
|
26
|
+
leftBrand === rightBrand ||
|
|
27
|
+
Fail`TimerBrands must match: ${q(leftBrand)} vs ${q(rightBrand)}`;
|
|
28
|
+
return leftBrand;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* `sharedTimerBrand` is internal to this module, and implements the
|
|
34
|
+
* transitional brand checking and contaigion logic explained in the `TimeMath`
|
|
35
|
+
* comment. It is used to define the binary operators that should follow
|
|
36
|
+
* this logic. It does the error checking between the operands, and returns
|
|
37
|
+
* the brand, if any, that should label the resulting time value.
|
|
38
|
+
*
|
|
39
|
+
* @param {Timestamp | RelativeTime} left
|
|
40
|
+
* @param {Timestamp | RelativeTime} right
|
|
41
|
+
* @returns {TimerBrand | undefined}
|
|
42
|
+
*/
|
|
43
|
+
const sharedTimerBrand = (left, right) => {
|
|
44
|
+
const leftBrand = typeof left === 'bigint' ? undefined : left.timerBrand;
|
|
45
|
+
const rightBrand = typeof right === 'bigint' ? undefined : right.timerBrand;
|
|
46
|
+
return agreedTimerBrand(leftBrand, rightBrand);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* `absLike` is internal to this module, and used to implement the binary
|
|
51
|
+
* operators in the case where the returned time should be a `Timestamp`
|
|
52
|
+
* rather than a `RelativeTime`.
|
|
53
|
+
*
|
|
54
|
+
* @param {Timestamp | RelativeTime} left
|
|
55
|
+
* @param {Timestamp | RelativeTime} right
|
|
56
|
+
* @param {TimestampValue} absValue
|
|
57
|
+
* @returns {Timestamp}
|
|
58
|
+
*/
|
|
59
|
+
const absLike = (left, right, absValue) => {
|
|
60
|
+
Nat(absValue);
|
|
61
|
+
const timerBrand = sharedTimerBrand(left, right);
|
|
62
|
+
if (timerBrand) {
|
|
63
|
+
return harden({
|
|
64
|
+
timerBrand,
|
|
65
|
+
absValue,
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
return absValue;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* `relLike` is internal to this module, and used to implement the binary
|
|
74
|
+
* operators in the case where the returned time should be a `RelativeTime`
|
|
75
|
+
* rather than a `Timestamp`.
|
|
76
|
+
*
|
|
77
|
+
* @param {Timestamp | RelativeTime} left
|
|
78
|
+
* @param {Timestamp | RelativeTime} right
|
|
79
|
+
* @param {RelativeTimeValue} relValue
|
|
80
|
+
* @returns {RelativeTime}
|
|
81
|
+
*/
|
|
82
|
+
const relLike = (left, right, relValue) => {
|
|
83
|
+
Nat(relValue);
|
|
84
|
+
const timerBrand = sharedTimerBrand(left, right);
|
|
85
|
+
if (timerBrand) {
|
|
86
|
+
return harden({
|
|
87
|
+
timerBrand,
|
|
88
|
+
relValue,
|
|
89
|
+
});
|
|
90
|
+
} else {
|
|
91
|
+
return relValue;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// For all the following time operators, their documentation is in
|
|
96
|
+
// the `TimeMathType`, since that is the documentation that shows up
|
|
97
|
+
// in the IDE. Well, at least the vscode IDE.
|
|
98
|
+
|
|
99
|
+
const absValue = abs => {
|
|
100
|
+
if (typeof abs === 'bigint') {
|
|
101
|
+
return Nat(abs);
|
|
102
|
+
}
|
|
103
|
+
mustMatch(abs, TimestampRecordShape, 'timestamp');
|
|
104
|
+
return Nat(abs.absValue);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const relValue = rel => {
|
|
108
|
+
if (typeof rel === 'bigint') {
|
|
109
|
+
return Nat(rel);
|
|
110
|
+
}
|
|
111
|
+
mustMatch(rel, RelativeTimeRecordShape, 'relative');
|
|
112
|
+
return Nat(rel.relValue);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const makeTimestampRecord = (abs, timerBrand) =>
|
|
116
|
+
harden({ absValue: abs, timerBrand });
|
|
117
|
+
const makeRelativeTimeRecord = (rel, timerBrand) =>
|
|
118
|
+
harden({ relValue: rel, timerBrand });
|
|
119
|
+
|
|
120
|
+
const coerceTimestampRecord = (ts, brand) => {
|
|
121
|
+
brand || Fail`must have a brand`;
|
|
122
|
+
if (typeof ts === 'number') {
|
|
123
|
+
ts = Nat(ts);
|
|
124
|
+
}
|
|
125
|
+
if (typeof ts === 'bigint') {
|
|
126
|
+
return makeTimestampRecord(ts, brand);
|
|
127
|
+
} else {
|
|
128
|
+
const { timerBrand } = ts;
|
|
129
|
+
mustMatch(ts, TimestampRecordShape, 'timestamp');
|
|
130
|
+
agreedTimerBrand(timerBrand, brand);
|
|
131
|
+
return ts;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const coerceRelativeTimeRecord = (rt, brand) => {
|
|
136
|
+
brand || Fail`must have a brand`;
|
|
137
|
+
if (typeof rt === 'number') {
|
|
138
|
+
rt = Nat(rt);
|
|
139
|
+
}
|
|
140
|
+
if (typeof rt === 'bigint') {
|
|
141
|
+
return makeRelativeTimeRecord(rt, brand);
|
|
142
|
+
} else {
|
|
143
|
+
const { timerBrand } = rt;
|
|
144
|
+
mustMatch(rt, RelativeTimeRecordShape, 'relativeTime');
|
|
145
|
+
agreedTimerBrand(timerBrand, brand);
|
|
146
|
+
return rt;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const addAbsRel = (abs, rel) =>
|
|
151
|
+
absLike(abs, rel, absValue(abs) + relValue(rel));
|
|
152
|
+
|
|
153
|
+
const addRelRel = (rel1, rel2) =>
|
|
154
|
+
relLike(rel1, rel2, relValue(rel1) + relValue(rel2));
|
|
155
|
+
|
|
156
|
+
const subtractAbsAbs = (abs1, abs2) =>
|
|
157
|
+
relLike(abs1, abs2, absValue(abs1) - absValue(abs2));
|
|
158
|
+
|
|
159
|
+
const clampedSubtractAbsAbs = (abs1, abs2) => {
|
|
160
|
+
const val1 = absValue(abs1);
|
|
161
|
+
const val2 = absValue(abs2);
|
|
162
|
+
return relLike(abs1, abs2, val1 > val2 ? val1 - val2 : 0n);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const subtractAbsRel = (abs, rel) =>
|
|
166
|
+
absLike(abs, rel, absValue(abs) - relValue(rel));
|
|
167
|
+
|
|
168
|
+
const subtractRelRel = (rel1, rel2) =>
|
|
169
|
+
relLike(rel1, rel2, relValue(rel1) - relValue(rel2));
|
|
170
|
+
|
|
171
|
+
const isRelZero = rel => relValue(rel) === 0n;
|
|
172
|
+
|
|
173
|
+
const multiplyRelNat = (rel, nat) => relLike(rel, nat, relValue(rel) * nat);
|
|
174
|
+
|
|
175
|
+
const divideRelNat = (rel, nat) => relLike(rel, nat, relValue(rel) / nat);
|
|
176
|
+
|
|
177
|
+
const divideRelRel = (rel1, rel2) => {
|
|
178
|
+
sharedTimerBrand(rel1, rel2); // just error check
|
|
179
|
+
return relValue(rel1) / relValue(rel2);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const modAbsRel = (abs, step) =>
|
|
183
|
+
relLike(abs, step, absValue(abs) % relValue(step));
|
|
184
|
+
|
|
185
|
+
const modRelRel = (rel, step) =>
|
|
186
|
+
relLike(rel, step, relValue(rel) % relValue(step));
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* `compareValues` is internal to this module, and used to implement
|
|
190
|
+
* the time comparison operators.
|
|
191
|
+
*
|
|
192
|
+
* @param {Timestamp | RelativeTime} left
|
|
193
|
+
* @param {Timestamp | RelativeTime} right
|
|
194
|
+
* @param {bigint} v1
|
|
195
|
+
* @param {bigint} v2
|
|
196
|
+
* @returns {import('@endo/marshal').RankComparison}
|
|
197
|
+
*/
|
|
198
|
+
const compareValues = (left, right, v1, v2) => {
|
|
199
|
+
sharedTimerBrand(left, right);
|
|
200
|
+
if (v1 < v2) {
|
|
201
|
+
return -1;
|
|
202
|
+
} else if (v1 === v2) {
|
|
203
|
+
return 0;
|
|
204
|
+
} else {
|
|
205
|
+
assert(v1 > v2);
|
|
206
|
+
return 1;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* The `TimeMath` object provides helper methods to do arithmetic on labeled
|
|
212
|
+
* time values, much like `AmountMath` provides helper methods to do arithmetic
|
|
213
|
+
* on labeled asset/money values. Both check for consistency of labels: a
|
|
214
|
+
* binary operation on two labeled objects ensures that the both carry
|
|
215
|
+
* the same label. If they produce another object from the same domain, it
|
|
216
|
+
* will carry the same label. If the operands have incompatible labels,
|
|
217
|
+
* an error is thrown.
|
|
218
|
+
*
|
|
219
|
+
* Unlike amount arithmetic, time arithmetic deals in two kinds of time objects:
|
|
220
|
+
* Timestamps, which represent absolute time, and RelativeTime, which represents
|
|
221
|
+
* the duration between two absolute times. Both kinds of time object
|
|
222
|
+
* are labeled by a `TimerBrand`. For a Timestamp object, the value is
|
|
223
|
+
* a bigint in an `absValue` property. For a RelativeTime object, the value
|
|
224
|
+
* is a bigint in a `relValue` property. Thus we have a runtime safety check
|
|
225
|
+
* to ensure that we don't confused the two, even if we have managed to fool
|
|
226
|
+
* the (unsound) static type system.
|
|
227
|
+
*
|
|
228
|
+
* As a transitional measure, currently many Timestamps and RelativeTimes are
|
|
229
|
+
* still represented by unlabeled bigints. During this transitional period,
|
|
230
|
+
* we allow this, both statically and dynamically. For a normal binary
|
|
231
|
+
* operation, if both inputs are labeled, then we do the full checking as
|
|
232
|
+
* explained above and return a labeled result. If both inputs are unlabeled
|
|
233
|
+
* bigints, we *assume* that they indicate a time of the right kind
|
|
234
|
+
* (Timestamp vs RelativeTime) and timer brand. Since we don't know what
|
|
235
|
+
* brand was intended, we can only return yet another unlabeled bigint.
|
|
236
|
+
*
|
|
237
|
+
* If one operand is labeled and the other is not, we check the labeled operand,
|
|
238
|
+
* *assume* the unlabeled bigint represents the value needed for the other
|
|
239
|
+
* operand, and return a labeled time object with the brand of the labeled
|
|
240
|
+
* operand.
|
|
241
|
+
*
|
|
242
|
+
* @type {TimeMathType}
|
|
243
|
+
*/
|
|
244
|
+
export const TimeMath = harden({
|
|
245
|
+
absValue,
|
|
246
|
+
relValue,
|
|
247
|
+
coerceTimestampRecord,
|
|
248
|
+
coerceRelativeTimeRecord,
|
|
249
|
+
// @ts-expect-error xxx dynamic typing
|
|
250
|
+
addAbsRel,
|
|
251
|
+
// @ts-expect-error xxx dynamic typing
|
|
252
|
+
addRelRel,
|
|
253
|
+
subtractAbsAbs,
|
|
254
|
+
clampedSubtractAbsAbs,
|
|
255
|
+
subtractAbsRel,
|
|
256
|
+
subtractRelRel,
|
|
257
|
+
isRelZero,
|
|
258
|
+
multiplyRelNat,
|
|
259
|
+
divideRelNat,
|
|
260
|
+
divideRelRel,
|
|
261
|
+
modAbsRel,
|
|
262
|
+
modRelRel,
|
|
263
|
+
compareAbs: (abs1, abs2) =>
|
|
264
|
+
compareValues(abs1, abs2, absValue(abs1), absValue(abs2)),
|
|
265
|
+
compareRel: (rel1, rel2) =>
|
|
266
|
+
compareValues(rel1, rel2, relValue(rel1), relValue(rel2)),
|
|
267
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { M } from '@endo/patterns';
|
|
2
|
+
|
|
3
|
+
export const TimerBrandShape = M.remotable('TimerBrand');
|
|
4
|
+
export const TimestampValueShape = M.nat();
|
|
5
|
+
export const RelativeTimeValueShape = M.nat(); // Should we allow negatives?
|
|
6
|
+
|
|
7
|
+
export const TimestampRecordShape = harden({
|
|
8
|
+
timerBrand: TimerBrandShape,
|
|
9
|
+
absValue: TimestampValueShape,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export const RelativeTimeRecordShape = harden({
|
|
13
|
+
timerBrand: TimerBrandShape,
|
|
14
|
+
relValue: RelativeTimeValueShape,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const TimestampShape = M.or(TimestampRecordShape, TimestampValueShape);
|
|
18
|
+
export const RelativeTimeShape = M.or(
|
|
19
|
+
RelativeTimeRecordShape,
|
|
20
|
+
RelativeTimeValueShape,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export const TimerServiceShape = M.remotable('TimerService');
|
package/src/types.d.ts
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import type { ERef, RemotableBrand } from '@endo/eventual-send';
|
|
2
|
+
|
|
3
|
+
import type { RankComparison, RemotableObject } from '@endo/marshal';
|
|
4
|
+
|
|
5
|
+
/// <reference types="@agoric/notifier/src/types.js" />
|
|
6
|
+
|
|
7
|
+
// These aren't in the global runtime environment. They are just types that are
|
|
8
|
+
// meant to be globally accessible as a side-effect of importing this module.
|
|
9
|
+
/**
|
|
10
|
+
* The TimerBrand is a unique object that represents the kind of Time
|
|
11
|
+
* used in Timestamp/RelativeTime records. Times from different sources
|
|
12
|
+
* are not comparable.
|
|
13
|
+
*
|
|
14
|
+
* Do not call `isMyTimerService(myTimerService)` on an untrusted
|
|
15
|
+
* brand, because that will leak your closely-held timer authority. If
|
|
16
|
+
* the goal is to check the suitability of a client-provided
|
|
17
|
+
* Timestamp, use coerceTimestampRecord() or add/subtract it to a
|
|
18
|
+
* known-good Timestamp, or extract its brand and === against
|
|
19
|
+
* `timerService.getTimerBrand()`.
|
|
20
|
+
*
|
|
21
|
+
* TODO Not all Timestamps are labeled with the TimerBrand (in much
|
|
22
|
+
* the same way that `Amounts` are asset/money values labeled by
|
|
23
|
+
* `Brands`), but the SwingSet vat-timer TimerService will use branded
|
|
24
|
+
* TimestampRecord/RelativeTimeRecord in all messages it emits. Also,
|
|
25
|
+
* a `TimerService` is still used everywhere a `TimerBrand` is called
|
|
26
|
+
* for.
|
|
27
|
+
*
|
|
28
|
+
* See https://github.com/Agoric/agoric-sdk/issues/5798
|
|
29
|
+
* and https://github.com/Agoric/agoric-sdk/pull/5821
|
|
30
|
+
*/
|
|
31
|
+
export type TimerBrand = RemotableObject & {
|
|
32
|
+
isMyTimerService: (timer: TimerService) => ERef<boolean>;
|
|
33
|
+
isMyClock: (clock: Clock) => ERef<boolean>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @deprecated use TimestampRecord
|
|
38
|
+
*
|
|
39
|
+
* An absolute time returned by a TimerService. Note that different timer
|
|
40
|
+
* services may have different interpretations of actual TimestampValue values.
|
|
41
|
+
* Will generally be a count of some number of units starting at some starting
|
|
42
|
+
* point. But what the starting point is and what units are counted is purely up
|
|
43
|
+
* to the meaning of that particular TimerService
|
|
44
|
+
*/
|
|
45
|
+
export type TimestampValue = bigint;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @deprecated use RelativeTimeRecord
|
|
49
|
+
*
|
|
50
|
+
* Difference between two TimestampValues. Note that different timer services
|
|
51
|
+
* may have different interpretations of TimestampValues values.
|
|
52
|
+
*/
|
|
53
|
+
export type RelativeTimeValue = bigint;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The canonical representation of a typed absolute time. It bundles the brand
|
|
57
|
+
* with the time, as represented by a TimerService, which might represent time
|
|
58
|
+
* since the epoch, or blockheight on a particular chain.
|
|
59
|
+
*/
|
|
60
|
+
export type TimestampRecord = {
|
|
61
|
+
timerBrand: TimerBrand;
|
|
62
|
+
absValue: bigint;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The canonical representation of a typed relative time. It bundles the brand
|
|
67
|
+
* with an elapsed time, as represented by a TimerService, which might represent
|
|
68
|
+
* time since the epoch, or blockheight on a particular chain.
|
|
69
|
+
*/
|
|
70
|
+
export type RelativeTimeRecord = {
|
|
71
|
+
timerBrand: TimerBrand;
|
|
72
|
+
relValue: bigint;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @deprecated use TimestampRecord
|
|
77
|
+
*
|
|
78
|
+
* Transitional measure until all are converted to TimestampRecord.
|
|
79
|
+
* See `TimeMath` comment for an explanation of the representation
|
|
80
|
+
* during this transition. After the transition, `Timestamp` will simplify
|
|
81
|
+
* to the current definition of `TimestampRecord`, which will itself
|
|
82
|
+
* be deleted. All Timestamps will then be labeled by TimerBrands.
|
|
83
|
+
*/
|
|
84
|
+
export type Timestamp = TimestampRecord | TimestampValue;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @deprecated use RelativeTimeRecord
|
|
88
|
+
*
|
|
89
|
+
* Transitional measure until all are converted to RelativeTimeRecord
|
|
90
|
+
* See `TimeMath` comment for an explanation of the representation
|
|
91
|
+
* during this transition. After the transition, `RelativeTime` will simplify
|
|
92
|
+
* to the current definition of `RelativeTimeRecord`, which will itself
|
|
93
|
+
* be deleted. All RelativeTimes will then be labeled by TimerBrands.
|
|
94
|
+
*/
|
|
95
|
+
export type RelativeTime = RelativeTimeRecord | RelativeTimeValue;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* A CancelToken is an arbitrary marker object, passed in with
|
|
99
|
+
* each API call that creates a wakeup or repeater, and passed to
|
|
100
|
+
* cancel() to cancel them all. Multiple wakeups can rely on the same
|
|
101
|
+
* CancelToken so they can be cancelled collectively.
|
|
102
|
+
*/
|
|
103
|
+
export type CancelToken = object;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Gives the ability to get the current time,
|
|
107
|
+
* schedule a single wake() call, create a repeater that will allow scheduling
|
|
108
|
+
* of events at regular intervals, or remove scheduled calls.
|
|
109
|
+
*/
|
|
110
|
+
export interface TimerServiceI {
|
|
111
|
+
/**
|
|
112
|
+
* Retrieve the latest timestamp
|
|
113
|
+
*/
|
|
114
|
+
getCurrentTimestamp: () => TimestampRecord;
|
|
115
|
+
/**
|
|
116
|
+
* Return value is the time at which the call is scheduled to take place
|
|
117
|
+
*/
|
|
118
|
+
setWakeup: (
|
|
119
|
+
baseTime: Timestamp,
|
|
120
|
+
waker: ERef<TimerWaker>,
|
|
121
|
+
cancelToken?: CancelToken,
|
|
122
|
+
) => TimestampRecord;
|
|
123
|
+
/**
|
|
124
|
+
* Create and return a promise that will resolve after the absolute
|
|
125
|
+
* time has passed.
|
|
126
|
+
*/
|
|
127
|
+
wakeAt: (
|
|
128
|
+
baseTime: Timestamp,
|
|
129
|
+
cancelToken?: CancelToken,
|
|
130
|
+
) => Promise<TimestampRecord>;
|
|
131
|
+
/**
|
|
132
|
+
* Create and return a promise that will resolve after the relative time has
|
|
133
|
+
* passed.
|
|
134
|
+
*/
|
|
135
|
+
delay: (
|
|
136
|
+
delay: RelativeTime,
|
|
137
|
+
cancelToken?: CancelToken,
|
|
138
|
+
) => Promise<TimestampRecord>;
|
|
139
|
+
/**
|
|
140
|
+
* Create and return a repeater that will schedule `wake()` calls
|
|
141
|
+
* repeatedly at times that are a multiple of interval following delay.
|
|
142
|
+
* Interval is the difference between successive times at which wake will be
|
|
143
|
+
* called. When `schedule(w)` is called, `w.wake()` will be scheduled to be
|
|
144
|
+
* called after the next multiple of interval from the base. Since times can be
|
|
145
|
+
* coarse-grained, the actual call may occur later, but this won't change when
|
|
146
|
+
* the next event will be called.
|
|
147
|
+
*/
|
|
148
|
+
makeRepeater: (
|
|
149
|
+
delay: RelativeTime,
|
|
150
|
+
interval: RelativeTime,
|
|
151
|
+
cancelToken?: CancelToken,
|
|
152
|
+
) => TimerRepeater;
|
|
153
|
+
/**
|
|
154
|
+
* Create a repeater with a handler directly.
|
|
155
|
+
*/
|
|
156
|
+
repeatAfter: (
|
|
157
|
+
delay: RelativeTime,
|
|
158
|
+
interval: RelativeTime,
|
|
159
|
+
handler: TimerWaker,
|
|
160
|
+
cancelToken?: CancelToken,
|
|
161
|
+
) => void;
|
|
162
|
+
/**
|
|
163
|
+
* Create and return a Notifier that will deliver updates repeatedly at times
|
|
164
|
+
* that are a multiple of interval following delay.
|
|
165
|
+
*/
|
|
166
|
+
makeNotifier: (
|
|
167
|
+
delay: RelativeTime,
|
|
168
|
+
interval: RelativeTime,
|
|
169
|
+
cancelToken?: CancelToken,
|
|
170
|
+
) => import('@agoric/notifier').Notifier<TimestampRecord>;
|
|
171
|
+
/**
|
|
172
|
+
* Cancel a previously-established wakeup or repeater.
|
|
173
|
+
*/
|
|
174
|
+
cancel: (cancelToken: CancelToken) => void;
|
|
175
|
+
/**
|
|
176
|
+
* Retrieve the read-only Clock facet.
|
|
177
|
+
*/
|
|
178
|
+
getClock: () => Clock;
|
|
179
|
+
/**
|
|
180
|
+
* Retrieve the Brand for this timer service.
|
|
181
|
+
*/
|
|
182
|
+
getTimerBrand: () => TimerBrand;
|
|
183
|
+
}
|
|
184
|
+
// XXX copied from Remotable helper return type
|
|
185
|
+
export type TimerService = TimerServiceI &
|
|
186
|
+
RemotableObject<'TimerService'> &
|
|
187
|
+
RemotableBrand<{}, TimerServiceI>;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Read-only access to a TimeService's current time. This allows reading the
|
|
191
|
+
* current time (e.g. to see if a deadline has passed) without the ability to
|
|
192
|
+
* schedule events.
|
|
193
|
+
*/
|
|
194
|
+
export interface Clock {
|
|
195
|
+
/**
|
|
196
|
+
* Retrieve the latest timestamp
|
|
197
|
+
*/
|
|
198
|
+
getCurrentTimestamp: () => TimestampRecord;
|
|
199
|
+
/**
|
|
200
|
+
* Retrieve the Brand for this timer service.
|
|
201
|
+
*/
|
|
202
|
+
getTimerBrand: () => TimerBrand;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* The interface that must be implemented by objects which are to be invoked at
|
|
207
|
+
* scheduled times. Used by `TimerService.repeatAfter()`,
|
|
208
|
+
* `TimerService.setWakeup()`, and `TimerRepeater.schedule()`.
|
|
209
|
+
*/
|
|
210
|
+
export interface TimerWaker {
|
|
211
|
+
/**
|
|
212
|
+
* The timestamp passed to `wake()` is the time that the call was scheduled
|
|
213
|
+
* to occur.
|
|
214
|
+
*/
|
|
215
|
+
wake: (timestamp: TimestampRecord) => void;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Provides the ability to schedule wake() calls repeatedly at a regular
|
|
220
|
+
* interval, or to disable all future use of this TimerRepeater. Created by the
|
|
221
|
+
* deprecated makeRepeater(), new code should use repeatAfter(), which doesn't
|
|
222
|
+
* have a control object and doesn't require a second schedule step
|
|
223
|
+
*/
|
|
224
|
+
export interface TimerRepeater {
|
|
225
|
+
/**
|
|
226
|
+
* Returns the time scheduled for
|
|
227
|
+
* the first call to `E(waker).wake()`. The waker will continue to be scheduled
|
|
228
|
+
* every interval until the repeater is disabled.
|
|
229
|
+
*/
|
|
230
|
+
schedule: (waker: ERef<TimerWaker>) => TimestampRecord;
|
|
231
|
+
/**
|
|
232
|
+
* Disable this repeater, so `schedule(w)` can't
|
|
233
|
+
* be called, and wakers already scheduled with this repeater won't be
|
|
234
|
+
* rescheduled again after `E(waker).wake()` is next called on them.
|
|
235
|
+
*/
|
|
236
|
+
disable: () => void;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* TimeMath supports simple arithmetic on typed Time values, enforcing that
|
|
241
|
+
* values are combined in type-compatible ways. You can add 3 minutes to 3pm,
|
|
242
|
+
* or 5 minutes to a half hour, but it makes no sense to add 3pm and 5pm.
|
|
243
|
+
* Subtracting two Timestamps does produce a useful difference.
|
|
244
|
+
*
|
|
245
|
+
* The brands prevent you from accidentally combining time values from different
|
|
246
|
+
* TimerServices. Some chains track time in blocks, others follow wall clock
|
|
247
|
+
* time, some do both. Every local computer has its own unique notion of wall
|
|
248
|
+
* clock time. Even when these clocks are talking about the same thing (UTC),
|
|
249
|
+
* they can all drift in different ways. Using the correct brands lets you be
|
|
250
|
+
* precise about which particular source of time you mean, preventing confusion
|
|
251
|
+
* or attacks when the clocks diverge. Thus it is an error to e.g. use a time
|
|
252
|
+
* you got from chain A to schedule an event on chain B.
|
|
253
|
+
*
|
|
254
|
+
* The basic types are `RelativeTimeRecord` (durations) and `TimestampRecord`. The numeric
|
|
255
|
+
* values can be extracted from the typed values, but it's usually better to
|
|
256
|
+
* maintain values as their canonical typed form so these operations can be
|
|
257
|
+
* applied.
|
|
258
|
+
*/
|
|
259
|
+
export type TimeMathType = {
|
|
260
|
+
/**
|
|
261
|
+
* Validates that the operand represents a `Timestamp` and returns the bigint
|
|
262
|
+
* representing its absolute time value.
|
|
263
|
+
* During the transition explained in the`TimeMath` comment,
|
|
264
|
+
* `absValue` will also accept a bigint which it then just returns.
|
|
265
|
+
*/
|
|
266
|
+
absValue: (abs: Timestamp) => TimestampValue;
|
|
267
|
+
/**
|
|
268
|
+
* Validates that the operand represents a `RelativeTime` and returns the
|
|
269
|
+
* bigint representing its relative time value.
|
|
270
|
+
* During the transition explained in the`TimeMath` comment,
|
|
271
|
+
* `relValue` will also accept a bigint which it then just returns.
|
|
272
|
+
*/
|
|
273
|
+
relValue: (rel: RelativeTime) => RelativeTimeValue;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Coerces to a TimestampRecord if possible, else throws. If the value has a brand, ensure it matches.
|
|
277
|
+
* Return a Timestamp labeled with that brand.
|
|
278
|
+
*/
|
|
279
|
+
coerceTimestampRecord: (
|
|
280
|
+
abs: TimestampRecord | TimestampValue | number,
|
|
281
|
+
brand: TimerBrand,
|
|
282
|
+
) => TimestampRecord;
|
|
283
|
+
/**
|
|
284
|
+
* Coerces to a RelativeTime if possible. If a brand is provided, ensure it
|
|
285
|
+
* matches and return a RelativeTime labeled with that brand.
|
|
286
|
+
*/
|
|
287
|
+
coerceRelativeTimeRecord: (
|
|
288
|
+
rel: RelativeTimeRecord | RelativeTimeValue | number,
|
|
289
|
+
brand: TimerBrand,
|
|
290
|
+
) => RelativeTimeRecord;
|
|
291
|
+
/**
|
|
292
|
+
* An absolute time + a relative time gives a new absolute time.
|
|
293
|
+
*/
|
|
294
|
+
addAbsRel: <T extends Timestamp>(
|
|
295
|
+
abs: T,
|
|
296
|
+
rel: RelativeTime,
|
|
297
|
+
) => T extends TimestampRecord ? TimestampRecord : TimestampValue;
|
|
298
|
+
/**
|
|
299
|
+
* A relative time (i.e., a duration) + another relative time
|
|
300
|
+
* gives a new relative time.
|
|
301
|
+
*/
|
|
302
|
+
addRelRel: <T extends RelativeTime>(
|
|
303
|
+
rel1: T,
|
|
304
|
+
rel2: T,
|
|
305
|
+
) => T extends RelativeTimeRecord ? RelativeTimeRecord : RelativeTimeValue;
|
|
306
|
+
/**
|
|
307
|
+
* The difference between two absolute times is a relative time. If abs1 > abs2
|
|
308
|
+
* the difference would be negative, so this method throws instead.
|
|
309
|
+
*/
|
|
310
|
+
subtractAbsAbs: (abs1: Timestamp, abs2: Timestamp) => RelativeTime;
|
|
311
|
+
/**
|
|
312
|
+
* The difference between two absolute times is a relative time. If abs1 > abs2
|
|
313
|
+
* the difference would be negative, so this method returns a zero
|
|
314
|
+
* relative time instead.
|
|
315
|
+
*/
|
|
316
|
+
clampedSubtractAbsAbs: (abs1: Timestamp, abs2: Timestamp) => RelativeTime;
|
|
317
|
+
/**
|
|
318
|
+
* An absolute time - a relative time gives a new absolute time
|
|
319
|
+
*/
|
|
320
|
+
subtractAbsRel: (abs: Timestamp, rel: RelativeTime) => Timestamp;
|
|
321
|
+
/**
|
|
322
|
+
* The difference between two relative times.
|
|
323
|
+
*/
|
|
324
|
+
subtractRelRel: (rel1: RelativeTime, rel2: RelativeTime) => RelativeTime;
|
|
325
|
+
/**
|
|
326
|
+
* Does it represent a zero relative time, i.e., the difference
|
|
327
|
+
* of an absolute time with itself? (We choose not to define a similar
|
|
328
|
+
* isAbsZero, even though we could, because it is much less likely to be
|
|
329
|
+
* meaningful.)
|
|
330
|
+
*/
|
|
331
|
+
isRelZero: (rel: RelativeTime) => boolean;
|
|
332
|
+
multiplyRelNat: (rel: RelativeTime, nat: bigint) => RelativeTime;
|
|
333
|
+
divideRelNat: (rel: RelativeTime, nat: bigint) => RelativeTime;
|
|
334
|
+
divideRelRel: (rel1: RelativeTime, rel2: RelativeTime) => bigint;
|
|
335
|
+
/**
|
|
336
|
+
* An absolute time modulo a relative time is a relative time. For example,
|
|
337
|
+
* 20:17 on July 20, 1969 modulo 1 day is just 20:17, a relative time that
|
|
338
|
+
* can be added to the beginning of any day.
|
|
339
|
+
*/
|
|
340
|
+
modAbsRel: (abs: Timestamp, step: RelativeTime) => RelativeTime;
|
|
341
|
+
/**
|
|
342
|
+
* A relative time modulo a relative time is a relative time. For example,
|
|
343
|
+
* 3.5 hours modulo an hour is 30 minutes.
|
|
344
|
+
*/
|
|
345
|
+
modRelRel: (rel: RelativeTime, step: RelativeTime) => RelativeTime;
|
|
346
|
+
/**
|
|
347
|
+
* Compares two absolute times. This comparison function is compatible
|
|
348
|
+
* with JavaScript's `Array.prototype.sort` and so can be used to sort an
|
|
349
|
+
* array of absolute times. The result is -1, 0, or 1 indicating whether
|
|
350
|
+
* the first argument is less than, equal, or greater than the second.
|
|
351
|
+
*/
|
|
352
|
+
compareAbs: (abs1: Timestamp, abs2: Timestamp) => RankComparison;
|
|
353
|
+
/**
|
|
354
|
+
* Compares two relative times. This comparison function is compatible
|
|
355
|
+
* with JavaScript's `Array.prototype.sort` and so can be used to sort an
|
|
356
|
+
* array of relative times. The result is -1, 0, or 1 indicating whether
|
|
357
|
+
* the first argument is less than, equal, or greater than the second.
|
|
358
|
+
*/
|
|
359
|
+
compareRel: (rel1: RelativeTime, rel2: RelativeTime) => RankComparison;
|
|
360
|
+
};
|
package/src/types.js
ADDED
package/CHANGELOG.md
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Change Log
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
-
|
|
6
|
-
### [0.3.2](https://github.com/Agoric/agoric-sdk/compare/@agoric/time@0.3.1...@agoric/time@0.3.2) (2023-06-02)
|
|
7
|
-
|
|
8
|
-
**Note:** Version bump only for package @agoric/time
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
### [0.3.1](https://github.com/Agoric/agoric-sdk/compare/@agoric/time@0.3.0...@agoric/time@0.3.1) (2023-05-24)
|
|
15
|
-
|
|
16
|
-
**Note:** Version bump only for package @agoric/time
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
## 0.3.0 (2023-05-19)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
### Features
|
|
26
|
-
|
|
27
|
-
* **auction:** add an auctioneer to manage vault liquidation ([#7000](https://github.com/Agoric/agoric-sdk/issues/7000)) ([398b70f](https://github.com/Agoric/agoric-sdk/commit/398b70f7e028f957afc1582f0ee31eb2574c94d0)), closes [#6992](https://github.com/Agoric/agoric-sdk/issues/6992) [#7047](https://github.com/Agoric/agoric-sdk/issues/7047) [#7074](https://github.com/Agoric/agoric-sdk/issues/7074)
|
|
28
|
-
* create new @agoric/time package ([a61a3fb](https://github.com/Agoric/agoric-sdk/commit/a61a3fbb7a5ccfe07c715a310baa88ada8e572b2)), closes [#6003](https://github.com/Agoric/agoric-sdk/issues/6003)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
### Bug Fixes
|
|
32
|
-
|
|
33
|
-
* **time:** TimerService now returns branded TimestampRecord ([9137e9c](https://github.com/Agoric/agoric-sdk/commit/9137e9cab6f459c876b1a2ad8e681be7224749ce)), closes [#6003](https://github.com/Agoric/agoric-sdk/issues/6003)
|
|
34
|
-
* clean up types ([6f53f19](https://github.com/Agoric/agoric-sdk/commit/6f53f1915ce21e65fefc2fff900b7d4b947be6b1))
|
|
35
|
-
* move timer files to new package ([c105bde](https://github.com/Agoric/agoric-sdk/commit/c105bdefff2527a90b3c6b9d80d0462944dd51c3))
|
|
36
|
-
* TimerBrand has isMyTimerService(), not isMyTimer() ([9f4e867](https://github.com/Agoric/agoric-sdk/commit/9f4e8670694504ebbd451c8840f900a1a24b902f))
|
|
37
|
-
* **time:** fix the code/test to work in its new home ([504d333](https://github.com/Agoric/agoric-sdk/commit/504d3335cf632cc50e079fb27a82db604318bd4a))
|