Skip to content

Commit

Permalink
feat: make vat-localchain resumable
Browse files Browse the repository at this point in the history
Co-authored-by: Ikenna Omekam <ikenna@agoric.com>
  • Loading branch information
0xpatrickdev and iomekam committed Jun 11, 2024
1 parent 4687f0d commit ece3ec4
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 25 deletions.
2 changes: 1 addition & 1 deletion packages/orchestration/src/exos/local-chain-account-kit.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { makeTracer } from '@agoric/internal';
import { M } from '@agoric/vat-data';
import { TopicsRecordShape } from '@agoric/zoe/src/contractSupport/index.js';
import { InvitationShape } from '@agoric/zoe/src/typeGuards.js';
import { E } from '@endo/far';
import { V as E } from '@agoric/vow/vat.js';
import {
AmountArgShape,
ChainAddressShape,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js';

import { AmountMath } from '@agoric/ertp';
import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport/recorder.js';
import { E, Far } from '@endo/far';
import { V as E } from '@agoric/vow/vat.js';
import { Far } from '@endo/far';
import { commonSetup } from '../supports.js';
import { prepareLocalChainAccountKit } from '../../src/exos/local-chain-account-kit.js';
import { ChainAddress } from '../../src/orchestration-api.js';
Expand Down
114 changes: 96 additions & 18 deletions packages/vats/src/localchain.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@
import { E } from '@endo/far';
import { M } from '@endo/patterns';
import { AmountShape, BrandShape, PaymentShape } from '@agoric/ertp';
import { Shape as NetworkShape } from '@agoric/network';
import { V } from '@agoric/vow/vat.js';

const { Fail } = assert;

/**
* @import {TypedJson, ResponseTo, JsonSafe} from '@agoric/cosmic-proto';
* @import {PromiseVow, VowTools, Watcher} from '@agoric/vow';
* @import {TargetApp, TargetRegistration} from './bridge-target.js';
* @import {BankManager, Bank} from './vat-bank.js';
* @import {ScopedBridgeManager} from './types.js';
*/
Expand All @@ -27,33 +31,86 @@ const { Fail } = assert;
* }} LocalChainPowers
*/

/**
* @param {import('@agoric/zone').Zone} zone
*/
const prepareDepositPaymentHandler = zone =>
zone.exoClass(
'DepositPaymentHandler',
M.interface('DepositPaymentHandlerI', {
onFulfilled: M.call(BrandShape)
.optional({
bank: M.remotable('Bank Power'),
payment: PaymentShape,
optAmountShape: M.or(M.undefined(), AmountShape),
})
.returns(M.promise()),
}),
() => ({}),
/** @type {Watcher<Brand<'nat'>, Amount<'nat'>>} */
{
/**
* @param {Brand<'nat'>} brand
* @param {{
* bank: Bank;
* payment: Payment<'nat'>;
* optAmountShape: Amount<'nat'>;
* }} ctx
*/
onFulfilled(brand, { bank, payment, optAmountShape }) {
const purse = E(bank).getPurse(brand);
return E(purse).deposit(payment, optAmountShape);
},
},
);

/** @typedef {ReturnType<typeof prepareDepositPaymentHandler>} DepositPaymentHandler */

export const LocalChainAccountI = M.interface('LocalChainAccount', {
getAddress: M.callWhen().returns(M.string()),
getBalance: M.callWhen(BrandShape).returns(AmountShape),
deposit: M.callWhen(PaymentShape).optional(M.pattern()).returns(AmountShape),
deposit: M.callWhen(PaymentShape)
.optional(M.pattern())
.returns(NetworkShape.Vow$(AmountShape)),
withdraw: M.callWhen(AmountShape).returns(PaymentShape),
executeTx: M.callWhen(M.arrayOf(M.record())).returns(M.arrayOf(M.record())),
monitorTransfers: M.callWhen(M.remotable('TransferTap')).returns(
M.remotable('TargetRegistration'),
),
});

/** @param {import('@agoric/base-zone').Zone} zone */
const prepareLocalChainAccount = zone =>
zone.exoClass(
/**
* @param {import('@agoric/base-zone').Zone} zone
* @param {DepositPaymentHandler} makeDepositPaymentHandler
* @param {VowTools} vowTools
*/
const prepareLocalChainAccount = (
zone,
makeDepositPaymentHandler,
{ watch },
) => {
const depositPaymentHandler = makeDepositPaymentHandler();
return zone.exoClass(
'LocalChainAccount',
LocalChainAccountI,
/**
* @param {string} address
* @param {AccountPowers} powers
*/
(address, powers) => ({ address, ...powers, reserved: undefined }),
(address, powers) => ({
address,
...powers,
reserved: undefined,
}),
{
// Information that the account creator needs.
getAddress() {
return this.state.address;
},
/** @param {Brand<'nat'>} brand */
/**
* @param {Brand<'nat'>} brand
* @returns {PromiseVow<Amount>}
*/
async getBalance(brand) {
const { bank } = this.state;
const purse = E(bank).getPurse(brand);
Expand All @@ -67,20 +124,23 @@ const prepareLocalChainAccount = zone =>
* @param {Payment<'nat'>} payment
* @param {Pattern} [optAmountShape] throws if the Amount of the Payment
* does not match the provided Pattern
* @returns {Promise<Amount>}
* @returns {PromiseVow<Amount<'nat'>>}
*/
async deposit(payment, optAmountShape) {
const { bank } = this.state;

const allegedBrand = await E(payment).getAllegedBrand();
const purse = E(bank).getPurse(allegedBrand);
return E(purse).deposit(payment, optAmountShape);
const getBrandWatcher = watch(E(payment).getAllegedBrand());
return watch(getBrandWatcher, depositPaymentHandler, {
bank,
payment,
optAmountShape,
});
},
/**
* Withdraw a payment from the account's bank purse of the amount's brand.
*
* @param {Amount<'nat'>} amount
* @returns {Promise<Payment<'nat'>>} payment
* @returns {PromiseVow<Payment<'nat'>>} payment
*/
async withdraw(amount) {
const { bank } = this.state;
Expand All @@ -94,7 +154,9 @@ const prepareLocalChainAccount = zone =>
* @template {TypedJson[]} MT messages tuple (use const with multiple
* elements or it will be a mixed array)
* @param {MT} messages
* @returns {Promise<{ [K in keyof MT]: JsonSafe<ResponseTo<MT[K]>> }>}
* @returns {PromiseVow<{
* [K in keyof MT]: JsonSafe<ResponseTo<MT[K]>>;
* }>}
*/
async executeTx(messages) {
const { address, system } = this.state;
Expand All @@ -110,12 +172,17 @@ const prepareLocalChainAccount = zone =>
};
return E(system).toBridge(obj);
},
/**
* @param {TargetApp} tap
* @returns {PromiseVow<TargetRegistration>}
*/
async monitorTransfers(tap) {
const { address, transfer } = this.state;
return E(transfer).registerTap(address, tap);
},
},
);
};
/**
* @typedef {Awaited<
* ReturnType<Awaited<ReturnType<typeof prepareLocalChainAccount>>>
Expand Down Expand Up @@ -146,6 +213,8 @@ const prepareLocalChain = (zone, makeAccount) =>
* follows the ICA guidelines to help reduce collisions. See
* x/vlocalchain/keeper/keeper.go AllocateAddress for the use of the app
* hash and block data hash.
*
* @returns {PromiseVow<LocalChainAccount>}
*/
async makeAccount() {
const { system, bankManager, transfer } = this.state;
Expand All @@ -161,11 +230,11 @@ const prepareLocalChain = (zone, makeAccount) =>
* object.
*
* @param {import('@agoric/cosmic-proto').TypedJson} request
* @returns {Promise<import('@agoric/cosmic-proto').TypedJson>}
* @returns {PromiseVow<import('@agoric/cosmic-proto').TypedJson>}
*/
async query(request) {
const requests = harden([request]);
const results = await E(this.self).queryMany(requests);
const results = await V(this.self).queryMany(requests);
results.length === 1 ||
Fail`expected exactly one result; got ${results}`;
const { error, reply } = results[0];
Expand All @@ -180,7 +249,7 @@ const prepareLocalChain = (zone, makeAccount) =>
* failure.
*
* @param {import('@agoric/cosmic-proto').TypedJson[]} requests
* @returns {Promise<
* @returns {PromiseVow<
* {
* error?: string;
* reply: import('@agoric/cosmic-proto').TypedJson;
Expand All @@ -197,9 +266,18 @@ const prepareLocalChain = (zone, makeAccount) =>
},
);

/** @param {import('@agoric/base-zone').Zone} zone */
export const prepareLocalChainTools = zone => {
const makeAccount = prepareLocalChainAccount(zone);
/**
* @param {import('@agoric/base-zone').Zone} zone
* @param {VowTools} vowTools
*/
export const prepareLocalChainTools = (zone, vowTools) => {
const makeDepositHandlers = prepareDepositPaymentHandler(zone);
const makeAccount = prepareLocalChainAccount(
zone,
makeDepositHandlers,
vowTools,
);

const makeLocalChain = prepareLocalChain(zone, makeAccount);

return harden({ makeLocalChain });
Expand Down
2 changes: 1 addition & 1 deletion packages/vats/src/proposals/localchain-test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// @ts-check
import { E } from '@endo/far';
import { V as E } from '@agoric/vow/vat.js';
import { typedJson } from '@agoric/cosmic-proto/vatsafe';

/**
Expand Down
7 changes: 6 additions & 1 deletion packages/vats/src/vat-localchain.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
// @ts-check
import { Far } from '@endo/far';
import { makeDurableZone } from '@agoric/zone/durable.js';
import { prepareVowTools } from '@agoric/vow/vat.js';

import { prepareLocalChainTools } from './localchain.js';

export const buildRootObject = (_vatPowers, _args, baggage) => {
const zone = makeDurableZone(baggage);
const { makeLocalChain } = prepareLocalChainTools(zone.subZone('localchain'));
const vowTools = prepareVowTools(zone.subZone('VowTools'));
const { makeLocalChain } = prepareLocalChainTools(
zone.subZone('localchain'),
vowTools,
);

return Far('LocalChainVat', {
/**
Expand Down
6 changes: 3 additions & 3 deletions packages/vats/test/localchain.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import { AmountMath, AssetKind, makeIssuerKit } from '@agoric/ertp';
import { reincarnate } from '@agoric/swingset-liveslots/tools/setup-vat-data.js';
import { withAmountUtils } from '@agoric/zoe/tools/test-utils.js';
import { makeDurableZone } from '@agoric/zone/durable.js';
import { E } from '@endo/far';
import { getInterfaceOf } from '@endo/marshal';
import { VTRANSFER_IBC_EVENT } from '@agoric/internal';
import { prepareVowTools } from '@agoric/vow/vat.js';
import { prepareVowTools, V as E } from '@agoric/vow/vat.js';
import { prepareLocalChainTools } from '../src/localchain.js';
import { prepareBridgeTargetModule } from '../src/bridge-target.js';
import { prepareTransferTools } from '../src/transfer.js';
Expand Down Expand Up @@ -71,7 +70,8 @@ const makeTestContext = async _t => {
/** @param {LocalChainPowers} powers */
const makeLocalChain = async powers => {
const zone = makeDurableZone(provideBaggage('localchain'));
return prepareLocalChainTools(zone).makeLocalChain(powers);
const vowTools = prepareVowTools(zone.subZone('vows'));
return prepareLocalChainTools(zone, vowTools).makeLocalChain(powers);
};

const localchain = await makeLocalChain({
Expand Down

0 comments on commit ece3ec4

Please sign in to comment.