Skip to content

Commit

Permalink
support spendAction over bridge
Browse files Browse the repository at this point in the history
  • Loading branch information
turadg committed Sep 4, 2022
1 parent b5d3631 commit d70db59
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 25 deletions.
3 changes: 1 addition & 2 deletions packages/smart-wallet/src/offers.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ export const makeOffersFacet = (
// Any of these may throw.

const offerSpec = await unmarshallOfferSpec(capData);
console.log('DEBUG', { offerSpec });

const { id, invitationSpec, proposal, offerArgs } = offerSpec;
assert(invitationSpec, 'offer missing invitationSpec');
Expand Down Expand Up @@ -126,7 +125,7 @@ export const makeOffersFacet = (
seat.getOfferResult(),
result => {
const passStyle = passStyleOf(result);
console.log('DEBUG offerResult', passStyle, result);
console.log('offerResult', passStyle, result);
// someday can we get TS to type narrow based on the passStyleOf result match?
switch (passStyle) {
case 'copyRecord':
Expand Down
2 changes: 1 addition & 1 deletion packages/smart-wallet/src/smartWallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export const makeSmartWallet = async (

/** @type { (desc: Omit<BrandDescriptor, 'displayInfo'>) => Promise<void>} */
const addBrand = async desc => {
console.log('DEBUG addBrand', desc);
console.log('addBrand', desc);
// assert haven't received this issuer before.
const descriptorsHas = brandDescriptors.has(desc.brand);
const pursesHas = brandPurses.has(desc.brand);
Expand Down
45 changes: 31 additions & 14 deletions packages/smart-wallet/src/walletFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,34 @@ import { makeSmartWallet } from './smartWallet.js';
// Client message types. These are the shapes client must use to push messages over the bridge.
/**
* @typedef {{
* target: 'offers', // e.g. `getOffersFacet`
* target: 'deposit' | 'offers',
* method: string,
* arg: import('@endo/captp').CapData<string>,
* }} NormalAction
* Can't receive payments.
* }} Action
* Description of action, to be encoded in a bridge messsage 'action' or 'spendAction' field.
* Only actions sent in 'spendAction' (in WalletSpendActionMsg) can spend.
*
* @typedef {{
* target: 'deposit', 'offers', // e.g. `getDepositFacet`
* }} SpendAction
* Necessary for payments.
* The `target` field maps to a getter: 'foo' --> getFooFacet()
*/

/** @type {(action: NormalAction) => string} */
/** @type {(action: Action) => string} */
export const stringifyAction = ({ target, method, arg }) => {
assert(target === 'offers', `unsupported target ${target}`);
switch (target) {
case 'deposit':
assert(method === 'receive', `unsupported method ${method}`);
break;
case 'offers':
assert(method === 'executeOffer', `unsupported method ${method}`);
break;
default:
assert.fail(`unsupported target ${target}`);
}
// xxx utility for validating CapData shape?
assert(arg.body && arg.slots, 'invalid arg');

return `${target}.${method} ${JSON.stringify(arg)}`;
};
/** @type {(actionStr: string) => NormalAction} */
/** @type {(actionStr: string) => Action} */
export const parseActionStr = str => {
const space = str.indexOf(' ');
const left = str.substring(0, space);
Expand Down Expand Up @@ -104,16 +114,23 @@ export const start = async (zcf, privateArgs) => {
assert(obj, 'missing wallet action');
assert.typeof(obj, 'object');
assert.typeof(obj.owner, 'string');
assert(!('spendAction' in obj), 'spend actions not yet supported');
assert('action' in obj, 'missing action property');
const canSpend = 'spendAction' in obj;
assert(
canSpend || 'action' in obj,
'missing action/spendAction property',
);
const action = parseActionStr(canSpend ? obj.spendAction : obj.action);

const wallet = walletsByAddress.get(obj.owner); // or throw
const action = parseActionStr(obj.action);
console.log('walletFactory:', { wallet, action });
switch (action.target) {
case 'deposit':
assert(canSpend);
return E(E(wallet).getDepositFacet())[action.method](action.arg);
case 'offers':
return E(E(wallet).getOffersFacet())[action.method](action.arg);
default:
throw new Error('unsupportedaction target');
throw new Error(`unsupported action target ${action.target}`);
}
},
});
Expand Down
8 changes: 0 additions & 8 deletions packages/smart-wallet/test/supports.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,20 +89,14 @@ const makeFakeBridgeManager = () => {
/** @type {import('@agoric/vats/src/bridge').BridgeManager} */
const manager = {
register(srcID, handler) {
console.log('FakeBridgeManager register', srcID, handler);
console.log('handler keys', Object.keys(handler));
console.trace();
handlers[srcID] = handler;
},
toBridge(dstID, obj) {
console.log('FakeBridgeManager toBridge', dstID, obj);
const handler = handlers[dstID];
assert(handler, `No handler for ${dstID}`);
switch (obj.type) {
case ActionType.WALLET_ACTION:
case ActionType.WALLET_SPEND_ACTION: {
console.log('BRIDGE sending wallet obj');
console.log('handler keys', Object.keys(handler));
return E(handler).fromBridge(dstID, obj);
}

Expand All @@ -112,8 +106,6 @@ const makeFakeBridgeManager = () => {
}
},
unregister(srcID) {
console.log('FakeBridgeManager register', srcID);
// t.is(srcID, 'bank');
assert.fail('expected unregister');
},
};
Expand Down
44 changes: 44 additions & 0 deletions packages/smart-wallet/test/test-walletFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ test('bridge', async t => {
});
});

test.todo('spend action over bridge');

test('notifiers', async t => {
async function checkAddress(address) {
const smartWallet = await t.context.simpleProvideWallet(address);
Expand Down Expand Up @@ -99,6 +101,44 @@ test.todo(
);

test('wallet action encoding', t => {
const action = /** @type {const} */ ({
target: 'offers',
method: 'executeOffer',
arg: {
body: 'bogus',
slots: ['foo'],
},
});
t.is(
// @ts-expect-error CapData type should be readonly slots
stringifyAction(action),
'offers.executeOffer {"body":"bogus","slots":["foo"]}',
);

t.throws(
// @ts-expect-error
() => stringifyAction({ target: 'foo' }),
{
message: 'unsupported target foo',
},
);
t.throws(
// @ts-expect-error
() => stringifyAction({ target: 'deposit', method: 'foo' }),
{
message: 'unsupported method foo',
},
);
t.throws(
// @ts-expect-error
() => stringifyAction({ target: 'deposit', method: 'receive', arg: {} }),
{
message: 'invalid arg',
},
);
});

test('wallet action decoding', t => {
const action = /** @type {const} */ ({
target: 'offers',
method: 'executeOffer',
Expand All @@ -110,4 +150,8 @@ test('wallet action encoding', t => {
// @ts-expect-error CapData type should be readonly slots
const str = stringifyAction(action);
t.deepEqual(parseActionStr(str), action);

t.throws(() => parseActionStr(` ${str}`));
t.throws(() => parseActionStr(`,${str}`));
t.throws(() => parseActionStr(', '));
});

0 comments on commit d70db59

Please sign in to comment.