Skip to content

Commit

Permalink
fixup! refactor(time-helper): optionally durable
Browse files Browse the repository at this point in the history
  • Loading branch information
0xpatrickdev committed Jun 25, 2024
1 parent 1a9fdb6 commit 0a216a7
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 75 deletions.
4 changes: 2 additions & 2 deletions packages/orchestration/src/examples/stakeBld.contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { deeplyFulfilled } from '@endo/marshal';
import { M } from '@endo/patterns';
import { prepareLocalOrchestrationAccountKit } from '../exos/local-orchestration-account.js';
import { makeChainHub } from '../exos/chain-hub.js';
import { makeTimeHelper } from '../exos/time-helper.js';
import { prepareTimeHelper } from '../exos/time-helper.js';

/**
* @import {NameHub} from '@agoric/vats';
Expand Down Expand Up @@ -40,7 +40,7 @@ export const start = async (zcf, privateArgs, baggage) => {

const { makeRecorderKit } = prepareRecorderKitMakers(baggage, marshaller);
const vowTools = prepareVowTools(zone.subZone('vows'));
const timeHelper = makeTimeHelper(makeHeapZone(), {
const timeHelper = prepareTimeHelper(makeHeapZone(), {
timerService,
vowTools,
});
Expand Down
160 changes: 100 additions & 60 deletions packages/orchestration/src/exos/time-helper.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { RelativeTimeRecordShape, TimeMath } from '@agoric/time';
import { BrandShape } from '@agoric/ertp';
import {
RelativeTimeRecordShape,
TimeMath,
TimestampRecordShape,
} from '@agoric/time';
import { pickFacet } from '@agoric/vat-data';
import { VowShape } from '@agoric/vow';
import { E, Far } from '@endo/far';
import { E } from '@endo/far';
import { M } from '@endo/patterns';

/**
Expand All @@ -13,78 +19,112 @@ import { M } from '@endo/patterns';
export const SECONDS_PER_MINUTE = 60n;
export const NANOSECONDS_PER_SECOND = 1_000_000_000n;

/** @typedef {{ timerService: Remote<TimerService>; vowTools: VowTools }} TimeHelperPowers */

/**
* @param {Zone} zone
* @param {{ timerService: Remote<TimerService>; vowTools: VowTools }} powers
* @param {TimeHelperPowers} powers
*/
export const makeTimeHelper = (
const prepareTimeHelperKit = (
zone,
{ timerService, vowTools: { watch, allVows } },
) => {
const timeHelper = zone.exoClass(
) =>
zone.exoClassKit(
'Time Helper',
M.interface('TimeHelperI', {
getTimeoutTimestampNS: M.call()
.optional(RelativeTimeRecordShape)
.returns(VowShape),
getBrand: M.call().returns(VowShape),
}),
{
getBrandWatcher: M.interface('GetBrandWatcherI', {
onFulfilled: M.call(BrandShape)
.optional(M.arrayOf(M.undefined())) // does not need watcherContext
.returns(BrandShape),
}),
getTimestampWatcher: M.interface('GetBrandWatcherI', {
onFulfilled: M.call([TimestampRecordShape, BrandShape])
.optional({
relativeTime: M.or(RelativeTimeRecordShape, M.undefined()),
})
.returns(M.bigint()),
}),
public: M.interface('TimeHelperI', {
getTimeoutTimestampNS: M.call()
.optional(RelativeTimeRecordShape)
.returns(VowShape),
getBrand: M.call().returns(VowShape),
}),
},
() =>
/** @type {{ brandCache: TimerBrand | undefined }} */ ({
brandCache: undefined,
}),
{
/** @returns {Vow<TimerBrand>} */
getBrand() {
// XXX this is a common use case that should have a helper like `provideSingleton`
if (this.state.brandCache) return watch(this.state.brandCache);
return watch(
E(timerService).getTimerBrand(),
Far('BrandWatcher', {
/** @param {TimerBrand} timerBrand */
onFulfilled: timerBrand => {
this.state.brandCache = timerBrand;
return timerBrand;
},
}),
);
getBrandWatcher: {
/** @param {TimerBrand} timerBrand */
onFulfilled(timerBrand) {
this.state.brandCache = timerBrand;
return timerBrand;
},
},
/**
* Takes the current time from ChainTimerService and adds a relative time
* to determine a timeout timestamp in nanoseconds. Useful for
* {@link MsgTransfer.timeoutTimestamp}.
*
* @param {RelativeTimeRecord} [relativeTime] defaults to 5 minutes
* @returns {Vow<bigint>} Timeout timestamp in absolute nanoseconds since
* unix epoch
*/
getTimeoutTimestampNS(relativeTime) {
return watch(
allVows([
E(timerService).getCurrentTimestamp(),
this.self.getBrand(),
]),
Far('TimestampWatcher', {
/** @param {[TimestampRecord, TimerBrand]} results */
onFulfilled([currentTime, timerBrand]) {
const timeout =
relativeTime ||
TimeMath.coerceRelativeTimeRecord(
SECONDS_PER_MINUTE * 5n,
timerBrand,
);
return (
TimeMath.addAbsRel(currentTime, timeout).absValue *
NANOSECONDS_PER_SECOND
);
},
}),
);
getTimestampWatcher: {
/**
* @param {[TimestampRecord, TimerBrand]} results
* @param {{ relativeTime: RelativeTimeRecord }} ctx
*/
onFulfilled([currentTime, timerBrand], { relativeTime }) {
const timeout =
relativeTime ||
TimeMath.coerceRelativeTimeRecord(
SECONDS_PER_MINUTE * 5n,
timerBrand,
);
return (
TimeMath.addAbsRel(currentTime, timeout).absValue *
NANOSECONDS_PER_SECOND
);
},
},
public: {
/** @returns {Vow<TimerBrand>} */
getBrand() {
// XXX this is a common use case that should have a helper like `provideSingleton`
if (this.state.brandCache) return watch(this.state.brandCache);
return watch(
E(timerService).getTimerBrand(),
this.facets.getBrandWatcher,
);
},
/**
* Takes the current time from ChainTimerService and adds a relative
* time to determine a timeout timestamp in nanoseconds. Useful for
* {@link MsgTransfer.timeoutTimestamp}.
*
* @param {RelativeTimeRecord} [relativeTime] defaults to 5 minutes
* @returns {Vow<bigint>} Timeout timestamp in absolute nanoseconds
* since unix epoch
*/
getTimeoutTimestampNS(relativeTime) {
return watch(
allVows([
E(timerService).getCurrentTimestamp(),
this.facets.public.getBrand(),
]),
this.facets.getTimestampWatcher,
{ relativeTime },
);
},
},
},
);
return timeHelper();
harden(prepareTimeHelperKit);

/**
* @param {Zone} zone
* @param {TimeHelperPowers} powers
*/
export const prepareTimeHelper = (zone, powers) => {
const makeTimeHelperKit = prepareTimeHelperKit(zone, powers);
const makeTimeHelper = pickFacet(makeTimeHelperKit, 'public');
const timeHelper = makeTimeHelper();
return harden(timeHelper);
};
harden(makeTimeHelper);
harden(prepareTimeHelper);

/** @typedef {ReturnType<typeof makeTimeHelper>} TimeHelper */
/** @typedef {ReturnType<typeof prepareTimeHelper>} TimeHelper */
4 changes: 2 additions & 2 deletions packages/orchestration/src/utils/start-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { makeChainHub } from '../exos/chain-hub.js';
import { prepareRemoteChainFacade } from '../exos/remote-chain-facade.js';
import { prepareCosmosOrchestrationAccount } from '../exos/cosmos-orchestration-account.js';
import { prepareLocalChainFacade } from '../exos/local-chain-facade.js';
import { makeTimeHelper } from '../exos/time-helper.js';
import { prepareTimeHelper } from '../exos/time-helper.js';

/**
* @import {PromiseKit} from '@endo/promise-kit'
Expand Down Expand Up @@ -52,7 +52,7 @@ export const provideOrchestration = (
const { agoricNames, timerService } = remotePowers;

const chainHub = makeChainHub(agoricNames);
const timeHelper = makeTimeHelper(makeHeapZone(), {
const timeHelper = prepareTimeHelper(makeHeapZone(), {
timerService,
vowTools,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { prepareLocalOrchestrationAccountKit } from '../../src/exos/local-orches
import { ChainAddress } from '../../src/orchestration-api.js';
import { makeChainHub } from '../../src/exos/chain-hub.js';
import {
makeTimeHelper,
prepareTimeHelper,
NANOSECONDS_PER_SECOND,
} from '../../src/exos/time-helper.js';
import { commonSetup } from '../supports.js';
Expand Down Expand Up @@ -38,7 +38,7 @@ test('deposit, withdraw', async t => {
timer,
vowTools,
makeChainHub(bootstrap.agoricNames),
makeTimeHelper(makeHeapZone(), { timerService: timer, vowTools }),
prepareTimeHelper(makeHeapZone(), { timerService: timer, vowTools }),
);

t.log('request account from vat-localchain');
Expand Down Expand Up @@ -110,7 +110,7 @@ test('delegate, undelegate', async t => {
timer,
vowTools,
makeChainHub(bootstrap.agoricNames),
makeTimeHelper(makeHeapZone(), { timerService: timer, vowTools }),
prepareTimeHelper(makeHeapZone(), { timerService: timer, vowTools }),
);

t.log('request account from vat-localchain');
Expand Down Expand Up @@ -163,7 +163,7 @@ test('transfer', async t => {
timer,
vowTools,
makeChainHub(bootstrap.agoricNames),
makeTimeHelper(makeHeapZone(), { timerService: timer, vowTools }),
prepareTimeHelper(makeHeapZone(), { timerService: timer, vowTools }),
);

t.log('request account from vat-localchain');
Expand Down
15 changes: 8 additions & 7 deletions packages/orchestration/test/exos/time-helper.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/* eslint-disable @jessie.js/safe-await-separator */
import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js';
import { prepareVowTools, V } from '@agoric/vow/vat.js';
import { prepareVowTools, heapVowE } from '@agoric/vow/vat.js';
import { buildZoeManualTimer } from '@agoric/zoe/tools/manualTimer.js';
import { TimeMath } from '@agoric/time';
import { makeHeapZone } from '@agoric/zone';
import {
makeTimeHelper,
prepareTimeHelper,
NANOSECONDS_PER_SECOND,
SECONDS_PER_MINUTE,
} from '../../src/exos/time-helper.js';
Expand All @@ -16,23 +16,24 @@ test('makeTimeHelper - getCurrentTimestamp', async t => {
const timerBrand = timer.getTimerBrand();
t.is(timer.getCurrentTimestamp().absValue, 0n, 'current time is 0n');

const timeHelper = makeTimeHelper(makeHeapZone(), {
const timeHelper = prepareTimeHelper(makeHeapZone(), {
timerService: timer,
vowTools,
});

t.is(
await V.when(timeHelper.getTimeoutTimestampNS()),
await heapVowE.when(timeHelper.getTimeoutTimestampNS()),
5n * SECONDS_PER_MINUTE * NANOSECONDS_PER_SECOND,
'default timestamp is 5 minutes from current time, in nanoseconds',
);
t.is(
await V.when(timeHelper.getBrand()),
await heapVowE.when(timeHelper.getBrand()),
timerBrand,
'brand retrieved cache equals timerBrand',
);

t.is(
await V.when(
await heapVowE.when(
timeHelper.getTimeoutTimestampNS(
TimeMath.coerceRelativeTimeRecord(1n, timerBrand),
),
Expand All @@ -44,7 +45,7 @@ test('makeTimeHelper - getCurrentTimestamp', async t => {
// advance timer by 3 seconds
await timer.tickN(3);
t.is(
await V.when(
await heapVowE.when(
timeHelper.getTimeoutTimestampNS(
TimeMath.coerceRelativeTimeRecord(1n, timerBrand),
),
Expand Down

0 comments on commit 0a216a7

Please sign in to comment.