Skip to content

Commit

Permalink
chore: fix authority leak in wallet by adding attenuatedPursesNotifier (
Browse files Browse the repository at this point in the history
#2218)

chore: fix authority leak in wallet by adding attenuatedPursesNotifier (#2218)
  • Loading branch information
zarutian authored Jan 21, 2021
1 parent 0b4c018 commit 5154530
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 7 deletions.
58 changes: 54 additions & 4 deletions packages/dapp-svelte-wallet/api/src/lib-wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,57 @@ export function makeWallet({
updater: offersUpdater,
} = makeNotifierKit();

/** @type {NotifierRecord<PursesFullState[]>} */
const { notifier: pursesNotifier, updater: pursesUpdater } = makeNotifierKit(
[],
);
const { pursesNotifier, attenuatedPursesNotifier, pursesUpdater } = (() => {
/** @type {NotifierRecord<PursesFullState[]>} */
const { notifier: ipn, updater: ipu } = makeNotifierKit([]);
/** @type {NotifierRecord<PursesJSONState[]>} */
const { notifier: apn, updater: apu } = makeNotifierKit([]);
// explicit whitelist
/**
* @param {PursesFullState} _
* @returns {PursesJSONState}
*/
const innerFilter = ({
brandBoardId,
depositBoardId,
brandPetname,
pursePetname,
displayInfo,
value,
currentAmountSlots,
currentAmount,
}) =>
harden({
brandBoardId,
...(depositBoardId && { depositBoardId }),
brandPetname,
pursePetname,
...(displayInfo && { displayInfo }),
value,
currentAmountSlots,
currentAmount,
});
const filter = state => state.map(innerFilter);
const pu = harden({
updateState: newState => {
ipu.updateState(newState);
apu.updateState(filter(newState));
},
finish: finalState => {
ipu.finish(finalState);
apu.finish(filter(finalState));
},
fail: reason => {
ipu.fail(reason);
apu.fail(reason);
},
});
return harden({
pursesNotifier: ipn,
attenuatedPursesNotifier: apn,
pursesUpdater: pu,
});
})();

/**
* @param {Petname} pursePetname
Expand Down Expand Up @@ -1333,6 +1380,9 @@ export function makeWallet({
getPursesNotifier() {
return pursesNotifier;
},
getAttenuatedPursesNotifier() {
return attenuatedPursesNotifier;
},
getIssuersNotifier() {
return issuersNotifier;
},
Expand Down
2 changes: 1 addition & 1 deletion packages/dapp-svelte-wallet/api/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
* addOfferInvitation add an invitation to the specified offer
* @property {(brandBoardId: string) => Promise<string>} getDepositFacetId
* return the board ID to use to receive payments of the specified brand.
* @property {() => Promise<Notifier<Array<PursesFullState>>>} getPursesNotifier
* @property {() => Promise<Notifier<Array<PursesJSONState>>>} getPursesNotifier
* Follow changes to the purses.
* @property {() => Promise<Notifier<Array<OfferState>>>} getOffersNotifier
* Follow changes to the offers.
Expand Down
4 changes: 2 additions & 2 deletions packages/dapp-svelte-wallet/api/src/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export function buildRootObject(_vatPowers) {
const bridge = {
async getPursesNotifier() {
await approve();
const pursesNotifier = walletAdmin.getPursesNotifier();
const pursesNotifier = walletAdmin.getAttenuatedPursesNotifier();
const { notifier, updater } = makeNotifierKit();
observeIteration(makeApprovedNotifier(pursesNotifier), updater);
return notifier;
Expand Down Expand Up @@ -206,7 +206,7 @@ export function buildRootObject(_vatPowers) {
return walletAdmin.getOffersNotifier();
},
async getPursesNotifier() {
return walletAdmin.getPursesNotifier();
return walletAdmin.getAttenuatedPursesNotifier();
},
suggestInstallation(petname, installationBoardId) {
return walletAdmin.suggestInstallation(petname, installationBoardId);
Expand Down
117 changes: 117 additions & 0 deletions packages/dapp-svelte-wallet/api/test/test-getPursesNotifier.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// @ts-check
// eslint-disable-next-line import/no-extraneous-dependencies
import '@agoric/install-ses'; // calls lockdown()
// eslint-disable-next-line import/no-extraneous-dependencies
import test from 'ava';

import { makeIssuerKit } from '@agoric/ertp';
import { makeZoe } from '@agoric/zoe';
import fakeVatAdmin from '@agoric/zoe/src/contractFacet/fakeVatAdmin';
// eslint-disable-next-line import/no-extraneous-dependencies
import { makeBoard } from '@agoric/cosmic-swingset/lib/ag-solo/vats/lib-board';
import { makeWallet } from '../src/lib-wallet';

import '../src/types';

const setup = async () => {
const zoe = makeZoe(fakeVatAdmin);
const board = makeBoard();

const pursesStateChangeHandler = _data => {};
const inboxStateChangeHandler = _data => {};

const { admin: wallet, initialized } = makeWallet({
zoe,
board,
pursesStateChangeHandler,
inboxStateChangeHandler,
});
await initialized;
const MOOLA_ISSUER_PETNAME = 'moola';
const moolaKit = makeIssuerKit(MOOLA_ISSUER_PETNAME);

const MOOLA_PURSE_PETNAME = 'fun money';

const issuerManager = wallet.getIssuerManager();
await issuerManager.add(MOOLA_ISSUER_PETNAME, moolaKit.issuer);
await wallet.makeEmptyPurse(MOOLA_ISSUER_PETNAME, MOOLA_PURSE_PETNAME);
return { wallet, moolaKit, MOOLA_ISSUER_PETNAME, MOOLA_PURSE_PETNAME };
};

test('getPursesNotifier', async t => {
const {
wallet,
moolaKit,
MOOLA_ISSUER_PETNAME,
MOOLA_PURSE_PETNAME,
} = await setup();
const pursesNotifier = wallet.getPursesNotifier();
const update = await pursesNotifier.getUpdateSince();
t.is(update.updateCount, 7);
// Has the default Zoe invitation purse and a moola purse
t.is(update.value.length, 2);
const moolaPurseInfo = update.value[1];
t.truthy(moolaPurseInfo.actions);
t.is(moolaPurseInfo.brand, moolaKit.brand);
t.is(moolaPurseInfo.brandBoardId, '1532665031');
t.is(moolaPurseInfo.brandPetname, MOOLA_ISSUER_PETNAME);
t.deepEqual(moolaPurseInfo.currentAmount, {
brand: { kind: 'brand', petname: 'moola' }, // not a real amount
value: 0,
});
t.deepEqual(moolaPurseInfo.currentAmountSlots, {
body:
'{"brand":{"@qclass":"slot","iface":"Alleged: moola brand","index":0},"value":0}',
slots: [
{
kind: 'brand',
petname: 'moola',
},
],
});
t.deepEqual(moolaPurseInfo.displayInfo, {
amountMathKind: 'nat',
});
const moolaPurse = wallet.getPurse(MOOLA_PURSE_PETNAME);
t.is(moolaPurseInfo.purse, moolaPurse);
t.is(moolaPurseInfo.pursePetname, MOOLA_PURSE_PETNAME);
t.is(moolaPurseInfo.value, 0);
});

test('getAttenuatedPursesNotifier', async t => {
const { wallet, MOOLA_ISSUER_PETNAME, MOOLA_PURSE_PETNAME } = await setup();
const pursesNotifier = wallet.getAttenuatedPursesNotifier();
const update = await pursesNotifier.getUpdateSince();
t.is(update.updateCount, 7);
// Has the default Zoe invitation purse and a moola purse
t.is(update.value.length, 2);
const moolaPurseInfo = update.value[1];
// @ts-ignore
t.is(moolaPurseInfo.actions, undefined);
// @ts-ignore
t.is(moolaPurseInfo.brand, undefined);
t.is(moolaPurseInfo.brandBoardId, '1532665031');
t.is(moolaPurseInfo.brandPetname, MOOLA_ISSUER_PETNAME);
t.deepEqual(moolaPurseInfo.currentAmount, {
brand: { kind: 'brand', petname: 'moola' }, // not a real amount
value: 0,
});
t.deepEqual(moolaPurseInfo.currentAmountSlots, {
body:
'{"brand":{"@qclass":"slot","iface":"Alleged: moola brand","index":0},"value":0}',
slots: [
{
kind: 'brand',
petname: 'moola',
},
],
});
t.deepEqual(moolaPurseInfo.displayInfo, {
amountMathKind: 'nat',
});

// @ts-ignore
t.is(moolaPurseInfo.purse, undefined);
t.is(moolaPurseInfo.pursePetname, MOOLA_PURSE_PETNAME);
t.is(moolaPurseInfo.value, 0);
});

0 comments on commit 5154530

Please sign in to comment.