@agoric/time
Version:
Timestamps, time math, timer service API definition
359 lines (343 loc) • 13.6 kB
text/typescript
import type { ERef, RemotableBrand } from '@endo/eventual-send';
import type { RankComparison, RemotableObject } from '@endo/marshal';
// These aren't in the global runtime environment. They are just types that are
// meant to be globally accessible as a side-effect of importing this module.
/**
* The TimerBrand is a unique object that represents the kind of Time
* used in Timestamp/RelativeTime records. Times from different sources
* are not comparable.
*
* Do not call `isMyTimerService(myTimerService)` on an untrusted
* brand, because that will leak your closely-held timer authority. If
* the goal is to check the suitability of a client-provided
* Timestamp, use coerceTimestampRecord() or add/subtract it to a
* known-good Timestamp, or extract its brand and === against
* `timerService.getTimerBrand()`.
*
* TODO Not all Timestamps are labeled with the TimerBrand (in much
* the same way that `Amounts` are asset/money values labeled by
* `Brands`), but the SwingSet vat-timer TimerService will use branded
* TimestampRecord/RelativeTimeRecord in all messages it emits. Also,
* a `TimerService` is still used everywhere a `TimerBrand` is called
* for.
*
* See https://github.com/Agoric/agoric-sdk/issues/5798
* and https://github.com/Agoric/agoric-sdk/pull/5821
*/
export type TimerBrand = RemotableObject & {
isMyTimerService: (timer: TimerService) => ERef<boolean>;
isMyClock: (clock: Clock) => ERef<boolean>;
};
/**
* @deprecated use TimestampRecord
*
* An absolute time returned by a TimerService. Note that different timer
* services may have different interpretations of actual TimestampValue values.
* Will generally be a count of some number of units starting at some starting
* point. But what the starting point is and what units are counted is purely up
* to the meaning of that particular TimerService
*/
export type TimestampValue = bigint;
/**
* @deprecated use RelativeTimeRecord
*
* Difference between two TimestampValues. Note that different timer services
* may have different interpretations of TimestampValues values.
*/
export type RelativeTimeValue = bigint;
/**
* The canonical representation of a typed absolute time. It bundles the brand
* with the time, as represented by a TimerService, which might represent time
* since the epoch, or blockheight on a particular chain.
*/
export type TimestampRecord = {
timerBrand: TimerBrand;
absValue: bigint;
};
/**
* The canonical representation of a typed relative time. It bundles the brand
* with an elapsed time, as represented by a TimerService, which might represent
* time since the epoch, or blockheight on a particular chain.
*/
export type RelativeTimeRecord = {
timerBrand: TimerBrand;
relValue: bigint;
};
/**
* @deprecated use TimestampRecord
*
* Transitional measure until all are converted to TimestampRecord.
* See `TimeMath` comment for an explanation of the representation
* during this transition. After the transition, `Timestamp` will simplify
* to the current definition of `TimestampRecord`, which will itself
* be deleted. All Timestamps will then be labeled by TimerBrands.
*/
export type Timestamp = TimestampRecord | TimestampValue;
/**
* @deprecated use RelativeTimeRecord
*
* Transitional measure until all are converted to RelativeTimeRecord
* See `TimeMath` comment for an explanation of the representation
* during this transition. After the transition, `RelativeTime` will simplify
* to the current definition of `RelativeTimeRecord`, which will itself
* be deleted. All RelativeTimes will then be labeled by TimerBrands.
*/
export type RelativeTime = RelativeTimeRecord | RelativeTimeValue;
/**
* A CancelToken is an arbitrary marker object, passed in with
* each API call that creates a wakeup or repeater, and passed to
* cancel() to cancel them all. Multiple wakeups can rely on the same
* CancelToken so they can be cancelled collectively.
*/
export type CancelToken = object;
/**
* Gives the ability to get the current time,
* schedule a single wake() call, create a repeater that will allow scheduling
* of events at regular intervals, or remove scheduled calls.
*/
export interface TimerServiceCommon {
/**
* Retrieve the latest timestamp
*/
getCurrentTimestamp: () => TimestampRecord;
/**
* Return value is the time at which the call is scheduled to take place
*/
setWakeup: (
baseTime: Timestamp,
waker: ERef<TimerWaker>,
cancelToken?: CancelToken,
) => TimestampRecord;
/**
* Create and return a promise that will resolve after the absolute
* time has passed.
*/
wakeAt: (
baseTime: Timestamp,
cancelToken?: CancelToken,
) => Promise<TimestampRecord>;
/**
* Create and return a promise that will resolve after the relative time has
* passed.
*/
delay: (
delay: RelativeTime,
cancelToken?: CancelToken,
) => Promise<TimestampRecord>;
/**
* Create and return a repeater that will schedule `wake()` calls
* repeatedly at times that are a multiple of interval following delay.
* Interval is the difference between successive times at which wake will be
* called. When `schedule(w)` is called, `w.wake()` will be scheduled to be
* called after the next multiple of interval from the base. Since times can be
* coarse-grained, the actual call may occur later, but this won't change when
* the next event will be called.
*/
makeRepeater: (
delay: RelativeTime,
interval: RelativeTime,
cancelToken?: CancelToken,
) => TimerRepeater;
/**
* Create a repeater with a handler directly.
*/
repeatAfter: (
delay: RelativeTime,
interval: RelativeTime,
handler: TimerWaker,
cancelToken?: CancelToken,
) => void;
/**
* Create and return a Notifier that will deliver updates repeatedly at times
* that are a multiple of interval following delay.
*/
makeNotifier: (
delay: RelativeTime,
interval: RelativeTime,
cancelToken?: CancelToken,
) => import('@agoric/notifier').Notifier<TimestampRecord>;
/**
* Cancel a previously-established wakeup or repeater.
*/
cancel: (cancelToken: CancelToken) => void;
/**
* Retrieve the read-only Clock facet.
*/
getClock: () => Clock;
/**
* Retrieve the Brand for this timer service.
*/
getTimerBrand: () => TimerBrand;
}
// XXX copied from Remotable helper return type
export type TimerService = TimerServiceCommon &
RemotableObject<'TimerService'> &
RemotableBrand<object, TimerServiceCommon>;
/**
* Read-only access to a TimeService's current time. This allows reading the
* current time (e.g. to see if a deadline has passed) without the ability to
* schedule events.
*/
export interface Clock {
/**
* Retrieve the latest timestamp
*/
getCurrentTimestamp: () => TimestampRecord;
/**
* Retrieve the Brand for this timer service.
*/
getTimerBrand: () => TimerBrand;
}
/**
* The interface that must be implemented by objects which are to be invoked at
* scheduled times. Used by `TimerService.repeatAfter()`,
* `TimerService.setWakeup()`, and `TimerRepeater.schedule()`.
*/
export interface TimerWaker {
/**
* The timestamp passed to `wake()` is the time that the call was scheduled
* to occur.
*/
wake: (timestamp: TimestampRecord) => void;
}
/**
* Provides the ability to schedule wake() calls repeatedly at a regular
* interval, or to disable all future use of this TimerRepeater. Created by the
* deprecated makeRepeater(), new code should use repeatAfter(), which doesn't
* have a control object and doesn't require a second schedule step
*/
export interface TimerRepeater {
/**
* Returns the time scheduled for
* the first call to `E(waker).wake()`. The waker will continue to be scheduled
* every interval until the repeater is disabled.
*/
schedule: (waker: ERef<TimerWaker>) => TimestampRecord;
/**
* Disable this repeater, so `schedule(w)` can't
* be called, and wakers already scheduled with this repeater won't be
* rescheduled again after `E(waker).wake()` is next called on them.
*/
disable: () => void;
}
/**
* TimeMath supports simple arithmetic on typed Time values, enforcing that
* values are combined in type-compatible ways. You can add 3 minutes to 3pm,
* or 5 minutes to a half hour, but it makes no sense to add 3pm and 5pm.
* Subtracting two Timestamps does produce a useful difference.
*
* The brands prevent you from accidentally combining time values from different
* TimerServices. Some chains track time in blocks, others follow wall clock
* time, some do both. Every local computer has its own unique notion of wall
* clock time. Even when these clocks are talking about the same thing (UTC),
* they can all drift in different ways. Using the correct brands lets you be
* precise about which particular source of time you mean, preventing confusion
* or attacks when the clocks diverge. Thus it is an error to e.g. use a time
* you got from chain A to schedule an event on chain B.
*
* The basic types are `RelativeTimeRecord` (durations) and `TimestampRecord`. The numeric
* values can be extracted from the typed values, but it's usually better to
* maintain values as their canonical typed form so these operations can be
* applied.
*/
export type TimeMathType = {
/**
* Validates that the operand represents a `Timestamp` and returns the bigint
* representing its absolute time value.
* During the transition explained in the`TimeMath` comment,
* `absValue` will also accept a bigint which it then just returns.
*/
absValue: (abs: Timestamp) => TimestampValue;
/**
* Validates that the operand represents a `RelativeTime` and returns the
* bigint representing its relative time value.
* During the transition explained in the`TimeMath` comment,
* `relValue` will also accept a bigint which it then just returns.
*/
relValue: (rel: RelativeTime) => RelativeTimeValue;
/**
* Coerces to a TimestampRecord if possible, else throws. If the value has a brand, ensure it matches.
* Return a Timestamp labeled with that brand.
*/
coerceTimestampRecord: (
abs: TimestampRecord | TimestampValue | number,
brand: TimerBrand,
) => TimestampRecord;
/**
* Coerces to a RelativeTime if possible. If a brand is provided, ensure it
* matches and return a RelativeTime labeled with that brand.
*/
coerceRelativeTimeRecord: (
rel: RelativeTimeRecord | RelativeTimeValue | number,
brand: TimerBrand,
) => RelativeTimeRecord;
/**
* An absolute time + a relative time gives a new absolute time.
*/
addAbsRel: <T extends Timestamp>(
abs: T,
rel: RelativeTime,
) => T extends TimestampRecord ? TimestampRecord : TimestampValue;
/**
* A relative time (i.e., a duration) + another relative time
* gives a new relative time.
*/
addRelRel: <T extends RelativeTime>(
rel1: T,
rel2: T,
) => T extends RelativeTimeRecord ? RelativeTimeRecord : RelativeTimeValue;
/**
* The difference between two absolute times is a relative time. If abs1 > abs2
* the difference would be negative, so this method throws instead.
*/
subtractAbsAbs: (abs1: Timestamp, abs2: Timestamp) => RelativeTime;
/**
* The difference between two absolute times is a relative time. If abs1 > abs2
* the difference would be negative, so this method returns a zero
* relative time instead.
*/
clampedSubtractAbsAbs: (abs1: Timestamp, abs2: Timestamp) => RelativeTime;
/**
* An absolute time - a relative time gives a new absolute time
*/
subtractAbsRel: (abs: Timestamp, rel: RelativeTime) => Timestamp;
/**
* The difference between two relative times.
*/
subtractRelRel: (rel1: RelativeTime, rel2: RelativeTime) => RelativeTime;
/**
* Does it represent a zero relative time, i.e., the difference
* of an absolute time with itself? (We choose not to define a similar
* isAbsZero, even though we could, because it is much less likely to be
* meaningful.)
*/
isRelZero: (rel: RelativeTime) => boolean;
multiplyRelNat: (rel: RelativeTime, nat: bigint) => RelativeTime;
divideRelNat: (rel: RelativeTime, nat: bigint) => RelativeTime;
divideRelRel: (rel1: RelativeTime, rel2: RelativeTime) => bigint;
/**
* An absolute time modulo a relative time is a relative time. For example,
* 20:17 on July 20, 1969 modulo 1 day is just 20:17, a relative time that
* can be added to the beginning of any day.
*/
modAbsRel: (abs: Timestamp, step: RelativeTime) => RelativeTime;
/**
* A relative time modulo a relative time is a relative time. For example,
* 3.5 hours modulo an hour is 30 minutes.
*/
modRelRel: (rel: RelativeTime, step: RelativeTime) => RelativeTime;
/**
* Compares two absolute times. This comparison function is compatible
* with JavaScript's `Array.prototype.sort` and so can be used to sort an
* array of absolute times. The result is -1, 0, or 1 indicating whether
* the first argument is less than, equal, or greater than the second.
*/
compareAbs: (abs1: Timestamp, abs2: Timestamp) => RankComparison;
/**
* Compares two relative times. This comparison function is compatible
* with JavaScript's `Array.prototype.sort` and so can be used to sort an
* array of relative times. The result is -1, 0, or 1 indicating whether
* the first argument is less than, equal, or greater than the second.
*/
compareRel: (rel1: RelativeTime, rel2: RelativeTime) => RankComparison;
};