Skip to content

Commit

Permalink
adopt Remote type in orchestration (#9406)
Browse files Browse the repository at this point in the history
## Description

Many instances of `ERef` are more accurately typed as `Remote`. This
moves that type from the `vow` package down to `internal` to be used
more broadly. (Eventually it will move to Endo.)

It also adopts the type in `orchestration` package to get its benefits.
I chose this package because we're working in it actively. I didn't go
and migrate the many other places.

### Security Considerations

n/a, types

### Scaling Considerations

n/a, types


### Documentation Considerations

More implicit docs

### Testing Considerations

CI

### Upgrade Considerations

n/a, types
  • Loading branch information
mergify[bot] committed May 24, 2024
2 parents 60cffcf + 730b349 commit cc2bf0b
Show file tree
Hide file tree
Showing 13 changed files with 97 additions and 60 deletions.
3 changes: 3 additions & 0 deletions packages/internal/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@ export * from './utils.js';
export * from './method-tools.js';
export * from './typeGuards.js';

// eslint-disable-next-line import/export -- just types
export * from './types.js';

export { objectMap } from '@endo/common/object-map.js';
export { fromUniqueEntries } from '@endo/common/from-unique-entries.js';
37 changes: 37 additions & 0 deletions packages/internal/src/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
/* eslint-disable max-classes-per-file */
import type { Callable, RemotableBrand } from '@endo/eventual-send';
import type { Primitive } from '@endo/pass-style';

export declare class Callback<I extends (...args: unknown[]) => any> {
private iface: I;

Expand All @@ -18,3 +21,37 @@ export declare class SyncCallback<

public isSync: true;
}

/**
Returns a boolean for whether the given type is primitive value or primitive type.
@example
```
IsPrimitive<'string'>
//=> true
IsPrimitive<string>
//=> true
IsPrimitive<Object>
//=> false
```
*/
export type IsPrimitive<T> = [T] extends [Primitive] ? true : false;

/** Recursively extract the non-callable properties of T */
export type DataOnly<T> =
IsPrimitive<T> extends true
? T
: T extends Callable
? never
: { [P in keyof T as T[P] extends Callable ? never : P]: DataOnly<T[P]> };

/**
* A type that doesn't assume its parameter is local, but is satisfied with both
* local and remote references. It accepts both near and marshalled references
* that were returned from `Remotable` or `Far`.
*/
export type Remote<Primary, Local = DataOnly<Primary>> =
| Primary
| RemotableBrand<Local, Primary>;
2 changes: 2 additions & 0 deletions packages/internal/src/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Empty JS file to correspond with its .d.ts twin
export {};
23 changes: 23 additions & 0 deletions packages/internal/test/types.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { expectNotType, expectType } from 'tsd';
import { E, ERef } from '@endo/far';
import type { Remote } from '../src/types.js';
import type { StorageNode } from '../src/lib-chainStorage.js';

const eventualStorageNode: ERef<StorageNode> = null as any;
const remoteStorageNode: Remote<StorageNode> = null as any;

{
// When awaited, ERef makes the object look local
const storageNode = await eventualStorageNode;
expectType<StorageNode>(storageNode);
expectType<string>(storageNode.getPath());
}

{
// When awaited, Remote is correct
const storageNode = await remoteStorageNode; // no-op
expectType<Remote<StorageNode>>(storageNode);
// @ts-expect-error cannot call remote methods directly
storageNode.getPath();
expectType<Promise<string>>(E(storageNode).getPath());
}
5 changes: 3 additions & 2 deletions packages/orchestration/src/examples/stakeAtom.contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

import { makeTracer, StorageNodeShape } from '@agoric/internal';
import { TimerServiceShape } from '@agoric/time';
import { V as E } from '@agoric/vow/vat.js';
import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport';
import { InvitationShape } from '@agoric/zoe/src/typeGuards.js';
Expand All @@ -22,8 +23,8 @@ export const meta = harden({
privateArgsShape: {
orchestration: M.remotable('orchestration'),
storageNode: StorageNodeShape,
marshaller: M.remotable('Marshaller'),
timer: M.remotable('TimerService'),
marshaller: M.remotable('marshaller'),
timer: TimerServiceShape,
},
});
export const privateArgsShape = meta.privateArgsShape;
Expand Down
17 changes: 9 additions & 8 deletions packages/orchestration/src/examples/swapExample.contract.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { StorageNodeShape } from '@agoric/internal';
import { TimerServiceShape } from '@agoric/time';
import { withdrawFromSeat } from '@agoric/zoe/src/contractSupport/zoeHelpers.js';
import { Far } from '@endo/far';
import { deeplyFulfilled } from '@endo/marshal';
Expand All @@ -10,18 +11,18 @@ import { orcUtils } from '../utils/orc.js';
* @import {Orchestrator, IcaAccount, CosmosValidatorAddress} from '../types.js'
* @import {TimerService} from '@agoric/time';
* @import {LocalChain} from '@agoric/vats/src/localchain.js';
* @import {ERef} from '@endo/far'
* @import {Remote} from '@agoric/internal';
* @import {OrchestrationService} from '../service.js';
* @import {Zone} from '@agoric/zone';
*/

/** @type {ContractMeta} */
export const meta = {
privateArgsShape: {
localchain: M.any(),
orchestrationService: M.any(),
localchain: M.remotable('localchain'),
orchestrationService: M.or(M.remotable('orchestration'), null),
storageNode: StorageNodeShape,
timerService: M.any(),
timerService: M.or(TimerServiceShape, null),
zone: M.any(),
},
upgradability: 'canUpgrade',
Expand All @@ -40,10 +41,10 @@ export const makeNatAmountShape = (brand, min) =>
/**
* @param {ZCF} zcf
* @param {{
* localchain: ERef<LocalChain>;
* orchestrationService: ERef<OrchestrationService>;
* storageNode: ERef<StorageNode>;
* timerService: ERef<TimerService>;
* localchain: Remote<LocalChain>;
* orchestrationService: Remote<OrchestrationService> | null;
* storageNode: Remote<StorageNode>;
* timerService: Remote<TimerService> | null;
* zone: Zone;
* }} privateArgs
*/
Expand Down
10 changes: 5 additions & 5 deletions packages/orchestration/src/examples/unbondExample.contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ import { makeOrchestrationFacade } from '../facade.js';
* @import {Orchestrator, IcaAccount, CosmosValidatorAddress} from '../types.js'
* @import {TimerService} from '@agoric/time';
* @import {LocalChain} from '@agoric/vats/src/localchain.js';
* @import {ERef} from '@endo/far'
* @import {Remote} from '@agoric/internal';
* @import {OrchestrationService} from '../service.js';
* @import {Zone} from '@agoric/zone';
*/

/**
* @param {ZCF} zcf
* @param {{
* localchain: ERef<LocalChain>;
* orchestrationService: ERef<OrchestrationService>;
* storageNode: ERef<StorageNode>;
* timerService: ERef<TimerService>;
* localchain: Remote<LocalChain>;
* orchestrationService: Remote<OrchestrationService>;
* storageNode: Remote<StorageNode>;
* timerService: Remote<TimerService>;
* zone: Zone;
* }} privateArgs
*/
Expand Down
12 changes: 6 additions & 6 deletions packages/orchestration/src/facade.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { E } from '@endo/far';
* @import {Zone} from '@agoric/zone';
* @import {TimerService} from '@agoric/time';
* @import {LocalChain} from '@agoric/vats/src/localchain.js';
* @import {ERef} from '@endo/far';
* @import {Remote} from '@agoric/internal';
* @import {OrchestrationService} from './service.js';
* @import {Chain, ChainInfo, OrchestrationAccount, Orchestrator} from './types.js';
*/
Expand All @@ -15,7 +15,7 @@ import { E } from '@endo/far';
const anyVal = null;

/**
* @param {ERef<LocalChain>} localchain
* @param {Remote<LocalChain>} localchain
* @returns {Chain}
*/
const makeLocalChainFacade = localchain => {
Expand Down Expand Up @@ -134,11 +134,11 @@ const makeRemoteChainFacade = name => {
*
* @param {{
* zone: Zone;
* timerService: ERef<TimerService>;
* timerService: Remote<TimerService> | null;
* zcf: ZCF;
* storageNode: ERef<StorageNode>;
* orchestrationService: ERef<OrchestrationService>;
* localchain: ERef<LocalChain>;
* storageNode: Remote<StorageNode>;
* orchestrationService: Remote<OrchestrationService> | null;
* localchain: Remote<LocalChain>;
* }} powers
*/
export const makeOrchestrationFacade = ({
Expand Down
3 changes: 2 additions & 1 deletion packages/orchestration/src/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {

/**
* @import { Zone } from '@agoric/base-zone';
* @import {Remote} from '@agoric/internal';
* @import { Port, PortAllocator } from '@agoric/network';
* @import { IBCConnectionID } from '@agoric/vats';
* @import { ICQConnection, IcaAccount, ICQConnectionKit } from './types.js';
Expand All @@ -21,7 +22,7 @@ const { Fail, bare } = assert;

/**
* @typedef {object} OrchestrationPowers
* @property {ERef<PortAllocator>} portAllocator
* @property {Remote<PortAllocator>} portAllocator
*/

/**
Expand Down
3 changes: 1 addition & 2 deletions packages/vats/src/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { BridgeIdValue, Remote } from '@agoric/internal';
import type { Bytes } from '@agoric/network';
import type { PromiseVow, Remote } from '@agoric/vow';
import type { Guarded } from '@endo/exo';
import type { ERef } from '@endo/far';
import type { BridgeIdValue } from '@agoric/internal';

export type Board = ReturnType<
ReturnType<typeof import('./lib-board.js').prepareBoardKit>
Expand Down
2 changes: 1 addition & 1 deletion packages/vats/test/vat-bank.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { subscribeEach } from '@agoric/notifier';
import { buildRootObject } from '../src/vat-bank.js';

/**
* @import {Remote} from '@agoric/vow';
* @import {Remote} from '@agoric/internal';
* @import {BridgeHandler, ScopedBridgeManager} from '../src/types.js';
*/

Expand Down
3 changes: 3 additions & 0 deletions packages/vow/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ export { VowShape, toPassableCap } from './vow-utils.js';

// eslint-disable-next-line import/export
export * from './types.js';

// XXX re-exporting the Remote type for back-compat
export * from '@agoric/internal/src/types.js';
37 changes: 2 additions & 35 deletions packages/vow/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ export {};
* @import {RemotableBrand} from '@endo/eventual-send'
* @import {CopyTagged} from '@endo/pass-style'
* @import {RemotableObject} from '@endo/pass-style';
* @import {PromiseVow, Remote} from '@agoric/vow';
* @import {IsPrimitive, Remote} from '@agoric/internal';
* @import {PromiseVow} from '@agoric/vow';
* @import {prepareVowTools} from './tools.js'
*/
/** @typedef {(...args: any[]) => any} Callable */

/**
* @template T
Expand All @@ -21,29 +21,6 @@ export {};
* @typedef {T | PromiseLike<T>} ERef
*/

/**
* @template T
* @typedef {(
* T extends bigint ? true :
* T extends boolean ? true :
* T extends null ? true :
* T extends number ? true :
* T extends string ? true :
* T extends symbol ? true :
* T extends undefined ? true :
* false
* )} IsPrimitive Whether T is a primitive type.
*/

/**
* @template T
* @typedef {(
* IsPrimitive<T> extends true ? T :
* T extends Callable ? never :
* { [P in keyof T as T[P] extends Callable ? never : P]: DataOnly<T[P]> }
* )} DataOnly Recursively extract the non-callable properties of T.
*/

/**
* Follow the chain of vow shortening to the end, returning the final value.
* This is used within E, so we must narrow the type to its remote form.
Expand All @@ -57,16 +34,6 @@ export {};
* )} Unwrap
*/

/**
* A type that accepts both near and marshalled references that were
* returned from `Remotable` or `Far`.
* @template Primary The type of the primary reference.
* @template [Local=DataOnly<Primary>] The local properties of the object.
* @typedef {Primary | RemotableBrand<Local, Primary>} Remote A type that
* doesn't assume its parameter is local, but is satisfied with both local
* and remote references.
*/

/**
* @template [T=any]
* @typedef {object} VowV0 The first version of the vow implementation
Expand Down

0 comments on commit cc2bf0b

Please sign in to comment.