Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add and fully adopt "timer brand": identify a source of time without granting wakeup/repeater authority #5798

Open
warner opened this issue Jul 20, 2022 · 15 comments
Assignees
Labels
enhancement New feature or request resource-exhaustion Threats to availability from resource exhaustion attacks security SwingSet package: SwingSet vaults_triage DO NOT USE

Comments

@warner
Copy link
Member

warner commented Jul 20, 2022

What is the Problem Being Solved?

Today we discussed an attack in which off-chain clients use the published "terms" of a contract to access the TimerService it is using, and then instruct that service to make a bunch of wakeup calls, or a high-frequency repeater, as a DoS vector.

"Time" in distributed software is a fuzzy thing at best, and we don't want to enshrine one particular notion of time as the "one true time". Also, since clocks (at least high-resolution ones) enable code to read from a timing-based covert channel, we certainly don't want confined code to get ambient access to a clock. I like our current approach in which time sources are represented as ocaps, with an object that grants "what time is it now?" queries as well as "wake me up in 10 minutes" commands. This also matches well with having multiple sources of time: you just get multiple timer service objects.

Contracts publish which notion of time they're using so that clients can correctly compare things like option expiration dates. If the contract says the option expires at time 1234 according to clock A, and I want to compose that with some other contract which makes a derivative right that expires at time 1230, I want to make sure the second contract is also using clock A, otherwise bad things will ensue. So we need clocks to be comparable, independent of the authority we're sharing.

So we need contracts to publish a description of the kind of TimerService they're using, but we don't want this to enable an outsider to actually use that service.

After reading #3612 (comment) and #3612 (comment) , I'm thinking that this is similar to the way a Brand object describes a token type, without (directly?) granting the authority to e.g. make new Purses of that type. (I'm not sure whether Brands are the right notion to use, or Issuers, but the vernacular usage of "Brand" feels appropriate).

If each TimerService had a related (powerless) TimerBrand or TimerIdentity object, then the contract terms could safely cite the brand. Or, better yet, whenever they needed to reference a time value, they could instead emit a { time, timerID } pair, in much the same way our Amount pattern combines a number (for "Nat" -type Amounts) and a Brand. Two Amounts with the same Brand are comparable (X > Y, X >= Y, X === Y), while Amounts with different Brands are not comparable. A client who asks contract A when the option expires, and wants to ensure that contract B's option lasts at least as long, will want to do expiryA <= expiryB, and that fits very nicely into the AmountMath utilities (which would throw an error if the compared values have different Brands).

Description of the Design

So I think the swingset-side task is:

  • add timerService.getIdentity() -> TimerIdentifier
    • the identity is a powerless object: the only method I can imagine putting on it is a string-returning getDescription, but I'm not sure what value I'd use on the chain. agoric-chain-timer ??

Elsewhere within our chain (I'll make a separate ticket for this), we need to make sure the full-powered TimerService is unavailable outside the contracts that need it. That means:

  • don't publish it to the Board
    • instead, publish the TimerIdentity, which is powerless and safe to widely distribute
  • remove the TimerService from the contract's publically-visible terms
    • instead, include the TimerIdentity
  • remove the chain TimerService from the batch of chainServices supplied to newly-provisioned ag-solo clients, replacing it with the identity
  • when complete, only the core-bootstrap code should hang on to the TimerService in a way that could be provided to new code (via the Big JS Hammer eval() operation)
    • our initial contracts are given access to the TimerService when they are started
    • but no other contracts can get it unless instantiated via the Big JS Hammer

A deeper change, which I think is interesting (but I don't know how much work would be involved) would be to define an Amount-like { time, timerID } structure, maybe using AmountMath.Nat to manipulate it, and change all timer-sensitive contracts (at least the Vault, which needs to compute interest on a particular schedule) to use them. That would remove the TimerID from the contract's terms, and instead include it in each time value that crosses its API.

Security Considerations

Preventing access to the TimerService is important to prevent DoS attacks that leverage the timer.

Test Plan

Simple unit test which fetches a TimerID twice and asserts they are identical, plus one that demonstrates the lack of setWakeup or other API methods on the ID object.

@warner
Copy link
Member Author

warner commented Jul 22, 2022

cc @erights @Chris-Hibbert , especially about whether "Brand" is an accurate word for this authority-less unforgeable identifier

@erights
Copy link
Member

erights commented Jul 22, 2022

Just FYI, there's a related previous attempt at https://github.com/Agoric/agoric-sdk/pull/3778/files?diff=split&w=1 . I wouldn't spend too much time on it though. I doesn't try to restrict the authority provided by the timer, but it does try to use the timer + time together to represent a labeled time object in much the same way that our amounts use an issuer brand + a value to represent a labeled value. Idea originally from @katelynsills .

@erights
Copy link
Member

erights commented Jul 22, 2022

A deeper change, which I think is interesting (but I don't know how much work would be involved) would be to define an Amount-like { time, timerID } structure, maybe using AmountMath.Nat to manipulate it, and change all timer-sensitive contracts (at least the Vault, which needs to compute interest on a particular schedule) to use them. That would remove the TimerID from the contract's terms, and instead include it in each time value that crosses its API.

Exactly. The Timer/TimerBrand pair is closely analogous to the Issuer/Brand pair. One can ask a Timer for its TimerBrand. One cannot ask the TimerBrand for its Timer. However, one must be able to ask the TimerBrand "is this your Timer"? That way, a client like Zoe that has both a Timer and TimerBrand that allegedly agree can ensure that both the Timer and TimerBrand agree on the correspondence at least once. If they were both well behaved, the correspondence will be stable. If only one is well behaved, by definition it won't agree on the other. Only if they are jointly ill behaved, they can still damage only themselves and those who relied on them being well behaved.

Without this, a malicious Timer can claim that another Timer's TimerBrand is its own. Zoe does check the mutual agreement of Issuer and Brand before registering them. Zoe would need to do the same thing here.

The similarity of Time/TimerBrand/LabeledTime to Issuer/Brand/Amount finally proves the time-money equivalence conjecture.

@erights
Copy link
Member

erights commented Jul 22, 2022

but I don't know how much work would be involved

That old PR has an interesting hack called sharedTimeAuthority it its timeMath.js file to ease a transition from the old bare bigint representations of time to the new labeled time. The TimeMath also shows that as long as we're transitioning to a labeled representation, we should take that opportunity to also distinguish absolute time from relative duration.

There seems to be enough good here to build on that starting from here might take less time than starting from scratch. OTOH, I would be scared of trying to squeeze such an effort into MN1. Does the motivating problem need to be solved for MN1?

@warner
Copy link
Member Author

warner commented Jul 22, 2022

Ok, I'll build:

const { service, brand } = makeDurableTimerService(..); // two facets in same cohort
assert.equal(await E(service).getTimerBrand(), brand);
assert(await E(brand).isMyTimerService(service));

The existing setup pattern, timerService = await E(timerVat).createTimerService(timerDevice), will only return the service.. the caller will need to use getTimerBrand() to retrieve the brand and then publish it to the Board or whatever.

@erights any thoughts on the spelling of isMyTimerService()? I think that matches the ERTP brand.isMyIssuer() pattern.

@erights
Copy link
Member

erights commented Jul 22, 2022

makeDurableTimerServiceKit returns the pair.

makeDurableTimerService is perhaps deprecated, but as you say, you can still get the brand from the timer.

Given that the noun (phrase) in question is "timer service", then yes, isMyTimerService is the correct name. If we can instead just call it "timer" then isMyTimer. Can we call it "timer" or did we already use up that name for something related?

@erights
Copy link
Member

erights commented Jul 22, 2022

"timer service" is fine. With this change, we'll refer to this much less than "timer brand" anyway.

@erights
Copy link
Member

erights commented Jul 23, 2022

@warner please let me know if you'd find #5809 helpful. Thanks.

@warner
Copy link
Member Author

warner commented Jul 25, 2022

@Chris-Hibbert
Copy link
Contributor

we should take that opportunity to also distinguish absolute time from relative duration.

The typescript already distinguishes duration (RelativeTime) from absolute (Timestamp). I don't know that those are the right names, but the distinction should be perpetuated.

If we can instead just call it "timer" then isMyTimer. Can we call it "timer" or did we already use up that name for something related?

I don't think there's a distinct thing that's a Timer. Zoe uses timer: as a label in exit rules, but it's a label for the thing we're discussing. I also see timeAuhority, but it's used interchangeably with timer and timerService. TimerWaker is used to refer to an object with a wake method.

@warner
Copy link
Member Author

warner commented Jul 27, 2022

Yeah Time and TimeDelta is what my branch is using right now, both are BigInts but are logically distinct. Those are the names used by the python standard library (my usual source of wisdom).

I think TimerService is a useful concept for the largest collection of features, and is the current name. I've introduced clock as the attenuated facet that only offers get-current-time.

@Chris-Hibbert
Copy link
Contributor

I like clock as the name of a read-only facet.

In my house, the calendar hanging near the clock has additional powers for scheduling, and is read-write.

@zarutian
Copy link
Contributor

zarutian commented Aug 3, 2022

If #3657 ever gets implemented then analogous "condition checker brand" must be implemented for the same reasons stated in the opening comment of this issue.

warner added a commit that referenced this issue Aug 11, 2022
vat-timer is now fully virtualized, durablized, and upgradeable. RAM
usage should be O(N) in the number of:

* pending Promise wakeups (`wakeAt`, `delay`)
* active Notifier promises (`makeNotifier`)
* active Iterator promises (`makeNotifier()[Symbol.asyncIterator]`)

Pending promises will be disconnected (rejected) during upgrade, as
usual.

All handlers and Promises will fire with the most recent timestamp
available, which (under load) may be somewhat later than the scheduled
wakeup time. However Notifiers will always report a scheduled
time (some multiple of the interval). The opaque `updateCount` used in
Notifier updates is now a time value, not a counter, so user tests
should refrain from asserting sequentiality.

Asking for a wakeup in the past or present will fire immediately.

Most API calls will accept an arbitrary Far object as a CancelToken,
which can be used to cancel the wakeup/repeater. `makeRepeater` is the
exception.

This does not change the device-timer API or implementation, however
vat-timer now only uses a single device-side wakeup, and only exposes
a single handler object, to minimize the memory usage and object
retention by the device (since devices do not participate in GC).

This introduces a `Clock` which can return time values without also
providing scheduling authority, and a `TimerBrand` which can validate
time values without providing clock or scheduling authority.

`packages/SwingSet/tools/manual-timer.js` offers a manually-driven
timer service, which can help with unit tests.

closes #5668
closes #5709
closes #4282
refs #4286
closes #4296
closes #5616
closes #5709
refs #5798
warner added a commit that referenced this issue Aug 16, 2022
vat-timer is now fully virtualized, durablized, and upgradeable. RAM
usage should be O(N) in the number of:

* pending Promise wakeups (`wakeAt`, `delay`)
* active Notifier promises (`makeNotifier`)
* active Iterator promises (`makeNotifier()[Symbol.asyncIterator]`)

Pending promises will be disconnected (rejected) during upgrade, as
usual.

All handlers and Promises will fire with the most recent timestamp
available, which (under load) may be somewhat later than the scheduled
wakeup time.

Until cancellation, Notifiers will always report a scheduled time
(i.e. `start` plus some multiple of the interval). The opaque
`updateCount` used in Notifier updates is a counter starting from 1n.
When a Notifier is cancelled, the final/"finish" value is the
timestamp of cancellation, which may or may not be a multiple of the
interval (and might be a duplicate of the last non-final value). Once
in the cancelled state, `getUpdateSince(anything)` yields `{ value:
cancellationTimestamp, updateCount: undefined }`, and the
corresponding `iterator.next()` resolves to `{ value:
cancellationTimestamp, done: true }`. Neither will ever reject their
Promises (except due to upgrade).

Asking for a wakeup in the past or present will fire immediately.

Most API calls will accept an arbitrary Far object as a CancelToken,
which can be used to cancel the wakeup/repeater. `makeRepeater` is the
exception.

This does not change the device-timer API or implementation, however
vat-timer now only uses a single device-side wakeup, and only exposes
a single handler object, to minimize the memory usage and object
retention by the device (since devices do not participate in GC).

This introduces a `Clock` which can return time values without also
providing scheduling authority, and a `TimerBrand` which can validate
time values without providing clock or scheduling
authority. Timestamps are not yet Branded, but the scaffolding is in
place.

`packages/SwingSet/tools/manual-timer.js` offers a manually-driven
timer service, which can help with unit tests.

closes #5668
closes #5709
closes #4282
refs #4286
closes #4296
closes #5616
closes #5709
refs #5798
warner added a commit that referenced this issue Aug 16, 2022
vat-timer is now fully virtualized, durablized, and upgradeable. RAM
usage should be O(N) in the number of:

* pending Promise wakeups (`wakeAt`, `delay`)
* active Notifier promises (`makeNotifier`)
* active Iterator promises (`makeNotifier()[Symbol.asyncIterator]`)

Pending promises will be disconnected (rejected) during upgrade, as
usual.

All handlers and Promises will fire with the most recent timestamp
available, which (under load) may be somewhat later than the scheduled
wakeup time.

Until cancellation, Notifiers will always report a scheduled time
(i.e. `start` plus some multiple of the interval). The opaque
`updateCount` used in Notifier updates is a counter starting from 1n.
When a Notifier is cancelled, the final/"finish" value is the
timestamp of cancellation, which may or may not be a multiple of the
interval (and might be a duplicate of the last non-final value). Once
in the cancelled state, `getUpdateSince(anything)` yields `{ value:
cancellationTimestamp, updateCount: undefined }`, and the
corresponding `iterator.next()` resolves to `{ value:
cancellationTimestamp, done: true }`. Neither will ever reject their
Promises (except due to upgrade).

Asking for a wakeup in the past or present will fire immediately.

Most API calls will accept an arbitrary Far object as a CancelToken,
which can be used to cancel the wakeup/repeater. `makeRepeater` is the
exception.

This does not change the device-timer API or implementation, however
vat-timer now only uses a single device-side wakeup, and only exposes
a single handler object, to minimize the memory usage and object
retention by the device (since devices do not participate in GC).

This introduces a `Clock` which can return time values without also
providing scheduling authority, and a `TimerBrand` which can validate
time values without providing clock or scheduling
authority. Timestamps are not yet Branded, but the scaffolding is in
place.

`packages/SwingSet/tools/manual-timer.js` offers a manually-driven
timer service, which can help with unit tests.

closes #5668
closes #5709
closes #4282
refs #4286
closes #4296
closes #5616
closes #5709
refs #5798
warner added a commit that referenced this issue Aug 16, 2022
vat-timer is now fully virtualized, durablized, and upgradeable. RAM
usage should be O(N) in the number of:

* pending Promise wakeups (`wakeAt`, `delay`)
* active Notifier promises (`makeNotifier`)
* active Iterator promises (`makeNotifier()[Symbol.asyncIterator]`)

Pending promises will be disconnected (rejected) during upgrade, as
usual.

All handlers and Promises will fire with the most recent timestamp
available, which (under load) may be somewhat later than the scheduled
wakeup time.

Until cancellation, Notifiers will always report a scheduled time
(i.e. `start` plus some multiple of the interval). The opaque
`updateCount` used in Notifier updates is a counter starting from 1n.
When a Notifier is cancelled, the final/"finish" value is the
timestamp of cancellation, which may or may not be a multiple of the
interval (and might be a duplicate of the last non-final value). Once
in the cancelled state, `getUpdateSince(anything)` yields `{ value:
cancellationTimestamp, updateCount: undefined }`, and the
corresponding `iterator.next()` resolves to `{ value:
cancellationTimestamp, done: true }`. Neither will ever reject their
Promises (except due to upgrade).

Asking for a wakeup in the past or present will fire immediately.

Most API calls will accept an arbitrary Far object as a CancelToken,
which can be used to cancel the wakeup/repeater. `makeRepeater` is the
exception.

This does not change the device-timer API or implementation, however
vat-timer now only uses a single device-side wakeup, and only exposes
a single handler object, to minimize the memory usage and object
retention by the device (since devices do not participate in GC).

This introduces a `Clock` which can return time values without also
providing scheduling authority, and a `TimerBrand` which can validate
time values without providing clock or scheduling
authority. Timestamps are not yet Branded, but the scaffolding is in
place.

`packages/SwingSet/tools/manual-timer.js` offers a manually-driven
timer service, which can help with unit tests.

closes #5668
closes #5709
closes #4282
refs #4286
closes #4296
closes #5616
closes #5709
refs #5798
warner added a commit that referenced this issue Aug 16, 2022
vat-timer is now fully virtualized, durablized, and upgradeable. RAM
usage should be O(N) in the number of:

* pending Promise wakeups (`wakeAt`, `delay`)
* active Notifier promises (`makeNotifier`)
* active Iterator promises (`makeNotifier()[Symbol.asyncIterator]`)

Pending promises will be disconnected (rejected) during upgrade, as
usual.

All handlers and Promises will fire with the most recent timestamp
available, which (under load) may be somewhat later than the scheduled
wakeup time.

Until cancellation, Notifiers will always report a scheduled time
(i.e. `start` plus some multiple of the interval). The opaque
`updateCount` used in Notifier updates is a counter starting from 1n.
When a Notifier is cancelled, the final/"finish" value is the
timestamp of cancellation, which may or may not be a multiple of the
interval (and might be a duplicate of the last non-final value). Once
in the cancelled state, `getUpdateSince(anything)` yields `{ value:
cancellationTimestamp, updateCount: undefined }`, and the
corresponding `iterator.next()` resolves to `{ value:
cancellationTimestamp, done: true }`. Neither will ever reject their
Promises (except due to upgrade).

Asking for a wakeup in the past or present will fire immediately.

Most API calls will accept an arbitrary Far object as a CancelToken,
which can be used to cancel the wakeup/repeater. `makeRepeater` is the
exception.

This does not change the device-timer API or implementation, however
vat-timer now only uses a single device-side wakeup, and only exposes
a single handler object, to minimize the memory usage and object
retention by the device (since devices do not participate in GC).

This introduces a `Clock` which can return time values without also
providing scheduling authority, and a `TimerBrand` which can validate
time values without providing clock or scheduling
authority. Timestamps are not yet Branded, but the scaffolding is in
place.

`packages/SwingSet/tools/manual-timer.js` offers a manually-driven
timer service, which can help with unit tests.

closes #4282
refs #4286
closes #4296
closes #5616
closes #5668
closes #5709
refs #5798
warner added a commit that referenced this issue Aug 16, 2022
vat-timer is now fully virtualized, durablized, and upgradeable. RAM
usage should be O(N) in the number of:

* pending Promise wakeups (`wakeAt`, `delay`)
* active Notifier promises (`makeNotifier`)
* active Iterator promises (`makeNotifier()[Symbol.asyncIterator]`)

Pending promises will be disconnected (rejected) during upgrade, as
usual.

All handlers and Promises will fire with the most recent timestamp
available, which (under load) may be somewhat later than the scheduled
wakeup time.

Until cancellation, Notifiers will always report a scheduled time
(i.e. `start` plus some multiple of the interval). The opaque
`updateCount` used in Notifier updates is a counter starting from 1n.
When a Notifier is cancelled, the final/"finish" value is the
timestamp of cancellation, which may or may not be a multiple of the
interval (and might be a duplicate of the last non-final value). Once
in the cancelled state, `getUpdateSince(anything)` yields `{ value:
cancellationTimestamp, updateCount: undefined }`, and the
corresponding `iterator.next()` resolves to `{ value:
cancellationTimestamp, done: true }`. Neither will ever reject their
Promises (except due to upgrade).

Asking for a wakeup in the past or present will fire immediately.

Most API calls will accept an arbitrary Far object as a CancelToken,
which can be used to cancel the wakeup/repeater. `makeRepeater` is the
exception.

This does not change the device-timer API or implementation, however
vat-timer now only uses a single device-side wakeup, and only exposes
a single handler object, to minimize the memory usage and object
retention by the device (since devices do not participate in GC).

This introduces a `Clock` which can return time values without also
providing scheduling authority, and a `TimerBrand` which can validate
time values without providing clock or scheduling
authority. Timestamps are not yet Branded, but the scaffolding is in
place.

`packages/SwingSet/tools/manual-timer.js` offers a manually-driven
timer service, which can help with unit tests.

closes #4282
refs #4286
closes #4296
closes #5616
closes #5668
closes #5709
refs #5798
warner added a commit that referenced this issue Aug 16, 2022
vat-timer is now fully virtualized, durablized, and upgradeable. RAM
usage should be O(N) in the number of:

* pending Promise wakeups (`wakeAt`, `delay`)
* active Notifier promises (`makeNotifier`)
* active Iterator promises (`makeNotifier()[Symbol.asyncIterator]`)

Pending promises will be disconnected (rejected) during upgrade, as
usual.

All handlers and Promises will fire with the most recent timestamp
available, which (under load) may be somewhat later than the scheduled
wakeup time.

Until cancellation, Notifiers will always report a scheduled time
(i.e. `start` plus some multiple of the interval). The opaque
`updateCount` used in Notifier updates is a counter starting from 1n.
When a Notifier is cancelled, the final/"finish" value is the
timestamp of cancellation, which may or may not be a multiple of the
interval (and might be a duplicate of the last non-final value). Once
in the cancelled state, `getUpdateSince(anything)` yields `{ value:
cancellationTimestamp, updateCount: undefined }`, and the
corresponding `iterator.next()` resolves to `{ value:
cancellationTimestamp, done: true }`. Neither will ever reject their
Promises (except due to upgrade).

Asking for a wakeup in the past or present will fire immediately.

Most API calls will accept an arbitrary Far object as a CancelToken,
which can be used to cancel the wakeup/repeater. `makeRepeater` is the
exception.

This does not change the device-timer API or implementation, however
vat-timer now only uses a single device-side wakeup, and only exposes
a single handler object, to minimize the memory usage and object
retention by the device (since devices do not participate in GC).

This introduces a `Clock` which can return time values without also
providing scheduling authority, and a `TimerBrand` which can validate
time values without providing clock or scheduling
authority. Timestamps are not yet Branded, but the scaffolding is in
place.

`packages/SwingSet/tools/manual-timer.js` offers a manually-driven
timer service, which can help with unit tests.

closes #4282
refs #4286
closes #4296
closes #5616
closes #5668
closes #5709
refs #5798
warner added a commit that referenced this issue Aug 19, 2022
vat-timer is now fully virtualized, durablized, and upgradeable. RAM
usage should be O(N) in the number of:

* pending Promise wakeups (`wakeAt`, `delay`)
* active Notifier promises (`makeNotifier`)
* active Iterator promises (`makeNotifier()[Symbol.asyncIterator]`)

Pending promises will be disconnected (rejected) during upgrade, as
usual.

All handlers and Promises will fire with the most recent timestamp
available, which (under load) may be somewhat later than the scheduled
wakeup time.

Until cancellation, Notifiers will always report a scheduled time
(i.e. `start` plus some multiple of the interval). The opaque
`updateCount` used in Notifier updates is a counter starting from 1n.
When a Notifier is cancelled, the final/"finish" value is the
timestamp of cancellation, which may or may not be a multiple of the
interval (and might be a duplicate of the last non-final value). Once
in the cancelled state, `getUpdateSince(anything)` yields `{ value:
cancellationTimestamp, updateCount: undefined }`, and the
corresponding `iterator.next()` resolves to `{ value:
cancellationTimestamp, done: true }`. Neither will ever reject their
Promises (except due to upgrade).

Asking for a wakeup in the past or present will fire immediately.

Most API calls will accept an arbitrary Far object as a CancelToken,
which can be used to cancel the wakeup/repeater. `makeRepeater` is the
exception.

This does not change the device-timer API or implementation, however
vat-timer now only uses a single device-side wakeup, and only exposes
a single handler object, to minimize the memory usage and object
retention by the device (since devices do not participate in GC).

This introduces a `Clock` which can return time values without also
providing scheduling authority, and a `TimerBrand` which can validate
time values without providing clock or scheduling
authority. Timestamps are not yet Branded, but the scaffolding is in
place.

`packages/SwingSet/tools/manual-timer.js` offers a manually-driven
timer service, which can help with unit tests.

closes #4282
refs #4286
closes #4296
closes #5616
closes #5668
closes #5709
refs #5798
warner added a commit that referenced this issue Aug 19, 2022
vat-timer is now fully virtualized, durablized, and upgradeable. RAM
usage should be O(N) in the number of:

* pending Promise wakeups (`wakeAt`, `delay`)
* active Notifier promises (`makeNotifier`)
* active Iterator promises (`makeNotifier()[Symbol.asyncIterator]`)

Pending promises will be disconnected (rejected) during upgrade, as
usual.

All handlers and Promises will fire with the most recent timestamp
available, which (under load) may be somewhat later than the scheduled
wakeup time.

Until cancellation, Notifiers will always report a scheduled time
(i.e. `start` plus some multiple of the interval). The opaque
`updateCount` used in Notifier updates is a counter starting from 1n.
When a Notifier is cancelled, the final/"finish" value is the
timestamp of cancellation, which may or may not be a multiple of the
interval (and might be a duplicate of the last non-final value). Once
in the cancelled state, `getUpdateSince(anything)` yields `{ value:
cancellationTimestamp, updateCount: undefined }`, and the
corresponding `iterator.next()` resolves to `{ value:
cancellationTimestamp, done: true }`. Neither will ever reject their
Promises (except due to upgrade).

Asking for a wakeup in the past or present will fire immediately.

Most API calls will accept an arbitrary Far object as a CancelToken,
which can be used to cancel the wakeup/repeater. `makeRepeater` is the
exception.

This does not change the device-timer API or implementation, however
vat-timer now only uses a single device-side wakeup, and only exposes
a single handler object, to minimize the memory usage and object
retention by the device (since devices do not participate in GC).

This introduces a `Clock` which can return time values without also
providing scheduling authority, and a `TimerBrand` which can validate
time values without providing clock or scheduling
authority. Timestamps are not yet Branded, but the scaffolding is in
place.

`packages/SwingSet/tools/manual-timer.js` offers a manually-driven
timer service, which can help with unit tests.

closes #4282
refs #4286
closes #4296
closes #5616
closes #5668
closes #5709
refs #5798
warner added a commit that referenced this issue Aug 19, 2022
vat-timer is now fully virtualized, durablized, and upgradeable. RAM
usage should be O(N) in the number of:

* pending Promise wakeups (`wakeAt`, `delay`)
* active Notifier promises (`makeNotifier`)
* active Iterator promises (`makeNotifier()[Symbol.asyncIterator]`)

Pending promises will be disconnected (rejected) during upgrade, as
usual.

All handlers and Promises will fire with the most recent timestamp
available, which (under load) may be somewhat later than the scheduled
wakeup time.

Until cancellation, Notifiers will always report a scheduled time
(i.e. `start` plus some multiple of the interval). The opaque
`updateCount` used in Notifier updates is a counter starting from 1n.
When a Notifier is cancelled, the final/"finish" value is the
timestamp of cancellation, which may or may not be a multiple of the
interval (and might be a duplicate of the last non-final value). Once
in the cancelled state, `getUpdateSince(anything)` yields `{ value:
cancellationTimestamp, updateCount: undefined }`, and the
corresponding `iterator.next()` resolves to `{ value:
cancellationTimestamp, done: true }`. Neither will ever reject their
Promises (except due to upgrade).

Asking for a wakeup in the past or present will fire immediately.

Most API calls will accept an arbitrary Far object as a CancelToken,
which can be used to cancel the wakeup/repeater. `makeRepeater` is the
exception.

This does not change the device-timer API or implementation, however
vat-timer now only uses a single device-side wakeup, and only exposes
a single handler object, to minimize the memory usage and object
retention by the device (since devices do not participate in GC).

This introduces a `Clock` which can return time values without also
providing scheduling authority, and a `TimerBrand` which can validate
time values without providing clock or scheduling
authority. Timestamps are not yet Branded, but the scaffolding is in
place.

`packages/SwingSet/tools/manual-timer.js` offers a manually-driven
timer service, which can help with unit tests.

closes #4282
refs #4286
closes #4296
closes #5616
closes #5668
closes #5709
refs #5798
warner added a commit that referenced this issue Aug 19, 2022
vat-timer is now fully virtualized, durablized, and upgradeable. RAM
usage should be O(N) in the number of:

* pending Promise wakeups (`wakeAt`, `delay`)
* active Notifier promises (`makeNotifier`)
* active Iterator promises (`makeNotifier()[Symbol.asyncIterator]`)

Pending promises will be disconnected (rejected) during upgrade, as
usual.

All handlers and Promises will fire with the most recent timestamp
available, which (under load) may be somewhat later than the scheduled
wakeup time.

Until cancellation, Notifiers will always report a scheduled time
(i.e. `start` plus some multiple of the interval). The opaque
`updateCount` used in Notifier updates is a counter starting from 1n.
When a Notifier is cancelled, the final/"finish" value is the
timestamp of cancellation, which may or may not be a multiple of the
interval (and might be a duplicate of the last non-final value). Once
in the cancelled state, `getUpdateSince(anything)` yields `{ value:
cancellationTimestamp, updateCount: undefined }`, and the
corresponding `iterator.next()` resolves to `{ value:
cancellationTimestamp, done: true }`. Neither will ever reject their
Promises (except due to upgrade).

Asking for a wakeup in the past or present will fire immediately.

Most API calls will accept an arbitrary Far object as a CancelToken,
which can be used to cancel the wakeup/repeater. `makeRepeater` is the
exception.

This does not change the device-timer API or implementation, however
vat-timer now only uses a single device-side wakeup, and only exposes
a single handler object, to minimize the memory usage and object
retention by the device (since devices do not participate in GC).

This introduces a `Clock` which can return time values without also
providing scheduling authority, and a `TimerBrand` which can validate
time values without providing clock or scheduling
authority. Timestamps are not yet Branded, but the scaffolding is in
place.

`packages/SwingSet/tools/manual-timer.js` offers a manually-driven
timer service, which can help with unit tests.

closes #4282
refs #4286
closes #4296
closes #5616
closes #5668
closes #5709
refs #5798
@warner
Copy link
Member Author

warner commented Aug 19, 2022

The new Timer Service now offers a getTimerBrand(), plus methods to verify the match between TimerService, Clock, and TimerBrand.

It does not yet accept or return branded Timestamps.. that's the next step.

Other next steps are to change Zoe and the Board and Terms and such, to carry around brands instead of more powerful objects. I think our ultimate plan was to have bootstrap register the chain-time TimerService with zoe. Then later, when a client submits an exit criteria with a branded timestamp, zoe can look up the matching TimerService and use it to schedule a wakeup to trigger the exit (or query the right Clock when something requests an exit and zoe needs to confirm that it's not too late). When time appears in other places (Terms?), clients can use the TimerBrand. In this scheme, timeouts or time-based exit critera don't need to include TimerBrand as a separate argument because it's already baked into the branded Timestamp. cc @erights @Chris-Hibbert

@Chris-Hibbert
Copy link
Contributor

See #5799 for some of the follow-up.

@ivanlei ivanlei added the vaults_triage DO NOT USE label Jan 3, 2023
@dckc dckc added resource-exhaustion Threats to availability from resource exhaustion attacks security labels Jan 3, 2023
@warner warner assigned gibson042 and unassigned warner Mar 14, 2023
@gibson042 gibson042 changed the title add "timer brand": identify a source of time without granting wakeup/repeater authority add and fully adopt "timer brand": identify a source of time without granting wakeup/repeater authority Mar 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request resource-exhaustion Threats to availability from resource exhaustion attacks security SwingSet package: SwingSet vaults_triage DO NOT USE
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants