Vows
Native promises are not compatible with Agoric's durable stores, which means that on the Agoric platform, such promises disconnect their clients when their creator vat is upgraded. Vows are objects that represent promises that can be stored durably, this package also provides a when
operator to allow clients to tolerate upgrades of vow-hosting vats, as well as a watch
operator to subscribe to a vow in a way that survives upgrades of both the creator and subscribing client vats.
Vow Consumer
If your vat is a consumer of promises that are unexpectedly fulfilling to a Vow (something like):
import { E } from '@endo/far';
const a = await w1;
const b = await E(w2).something(...args);
console.log('Here they are:', { a, b });
Produces output like:
Here they are: {
a: Object [Vow] {
payload: { vowV0: Object [Alleged: VowInternalsKit vowV0] {} }
},
b: Object [Vow] {
payload: { vowV0: Object [Alleged: VowInternalsKit vowV0] {} }
}
}
You can use heapVowE
exported from @agoric/vow
, which converts a chain of
promises and vows to a promise for its final fulfillment, by unwrapping any
intermediate vows:
import { heapVowE as E } from '@agoric/vow';
[...]
const a = await E.when(w1);
const b = await E(w2).something(...args);
Vow Producer
Use the following to create and resolve a vow:
import { heapVowE, heapVowTools } from '@agoric/vow';
const { makeVowKit } = heapVowTools;
[...]
const { resolver, vow } = makeVowKit();
E(outsideReference).performSomeMethod(vow);
resolver.resolve('now you know the answer');
Durability
By default, the @agoric/vow
module allows vows to integrate with Agoric's vat
upgrade mechanism. To create vow tools that deal with durable objects:
import { E } from '@endo/far';
import { prepareVowTools } from '@agoric/vow';
import { makeDurableZone } from '@agoric/zone';
const zone = makeDurableZone(baggage);
const vowZone = zone.subZone('VowTools');
const { watch, makeVowKit } = prepareVowTools(vowZone);
VowTools
VowTools are a set of utility functions for working with Vows in Agoric smart contracts and vats. These tools help manage asynchronous operations in a way that's resilient to vat upgrades, ensuring your smart contract can handle long-running processes reliably.
Usage
VowTools are typically prepared in the start function of a smart contract or vat and passed in as a power to exos.
import { prepareVowTools } from '@agoric/vow/vat.js';
import { makeDurableZone } from '@agoric/zone/durable.js';
export const start = async (zcf, privateArgs, baggage) => {
const zone = makeDurableZone(baggage);
const vowTools = prepareVowTools(zone.subZone('vows'));
}
Available Tools
when(vowOrPromise)
Returns a Promise for the fulfillment of the very end of the vowOrPromise
chain. It can retry disconnections due to upgrades of other vats, but cannot survive the upgrade of the calling vat.
watch(promiseOrVow, [watcher], [context])
Watch a Vow and optionally provide a watcher
with onFulfilled
/onRejected
handlers and a context
value for the handlers. When handlers are not provided the fulfillment or rejection will simply pass through.
It also registers pending Promises, so if the current vat is upgraded, the watcher is rejected because the Promise was lost when the heap was reset.
all(arrayOfPassables, [watcher], [context])
Vow-tolerant implementation of Promise.all that takes an iterable of vows and other Passables and returns a single Vow. It resolves with an array of values when all of the input's promises or vows are fulfilled and rejects with the first rejection reason when any of the input's promises or vows are rejected.
allSettled(arrayOfPassables, [watcher], [context])
Vow-tolerant implementation of Promise.allSettled that takes an iterable of vows and other Passables and returns a single Vow. It resolves when all of the input's promises or vows are settled with an array of settled outcome objects.
asVow(fn)
Takes a function that might return synchronously, throw an Error, or return a Promise or Vow and returns a Vow.
asPromise(vow)
Converts a Vow back into a Promise.
Example
const { when, watch, all, allSettled } = vowTools;
const myVow = watch(someAsyncOperation());
const result = await when(myVow);
const results = await when(all([vow, vowForVow, promise]));
const outcomes = await when(allSettled([vow, vowForVow, promise]));
Internals
The current "version 0" vow internals expose a shorten()
method, returning a
promise for the next resolution. watch
and when
use shorten()
to advance
through the vow chain step-by-step, tolerating disconnects by retrying a failed
step, rather than just making one giant leap to the end of a promise chain with
.then(...)
.
Here is an (oversimplified) algorithm that watch
and when
use to obtain a
final result:
let result = await specimenP;
let vowInternals = getVowInternals(result);
let disconnectionState = undefined;
while (vowInternals) {
try {
const shortened = await E(vowInternals.vowV0).shorten();
const nextInternals = getVowInternals(shortened);
result = shortened;
vowInternals = nextInternals;
} catch (e) {
const nextDisconnectionState = isDisconnectionReason(e, disconnectionState);
if (!nextDisconnectionState) {
throw e;
}
disconnectionState = nextDisconnectionState;
}
}
return result;