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

feat(orchestrator): membrane-friendly timerUtils #9546

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions packages/orchestration/src/examples/stakeBld.contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import { withdrawFromSeat } from '@agoric/zoe/src/contractSupport/zoeHelpers.js'
import { InvitationShape } from '@agoric/zoe/src/typeGuards.js';
import { makeDurableZone } from '@agoric/zone/durable.js';
import { prepareVowTools, heapVowE as E } from '@agoric/vow/vat.js';
import { makeHeapZone } from '@agoric/zone';
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 { prepareTimeHelper } from '../exos/time-helper.js';

/**
* @import {NameHub} from '@agoric/vats';
Expand All @@ -34,20 +36,23 @@ const trace = makeTracer('StakeBld');
*/
export const start = async (zcf, privateArgs, baggage) => {
const zone = makeDurableZone(baggage);
const { agoricNames, marshaller, timerService } = privateArgs;

const { makeRecorderKit } = prepareRecorderKitMakers(
baggage,
privateArgs.marshaller,
);
const { makeRecorderKit } = prepareRecorderKitMakers(baggage, marshaller);
const vowTools = prepareVowTools(zone.subZone('vows'));
const timeHelper = prepareTimeHelper(makeHeapZone(), {
timerService,
vowTools,
});

const makeLocalOrchestrationAccountKit = prepareLocalOrchestrationAccountKit(
zone,
makeRecorderKit,
zcf,
privateArgs.timerService,
timerService,
vowTools,
makeChainHub(privateArgs.agoricNames),
makeChainHub(agoricNames),
timeHelper,
);

// ----------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ import {
CoinShape,
DelegationShape,
} from '../typeGuards.js';
import { maxClockSkew, tryDecodeResponse } from '../utils/cosmos.js';
import {
maxClockSkew,
tryDecodeResponse,
dateInSeconds,
} from '../utils/cosmos.js';
import { orchestrationAccountMethods } from '../utils/orchestrationAccount.js';
import { dateInSeconds } from '../utils/time.js';

/**
* @import {AmountArg, IcaAccount, ChainAddress, CosmosValidatorAddress, ICQConnection, StakingAccountActions, DenomAmount, OrchestrationAccountI, DenomArg} from '../types.js';
Expand Down
17 changes: 8 additions & 9 deletions packages/orchestration/src/exos/local-orchestration-account.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ import {
ChainAmountShape,
IBCTransferOptionsShape,
} from '../typeGuards.js';
import { maxClockSkew } from '../utils/cosmos.js';
import { maxClockSkew, dateInSeconds } from '../utils/cosmos.js';
import { orchestrationAccountMethods } from '../utils/orchestrationAccount.js';
import { dateInSeconds, makeTimestampHelper } from '../utils/time.js';

/**
* @import {LocalChainAccount} from '@agoric/vats/src/localchain.js';
Expand All @@ -26,6 +25,7 @@ import { dateInSeconds, makeTimestampHelper } from '../utils/time.js';
* @import {PromiseVow, VowTools} from '@agoric/vow';
* @import {TypedJson} from '@agoric/cosmic-proto';
* @import {ChainHub} from './chain-hub.js';
* @import {TimeHelper} from './time-helper.js';
*/

const trace = makeTracer('LOA');
Expand Down Expand Up @@ -65,6 +65,7 @@ const PUBLIC_TOPICS = {
* @param {Remote<TimerService>} timerService
* @param {VowTools} vowTools
* @param {ChainHub} chainHub
* @param {TimeHelper} timeHelper
*/
export const prepareLocalOrchestrationAccountKit = (
zone,
Expand All @@ -73,11 +74,10 @@ export const prepareLocalOrchestrationAccountKit = (
timerService,
{ watch, when, allVows },
chainHub,
) => {
const timestampHelper = makeTimestampHelper(timerService);

timeHelper,
) =>
/** Make an object wrapping an LCA with Zoe interfaces. */
const makeLocalOrchestrationAccountKit = zone.exoClassKit(
zone.exoClassKit(
'Local Orchestration Account Kit',
{
holder: HolderI,
Expand Down Expand Up @@ -402,7 +402,7 @@ export const prepareLocalOrchestrationAccountKit = (
// TODO #9324 what's a reasonable default? currently 5 minutes
// FIXME: do not call `getTimeoutTimestampNS` if `opts.timeoutTimestamp` or `opts.timeoutHeight` is provided
const timeoutTimestampV = watch(
timestampHelper.getTimeoutTimestampNS(),
timeHelper.getTimeoutTimestampNS(),
this.facets.getTimeoutTimestampWatcher,
{ opts },
);
Expand All @@ -422,7 +422,6 @@ export const prepareLocalOrchestrationAccountKit = (
},
},
);
return makeLocalOrchestrationAccountKit;
};

/** @typedef {ReturnType<typeof prepareLocalOrchestrationAccountKit>} MakeLocalOrchestrationAccountKit */
/** @typedef {ReturnType<MakeLocalOrchestrationAccountKit>} LocalOrchestrationAccountKit */
130 changes: 130 additions & 0 deletions packages/orchestration/src/exos/time-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
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 } from '@endo/far';
import { M } from '@endo/patterns';

/**
* @import {Remote} from '@agoric/internal';*
* @import {RelativeTimeRecord, TimerBrand, TimerService, TimestampRecord} from '@agoric/time';
* @import {Vow, VowTools} from '@agoric/vow';
* @import {Zone} from '@agoric/zone';
*/

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 {TimeHelperPowers} powers
*/
const prepareTimeHelperKit = (
zone,
{ timerService, vowTools: { watch, allVows } },
) =>
zone.exoClassKit(
'Time Helper',
{
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,
}),
{
getBrandWatcher: {
/** @param {TimerBrand} timerBrand */
onFulfilled(timerBrand) {
this.state.brandCache = timerBrand;
return timerBrand;
},
},
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 },
);
},
},
},
);
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(prepareTimeHelper);

/** @typedef {ReturnType<typeof prepareTimeHelper>} TimeHelper */
9 changes: 9 additions & 0 deletions packages/orchestration/src/utils/cosmos.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,12 @@ export const tryDecodeResponse = (ackStr, fromProtoMsg) => {
throw assert.error(`bad response: ${ackStr}`, undefined, { cause });
}
};

/**
* Convert a Date from a Cosmos message, which has millisecond precision, to a
* BigInt for number of seconds since epoch, for use in a timer.
*
* @param {Date} date
* @returns {bigint}
*/
export const dateInSeconds = date => BigInt(Math.floor(date.getTime() / 1000));
11 changes: 8 additions & 3 deletions packages/orchestration/src/utils/start-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { prepareAsyncFlowTools } from '@agoric/async-flow';
import { prepareVowTools } from '@agoric/vow';
import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport/recorder.js';
import { makeDurableZone } from '@agoric/zone/durable.js';
import { makeHeapZone } from '@agoric/zone';
import { prepareLocalOrchestrationAccountKit } from '../exos/local-orchestration-account.js';
import { makeOrchestrationFacade } from '../facade.js';
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 { prepareTimeHelper } from '../exos/time-helper.js';

/**
* @import {PromiseKit} from '@endo/promise-kit'
Expand Down Expand Up @@ -45,11 +47,13 @@ export const provideOrchestration = (
marshaller,
) => {
const zone = makeDurableZone(baggage);
const vowTools = prepareVowTools(zone.subZone('vows'));
const { agoricNames, timerService } = remotePowers;

const chainHub = makeChainHub(agoricNames);

const vowTools = prepareVowTools(zone.subZone('vows'));
const timeHelper = prepareTimeHelper(makeHeapZone(), {
timerService,
vowTools,
});

const { makeRecorderKit } = prepareRecorderKitMakers(baggage, marshaller);
const makeLocalOrchestrationAccountKit = prepareLocalOrchestrationAccountKit(
Expand All @@ -59,6 +63,7 @@ export const provideOrchestration = (
timerService,
vowTools,
chainHub,
timeHelper,
);

const asyncFlowTools = prepareAsyncFlowTools(zone.subZone('asyncFlow'), {
Expand Down
63 changes: 0 additions & 63 deletions packages/orchestration/src/utils/time.js

This file was deleted.

Loading
Loading