Skip to content

Commit

Permalink
chore(orchestration): endowments are membrane-friendly (#9591)
Browse files Browse the repository at this point in the history
closes: #XXXX
refs: #9449
refs: #9308

## Description

Changes in this PR are related to #9449, primarily for `local-orchestration-account.js` and `cosmos-orchestration-account.js`, `service.js`, and `orchestrator.js`. Attenuated versions of these kit are what the caller of `...getChain('agoric' | 'cosmos').makeAccount()` receives. 

This PR also includes a change to `smart-wallet` so it understands how to unwrap **offerResult**s that are **Vow**s. This  uses **heapVowTools** which means it does  _**not**_  cover resumability in the scenario of `smart-wallet` upgrading - only the contracts that rely on it.

Other small changes included changes:
- `PromiseToVow` and `VowifyAll` type helpers, so we can still reference the idealized API spec in code that returns Vows
- fix/refactor of `getTimeoutTimestampNS` logic that removes an unnecessary network call when the caller supplies values
- lint rule: adds a warning when importing `heapVowE` in resumable code
- resumable lint rule changes to **error**, now that all warnings are resolved

### Security Considerations
N/A

### Scaling Considerations
N/A

### Documentation Considerations
N/A

### Testing Considerations

Adds a test boot and unit tests for a rejected Delegate wallet offer, which is helpful for ensuring errors are properly propagated through the vow chain.

Upgrade tests would be helpful here, but we haven't started on these yet. I don't think we need those to land this PR, although they are certainly critical before release. Would love to pair with someone on this and learn more about how we do that. 

### Upgrade Considerations

This PR contains a change to **smart-wallet**.
  • Loading branch information
mergify[bot] authored Jun 30, 2024
2 parents 4874a7f + 100e0a3 commit efd3370
Show file tree
Hide file tree
Showing 25 changed files with 806 additions and 463 deletions.
13 changes: 11 additions & 2 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ const resumable = [
message:
'callWhen wraps the function in a promise; instead immediately return a vow',
},
{
selector: "Identifier[name='heapVowE']",
message:
'heapVowE shortens vows to promises; instead use `E` from `@endo/far` with `watch` from durable vowTools',
},
{
selector: "Identifier[name='heapVowTools']",
message:
'heapVowTools are not durable; instead use `prepareVowTools` with a durable zone',
},
];

module.exports = {
Expand Down Expand Up @@ -160,8 +170,7 @@ module.exports = {
// Modules with exports that must be resumable
files: ['packages/orchestration/src/exos/**'],
rules: {
// TODO tighten to error
'no-restricted-syntax': ['warn', ...resumable],
'no-restricted-syntax': ['error', ...resumable],
},
},
{
Expand Down
24 changes: 24 additions & 0 deletions packages/boot/test/bootstrapTests/lca.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,28 @@ test.serial('stakeBld', async t => {
},
},
});
t.like(wd.getLatestUpdateRecord(), {
status: { id: 'request-delegate', numWantsSatisfied: 1 },
});

await t.throwsAsync(
wd.executeOffer({
id: 'request-delegate-504',
invitationSpec: {
source: 'continuing',
previousOffer: 'request-stake',
invitationMakerName: 'Delegate',
invitationArgs: ['agoric1validator1', { brand: BLD, value: 504n }],
},
proposal: {
give: {
// @ts-expect-error XXX BoardRemote
In: { brand: BLD, value: 504n },
},
},
}),
// TODO propagate error message through bridge
// FIXME should receive "simulated packet timeout" error
// { message: 'simulated packet timeout' },
);
});
23 changes: 8 additions & 15 deletions packages/boot/test/bootstrapTests/vat-orchestration.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js';
import type { ExecutionContext, TestFn } from 'ava';
import type { TestFn } from 'ava';

import { toRequestQueryJson } from '@agoric/cosmic-proto';
import {
Expand All @@ -11,10 +11,7 @@ import {
MsgDelegate,
MsgDelegateResponse,
} from '@agoric/cosmic-proto/cosmos/staking/v1beta1/tx.js';
import type {
OrchestrationService,
ICQConnection,
} from '@agoric/orchestration';
import type { OrchestrationService } from '@agoric/orchestration';
import { decodeBase64 } from '@endo/base64';
import { M, matches } from '@endo/patterns';
import {
Expand Down Expand Up @@ -75,8 +72,7 @@ test.skip('makeAccount returns an ICA connection', async t => {
runUtils: { EV },
} = t.context;

const orchestration: OrchestrationService =
await EV.vat('bootstrap').consumeItem('orchestration');
const orchestration = await EV.vat('bootstrap').consumeItem('orchestration');

const account = await EV(orchestration).makeAccount(
'somechain-1',
Expand Down Expand Up @@ -112,8 +108,7 @@ test.skip('ICA connection can be closed', async t => {
runUtils: { EV },
} = t.context;

const orchestration: OrchestrationService =
await EV.vat('bootstrap').consumeItem('orchestration');
const orchestration = await EV.vat('bootstrap').consumeItem('orchestration');

const account = await EV(orchestration).makeAccount(
'somechain-1',
Expand All @@ -134,8 +129,7 @@ test.skip('ICA connection can send msg with proto3', async t => {
runUtils: { EV },
} = t.context;

const orchestration: OrchestrationService =
await EV.vat('bootstrap').consumeItem('orchestration');
const orchestration = await EV.vat('bootstrap').consumeItem('orchestration');

const account = await EV(orchestration).makeAccount(
'somechain-1',
Expand All @@ -144,7 +138,6 @@ test.skip('ICA connection can send msg with proto3', async t => {
);
t.truthy(account, 'makeAccount returns an account');

// @ts-expect-error intentional
await t.throwsAsync(EV(account).executeEncodedTx('malformed'), {
message:
'In "executeEncodedTx" method of (ChainAccountKit account): arg 0: string "malformed" - Must be a copyArray',
Expand Down Expand Up @@ -195,7 +188,7 @@ test.skip('Query connection can be created', async t => {
} = t.context;

type Powers = { orchestration: OrchestrationService };
const contract = async ({ orchestration }: Powers) => {
const contract = async ({ orchestration }) => {
const connection =
await EV(orchestration).provideICQConnection('connection-0');
t.log('Query Connection', connection);
Expand All @@ -221,8 +214,8 @@ test.skip('Query connection can send a query', async t => {
} = t.context;

type Powers = { orchestration: OrchestrationService };
const contract = async ({ orchestration }: Powers) => {
const queryConnection: ICQConnection =
const contract = async ({ orchestration }) => {
const queryConnection =
await EV(orchestration).provideICQConnection('connection-0');

const [result] = await EV(queryConnection).query([balanceQuery]);
Expand Down
20 changes: 17 additions & 3 deletions packages/boot/tools/supports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,9 +416,23 @@ export const makeSwingsetTestKit = async (
switch (obj.type) {
case 'VLOCALCHAIN_ALLOCATE_ADDRESS':
return 'agoric1mockVlocalchainAddress';
case 'VLOCALCHAIN_EXECUTE_TX':
// returns one empty object per message
return obj.messages.map(() => ({}));
case 'VLOCALCHAIN_EXECUTE_TX': {
return obj.messages.map(message => {
switch (message['@type']) {
case '/cosmos.staking.v1beta1.MsgDelegate': {
if (message.amount.amount === '504') {
// FIXME - how can we propagate the error?
// this results in `syscall.callNow failed: device.invoke failed, see logs for details`
throw Error('simulated packet timeout');
}
return /** @type {JsonSafe<MsgDelegateResponse>} */ {};
}
// returns one empty object per message unless specified
default:
return {};
}
});
}
default:
throw Error(`VLOCALCHAIN message of unknown type ${obj.type}`);
}
Expand Down
8 changes: 4 additions & 4 deletions packages/orchestration/src/exos/agoric-names-tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const makeResumableAgoricNamesHack = (
}),
vbankAssetEntriesWatcher: M.interface('vbankAssetEntriesWatcher', {
onFulfilled: M.call(M.arrayOf(M.record()))
.optional({ brand: BrandShape })
.optional(BrandShape)
.returns(VowShape),
}),
},
Expand All @@ -55,9 +55,9 @@ export const makeResumableAgoricNamesHack = (
vbankAssetEntriesWatcher: {
/**
* @param {AssetInfo[]} assets
* @param {{ brand: Brand<'nat'> }} ctx
* @param {Brand<'nat'>} brand
*/
onFulfilled(assets, { brand }) {
onFulfilled(assets, brand) {
return asVow(() => {
const { vbankAssetsByBrand } = this.state;
vbankAssetsByBrand.addAll(
Expand Down Expand Up @@ -96,7 +96,7 @@ export const makeResumableAgoricNamesHack = (
return watch(
vbankAssetEntriesP,
this.facets.vbankAssetEntriesWatcher,
{ brand },
brand,
);
});
},
Expand Down
10 changes: 6 additions & 4 deletions packages/orchestration/src/exos/chain-account-kit.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,14 @@ export const prepareChainAccountKit = (zone, { watch, asVow }) =>
);
},
getBalance(_denom) {
// TODO https://github.com/Agoric/agoric-sdk/issues/9610
// UNTIL https://github.com/Agoric/agoric-sdk/issues/9326
return asVow(() => Fail`'not yet implemented'`);
return asVow(() => Fail`not yet implemented`);
},
getBalances() {
// TODO https://github.com/Agoric/agoric-sdk/issues/9610
// UNTIL https://github.com/Agoric/agoric-sdk/issues/9326
return asVow(() => Fail`'not yet implemented'`);
return asVow(() => Fail`not yet implemented`);
},
getLocalAddress() {
return NonNullish(
Expand All @@ -125,7 +127,7 @@ export const prepareChainAccountKit = (zone, { watch, asVow }) =>
return this.state.port;
},
executeTx() {
return asVow(() => Fail`'not yet implemented'`);
return asVow(() => Fail`not yet implemented`);
},
/**
* Submit a transaction on behalf of the remote account for execution on
Expand Down Expand Up @@ -170,7 +172,7 @@ export const prepareChainAccountKit = (zone, { watch, asVow }) =>
*/
getPurse(brand) {
console.log('getPurse got', brand);
return asVow(() => Fail`'not yet implemented'`);
return asVow(() => Fail`not yet implemented`);
},
},
connectionHandler: {
Expand Down
2 changes: 2 additions & 0 deletions packages/orchestration/src/exos/chain-hub.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { VowShape } from '@agoric/vow';
// eslint-disable-next-line no-restricted-syntax
import { heapVowTools } from '@agoric/vow/vat.js';
import { makeHeapZone } from '@agoric/zone';
import { E } from '@endo/far';
import { M } from '@endo/patterns';
import { CosmosChainInfoShape, IBCConnectionInfoShape } from '../typeGuards.js';

// FIXME test thoroughly whether heap suffices for ChainHub
// eslint-disable-next-line no-restricted-syntax
const { allVows, watch } = heapVowTools;

const { Fail } = assert;
Expand Down
Loading

0 comments on commit efd3370

Please sign in to comment.