From 1753df83465785b5ee71b250770c9b012d750ffc Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Mon, 1 May 2023 10:44:15 -0700 Subject: [PATCH 1/3] feat!: emit smallcaps-format data in all marshallers This changes every `makeMarshal()` instance which performs object graph serialization to add the `{serializeBodyFormat: 'smallcaps'}` option. All emitted data will be in the "smallcaps" format, which is more concise (less overhead). For example, `1n` (a BigInt) is encoded as `#"+1"` instead of `{"@qclass":"bigint","digits":"1"}`. The old format was JSON except with those special `@qclass` objects to capture everything that JSON could not, including object references ("slots"). The new format is a leading `#` prefix, plus JSON, except that all the special cases are expressed as strings. Most significantly, this changes the format of the data we publish to `chainStorage`. The old format encoded simple data as mostly JSON, so bespoke parsers mostly worked, but even BigInts required special handling. Off-chain followers who perform RPC queries to the chain are highly encouraged to use the `@endo/marshal` library (or equivalent) to parse the data. This library can understand old-format data too, such as chain data published before this upgrade. Notable changes: * Many ad-hoc serializers were replaced with a real `makeMarshal`, which requires hardened inputs, so some new `harden()` calls were added * Some marshallers which only perform unserialization (`fromCapData`) were not modified * All unit tests which compared serialized data against manually-constructed examples had their examples updated * Previously, the `makeMockChainStorageRoot()` utility parsed Far/Remotable objects into an object with `{ iface }`. Now, it is unserialized into actual Remotables. Both `t.deepEqual` and `t.snapshot` will correctly compare the `iface` values of two Far objects, so tests should simply construct a `Far(iface)` and include it in the specimen data. As a result, many `t.snapshot`-style tests had their snapshot data changed. refs #6822 Co-authored-by: Chip Morningstar --- packages/agoric-cli/src/lib/wallet.js | 2 +- packages/agoric-cli/test/test-inter-cli.js | 6 +- packages/cache/src/store.js | 4 +- packages/cache/test/test-storage.js | 45 +-- packages/casting/src/defaults.js | 4 +- packages/cosmic-swingset/src/chain-main.js | 4 +- .../unitTests/snapshots/test-committee.js.md | 16 +- .../snapshots/test-committee.js.snap | Bin 915 -> 887 bytes .../test/unitTests/test-committee.js | 15 +- .../snapshots/test-auctionContract.js.md | 68 ++--- .../snapshots/test-auctionContract.js.snap | Bin 1693 -> 1639 bytes .../snapshots/test-fluxAggregatorKit.js.md | 20 +- .../snapshots/test-fluxAggregatorKit.js.snap | Bin 812 -> 764 bytes .../test/price/test-fluxAggregatorKit.js | 16 +- .../test/psm/snapshots/test-psm.js.md | 56 +--- .../test/psm/snapshots/test-psm.js.snap | Bin 1312 -> 1245 bytes packages/inter-protocol/test/psm/test-psm.js | 16 +- .../test/reserve/snapshots/test-reserve.js.md | 32 +- .../reserve/snapshots/test-reserve.js.snap | Bin 957 -> 913 bytes .../snapshots/test-vaultFactory.js.md | 280 +++++------------- .../snapshots/test-vaultFactory.js.snap | Bin 4086 -> 3897 bytes packages/internal/src/storage-test-utils.js | 67 ++++- .../internal/test/test-storage-test-utils.js | 38 ++- packages/notifier/src/storesub.js | 1 + packages/smart-wallet/src/marshal-contexts.js | 4 +- .../test/test-marshal-contexts.js | 38 +-- packages/vats/src/lib-board.js | 8 +- packages/vats/test/bootstrapTests/supports.js | 44 +-- .../bootstrapTests/test-vaults-integration.js | 25 +- packages/vats/test/test-board-utils.js | 34 +-- packages/vats/test/test-lib-board.js | 10 +- packages/vats/tools/board-utils.js | 99 ++----- packages/wallet/api/src/lib-dehydrate.js | 1 + packages/wallet/api/src/lib-wallet.js | 15 +- .../wallet/api/test/test-getPursesNotifier.js | 4 +- .../wallet/api/test/test-lib-dehydrate.js | 20 +- packages/wallet/api/test/test-lib-wallet.js | 14 +- packages/wallet/api/test/test-middleware.js | 12 +- .../contracts/test-priceAggregator.js | 13 +- 39 files changed, 409 insertions(+), 622 deletions(-) diff --git a/packages/agoric-cli/src/lib/wallet.js b/packages/agoric-cli/src/lib/wallet.js index 5a45afe4f021..98257dbc1385 100644 --- a/packages/agoric-cli/src/lib/wallet.js +++ b/packages/agoric-cli/src/lib/wallet.js @@ -37,7 +37,7 @@ export const getLastUpdate = (addr, { readLatestHead }) => { * @param {Pick} [stdout] */ export const outputAction = (bridgeAction, stdout = process.stdout) => { - const capData = marshaller.serialize(bridgeAction); + const capData = marshaller.serialize(harden(bridgeAction)); stdout.write(JSON.stringify(capData)); stdout.write('\n'); }; diff --git a/packages/agoric-cli/test/test-inter-cli.js b/packages/agoric-cli/test/test-inter-cli.js index 7f86058f50c6..2a432b9f92cb 100644 --- a/packages/agoric-cli/test/test-inter-cli.js +++ b/packages/agoric-cli/test/test-inter-cli.js @@ -82,7 +82,7 @@ const makeNet = published => { const ctx = makeFromBoard(); const m = boardSlottingMarshaller(ctx.convertSlotToVal); const fmt = obj => { - const capData = m.serialize(obj); + const capData = m.serialize(harden(obj)); const values = [JSON.stringify(capData)]; const specimen = { blockHeight: undefined, values }; const txt = JSON.stringify({ @@ -583,10 +583,10 @@ test('README ex1: inter bid place by-price: printed offer is correct', async t = const txt = out.join('').trim(); const obj = net.marshaller.unserialize(JSON.parse(txt)); - obj.offer.result = 'Your bid has been accepted'; // pretend we processed it + const offer = { ...obj.offer, result: 'Your bid has been accepted' }; // pretend we processed it const assets = Object.values(agoricNames.vbankAsset); - const bidInfo = fmtBid(obj.offer, assets); + const bidInfo = fmtBid(offer, assets); t.deepEqual(bidInfo, expected); }); diff --git a/packages/cache/src/store.js b/packages/cache/src/store.js index d1aa03fd9dfc..81e7d7e73110 100644 --- a/packages/cache/src/store.js +++ b/packages/cache/src/store.js @@ -24,7 +24,9 @@ const makeKeyToString = (sanitize = obj => obj) => { return slot; }; - const { serialize: keyToJsonable } = makeMarshal(valToSlot); + const { serialize: keyToJsonable } = makeMarshal(valToSlot, undefined, { + serializeBodyFormat: 'smallcaps', + }); const keyToString = async keyP => { const key = await sanitize(keyP); const obj = keyToJsonable(key); diff --git a/packages/cache/test/test-storage.js b/packages/cache/test/test-storage.js index 271ab7ac1dd8..1d410ef623d9 100644 --- a/packages/cache/test/test-storage.js +++ b/packages/cache/test/test-storage.js @@ -19,6 +19,7 @@ const makeSimpleMarshaller = () => { }; const toVal = slot => vals[slot]; return makeMarshal(fromVal, toVal, { + serializeBodyFormat: 'smallcaps', marshalSaveError: err => { throw err; }, @@ -47,8 +48,8 @@ test('makeChainStorageCoordinator with non-remote values', async t => { t.is(await cache('brandName', 'barbosa'), 'barbosa'); t.deepEqual(Object.keys(storageNodeState), ['cache']); t.deepEqual(JSON.parse(storageNodeState.cache), { - '{"body":"\\"brandName\\"","slots":[]}': { - body: '{"generation":{"@qclass":"bigint","digits":"1"},"value":"barbosa"}', + '{"body":"#\\"brandName\\"","slots":[]}': { + body: '#{"generation":"+1","value":"barbosa"}', slots: [], }, }); @@ -56,12 +57,12 @@ test('makeChainStorageCoordinator with non-remote values', async t => { // One-time initialization (of 'frotz') t.is(await cache('frotz', 'default'), 'default'); const afterFirstFrotz = { - '{"body":"\\"brandName\\"","slots":[]}': { - body: '{"generation":{"@qclass":"bigint","digits":"1"},"value":"barbosa"}', + '{"body":"#\\"brandName\\"","slots":[]}': { + body: '#{"generation":"+1","value":"barbosa"}', slots: [], }, - '{"body":"\\"frotz\\"","slots":[]}': { - body: '{"generation":{"@qclass":"bigint","digits":"1"},"value":"default"}', + '{"body":"#\\"frotz\\"","slots":[]}': { + body: '#{"generation":"+1","value":"default"}', slots: [], }, }; @@ -83,8 +84,8 @@ test('makeChainStorageCoordinator with non-remote values', async t => { ); t.deepEqual(JSON.parse(storageNodeState.cache), { ...afterFirstFrotz, - '{"body":"[\\"complex\\",\\"passable\\"]","slots":[]}': { - body: '{"generation":{"@qclass":"bigint","digits":"1"},"value":{"arr":["hi","there"],"big":{"@qclass":"bigint","digits":"1"},"num":53,"str":"string"}}', + '{"body":"#[\\"complex\\",\\"passable\\"]","slots":[]}': { + body: '#{"generation":"+1","value":{"arr":["hi","there"],"big":"+1","num":53,"str":"string"}}', slots: [], }, }); @@ -98,8 +99,8 @@ test('makeChainStorageCoordinator with remote values', async t => { t.is(await cache('brand', farThing), farThing); t.deepEqual(Object.keys(storageNodeState), ['cache']); t.deepEqual(JSON.parse(storageNodeState.cache), { - '{"body":"\\"brand\\"","slots":[]}': { - body: '{"generation":{"@qclass":"bigint","digits":"1"},"value":{"@qclass":"slot","iface":"Alleged: farThing","index":0}}', + '{"body":"#\\"brand\\"","slots":[]}': { + body: '#{"generation":"+1","value":"$0.Alleged: farThing"}', slots: [0], }, }); @@ -114,8 +115,8 @@ test('makeChainStorageCoordinator with updater', async t => { t.is(await cache('counter', increment), 1); t.deepEqual(Object.keys(storageNodeState), ['cache']); t.deepEqual(JSON.parse(storageNodeState.cache), { - '{"body":"\\"counter\\"","slots":[]}': { - body: '{"generation":{"@qclass":"bigint","digits":"1"},"value":1}', + '{"body":"#\\"counter\\"","slots":[]}': { + body: '#{"generation":"+1","value":1}', slots: [], }, }); @@ -124,8 +125,8 @@ test('makeChainStorageCoordinator with updater', async t => { t.is(await cache('counter', increment), 1); t.deepEqual(Object.keys(storageNodeState), ['cache']); t.deepEqual(JSON.parse(storageNodeState.cache), { - '{"body":"\\"counter\\"","slots":[]}': { - body: '{"generation":{"@qclass":"bigint","digits":"1"},"value":1}', + '{"body":"#\\"counter\\"","slots":[]}': { + body: '#{"generation":"+1","value":1}', slots: [], }, }); @@ -134,8 +135,8 @@ test('makeChainStorageCoordinator with updater', async t => { t.is(await cache('counter', increment, M.any()), 2); t.deepEqual(Object.keys(storageNodeState), ['cache']); t.deepEqual(JSON.parse(storageNodeState.cache), { - '{"body":"\\"counter\\"","slots":[]}': { - body: '{"generation":{"@qclass":"bigint","digits":"2"},"value":2}', + '{"body":"#\\"counter\\"","slots":[]}': { + body: '#{"generation":"+2","value":2}', slots: [], }, }); @@ -155,8 +156,8 @@ test('makeChainStorageCoordinator with remote updater', async t => { t.is(await cache('counter', counterObj.increment), 1); t.deepEqual(Object.keys(storageNodeState), ['cache']); t.deepEqual(JSON.parse(storageNodeState.cache), { - '{"body":"\\"counter\\"","slots":[]}': { - body: '{"generation":{"@qclass":"bigint","digits":"1"},"value":1}', + '{"body":"#\\"counter\\"","slots":[]}': { + body: '#{"generation":"+1","value":1}', slots: [], }, }); @@ -166,8 +167,8 @@ test('makeChainStorageCoordinator with remote updater', async t => { t.is(counter, 1); t.deepEqual(Object.keys(storageNodeState), ['cache']); t.deepEqual(JSON.parse(storageNodeState.cache), { - '{"body":"\\"counter\\"","slots":[]}': { - body: '{"generation":{"@qclass":"bigint","digits":"1"},"value":1}', + '{"body":"#\\"counter\\"","slots":[]}': { + body: '#{"generation":"+1","value":1}', slots: [], }, }); @@ -177,8 +178,8 @@ test('makeChainStorageCoordinator with remote updater', async t => { t.is(counter, 2); t.deepEqual(Object.keys(storageNodeState), ['cache']); t.deepEqual(JSON.parse(storageNodeState.cache), { - '{"body":"\\"counter\\"","slots":[]}': { - body: '{"generation":{"@qclass":"bigint","digits":"2"},"value":2}', + '{"body":"#\\"counter\\"","slots":[]}': { + body: '#{"generation":"+2","value":2}', slots: [], }, }); diff --git a/packages/casting/src/defaults.js b/packages/casting/src/defaults.js index 112142d3472a..d237e1da7a76 100644 --- a/packages/casting/src/defaults.js +++ b/packages/casting/src/defaults.js @@ -114,6 +114,8 @@ export const MAKE_DEFAULT_UNSERIALIZER = () => { return obj; }; return Far('marshal unserializer', { - unserialize: makeMarshal(undefined, slotToVal).unserialize, + unserialize: makeMarshal(undefined, slotToVal, { + serializeBodyFormat: 'smallcaps', + }).unserialize, }); }; diff --git a/packages/cosmic-swingset/src/chain-main.js b/packages/cosmic-swingset/src/chain-main.js index 07996360b132..dd02c7b7e51c 100644 --- a/packages/cosmic-swingset/src/chain-main.js +++ b/packages/cosmic-swingset/src/chain-main.js @@ -341,7 +341,9 @@ export default async function main(progname, args, { env, homedir, agcc }) { STORAGE_PATH.BUNDLES, { sequence: true }, ); - const marshaller = makeMarshal(); + const marshaller = makeMarshal(undefined, undefined, { + serializeBodyFormat: 'smallcaps', + }); const publish = makeSerializeToStorage( installationStorageNode, marshaller, diff --git a/packages/governance/test/unitTests/snapshots/test-committee.js.md b/packages/governance/test/unitTests/snapshots/test-committee.js.md index bcc773b27862..f2768c96489b 100644 --- a/packages/governance/test/unitTests/snapshots/test-committee.js.md +++ b/packages/governance/test/unitTests/snapshots/test-committee.js.md @@ -16,9 +16,7 @@ Generated by [AVA](https://avajs.dev). 'published.committees.Economic_Committee.latestOutcome', { outcome: 'fail', - question: { - iface: 'Alleged: QuestionHandle', - }, + question: Object @Alleged: QuestionHandle {}, reason: 'No quorum', }, ], @@ -27,13 +25,9 @@ Generated by [AVA](https://avajs.dev). { closingRule: { deadline: 1n, - timer: { - iface: 'Alleged: ManualTimer', - }, - }, - counterInstance: { - iface: 'Alleged: InstanceHandle', + timer: Object @Alleged: ManualTimer {}, }, + counterInstance: Object @Alleged: InstanceHandle {}, electionType: 'survey', issue: { text: 'why3', @@ -49,9 +43,7 @@ Generated by [AVA](https://avajs.dev). text: 'why not?', }, ], - questionHandle: { - iface: 'Alleged: QuestionHandle', - }, + questionHandle: Object @Alleged: QuestionHandle {}, quorumRule: 'majority', tieOutcome: { text: 'why not?', diff --git a/packages/governance/test/unitTests/snapshots/test-committee.js.snap b/packages/governance/test/unitTests/snapshots/test-committee.js.snap index 29dc9b684dbe72c5adab70ec3151e15d6c76785b..3602eab55cf63d3f25e563503a7b9f2beac29121 100644 GIT binary patch literal 887 zcmV--1Bm=VRzV*_pC43$30sUi}k1sPW)U@BRgzyvd2XcTOIRZ@28)-6Di0U-r$+oA15%?c3M) zn=PT7ZT;#47;Xe~=N#qx+!%mMssoblgrE&qa>3_aaOdbAHy+iJb+o4pPYk^8y3DZk z@~Ul(SkaGEh{osFeruPK;8 zHI_qzZV)H>i1ZUZj&}ZRv@1tS$Gk5|B0%vwnm-VSi1ETvZRO?G7C463sW~w*5xLjr z(d7nW9q|J33GoeafUs=Kx`4QYXd&L(Il6L|Zp?~GX+%{eMUAL_^~t)p$+)n{I3b^r zEZME>q{U=zmjhs0$z(bDL}7!eAoSx{wU}prfT!jNrMYZB4F$x9cJbd~!%?9$5Im6D zFzG-pRviS^fjO-8I%&FP9R|D5i@B~2aFw{$VVZ!;?=$I&p)C6fMiQk_e72JNj{7%Ip>r82jM6sSy+sZ@c-OomLX zMOh`~|G@~wa<2S(Uj4!>x-|1{V?!#PnB+(c92ROgLpdN=3q<$enRFqbGwE2JnWM%7 zI&r$gAqv)i N`4{xHQ_YJ9005V;vrzy5 literal 915 zcmV;E18n?3RzVu*xM*rSy<&4*xO19(Z+Z8-1Tlwh2X)%&Brs(`?)jE zSGqz4Yv#cVu-pph&ju=vxwQaCR1YM*n}D%g$@!S~A)KWf+(y($)-#c^JRjisFl3h9 zuI(>-H81^E;|2f10VmI-3k?Nw>Y?F|D)M~Xn((p*hBa?(&92~@vP5()z@eGGA z5nmjQ(uf8Uw~_E2u}DUV)jt?(BBSK(A!RS!KG93Mf}04!P7%H7oC}mxF%Oo{1++@Oa?+3E?2K70aNMQ^w8FHp3U=2hGNXiK?c(l6;@()M`>;c zaXuN6YFG|M|JF(A1MYpSI9*V2o2vW+DWvLo2dWOW=2A=ZNLg@c6JxXlDfeAkW6bbV zq`W}9sg8cslg`&)dW(ejuHN*R*{?|X=F(bYUGG-0%2!xWq<8P45U>j2JiX==W-7Y| zMvm4)V4>i0aGEp;IhIvrpFh-nmdBuXWI1p8A z`MVw*UE3v{51i$_)+ia$Ch`BcxVQMvGH8 zl2)_8b{b142YnXz1W+`oHYgV&F}RDbO=ipl5yXtDm28&2-?Z>2UDD8LG;W|M6GqXl pVmhQT( { maxChoices: 1, maxWinners: 1, closingRule: { - timer: buildManualTimer(t.log), + timer: harden(buildManualTimer(t.log)), deadline: 2n, }, quorumRule: QuorumRule.MAJORITY, @@ -102,13 +103,9 @@ test('committee-open question:one', async t => { { closingRule: { deadline: 2n, - timer: { - iface: 'Alleged: ManualTimer', - }, - }, - counterInstance: { - iface: 'Alleged: InstanceHandle', + timer: Far('ManualTimer'), }, + counterInstance: Far('InstanceHandle'), electionType: 'survey', issue: { text: 'why', @@ -124,9 +121,7 @@ test('committee-open question:one', async t => { text: 'why not?', }, ], - questionHandle: { - iface: 'Alleged: QuestionHandle', - }, + questionHandle: Far('QuestionHandle'), quorumRule: 'majority', tieOutcome: { text: 'why not?', diff --git a/packages/inter-protocol/test/auction/snapshots/test-auctionContract.js.md b/packages/inter-protocol/test/auction/snapshots/test-auctionContract.js.md index af1821265f8b..f2aab85afd4d 100644 --- a/packages/inter-protocol/test/auction/snapshots/test-auctionContract.js.md +++ b/packages/inter-protocol/test/auction/snapshots/test-auctionContract.js.md @@ -16,44 +16,32 @@ Generated by [AVA](https://avajs.dev). 'published.auction.book0', { collateralAvailable: { - brand: { - iface: 'Alleged: Collateral brand', - }, + brand: Object @Alleged: Collateral brand {}, value: 0n, }, currentPriceLevel: { denominator: { - brand: { - iface: 'Alleged: Collateral brand', - }, + brand: Object @Alleged: Collateral brand {}, value: 10000000000000n, }, numerator: { - brand: { - iface: 'Alleged: Bid brand', - }, + brand: Object @Alleged: Bid brand {}, value: 9350000000000n, }, }, proceedsRaised: undefined, remainingProceedsGoal: null, startCollateral: { - brand: { - iface: 'Alleged: Collateral brand', - }, + brand: Object @Alleged: Collateral brand {}, value: 100n, }, startPrice: { denominator: { - brand: { - iface: 'Alleged: Collateral brand', - }, + brand: Object @Alleged: Collateral brand {}, value: 1000000000n, }, numerator: { - brand: { - iface: 'Alleged: Bid brand', - }, + brand: Object @Alleged: Bid brand {}, value: 1100000000n, }, }, @@ -68,18 +56,14 @@ Generated by [AVA](https://avajs.dev). type: 'relativeTime', value: { relValue: 10n, - timerBrand: { - iface: 'Alleged: timerBrand', - }, + timerBrand: Object @Alleged: timerBrand {}, }, }, ClockStep: { type: 'relativeTime', value: { relValue: 5n, - timerBrand: { - iface: 'Alleged: timerBrand', - }, + timerBrand: Object @Alleged: timerBrand {}, }, }, DiscountStep: { @@ -89,21 +73,13 @@ Generated by [AVA](https://avajs.dev). Electorate: { type: 'invitation', value: { - brand: { - iface: 'Alleged: Zoe Invitation brand', - }, + brand: Object @Alleged: Zoe Invitation brand {}, value: [ { description: 'questionPoser', - handle: { - iface: 'Alleged: InvitationHandle', - }, - installation: { - iface: 'Alleged: BundleInstallation', - }, - instance: { - iface: 'Alleged: InstanceHandle', - }, + handle: Object @Alleged: InvitationHandle {}, + installation: Object @Alleged: BundleInstallation {}, + instance: Object @Alleged: InstanceHandle {}, }, ], }, @@ -116,18 +92,14 @@ Generated by [AVA](https://avajs.dev). type: 'relativeTime', value: { relValue: 3n, - timerBrand: { - iface: 'Alleged: timerBrand', - }, + timerBrand: Object @Alleged: timerBrand {}, }, }, StartFrequency: { type: 'relativeTime', value: { relValue: 40n, - timerBrand: { - iface: 'Alleged: timerBrand', - }, + timerBrand: Object @Alleged: timerBrand {}, }, }, StartingRate: { @@ -142,21 +114,15 @@ Generated by [AVA](https://avajs.dev). { activeStartTime: { absValue: 170n, - timerBrand: { - iface: 'Alleged: timerBrand', - }, + timerBrand: Object @Alleged: timerBrand {}, }, nextDescendingStepTime: { absValue: 180n, - timerBrand: { - iface: 'Alleged: timerBrand', - }, + timerBrand: Object @Alleged: timerBrand {}, }, nextStartTime: { absValue: 210n, - timerBrand: { - iface: 'Alleged: timerBrand', - }, + timerBrand: Object @Alleged: timerBrand {}, }, }, ], diff --git a/packages/inter-protocol/test/auction/snapshots/test-auctionContract.js.snap b/packages/inter-protocol/test/auction/snapshots/test-auctionContract.js.snap index ff3d24e68e8c1614a86aa3ae6c74ee8f4e938035..d8afa2126835b5dd5fa1baa1c7491bf15788c46e 100644 GIT binary patch literal 1639 zcmV-t2AKIlRzVF{3T?$ zt*#rd%^!;h00000000BEm|JWVRUF3W?4{jqyEn?UTpS7rD3+E>PzVW23qmBJwy01O z;OxwyJ2E@7%*<}f113gIyd_><5RDgnF)@Y*BhjddiJEAliP3n8mumFM7vaGd(M0^_ z%5$wB%L=(02i-pwujj8P2e!DQq9BGhHG%w_%zRA`h;6C}gC2cE_Pp4uDL zQJ$Cjnr-!6uy{W-H84QM*xxRekP{0GDyOfE&z!Wl2d| z32jRXeU}isIz9&ED0E6h+al<*KwgB-i)cE6egou3=#q$bifG5&Hc9G%wnGP@I`j;5 z4*DFr*e1BhN1|o0Ky^b~+68oV1RVu(2s$OAYa{3xATL25is(&|2@vC*@^gEuQ<5c% z8mcgYl69)r82?4O2l=2I=(PL&yEMKdTCXkNH2P}pJADj2xD$hSV0npNcZKz}f{ zK2z+|SB{SG;G+PL<=O7G*ohi56ffh8!L)ce zW}xTZYqn79rfA8&JKVrq`njK;bRbCw)6?4N_0H9Kxz+Qs>wkIKFyMK)<4Rsq;o*(q zMMYg_<4oNuw^mA-kE`b7xD(FqfsC{;Ioc!h=s6%SLvM*_DvaVk$qjx5_#1RtH0X-> zUfn54w?IRk0-A}SIuHwbUPKEK^bH_yL%)dV+z9$7kbj_MT>?5kg6;sa8>)!tf(ZH$ zkjJ3cMD&IT`WcWfp}$2`j-YcglC%&iW(2f1f*u5N7`k6XR|;sjbPaZXf9SM^ShIWA zG(Y{N=WJ$H3s)8kSKu$;=+)Ud%G|bb^R#}xn4O>Wz;sT&ovo7r(MgC-I47&jUyUp* ztM0+UKMPC+qjAUA;w6=xy_Yz)i5Y8_*l<;KN^3@4bEvy>@LV=tms~b&zP9!~d=1$| zy*zq7>XaPl=ap(Ko#DyYQ6O{t$+%Nf0}Hsvc%FpNn#LfBWs({-^^H()6qH#BJ^M_}S`yUOk$!mbeujE7+Qr$DJ@7)oDMFq0d zHyAUSyw{)Uq4PxSX=HFZAAVl%2|cfS{=Lt1^F6}ZeB;eT@+(|@4|NvsfD9E2!uVJm z86OFud2sf%6-%qS+4&DwI279MjbYPf-ii3$LaVXb%BVx*(e{bJ{VvVG)1rHHYOZPT z2TD`Cl)o7e+G|Ph?A2EJMPi~S=^fBH5c0(2tA$2vI1EUh~Z`8N5HyOV*2E# zcNr?PHMDIG;DKojZ@q(gPHcXYtnk}mgu5d(DWyj=5gJ3XMOvAD8oZFW^Psa>=BYDp#mw lmr++7&fQpbJZ&SgEw4F(Txhk*sC8s*-@nlDz`Yg|001q~D%$`6 literal 1693 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 { const { toTS } = aggregator; // mockStorage doesn't preserve identity of the timerBrand, so build // an equivalent shape for the t.deepEqual comparison - const mockBrand = { iface: 'Alleged: timerBrand' }; + const mockBrand = Far('timerBrand'); const toMockTS = val => ({ absValue: val, timerBrand: mockBrand }); const { oracle: oracleA } = await E(aggregator.creator).initOracle( @@ -631,12 +631,12 @@ test('notifications', async t => { 'mockChainStorageRoot.priceAggregator.LINK-USD_price_feed', ), { - amountIn: { brand: { iface: 'Alleged: $LINK brand' }, value: 1n }, + amountIn: { brand: Far('$LINK brand'), value: 1n }, amountOut: { - brand: { iface: 'Alleged: $USD brand' }, + brand: Far('$USD brand'), value: 150n, // AVG(100, 200) }, - timer: { iface: 'Alleged: ManualTimer' }, + timer: Far('ManualTimer'), timestamp: toMockTS(1n), }, ); @@ -678,12 +678,12 @@ test('notifications', async t => { 'mockChainStorageRoot.priceAggregator.LINK-USD_price_feed', ), { - amountIn: { brand: { iface: 'Alleged: $LINK brand' }, value: 1n }, + amountIn: { brand: Far('$LINK brand'), value: 1n }, amountOut: { - brand: { iface: 'Alleged: $USD brand' }, + brand: Far('$USD brand'), value: 1000n, // AVG(1000, 1000) }, - timer: { iface: 'Alleged: ManualTimer' }, + timer: Far('ManualTimer'), timestamp: toMockTS(1n), }, ); diff --git a/packages/inter-protocol/test/psm/snapshots/test-psm.js.md b/packages/inter-protocol/test/psm/snapshots/test-psm.js.md index a03f54499654..a582588a4fa7 100644 --- a/packages/inter-protocol/test/psm/snapshots/test-psm.js.md +++ b/packages/inter-protocol/test/psm/snapshots/test-psm.js.md @@ -19,21 +19,13 @@ Generated by [AVA](https://avajs.dev). Electorate: { type: 'invitation', value: { - brand: { - iface: 'Alleged: Zoe Invitation brand', - }, + brand: Object @Alleged: Zoe Invitation brand {}, value: [ { description: 'questionPoser', - handle: { - iface: 'Alleged: InvitationHandle', - }, - installation: { - iface: 'Alleged: BundleInstallation', - }, - instance: { - iface: 'Alleged: InstanceHandle', - }, + handle: Object @Alleged: InvitationHandle {}, + installation: Object @Alleged: BundleInstallation {}, + instance: Object @Alleged: InstanceHandle {}, }, ], }, @@ -42,15 +34,11 @@ Generated by [AVA](https://avajs.dev). type: 'ratio', value: { denominator: { - brand: { - iface: 'Alleged: IST brand', - }, + brand: Object @Alleged: IST brand {}, value: 10000n, }, numerator: { - brand: { - iface: 'Alleged: IST brand', - }, + brand: Object @Alleged: IST brand {}, value: 3n, }, }, @@ -58,9 +46,7 @@ Generated by [AVA](https://avajs.dev). MintLimit: { type: 'amount', value: { - brand: { - iface: 'Alleged: IST brand', - }, + brand: Object @Alleged: IST brand {}, value: 20000000000000n, }, }, @@ -68,15 +54,11 @@ Generated by [AVA](https://avajs.dev). type: 'ratio', value: { denominator: { - brand: { - iface: 'Alleged: IST brand', - }, + brand: Object @Alleged: IST brand {}, value: 10000n, }, numerator: { - brand: { - iface: 'Alleged: IST brand', - }, + brand: Object @Alleged: IST brand {}, value: 1n, }, }, @@ -88,33 +70,23 @@ Generated by [AVA](https://avajs.dev). 'published.psm.IST.AUSD.metrics', { anchorPoolBalance: { - brand: { - iface: 'Alleged: aUSD brand', - }, + brand: Object @Alleged: aUSD brand {}, value: 100030000n, }, feePoolBalance: { - brand: { - iface: 'Alleged: IST brand', - }, + brand: Object @Alleged: IST brand {}, value: 50000n, }, mintedPoolBalance: { - brand: { - iface: 'Alleged: IST brand', - }, + brand: Object @Alleged: IST brand {}, value: 100030000n, }, totalAnchorProvided: { - brand: { - iface: 'Alleged: aUSD brand', - }, + brand: Object @Alleged: aUSD brand {}, value: 99970000n, }, totalMintedProvided: { - brand: { - iface: 'Alleged: IST brand', - }, + brand: Object @Alleged: IST brand {}, value: 200000000n, }, }, diff --git a/packages/inter-protocol/test/psm/snapshots/test-psm.js.snap b/packages/inter-protocol/test/psm/snapshots/test-psm.js.snap index bfd0d98bcc1cb947b947191534cfc6e93fc15d14..4f178d9410cac3c6514c2d37020f973dfcbbff04 100644 GIT binary patch literal 1245 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 { 'totalMintedProvided', ]); t.like(driver.getStorageChildBody('metrics'), { - anchorPoolBalance: { brand: { iface: 'Alleged: aUSD brand' }, value: 0n }, - feePoolBalance: { brand: { iface: 'Alleged: IST brand' }, value: 0n }, + anchorPoolBalance: { brand: Far('aUSD brand'), value: 0n }, + feePoolBalance: { brand: Far('IST brand'), value: 0n }, mintedPoolBalance: { - brand: { iface: 'Alleged: IST brand' }, + brand: Far('IST brand'), value: 0n, }, totalAnchorProvided: { - brand: { iface: 'Alleged: aUSD brand' }, + brand: Far('aUSD brand'), value: 0n, }, totalMintedProvided: { - brand: { iface: 'Alleged: IST brand' }, + brand: Far('IST brand'), value: 0n, }, }); @@ -589,7 +589,7 @@ test('metrics, with snapshot', async t => { }, feePoolBalance: { value: 20_000n }, mintedPoolBalance: { - brand: { iface: 'Alleged: IST brand' }, + brand: Far('IST brand'), value: giveAnchor.value, }, totalAnchorProvided: { @@ -608,7 +608,7 @@ test('metrics, with snapshot', async t => { }, feePoolBalance: { value: 20_000n }, mintedPoolBalance: { - brand: { iface: 'Alleged: IST brand' }, + brand: Far('IST brand'), value: giveAnchor.value, }, totalAnchorProvided: { @@ -634,7 +634,7 @@ test('metrics, with snapshot', async t => { }, feePoolBalance: { value: 50_000n }, mintedPoolBalance: { - brand: { iface: 'Alleged: IST brand' }, + brand: Far('IST brand'), value: giveAnchor.value - giveMinted.value + fee, }, totalAnchorProvided: { diff --git a/packages/inter-protocol/test/reserve/snapshots/test-reserve.js.md b/packages/inter-protocol/test/reserve/snapshots/test-reserve.js.md index 3ca385413076..174c67bcd806 100644 --- a/packages/inter-protocol/test/reserve/snapshots/test-reserve.js.md +++ b/packages/inter-protocol/test/reserve/snapshots/test-reserve.js.md @@ -19,21 +19,13 @@ Generated by [AVA](https://avajs.dev). Electorate: { type: 'invitation', value: { - brand: { - iface: 'Alleged: Zoe Invitation brand', - }, + brand: Object @Alleged: Zoe Invitation brand {}, value: [ { description: 'questionPoser', - handle: { - iface: 'Alleged: InvitationHandle', - }, - installation: { - iface: 'Alleged: BundleInstallation', - }, - instance: { - iface: 'Alleged: InstanceHandle', - }, + handle: Object @Alleged: InvitationHandle {}, + installation: Object @Alleged: BundleInstallation {}, + instance: Object @Alleged: InstanceHandle {}, }, ], }, @@ -46,28 +38,20 @@ Generated by [AVA](https://avajs.dev). { allocations: { Fee: { - brand: { - iface: 'Alleged: IST brand', - }, + brand: Object @Alleged: IST brand {}, value: 0n, }, }, shortfallBalance: { - brand: { - iface: 'Alleged: IST brand', - }, + brand: Object @Alleged: IST brand {}, value: 1000n, }, totalFeeBurned: { - brand: { - iface: 'Alleged: IST brand', - }, + brand: Object @Alleged: IST brand {}, value: 1000n, }, totalFeeMinted: { - brand: { - iface: 'Alleged: IST brand', - }, + brand: Object @Alleged: IST brand {}, value: 0n, }, }, diff --git a/packages/inter-protocol/test/reserve/snapshots/test-reserve.js.snap b/packages/inter-protocol/test/reserve/snapshots/test-reserve.js.snap index 9dbb077943640e78884fdc951cedd06b85d77791..bf928be02c6326845cb24a1df3b3b4cb1bcac59b 100644 GIT binary patch literal 913 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 H^+Nyv&xd%G literal 4086 zcmVRD>y_wDZ>snOJyD`{#Kr#G=s%&w$$pLx2qPqi{;a}%h z^x$OoX1~$u4hH1*U@JCu7hvP_$%eeD@zo?LO)~#E(j9{?!*>g$LYtv$pq; z^fdG$^cr*+dJoETqz-nZ)atst)#2za<>cf@4wvM}mK>Q5$8kFdN`(%5JFMb7v*@&n z8_i;pRlMCSCR@d4%wmdFeBUglTE!eEifLA{#w?~=McFK7SjFvT(Pb6CV-_>5;@f61 z%iPhVj3i0QfohW?lXpfW4{#BH<?3ce5EBd9zj67*4UHh?;4I{^c1uL10bju3DQ1wRJxPiRJJ1n9g* zqT~V)i=bVE+`-6uKs*Q?Bjg@No&<3Unv@nv-Y3z{>NI;u9=`I;&X_XTF{VA#qWXia zim#+Y4ynch*=uO}){^yFhoT4MfLF2lR#lqasiV0l?!`sd*&6IFlv z9Wk7i_Sl<*t zWtQJv1Maun^85UN`>nA2o)~bym6qQz83XON%JO?r48Nm#^DA`x&My0Ohx2h1pUmjG zq@*rw*`Rn0M^d<$o11H5$*%zUHFOfncM<$tiaVzRnM<+rC^l#nkY?z5=xfj~piiLl zG9_sxv=zD&Is_etq%29AkVV|irEZg!0$B;^6kW{r>lPq;pr@c$DEuay{oBbb|^$DyB4 zekbQY2J=s7NDkuU5dIFvCpCdt1$9vNe!^y3oo|rCu^(i2zfJRobX^G;R&|n9{X3z0 zh*xWC*FpiG;%f{Timn6=i*e@mFj@Ytn&q;gShZ7x+I$sTgGRUY%66Ycg_F57x0htH zt?FGu^-$HnDddN}Kd^qjrkaj*S+%eq3s{bT)hR9TmSdi(;p#xBt=4P!JCuv9Gu+yq zY>p2juzX;PdK`(YM9=~e)dIoV7EL!4Uvzjlf#;)vRYk<=hDvdj5{ zg`o=rrkWEWGkC;Na#6S;sde)>ZE8bL+=CfF!T&X z=QH#VK>iGULeYAL4j(Q_qoB&+1YN+;RY01d4vH>h=x!i)K~GS$fuX+vau_;6(ME>m z7D`e9G^LQB7cg`YkmZn{q8Bps1|T;<4^eap8?^oi$nz9i#XOz_ata!94nZ$t=wu*e zP$NZK7^(u%p&KZ=j-dyDd>uMOQI(-bfE?M@8F&dRM)wm~+PNLG%+5bpfBI_1iJ6BW{`La!c@HSq<4YOh=X@t-Q3XqO`Qsa^xBROv4U#CJ=mAe74D+ zs?r+4d|v{g?@hq=p#*Fnjbe+=Ex>3}S+Dq}yRAuX8)Vi?=HnBQ3TG+jPgnco-uzY8uMjbw` zkDVPC_gU)det4;lvz};%*T8NUe5INfesdy-;mfnw{u1>5I5Of(VIv*acuC5Is>c&F zm7z<3tbjICbPz)y0P+a*3Pp4Hm>0-VDC0bW4q@mNAmz|%ismtN6Ohf&O%xr;(C2{s z6#5fIM>BNr1W6hOl}sS$7`~_tWF@3hw1^ELcL2GKV&j;{7l6DBeMHe>h9(uGJ)ud( z1f9gtc|aPV4HTWq&`u!RphqZL&d_Ipyaatf(Q1Z%1|(%7=I9d%I)gRTY#?rWEThkb{{i3=DEEBgZw+f150GkT z1x1@#Xbnh+VqWI)VIYq|FHm$nL;nKgLnv($LH#Uv5s+evwee{vkY=cZqMI1H2grWt zX^I9}@ZSJAOtH<(W9DQ@8VnUrCg>FmZ2+qV9m?xoMQubCJ{}`APl-tdH7L}mo zp*bal-N)I>!T6v~%HGA}?+0^$a`$rXbue#I?g1X#H5Df&G+`=XAL0{rI?+rvwzh|d zz)6^^#va2|l~&aBj3#>bo+m58(@5&fHPJo2W@E07PmOE%HPjw!l7E7~<8k}*OslFc^OSo$71fm$ zrPFH4t3-b%vK$@NC7yCr9AA=(klqneKPlg(oe^p6*GD-AAiE@%8YE z>T+>aQLyH#ib5q>7d*`d>spLVDCM_erJP!JW>WT)#djsB6f?fGEJnuHo}r8nCX(?R zu`-s*&rHUX$`k1pP319CzWNNM{AheBM>g{7u`>SbOf_=F>91-wRm5uIih)izu_F`T zekOS;J=U1@b=bWL9j_qMl{7ZIO{Gr6| zPsBI~qo<&sQ{#y&o#Q}GnphP4f%DbOS@52671cEk!r7;Qt2lUyA8`k#L$Ml|vUyBdEd9jX*X*H&8Uh zf8>7+>zg&)$1MEwsy^qVO=@959_tDthe`N{oPpB@AhS#H7AC3 z^_*ULo??SbQVLKfhf?G6rIh` zyMa6a{eYs&`Md?ld(bJ0u3$ZMbRBZ7BiO~v<0U}WKvz@r64p;I7d~5sPhI%DvgZ>s zAMxv!h_5?)zWO{>M}|+;)tch&d54v)VBb|Iymk}sxgYlOi$uJHuVaq-MD9;%@mY62 zAF_?J6AGOdw{@jpT^OG=dsl08eD)Emh~pF5?@4hsxkohiB)Hqvai(4wVLdB8>nPJb z-)ZOH-)ByXVQSJ=~={=ENf9$It0CuA1!=6AXy_p$Vk>#m zfXsn9p?%Qzs7V34?45tQpSTGB3hsYLLo~5#b^1a{8nlpLD;YK$NFBv4Wy>4Mq-ima z4SIEdyLrv*-j=;7gbAhje!3Ryns9~|3NI{IeH`;{fRw} zbThJ78px5(y;{J~Q7&<epBx*ZbmwCd%Gg*%@a%h4~#L`Lhecc01QyU{r~^~ diff --git a/packages/internal/src/storage-test-utils.js b/packages/internal/src/storage-test-utils.js index 79b89e4a9220..9d662dcddc24 100644 --- a/packages/internal/src/storage-test-utils.js +++ b/packages/internal/src/storage-test-utils.js @@ -1,11 +1,67 @@ // @ts-check import { Far } from '@endo/far'; -import { makeMarshal } from '@endo/marshal'; +import { makeMarshal, Remotable } from '@endo/marshal'; import { makeChainStorageRoot } from './lib-chainStorage.js'; import { bindAllMethods } from './method-tools.js'; const { Fail, quote: q } = assert; +/** + * A convertSlotToVal function that produces basic Remotables + * + * @param {string} _slotId + * @param {string} iface + */ +export const slotToRemotable = (_slotId, iface = 'Alleged: Remotable') => + Remotable(iface); + +/** + * A basic marshaller whose unserializer produces Remotables. It can + * only serialize plain data, not Remotables. + */ +export const defaultMarshaller = makeMarshal(undefined, slotToRemotable, { + serializeBodyFormat: 'smallcaps', +}); + +/** + * A deserializer which produces slot strings instead of Remotables, + * so if `a = Far('iface')`, and serializing `{ a }` into `capData` + * assigned it slot `board123`, then `slotStringUnserialize(capData)` + * would produce `{ a: 'board123' }`. + * + * This may be useful for display purposes. + * + * One limitation: serialized data that contains a particular unusual + * string will be unserialized into a BigInt. + */ +const makeSlotStringUnserialize = () => { + /** @type { (slot: string, iface: string) => any } */ + const identitySlotToValFn = (slot, _) => Far('unk', { toJSON: () => slot }); + const { fromCapData } = makeMarshal(undefined, identitySlotToValFn); + /** @type { (capData: any) => any } */ + const unserialize = capData => + JSON.parse( + JSON.stringify(fromCapData(capData), (_, val) => { + if (typeof val === 'bigint') { + // JSON cannot accept BigInts. This unusual string is a + // cheap alternative to a proper Hilbert Hotel. + return `@encromulate:${val}`; + } else { + return val; + } + }), + (_key, val) => { + if (typeof val === 'string' && val.startsWith('@encromulate')) { + return BigInt(val.split(':')[1]); + } else { + return val; + } + }, + ); + return harden(unserialize); +}; +export const slotStringUnserialize = makeSlotStringUnserialize(); + /** * For testing, creates a chainStorage root node over an in-memory map * and exposes both the map and the sequence of received messages. @@ -74,17 +130,10 @@ harden(makeFakeStorageKit); export const makeMockChainStorageRoot = () => { const { rootNode, data } = makeFakeStorageKit('mockChainStorageRoot'); - - const defaultMarshaller = makeMarshal(undefined, (_slotId, iface) => ({ - iface, - })); - return Far('mockChainStorage', { ...bindAllMethods(rootNode), /** - * Defaults to deserializing pass-by-presence objects into { iface } representations. - * Note that this is **not** a null transformation; capdata `@qclass` and `index` properties - * are dropped and `iface` is _added_ for repeat references. + * Defaults to deserializing pass-by-presence objects into Remotable objects. * * @param {string} path * @param {import('./lib-chainStorage.js').Marshaller} marshaller diff --git a/packages/internal/test/test-storage-test-utils.js b/packages/internal/test/test-storage-test-utils.js index a6ce2e44e369..e4cbd200739c 100644 --- a/packages/internal/test/test-storage-test-utils.js +++ b/packages/internal/test/test-storage-test-utils.js @@ -1,8 +1,14 @@ // @ts-check import test from 'ava'; -import '@endo/init'; +import '@endo/init/debug.js'; +import { Far } from '@endo/far'; +import { makeMarshal } from '@endo/marshal'; -import { makeFakeStorageKit } from '../src/storage-test-utils.js'; +import { + defaultMarshaller, + makeFakeStorageKit, + slotStringUnserialize, +} from '../src/storage-test-utils.js'; test('makeFakeStorageKit', async t => { const rootPath = 'root'; @@ -237,3 +243,31 @@ test('makeFakeStorageKit sequence data', async t => { 'manual-sequence grandchild setValue message', ); }); + +test('marshallers', t => { + // create capdata with specific slots + /** @typedef { { getBoardId: () => string } } SlottedRemotable */ + const foo = Far('foo'); + const foo1 = Far('foo', { getBoardId: () => 'board1' }); + const foo2 = Far('foo', { getBoardId: () => 'board2' }); + const bar = Far('bar'); + const bar1 = Far('bar', { getBoardId: () => 'board1' }); + /** @type {(val: SlottedRemotable) => string} */ + const convertValToSlot = val => val.getBoardId(); + const m = makeMarshal(convertValToSlot, undefined, { + serializeBodyFormat: 'smallcaps', + }); + const foo1CD = m.toCapData(harden({ o: foo1 })); + const foo2CD = m.toCapData(harden({ o: foo2 })); + const bar1CD = m.toCapData(harden({ o: bar1 })); + + // the default marshaller will produce Remotables with a matching + // iface, and t.deepEqual knows how to compare them + t.deepEqual(defaultMarshaller.fromCapData(foo1CD), { o: foo }); + t.notDeepEqual(defaultMarshaller.fromCapData(foo1CD), { o: bar }); + + // the display unserializer reports the slot values, but not ifaces + t.deepEqual(slotStringUnserialize(foo1CD), { o: 'board1' }); + t.deepEqual(slotStringUnserialize(foo2CD), { o: 'board2' }); + t.deepEqual(slotStringUnserialize(bar1CD), { o: 'board1' }); +}); diff --git a/packages/notifier/src/storesub.js b/packages/notifier/src/storesub.js index 3878a9b498c7..38c845b394af 100644 --- a/packages/notifier/src/storesub.js +++ b/packages/notifier/src/storesub.js @@ -94,6 +94,7 @@ export const makeStoredSubscription = ( storageNode, marshaller = makeMarshal(undefined, undefined, { marshalSaveError: () => {}, + serializeBodyFormat: 'smallcaps', }), ) => { /** @type {Unserializer} */ diff --git a/packages/smart-wallet/src/marshal-contexts.js b/packages/smart-wallet/src/marshal-contexts.js index 20aed2ba41d6..de5b3b94160c 100644 --- a/packages/smart-wallet/src/marshal-contexts.js +++ b/packages/smart-wallet/src/marshal-contexts.js @@ -224,7 +224,7 @@ export const makeExportContext = () => { } initSlotVal(boardObjects, id, val); }, - ...makeMarshal(valToSlot, slotToVal), + ...makeMarshal(valToSlot, slotToVal, { serializeBodyFormat: 'smallcaps' }), }); }; /** @typedef {ReturnType} ExportContext */ @@ -323,9 +323,11 @@ export const makeImportContext = (makePresence = defaultMakePresence) => { const marshal = { fromBoard: makeMarshal(valToSlot.fromBoard, slotToVal.fromBoard, { marshalName: 'fromBoard', + serializeBodyFormat: 'smallcaps', }), fromMyWallet: makeMarshal(valToSlot.fromMyWallet, slotToVal.fromMyWallet, { marshalName: 'fromMyWallet', + serializeBodyFormat: 'smallcaps', }), }; diff --git a/packages/smart-wallet/test/test-marshal-contexts.js b/packages/smart-wallet/test/test-marshal-contexts.js index fc774bf6a99c..08fc4ae3d450 100644 --- a/packages/smart-wallet/test/test-marshal-contexts.js +++ b/packages/smart-wallet/test/test-marshal-contexts.js @@ -59,9 +59,7 @@ test('makeImportContext preserves identity across AMM and wallet', t => { const ammMetricsCapData = amm.getMetrics(); t.deepEqual(ammMetricsCapData, { - body: JSON.stringify([ - { '@qclass': 'slot', iface: 'Alleged: ATOM brand', index: 0 }, - ]), + body: '#["$0.Alleged: ATOM brand"]', slots: ['board011'], }); @@ -75,16 +73,16 @@ test('makeImportContext preserves identity across AMM and wallet', t => { myWallet.suggestIssuer('ATOM', 'board011'); const walletCapData = myWallet.publish(); t.deepEqual(walletCapData, { - body: JSON.stringify([ + body: `#${JSON.stringify([ { - brand: { '@qclass': 'slot', iface: 'Alleged: ATOM brand', index: 0 }, + brand: '$0.Alleged: ATOM brand', brandPetname: 'ATOM', - currentAmount: { brand: { '@qclass': 'slot', index: 0 }, value: 100 }, + currentAmount: { brand: '$0', value: 100 }, meta: { id: 0 }, - purse: { '@qclass': 'slot', iface: 'Alleged: ATOM purse', index: 1 }, + purse: '$1.Alleged: ATOM purse', pursePetname: 'ATOM purse', }, - ]), + ])}`, slots: ['board011', 'purse:ATOM'], }); @@ -129,22 +127,14 @@ test('makeExportContext.serialize handles unregistered identities', t => { const actual = context.serialize(invitationAmount); t.deepEqual(actual, { - body: JSON.stringify({ - brand: { - '@qclass': 'slot', - iface: 'Alleged: Zoe invitation brand', - index: 0, - }, + body: `#${JSON.stringify({ + brand: '$0.Alleged: Zoe invitation brand', value: [ { - instance: { - '@qclass': 'slot', - iface: 'Alleged: amm instance', - index: 1, - }, + instance: '$1.Alleged: amm instance', }, ], - }), + })}`, slots: ['board01', 'unknown:1'], }); @@ -156,11 +146,7 @@ test('makeExportContext.serialize handles unregistered identities', t => { context.savePaymentActions(myPayment); const cap2 = context.serialize(myPayment); t.deepEqual(cap2, { - body: JSON.stringify({ - '@qclass': 'slot', - iface: 'Alleged: payment', - index: 0, - }), + body: '#"$0.Alleged: payment"', slots: ['payment:1'], }); t.deepEqual(context.unserialize(cap2), myPayment); @@ -178,7 +164,7 @@ test('fromBoard.serialize requires board ids', t => { context.ensureBoardId('board0123', unpassable.instance); t.deepEqual(context.fromBoard.serialize(unpassable), { - body: '{"instance":{"@qclass":"slot","iface":"Alleged: InstanceHandle","index":0}}', + body: '#{"instance":"$0.Alleged: InstanceHandle"}', slots: ['board0123'], }); }); diff --git a/packages/vats/src/lib-board.js b/packages/vats/src/lib-board.js index a7445ad1c10d..baab9c1eb437 100644 --- a/packages/vats/src/lib-board.js +++ b/packages/vats/src/lib-board.js @@ -218,7 +218,9 @@ const makeReadonlyMarshaller = state => { // Published value. return valToId.get(val); }; - return makeMarshal(valToSlot, slotToVal); + return makeMarshal(valToSlot, slotToVal, { + serializeBodyFormat: 'smallcaps', + }); }; /** @@ -229,7 +231,9 @@ const makePublishingMarshaller = state => { const slotToVal = makeSlotToVal(state); // Always put the value in the board. const valToSlot = val => getId(val, state); - return makeMarshal(valToSlot, slotToVal); + return makeMarshal(valToSlot, slotToVal, { + serializeBodyFormat: 'smallcaps', + }); }; //#endregion diff --git a/packages/vats/test/bootstrapTests/supports.js b/packages/vats/test/bootstrapTests/supports.js index e6a1076d1bfa..017563e44401 100644 --- a/packages/vats/test/bootstrapTests/supports.js +++ b/packages/vats/test/bootstrapTests/supports.js @@ -1,14 +1,17 @@ // @ts-check /* eslint-disable import/no-extraneous-dependencies */ import { Fail } from '@agoric/assert'; +import { Far } from '@endo/far'; import { buildSwingset } from '@agoric/cosmic-swingset/src/launch-chain.js'; import { BridgeId, VBankAccount } from '@agoric/internal'; -import { makeFakeStorageKit } from '@agoric/internal/src/storage-test-utils.js'; +import { + makeFakeStorageKit, + slotToRemotable, +} from '@agoric/internal/src/storage-test-utils.js'; import { initSwingStore } from '@agoric/swing-store'; import { kunser } from '@agoric/swingset-liveslots/test/kmarshal.js'; import { loadSwingsetConfigFile } from '@agoric/swingset-vat'; import { E } from '@endo/eventual-send'; -import { makeMarshal } from '@endo/marshal'; import { makeQueue } from '@endo/stream'; import { promises as fs } from 'fs'; import { resolve as importMetaResolve } from 'import-meta-resolve'; @@ -185,7 +188,7 @@ export const makeWalletFactoryDriver = async ( 'namesByAddressAdmin', ); - const marshaller = boardSlottingMarshaller(); + const marshaller = boardSlottingMarshaller(slotToRemotable); /** * @param {string} walletAddress @@ -197,10 +200,12 @@ export const makeWalletFactoryDriver = async ( * @returns {Promise} */ executeOffer(offer) { - const offerCapData = marshaller.serialize({ - method: 'executeOffer', - offer, - }); + const offerCapData = marshaller.serialize( + harden({ + method: 'executeOffer', + offer, + }), + ); return EV(walletPresence).handleBridgeAction(offerCapData, true); }, /** @@ -208,18 +213,22 @@ export const makeWalletFactoryDriver = async ( * @returns {Promise} */ sendOffer(offer) { - const offerCapData = marshaller.serialize({ - method: 'executeOffer', - offer, - }); + const offerCapData = marshaller.serialize( + harden({ + method: 'executeOffer', + offer, + }), + ); return EV.sendOnly(walletPresence).handleBridgeAction(offerCapData, true); }, tryExitOffer(offerId) { - const capData = marshaller.serialize({ - method: 'tryExitOffer', - offerId, - }); + const capData = marshaller.serialize( + harden({ + method: 'tryExitOffer', + offerId, + }), + ); return EV(walletPresence).handleBridgeAction(capData, true); }, /** @@ -250,7 +259,7 @@ export const makeWalletFactoryDriver = async ( getLatestUpdateRecord() { const key = `published.wallet.${walletAddress}`; const lastWalletStatus = JSON.parse(storage.data.get(key)?.at(-1)); - return JSON.parse(lastWalletStatus.body); + return marshaller.unserialize(lastWalletStatus); }, }); @@ -332,7 +341,8 @@ export const makeSwingsetTestKit = async ( const storage = makeFakeStorageKit('bootstrapTests'); - const marshal = makeMarshal(); + const slotToVal = (_slotId, iface = 'Remotable') => Far(iface); + const marshal = boardSlottingMarshaller(slotToVal); const readLatest = path => { const str = storage.data.get(path)?.at(-1); diff --git a/packages/vats/test/bootstrapTests/test-vaults-integration.js b/packages/vats/test/bootstrapTests/test-vaults-integration.js index e0c3bdf87b77..3591c6e0686e 100644 --- a/packages/vats/test/bootstrapTests/test-vaults-integration.js +++ b/packages/vats/test/bootstrapTests/test-vaults-integration.js @@ -5,9 +5,13 @@ import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js'; import { Fail } from '@agoric/assert'; +import { makeMarshal } from '@endo/marshal'; import { Offers } from '@agoric/inter-protocol/src/clientSupport.js'; import { eventLoopIteration } from '@agoric/internal/src/testing-utils.js'; -import { makeAgoricNamesRemotesFromFakeStorage } from '../../tools/board-utils.js'; +import { + makeAgoricNamesRemotesFromFakeStorage, + slotToBoardRemote, +} from '../../tools/board-utils.js'; import { makeSwingsetTestKit, makeWalletFactoryDriver } from './supports.js'; /** @@ -20,14 +24,10 @@ const collateralBrandKey = 'ATOM'; const likePayouts = (collateral, minted) => ({ Collateral: { - value: { - digits: String(collateral * 1_000_000), - }, + value: BigInt(collateral * 1_000_000), }, Minted: { - value: { - digits: String(minted * 1_000_000), - }, + value: BigInt(minted * 1_000_000), }, }); @@ -65,7 +65,9 @@ const makeDefaultTestContext = async t => { test.before(async t => { t.context = await makeDefaultTestContext(t); }); -test.after.always(t => t.context.shutdown()); +test.after.always(t => { + t.context.shutdown && t.context.shutdown(); +}); test('metrics path', async t => { const { EV } = t.context.runUtils; @@ -290,7 +292,7 @@ test('exit bid', async t => { numWantsSatisfied: 1, // trivially 1 because there were no "wants" in the proposal payouts: { // got back the give - Bid: { value: { digits: '100000' } }, + Bid: { value: 100000n }, }, }, }); @@ -352,8 +354,9 @@ test('propose change to auction governance param', async t => { const key = `published.committees.Economic_Committee.latestQuestion`; const capData = JSON.parse(storage.data.get(key)?.at(-1)); - const lastQuestion = JSON.parse(capData.body); + const { unserialize } = makeMarshal(undefined, slotToBoardRemote); + const lastQuestion = unserialize(capData); const changes = lastQuestion?.issue?.spec?.changes; t.log('check Economic_Committee.latestQuestion against proposal'); - t.like(changes, { StartFrequency: { relValue: { digits: '300' } } }); + t.like(changes, { StartFrequency: { relValue: 300n } }); }); diff --git a/packages/vats/test/test-board-utils.js b/packages/vats/test/test-board-utils.js index 602cd33c0d6e..db3dca9a43c6 100644 --- a/packages/vats/test/test-board-utils.js +++ b/packages/vats/test/test-board-utils.js @@ -3,6 +3,7 @@ import { test } from '@agoric/swingset-vat/tools/prepare-test-env-ava.js'; import { makeFakeStorageKit } from '@agoric/internal/src/storage-test-utils.js'; import { chainStorageEntries } from '@agoric/inter-protocol/test/psm/psm-storage-fixture.js'; +import { Far } from '@endo/far'; import { boardSlottingMarshaller, @@ -164,44 +165,39 @@ test('makeAgoricNamesRemotesFromFakeStorage', t => { // XXX ses-ava can't test.macro const serialize = (t, specimen, expected) => { const marshaller = boardSlottingMarshaller(); - const actual = marshaller.serialize(specimen); + const actual = marshaller.serialize(harden(specimen)); t.deepEqual(actual, expected); }; -test('undefined', serialize, undefined, { body: undefined, slots: [] }); -test('null', serialize, undefined, { body: undefined, slots: [] }); -test('empty string', serialize, '', { body: '""', slots: [] }); -test('bigint', serialize, 123n, { - body: '{"@qclass":"bigint","digits":"123"}', - slots: [], -}); +test('undefined', serialize, undefined, { body: '#"#undefined"', slots: [] }); +test('null', serialize, undefined, { body: '#"#undefined"', slots: [] }); +test('empty string', serialize, '', { body: '#""', slots: [] }); +test('bigint', serialize, 123n, { body: '#"+123"', slots: [] }); test( 'record', serialize, { a: 1, b: 'str' }, { - body: '{"a":1,"b":"str"}', + body: '#{"a":1,"b":"str"}', slots: [], }, ); -test( - 'board ids', - serialize, - { getBoardId: () => 'board123' }, - { body: '{"@qclass":"slot","index":0}', slots: ['board123'] }, -); +test('board ids', serialize, Far('iface', { getBoardId: () => 'board123' }), { + body: '#"$0.Alleged: iface"', + slots: ['board123'], +}); test( 'nested board ids', serialize, { - istBrand: { getBoardId: () => 'board123Ist' }, - atomBrand: { getBoardId: () => 'board123Atom' }, + istBrand: Far('iface', { getBoardId: () => 'board123Ist' }), + atomBrand: Far('iface', { getBoardId: () => 'board123Atom' }), }, { - body: '{"istBrand":{"@qclass":"slot","index":0},"atomBrand":{"@qclass":"slot","index":1}}', - slots: ['board123Ist', 'board123Atom'], + body: '#{"atomBrand":"$0.Alleged: iface","istBrand":"$1.Alleged: iface"}', + slots: ['board123Atom', 'board123Ist'], }, ); diff --git a/packages/vats/test/test-lib-board.js b/packages/vats/test/test-lib-board.js index 88f85f5795ad..ba44bc3cce5e 100644 --- a/packages/vats/test/test-lib-board.js +++ b/packages/vats/test/test-lib-board.js @@ -70,13 +70,13 @@ const testBoardMarshaller = async (t, board, marshaller, publishing) => { unpublished2: unpublished, }), ); - const pub1ser = `{"@qclass":"slot","iface":"Alleged: published","index":0}`; - const pub2ser = `{"@qclass":"slot","index":0}`; - const unpub1ser = `{"@qclass":"slot","iface":"Alleged: unpublished","index":1}`; - const unpub2ser = `{"@qclass":"slot","index":1}`; + const pub1ser = `"$0.Alleged: published"`; + const pub2ser = `"$0"`; + const unpub1ser = `"$1.Alleged: unpublished"`; + const unpub2ser = `"$1"`; t.is( ser.body, - `{"published1":${pub1ser},"published2":${pub2ser},"unpublished1":${unpub1ser},"unpublished2":${unpub2ser}}`, + `#{"published1":${pub1ser},"published2":${pub2ser},"unpublished1":${unpub1ser},"unpublished2":${unpub2ser}}`, ); t.is(ser.slots.length, 2); t.is(ser.slots[0], published1id); diff --git a/packages/vats/tools/board-utils.js b/packages/vats/tools/board-utils.js index caec3177901e..5dc400034e25 100644 --- a/packages/vats/tools/board-utils.js +++ b/packages/vats/tools/board-utils.js @@ -24,7 +24,8 @@ */ import { Fail } from '@agoric/assert'; -import { Far, isObject } from '@endo/marshal'; +import { Far } from '@endo/far'; +import { isObject, makeMarshal } from '@endo/marshal'; /** * @param {*} slotInfo @@ -36,6 +37,16 @@ export const makeBoardRemote = ({ boardId, iface }) => { return Far(`BoardRemote${nonalleged}`, { getBoardId: () => boardId }); }; +export const slotToBoardRemote = (boardId, iface) => + makeBoardRemote({ boardId, iface }); + +export const boardValToSlot = val => { + if ('getBoardId' in val) { + return val.getBoardId(); + } + Fail`unknown obj in boardSlottingMarshaller.valToSlot ${val}`; +}; + /** * @param {import("@agoric/internal/src/storage-test-utils.js").FakeStorageKit} fakeStorageKit * @returns {AgoricNamesRemotes} @@ -43,6 +54,10 @@ export const makeBoardRemote = ({ boardId, iface }) => { export const makeAgoricNamesRemotesFromFakeStorage = fakeStorageKit => { const { data } = fakeStorageKit; + // this produces Remotables that can roundtrip through a + // boardValToSlot-using marshaller + + const { unserialize } = makeMarshal(undefined, slotToBoardRemote); const reverse = {}; // TODO support vbankAsset which must recur const entries = ['brand', 'instance'].map(kind => { @@ -53,13 +68,7 @@ export const makeAgoricNamesRemotesFromFakeStorage = fakeStorageKit => { /** @type {import("@endo/marshal").CapData} */ const latestCapData = JSON.parse(values.at(-1)); /** @type {Array<[string, import('@agoric/vats/tools/board-utils.js').BoardRemote]>} */ - const parts = JSON.parse(latestCapData.body).map(([name, slotInfo]) => [ - name, - makeBoardRemote({ - boardId: latestCapData.slots[slotInfo.index], - iface: slotInfo.iface, - }), - ]); + const parts = unserialize(latestCapData); for (const [name, remote] of parts) { reverse[remote.getBoardId()] = name; } @@ -70,74 +79,18 @@ export const makeAgoricNamesRemotesFromFakeStorage = fakeStorageKit => { harden(makeAgoricNamesRemotesFromFakeStorage); /** - * Like makeMarshal but, - * - slotToVal takes an iface arg - * - if a part being serialized has getBoardId(), it passes through as a slot value whereas the normal marshaller would treat it as a copyRecord + * A marshaller which can serialize getBoardId() -bearing + * Remotables. This allows the caller to pick their slots. The + * deserializer is configurable: the default cannot handle + * Remotable-bearing data. * - * @param {(slot: string, iface: string) => any} slotToVal + * @param {(slot: string, iface: string) => any} [slotToVal] * @returns {import('@endo/marshal').Marshal} */ -export const boardSlottingMarshaller = (slotToVal = (s, _i) => s) => { - const marshaller = { - /** @param {{body: string, slots: string[]}} capData */ - unserialize: ({ body, slots }) => { - const reviver = (_key, obj) => { - const qclass = - obj !== null && typeof obj === 'object' && obj['@qclass']; - // NOTE: hilbert hotel not impl - switch (qclass) { - case 'slot': { - const { index, iface } = obj; - return slotToVal(slots[index], iface); - } - case 'bigint': - return BigInt(obj.digits); - case 'undefined': - return undefined; - default: - return obj; - } - }; - return JSON.parse(body, reviver); - }, - serialize: whole => { - /** @type {Map} */ - const seen = new Map(); - /** @type {(boardId: string) => number} */ - const slotIndex = boardId => { - let index = seen.get(boardId); - if (index === undefined) { - index = seen.size; - seen.set(boardId, index); - } - return index; - }; - const recur = part => { - if (part === null) return null; - if (typeof part === 'bigint') { - return { '@qclass': 'bigint', digits: `${part}` }; - } - if (Array.isArray(part)) { - return part.map(recur); - } - if (typeof part === 'object') { - if ('getBoardId' in part) { - const index = slotIndex(part.getBoardId()); - return { '@qclass': 'slot', index }; - } - return Object.fromEntries( - Object.entries(part).map(([k, v]) => [k, recur(v)]), - ); - } - return part; - }; - const after = recur(whole); - return { body: JSON.stringify(after), slots: [...seen.keys()] }; - }, - toCapData: whole => marshaller.serialize(whole), - fromCapData: capData => marshaller.unserialize(capData), - }; - return marshaller; +export const boardSlottingMarshaller = (slotToVal = undefined) => { + return makeMarshal(boardValToSlot, slotToVal, { + serializeBodyFormat: 'smallcaps', + }); }; /** diff --git a/packages/wallet/api/src/lib-dehydrate.js b/packages/wallet/api/src/lib-dehydrate.js index 03506bb08da7..528d632773be 100644 --- a/packages/wallet/api/src/lib-dehydrate.js +++ b/packages/wallet/api/src/lib-dehydrate.js @@ -341,6 +341,7 @@ export const makeDehydrator = (initialUnnamedCount = 0) => { // TODO Temporary hack. // See https://github.com/Agoric/agoric-sdk/issues/2780 errorIdNum: 30000, + serializeBodyFormat: 'smallcaps', }, ); return harden({ diff --git a/packages/wallet/api/src/lib-wallet.js b/packages/wallet/api/src/lib-wallet.js index 49cbecf8455b..9325a0e17e7a 100644 --- a/packages/wallet/api/src/lib-wallet.js +++ b/packages/wallet/api/src/lib-wallet.js @@ -14,6 +14,7 @@ import { assert, q, Fail } from '@agoric/assert'; import { makeScalarStoreCoordinator } from '@agoric/cache'; import { objectMap, WalletName } from '@agoric/internal'; +import { slotStringUnserialize } from '@agoric/internal/src/storage-test-utils.js'; import { makeLegacyMap, makeScalarMapStore, @@ -23,7 +24,7 @@ import { makeScalarBigMapStore } from '@agoric/vat-data'; import { AmountMath } from '@agoric/ertp'; import { E } from '@endo/eventual-send'; -import { makeMarshal, passStyleOf, Far, mapIterable } from '@endo/marshal'; +import { passStyleOf, Far, mapIterable } from '@endo/marshal'; import { Nat } from '@endo/nat'; import { makeNotifierFromSubscriber, @@ -228,18 +229,10 @@ export function makeWalletRoot({ return getSortedValues(inboxState); } - const noOp = () => {}; - const identitySlotToValFn = (slot, _) => slot; - // Instead of { body, slots }, fill the slots. This is useful for // display but not for data processing, since the special identifier // @qclass is lost. - const { unserialize: fillInSlots } = makeMarshal(noOp, identitySlotToValFn, { - marshalName: 'wallet', - // TODO Temporary hack. - // See https://github.com/Agoric/agoric-sdk/issues/2780 - errorIdNum: 40000, - }); + const fillInSlots = slotStringUnserialize; /** @type {NotifierRecord} */ const { notifier: offersNotifier, updater: offersUpdater } = makeNotifierKit( @@ -1802,7 +1795,7 @@ export function makeWalletRoot({ const firstPathToLookup = createRootLookups(); - /** @type {ReturnType} */ + /** @type {ReturnType} */ const marshaller = harden({ fromCapData: context.fromCapData, toCapData: context.toCapData, diff --git a/packages/wallet/api/test/test-getPursesNotifier.js b/packages/wallet/api/test/test-getPursesNotifier.js index dde0e5fea030..bb84e98c1617 100644 --- a/packages/wallet/api/test/test-getPursesNotifier.js +++ b/packages/wallet/api/test/test-getPursesNotifier.js @@ -66,7 +66,7 @@ test('getPursesNotifier', async t => { value: 0n, }); t.deepEqual(moolaPurseInfo.currentAmountSlots, { - body: '{"brand":{"@qclass":"slot","iface":"Alleged: moola brand","index":0},"value":{"@qclass":"bigint","digits":"0"}}', + body: '#{"brand":"$0.Alleged: moola brand","value":"+0"}', slots: [ { kind: 'brand', @@ -100,7 +100,7 @@ test('getAttenuatedPursesNotifier', async t => { value: 0n, }); t.deepEqual(moolaPurseInfo.currentAmountSlots, { - body: '{"brand":{"@qclass":"slot","iface":"Alleged: moola brand","index":0},"value":{"@qclass":"bigint","digits":"0"}}', + body: '#{"brand":"$0.Alleged: moola brand","value":"+0"}', slots: [ { kind: 'brand', diff --git a/packages/wallet/api/test/test-lib-dehydrate.js b/packages/wallet/api/test/test-lib-dehydrate.js index d0740d244b4c..0b1ffa5a4483 100644 --- a/packages/wallet/api/test/test-lib-dehydrate.js +++ b/packages/wallet/api/test/test-lib-dehydrate.js @@ -111,7 +111,7 @@ test('makeDehydrator', async t => { t.deepEqual( dehydrate(harden({ handle: handle1 })), { - body: '{"handle":{"@qclass":"slot","iface":"Alleged: handle1","index":0}}', + body: '#{"handle":"$0.Alleged: handle1"}', slots: [{ kind: 'instanceHandle', petname: 'simpleExchange' }], }, `serialize val with petname`, @@ -119,7 +119,7 @@ test('makeDehydrator', async t => { t.deepEqual( hydrate( harden({ - body: '{"handle":{"@qclass":"slot","index":0}}', + body: '#{"handle":"$0"}', slots: [ { kind: 'instanceHandle', @@ -134,7 +134,7 @@ test('makeDehydrator', async t => { t.deepEqual( dehydrate(harden({ brand: brand1, value: 40 })), harden({ - body: '{"brand":{"@qclass":"slot","iface":"Alleged: mock brand","index":0},"value":40}', + body: '#{"brand":"$0.Alleged: mock brand","value":40}', slots: [{ kind: 'brand', petname: 'moola' }], }), `serialize brand with petname`, @@ -142,7 +142,7 @@ test('makeDehydrator', async t => { t.deepEqual( hydrate( harden({ - body: '{"brand":{"@qclass":"slot","index":0},"value":40}', + body: '#{"brand":"$0","value":40}', slots: [{ kind: 'brand', petname: 'moola' }], }), ), @@ -167,7 +167,7 @@ test('makeDehydrator', async t => { t.deepEqual( dehydrate(proposal), { - body: '{"exit":{"afterDeadline":{"deadline":55,"timer":{"@qclass":"slot","iface":"Alleged: timer","index":0}}},"give":{"Price":{"brand":{"@qclass":"slot","iface":"Alleged: mock brand","index":1},"value":3}},"want":{"Asset1":{"brand":{"@qclass":"slot","iface":"Alleged: mock brand","index":2},"value":60},"Asset2":{"brand":{"@qclass":"slot","iface":"Alleged: mock brand","index":3},"value":{"instanceHandle":{"@qclass":"slot","iface":"Alleged: handle3","index":4}}}}}', + body: '#{"exit":{"afterDeadline":{"deadline":55,"timer":"$0.Alleged: timer"}},"give":{"Price":{"brand":"$1.Alleged: mock brand","value":3}},"want":{"Asset1":{"brand":"$2.Alleged: mock brand","value":60},"Asset2":{"brand":"$3.Alleged: mock brand","value":{"instanceHandle":"$4.Alleged: handle3"}}}}', slots: [ { kind: 'unnamed', petname: 'unnamed-1' }, { kind: 'brand', petname: 'simolean' }, @@ -181,7 +181,7 @@ test('makeDehydrator', async t => { t.deepEqual( hydrate( harden({ - body: '{"want":{"Asset1":{"brand":{"@qclass":"slot","index":0},"value":60},"Asset2":{"brand":{"@qclass":"slot","index":1},"value":{"instanceHandle":{"@qclass":"slot","index":2}}}},"give":{"Price":{"brand":{"@qclass":"slot","index":3},"value":3}},"exit":{"afterDeadline":{"timer":{"@qclass":"slot","index":4},"deadline":55}}}', + body: '#{"want":{"Asset1":{"brand":"$0","value":60},"Asset2":{"brand":"$1","value":{"instanceHandle":"$2"}}},"give":{"Price":{"brand":"$3","value":3}},"exit":{"afterDeadline":{"timer":"$4","deadline":55}}}', slots: [ { kind: 'brand', petname: 'moola' }, { kind: 'brand', petname: 'zoeInvite' }, @@ -198,7 +198,7 @@ test('makeDehydrator', async t => { t.deepEqual( dehydrate(harden({ handle: handle4 })), { - body: '{"handle":{"@qclass":"slot","iface":"Alleged: handle4","index":0}}', + body: '#{"handle":"$0.Alleged: handle4"}', slots: [{ kind: 'unnamed', petname: 'unnamed-2' }], }, `serialize val with no petname`, @@ -206,7 +206,7 @@ test('makeDehydrator', async t => { t.deepEqual( hydrate( harden({ - body: '{"handle":{"@qclass":"slot","index":0}}', + body: '#{"handle":"$0"}', slots: [{ kind: 'unnamed', petname: 'unnamed-2' }], }), ), @@ -218,7 +218,7 @@ test('makeDehydrator', async t => { t.deepEqual( dehydrate(harden({ handle: handle4 })), { - body: '{"handle":{"@qclass":"slot","iface":"Alleged: handle4","index":0}}', + body: '#{"handle":"$0.Alleged: handle4"}', slots: [{ kind: 'instanceHandle', petname: 'autoswap' }], }, `serialize val with new petname`, @@ -226,7 +226,7 @@ test('makeDehydrator', async t => { t.deepEqual( hydrate( harden({ - body: '{"handle":{"@qclass":"slot","index":0}}', + body: '#{"handle":"$0"}', slots: [{ kind: 'instanceHandle', petname: 'autoswap' }], }), ), diff --git a/packages/wallet/api/test/test-lib-wallet.js b/packages/wallet/api/test/test-lib-wallet.js index 2b316e56e1d9..2ad5c16395ad 100644 --- a/packages/wallet/api/test/test-lib-wallet.js +++ b/packages/wallet/api/test/test-lib-wallet.js @@ -320,7 +320,7 @@ test('lib-wallet issuer and purse methods', async t => { pursePetname: 'Default Zoe invite purse', value: [], currentAmountSlots: { - body: '{"brand":{"@qclass":"slot","iface":"Alleged: Zoe Invitation brand","index":0},"value":[]}', + body: '#{"brand":"$0.Alleged: Zoe Invitation brand","value":[]}', slots: [{ kind: 'brand', petname: 'zoe invite' }], }, currentAmount: { @@ -342,7 +342,7 @@ test('lib-wallet issuer and purse methods', async t => { pursePetname: 'fun money', value: 100, currentAmountSlots: { - body: '{"brand":{"@qclass":"slot","iface":"Alleged: moola brand","index":0},"value":{"@qclass":"bigint","digits":"100"}}', + body: '#{"brand":"$0.Alleged: moola brand","value":"+100"}', slots: [{ kind: 'brand', petname: 'moola' }], }, currentAmount: { @@ -472,7 +472,7 @@ test('lib-wallet dapp suggests issuer, instance, installation petnames', async t }, ], currentAmountSlots: { - body: '{"brand":{"@qclass":"slot","iface":"Alleged: Zoe Invitation brand","index":0},"value":[{"description":"getRefund","handle":{"@qclass":"slot","iface":"Alleged: InvitationHandle","index":1},"installation":{"@qclass":"slot","iface":"Alleged: BundleInstallation","index":2},"instance":{"@qclass":"slot","iface":"Alleged: InstanceHandle","index":3}}]}', + body: '#{"brand":"$0.Alleged: Zoe Invitation brand","value":[{"description":"getRefund","handle":"$1.Alleged: InvitationHandle","installation":"$2.Alleged: BundleInstallation","instance":"$3.Alleged: InstanceHandle"}]}', slots: [ { kind: 'brand', petname: 'zoe invite' }, { kind: 'unnamed', petname: 'unnamed-4' }, @@ -559,7 +559,7 @@ test('lib-wallet dapp suggests issuer, instance, installation petnames', async t }, ], currentAmountSlots: { - body: '{"brand":{"@qclass":"slot","iface":"Alleged: Zoe Invitation brand","index":0},"value":[{"description":"getRefund","handle":{"@qclass":"slot","iface":"Alleged: InvitationHandle","index":1},"installation":{"@qclass":"slot","iface":"Alleged: BundleInstallation","index":2},"instance":{"@qclass":"slot","iface":"Alleged: InstanceHandle","index":3}}]}', + body: '#{"brand":"$0.Alleged: Zoe Invitation brand","value":[{"description":"getRefund","handle":"$1.Alleged: InvitationHandle","installation":"$2.Alleged: BundleInstallation","instance":"$3.Alleged: InstanceHandle"}]}', slots: [ { kind: 'brand', petname: 'zoe invite' }, { kind: 'unnamed', petname: 'unnamed-4' }, @@ -698,7 +698,7 @@ test('lib-wallet dapp suggests issuer, instance, installation petnames', async t }, ], currentAmountSlots: { - body: '{"brand":{"@qclass":"slot","iface":"Alleged: Zoe Invitation brand","index":0},"value":[{"description":"getRefund","handle":{"@qclass":"slot","iface":"Alleged: InvitationHandle","index":1},"installation":{"@qclass":"slot","iface":"Alleged: BundleInstallation","index":2},"instance":{"@qclass":"slot","iface":"Alleged: InstanceHandle","index":3}}]}', + body: '#{"brand":"$0.Alleged: Zoe Invitation brand","value":[{"description":"getRefund","handle":"$1.Alleged: InvitationHandle","installation":"$2.Alleged: BundleInstallation","instance":"$3.Alleged: InstanceHandle"}]}', slots: [ { kind: 'brand', petname: 'zoe invite' }, { kind: 'unnamed', petname: 'unnamed-4' }, @@ -905,7 +905,7 @@ test('lib-wallet offer methods', async t => { pursePetname: 'Default Zoe invite purse', value: [], currentAmountSlots: { - body: '{"brand":{"@qclass":"slot","iface":"Alleged: Zoe Invitation brand","index":0},"value":[]}', + body: '#{"brand":"$0.Alleged: Zoe Invitation brand","value":[]}', slots: [{ kind: 'brand', petname: 'zoe invite' }], }, currentAmount: { @@ -928,7 +928,7 @@ test('lib-wallet offer methods', async t => { depositBoardId: 'board0371', value: 100, currentAmountSlots: { - body: '{"brand":{"@qclass":"slot","iface":"Alleged: moola brand","index":0},"value":{"@qclass":"bigint","digits":"100"}}', + body: '#{"brand":"$0.Alleged: moola brand","value":"+100"}', slots: [{ kind: 'brand', petname: 'moola' }], }, meta: { diff --git a/packages/wallet/api/test/test-middleware.js b/packages/wallet/api/test/test-middleware.js index 4e14134776c6..5ad6657f1276 100644 --- a/packages/wallet/api/test/test-middleware.js +++ b/packages/wallet/api/test/test-middleware.js @@ -9,15 +9,7 @@ import { } from '@agoric/smart-wallet/src/marshal-contexts.js'; const capData1 = { - body: JSON.stringify([ - [ - 'applyMethod', - { '@qclass': 'slot', iface: 'Alleged: purse.actions', index: 0 }, - 'deposit', - [{ '@qclass': 'slot', iface: 'Alleged: payment', index: 1 }], - ], - ['applyFunction', { '@qclass': 'slot', index: 0 }, [1, 'thing']], - ]), + body: '#[["applyMethod","$0.Alleged: purse.actions","deposit",["$1.Alleged: payment"]],["applyFunction","$0",[1,"thing"]]]', slots: ['purse:1', 'payment:1'], }; @@ -64,7 +56,7 @@ test('makeImportContext in wallet UI can unserialize messages', async t => { const capData = ctx.fromMyWallet.serialize(harden([...msgs])); t.deepEqual(capData, { - body: '[["applyMethod",{"@qclass":"slot","iface":"Alleged: purse.actions","index":0},"transfer",[1]]]', + body: '#[["applyMethod","$0.Alleged: purse.actions","transfer",[1]]]', slots: ['purse:1'], }); }); diff --git a/packages/zoe/test/unitTests/contracts/test-priceAggregator.js b/packages/zoe/test/unitTests/contracts/test-priceAggregator.js index b70f9dc9cef5..85a8764b284d 100644 --- a/packages/zoe/test/unitTests/contracts/test-priceAggregator.js +++ b/packages/zoe/test/unitTests/contracts/test-priceAggregator.js @@ -6,7 +6,7 @@ import path from 'path'; import bundleSource from '@endo/bundle-source'; import { E } from '@endo/eventual-send'; -import { Far } from '@endo/marshal'; +import { Far } from '@endo/far'; import { makeIssuerKit, AmountMath } from '@agoric/ertp'; import { makePromiseKit } from '@endo/promise-kit'; @@ -1048,9 +1048,6 @@ test('storage', async t => { const aggregator = await makeMedianAggregator(1n); const { timer: oracleTimer } = await E(zoe).getTerms(aggregator.instance); - // Accommodate the default deserialization of makeMockChainStorageRoot. - const remotable = iface => ({ iface: `Alleged: ${iface}` }); - const price1000 = await makeFakePriceOracle(1000n); await E(aggregator.creatorFacet).initOracle(price1000.instance, { increment: 10n, @@ -1061,10 +1058,10 @@ test('storage', async t => { 'mockChainStorageRoot.priceAggregator.ATOM-USD_price_feed', ), { - amountIn: { brand: remotable('$ATOM brand'), value: 1n }, - amountOut: { brand: remotable('$USD brand'), value: 1020n }, - timer: remotable('ManualTimer'), - timestamp: { timerBrand: remotable('timerBrand'), absValue: 1n }, + amountIn: { brand: Far('$ATOM brand'), value: 1n }, + amountOut: { brand: Far('$USD brand'), value: 1020n }, + timer: Far('ManualTimer'), + timestamp: { timerBrand: Far('timerBrand'), absValue: 1n }, }, ); }); From 6afd804d0c7feedbe151a3a7585fcf2b76a85eff Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Tue, 2 May 2023 18:26:39 -0700 Subject: [PATCH 2/3] remove leftover makeMarshal from vatPowers A long time ago, `makeMarshal` used an module-level WeakSet to keep track of which objects had been registered with Far, which mean the code doing the marshalling (i.e. liveslots) and the code providing the marshalled values (i.e. userspace, calling Far) needed to get those two pieces from the same place. This was a problem for vats, because they bundle `@endo/marshal` or `@endo/far` independently of liveslots' copy. We no longer do that, but for some period, we thought it was useful and important to give userspace vat code access to the same `makeMarshal` that liveslots used. To facilitate that, we included a reference in `vatPowers.makeMarshal`, which meant that the supervisors had to provide a copy in `allVatPowers`. We remove that from manager-local a while back, but forgot to remove it from the xsnap worker. This removes the remnants of makeMarshal from vatPowers in all manager types. --- .../SwingSet/src/kernel/vat-loader/manager-local.js | 13 +++---------- packages/SwingSet/src/types-ambient.js | 1 - packages/SwingSet/src/types-external.js | 7 +------ .../lib/supervisor-subprocess-xsnap.js | 2 -- 4 files changed, 4 insertions(+), 19 deletions(-) diff --git a/packages/SwingSet/src/kernel/vat-loader/manager-local.js b/packages/SwingSet/src/kernel/vat-loader/manager-local.js index 4b51cf5b1b60..a06980145af2 100644 --- a/packages/SwingSet/src/kernel/vat-loader/manager-local.js +++ b/packages/SwingSet/src/kernel/vat-loader/manager-local.js @@ -15,10 +15,7 @@ export function makeLocalVatManagerFactory({ vatEndowments, gcTools, }) { - const baseVP = { - makeMarshal: allVatPowers.makeMarshal, - }; - // testLog is also a vatPower, only for unit tests + const { testLog } = allVatPowers; // used by unit tests function prepare(managerOptions) { const { retainSyscall = false } = managerOptions; @@ -44,10 +41,9 @@ export function makeLocalVatManagerFactory({ assert.typeof(setup, 'function', 'setup is not an in-realm function'); const { syscall, finish } = prepare(managerOptions); - const { testLog } = allVatPowers; const helpers = harden({}); // DEPRECATED, todo remove from setup() const state = null; // TODO remove from setup() - const vatPowers = harden({ ...baseVP, testLog }); + const vatPowers = harden({ testLog }); const dispatch = setup(syscall, state, helpers, vatPowers); return finish(dispatch); @@ -64,10 +60,7 @@ export function makeLocalVatManagerFactory({ const { syscall, finish } = prepare(managerOptions); - const vatPowers = harden({ - ...baseVP, - testLog: allVatPowers.testLog, - }); + const vatPowers = harden({ testLog }); const makeLogMaker = source => { const makeLog = level => { diff --git a/packages/SwingSet/src/types-ambient.js b/packages/SwingSet/src/types-ambient.js index d0a82e7ef3ca..ea96d0a53786 100644 --- a/packages/SwingSet/src/types-ambient.js +++ b/packages/SwingSet/src/types-ambient.js @@ -24,7 +24,6 @@ * * @typedef { import('./types-external.js').VatPowers } VatPowers * @typedef { import('./types-external.js').StaticVatPowers } StaticVatPowers - * @typedef { import('./types-external.js').MarshallingVatPowers } MarshallingVatPowers * @typedef { import('./types-external.js').MeteringVatPowers } MeteringVatPowers * * @typedef { import('./types-external.js').TerminationVatPowers } TerminationVatPowers diff --git a/packages/SwingSet/src/types-external.js b/packages/SwingSet/src/types-external.js index 23e50e202c8b..0189fac650c5 100644 --- a/packages/SwingSet/src/types-external.js +++ b/packages/SwingSet/src/types-external.js @@ -34,16 +34,11 @@ export {}; /** * See ../docs/static-vats.md#vatpowers * - * @typedef { MarshallingVatPowers & TerminationVatPowers } VatPowers + * @typedef { TerminationVatPowers } VatPowers * * @typedef { (VatPowers & MeteringVatPowers) } StaticVatPowers * * @typedef {{ - * Remotable: unknown, - * getInterfaceOf: unknown, - * }} MarshallingVatPowers - * - * @typedef {{ * makeGetMeter: unknown, * transformMetering: unknown, * }} MeteringVatPowers diff --git a/packages/swingset-xsnap-supervisor/lib/supervisor-subprocess-xsnap.js b/packages/swingset-xsnap-supervisor/lib/supervisor-subprocess-xsnap.js index 12974c79fc45..50499d975897 100644 --- a/packages/swingset-xsnap-supervisor/lib/supervisor-subprocess-xsnap.js +++ b/packages/swingset-xsnap-supervisor/lib/supervisor-subprocess-xsnap.js @@ -1,7 +1,6 @@ /* global globalThis WeakRef FinalizationRegistry */ import { assert, Fail } from '@agoric/assert'; import { importBundle } from '@endo/import-bundle'; -import { makeMarshal } from '@endo/marshal'; import { makeLiveSlots, insistVatDeliveryObject, @@ -208,7 +207,6 @@ function makeWorker(port) { const syscall = makeSupervisorSyscall(syscallToManager); const vatPowers = { - makeMarshal, testLog: (...args) => port.send([ 'testLog', From 49b01bcbea3937dc0e777f65ecdd7457882b5edb Mon Sep 17 00:00:00 2001 From: Michael FIG Date: Tue, 4 Apr 2023 12:53:10 -0600 Subject: [PATCH 3/3] fix(SwingSet): dedupe `bundleTool.js` with Endo --- packages/SwingSet/tools/bundleTool.js | 261 +++----------------------- 1 file changed, 21 insertions(+), 240 deletions(-) diff --git a/packages/SwingSet/tools/bundleTool.js b/packages/SwingSet/tools/bundleTool.js index 5d62654644c2..d5d2cbd54949 100755 --- a/packages/SwingSet/tools/bundleTool.js +++ b/packages/SwingSet/tools/bundleTool.js @@ -1,247 +1,29 @@ // eslint-disable-next-line import/no-extraneous-dependencies -import bundleSource from '@endo/bundle-source'; -import { makeReadPowers } from '@endo/compartment-mapper/node-powers.js'; -import { makePromiseKit } from '@endo/promise-kit'; +import { makeNodeBundleCache as wrappedMaker } from '@endo/bundle-source/cache.js'; import styles from 'ansi-styles'; // less authority than 'chalk' -const { quote: q, Fail } = assert; - -/** - * @typedef {object} BundleMeta - * @property {string} bundleFileName - * @property {string} bundleTime as ISO string - * @property {{relative: string, absolute: string}} moduleSource - * @property {Array<{relativePath: string, mtime: string}>} contents - */ - -export const makeFileReader = (fileName, { fs, path }) => { - const make = there => makeFileReader(there, { fs, path }); - return harden({ - toString: () => fileName, - readText: () => fs.promises.readFile(fileName, 'utf-8'), - neighbor: ref => make(path.resolve(fileName, ref)), - stat: () => fs.promises.stat(fileName), - absolute: () => path.normalize(fileName), - relative: there => path.relative(fileName, there), - exists: () => fs.existsSync(fileName), - }); -}; - -/** - * @param {string} fileName - * @param {{ fs: import('fs'), path: import('path') }} io - */ -export const makeFileWriter = (fileName, { fs, path }) => { - const make = there => makeFileWriter(there, { fs, path }); - return harden({ - toString: () => fileName, - writeText: txt => fs.promises.writeFile(fileName, txt), - readOnly: () => makeFileReader(fileName, { fs, path }), - neighbor: ref => make(path.resolve(fileName, ref)), - mkdir: opts => fs.promises.mkdir(fileName, opts), - }); -}; - -/** @type {(n: string) => string} */ -const toBundleName = n => `bundle-${n}.js`; -/** @type {(n: string) => string} */ -const toBundleMeta = n => `bundle-${n}-meta.json`; - -/** @type {Map>} */ -const providedCaches = new Map(); - -/** - * @param {ReturnType} wr - * @param {*} bundleOptions - * @param {ReturnType} cwd - * @param {*} readPowers - */ -export const makeBundleCache = (wr, bundleOptions, cwd, readPowers) => { - const dimLog = (...args) => - console.log( - `${styles.dim.open}[bundleTool] ${[...args].join(' ')}${ - styles.dim.close - }`, +export const makeNodeBundleCache = async (dest, options, loadModule) => { + const log = (...args) => { + const flattened = args.map(arg => + // Don't print stack traces. + arg instanceof Error ? arg.message : arg, ); - - const add = async (rootPath, targetName) => { - const srcRd = cwd.neighbor(rootPath); - - const modTimeByPath = new Map(); - - const loggedRead = async loc => { - if (!loc.match(/\bpackage.json$/)) { - try { - const itemRd = cwd.neighbor(new URL(loc).pathname); - const ref = srcRd.relative(itemRd.absolute()); - const { mtime } = await itemRd.stat(); - modTimeByPath.set(ref, mtime); - // console.log({ loc, mtime, ref }); - } catch (oops) { - console.error(oops); - } - } - return readPowers.read(loc); - }; - const bundle = await bundleSource(rootPath, bundleOptions, { - ...readPowers, - read: loggedRead, - }); - - const { moduleFormat } = bundle; - assert.equal(moduleFormat, 'endoZipBase64'); - - const code = `export default ${JSON.stringify(bundle)};`; - await wr.mkdir({ recursive: true }); - const bundleFileName = toBundleName(targetName); - const bundleWr = wr.neighbor(bundleFileName); - await bundleWr.writeText(code); - const { mtime: bundleTime } = await bundleWr.readOnly().stat(); - - /** @type {BundleMeta} */ - const meta = { - bundleFileName, - bundleTime: bundleTime.toISOString(), - moduleSource: { - relative: bundleWr.readOnly().relative(srcRd.absolute()), - absolute: srcRd.absolute(), - }, - contents: [...modTimeByPath.entries()].map(([relativePath, mtime]) => ({ - relativePath, - mtime: mtime.toISOString(), - })), - }; - - await wr - .neighbor(toBundleMeta(targetName)) - .writeText(JSON.stringify(meta, null, 2)); - return meta; - }; - - const validate = async (targetName, rootOpt) => { - const metaRd = wr.readOnly().neighbor(toBundleMeta(targetName)); - let txt; - try { - txt = await metaRd.readText(); - } catch (ioErr) { - Fail`${q(targetName)}: cannot read bundle metadata: ${q(ioErr)}`; - } - const meta = JSON.parse(txt); - const { - bundleFileName, - bundleTime, - contents, - moduleSource: { absolute: moduleSource }, - } = meta; - assert.equal(bundleFileName, toBundleName(targetName)); - if (rootOpt) { - moduleSource === cwd.neighbor(rootOpt).absolute() || - Fail`bundle ${targetName} was for ${moduleSource}, not ${rootOpt}`; - } - const { mtime: actualBundleTime } = await wr - .readOnly() - .neighbor(bundleFileName) - .stat(); - assert.equal(actualBundleTime.toISOString(), bundleTime); - const moduleRd = wr.readOnly().neighbor(moduleSource); - const actualTimes = await Promise.all( - contents.map(async ({ relativePath }) => { - const itemRd = moduleRd.neighbor(relativePath); - const { mtime } = await itemRd.stat(); - return { relativePath, mtime: mtime.toISOString() }; - }), + console.log( + // Make all messages prefixed and dim. + `${styles.dim.open}[bundleTool]`, + ...flattened, + styles.dim.close, ); - const outOfDate = actualTimes.filter(({ mtime }) => mtime > bundleTime); - outOfDate.length === 0 || - Fail`out of date: ${q(outOfDate)}. ${q(targetName)} bundled at ${q( - bundleTime, - )}`; - return meta; - }; - - /** - * - * @param {string} rootPath - * @param {string} targetName - * @returns {Promise} - */ - const validateOrAdd = async (rootPath, targetName) => { - let meta; - if (wr.readOnly().neighbor(toBundleMeta(targetName)).exists()) { - try { - meta = await validate(targetName, rootPath); - } catch (invalid) { - dimLog(invalid.message); - } - } - if (!meta) { - dimLog(`${wr}`, 'add:', targetName, 'from', rootPath); - meta = await add(rootPath, targetName); - } - return meta; }; - - const loaded = new Map(); - /** - * @param {string} rootPath - * @param {string} [targetName] - */ - const load = async ( - rootPath, - targetName = readPowers.basename(rootPath, '.js'), - ) => { - const found = loaded.get(targetName); - // console.log('load', { targetName, found: !!found, rootPath }); - if (found && found.rootPath === rootPath) { - return found.bundle; - } - const todo = makePromiseKit(); - loaded.set(targetName, { rootPath, bundle: todo.promise }); - const bundle = await validateOrAdd(rootPath, targetName) - .then(({ bundleFileName }) => - import(`${wr.readOnly().neighbor(bundleFileName)}`), - ) - .then(m => harden(m.default)); - assert.equal(bundle.moduleFormat, 'endoZipBase64'); - todo.resolve(bundle); - return bundle; - }; - - return harden({ - add, - validate, - validateOrAdd, - load, - }); + return wrappedMaker(dest, { log, ...options }, loadModule); }; -/** - * Make a new bundle cache for the destination. If there is already one for that destination, error. - * - * @param {string} dest - * @param {{ format?: string, dev?: boolean }} options - * @param {(id: string) => Promise} loadModule - */ -export const makeNodeBundleCache = async (dest, options, loadModule) => { - const [fs, path, url, crypto] = await Promise.all([ - await loadModule('fs'), - await loadModule('path'), - await loadModule('url'), - await loadModule('crypto'), - ]); - - const readPowers = { - ...makeReadPowers({ fs, url, crypto }), - basename: path.basename, - }; - - const cwd = makeFileReader('', { fs, path }); - const destWr = makeFileWriter(dest, { fs, path }); - return makeBundleCache(destWr, options, cwd, readPowers); -}; +/** @type {Map>} */ +const providedCaches = new Map(); /** - * Make a new bundle cache for the destination. If there is already one for that destination, error. + * Make a new bundle cache for the destination. If there is already one for that + * destination, return it. * * @param {string} dest * @param {{ format?: string, dev?: boolean }} options @@ -249,13 +31,12 @@ export const makeNodeBundleCache = async (dest, options, loadModule) => { */ export const provideBundleCache = (dest, options, loadModule) => { const uniqueDest = [dest, options.format, options.dev].join('-'); - if (!providedCaches.has(uniqueDest)) { - providedCaches.set( - uniqueDest, - makeNodeBundleCache(dest, options, loadModule), - ); + let bundleCache = providedCaches.get(uniqueDest); + if (!bundleCache) { + bundleCache = makeNodeBundleCache(dest, options, loadModule); + providedCaches.set(uniqueDest, bundleCache); } - return providedCaches.get(uniqueDest); + return bundleCache; }; harden(provideBundleCache);