From bd7ae9e69f5f516a091c2516b66d9c414049d4ec Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Tue, 21 Jan 2025 16:41:12 -0800 Subject: [PATCH 1/6] refactor!: remove reallocate and support for staged allocations --- packages/zoe/docs/AttackGuide.md | 2 + packages/zoe/docs/seats.md | 5 - .../zoe/src/contractFacet/types-ambient.d.ts | 57 ----- packages/zoe/src/contractFacet/zcfMint.js | 12 - packages/zoe/src/contractFacet/zcfSeat.js | 195 +------------- packages/zoe/src/contractFacet/zcfZygote.js | 1 - packages/zoe/src/internal-types.js | 1 - .../unitTests/zcf/allStagedSeatsUsed.test.js | 93 ------- .../unitTests/zcf/reallocate-empty.test.js | 63 ----- .../zcf/reallocateForZCFMint.test.js | 217 ---------------- packages/zoe/test/unitTests/zcf/zcf.test.js | 242 ------------------ 11 files changed, 9 insertions(+), 879 deletions(-) delete mode 100644 packages/zoe/test/unitTests/zcf/allStagedSeatsUsed.test.js delete mode 100644 packages/zoe/test/unitTests/zcf/reallocate-empty.test.js delete mode 100644 packages/zoe/test/unitTests/zcf/reallocateForZCFMint.test.js diff --git a/packages/zoe/docs/AttackGuide.md b/packages/zoe/docs/AttackGuide.md index 2c15b32fefb..ceb02cbe7d8 100644 --- a/packages/zoe/docs/AttackGuide.md +++ b/packages/zoe/docs/AttackGuide.md @@ -18,6 +18,8 @@ The main focus of most threats would be a breach of one of Zoe's core invariants ## Reallocation +THIS SECTION IS OBSOLETE. We've converted all code to use attomicRearrange + The current approach (staging, incrementBy/decrementBy and the fact that all seats must be included in realloc) has led to a few bugs. It's probably worth looking for other cases that create new stagings or presume there are none outstanding. We plan to replace this diff --git a/packages/zoe/docs/seats.md b/packages/zoe/docs/seats.md index 1879118f9b0..4e09fc9752e 100644 --- a/packages/zoe/docs/seats.md +++ b/packages/zoe/docs/seats.md @@ -67,12 +67,7 @@ The type of the ZCFSeat is: * @property {() => ProposalRecord} getProposal * @property {ZCFGetAmountAllocated} getAmountAllocated * @property {() => Allocation} getCurrentAllocation - * @property {() => Allocation} getStagedAllocation - * @property {() => boolean} hasStagedAllocation * @property {(newAllocation: Allocation) => boolean} isOfferSafe - * @property {(amountKeywordRecord: AmountKeywordRecord) => AmountKeywordRecord} incrementBy - * @property {(amountKeywordRecord: AmountKeywordRecord) => AmountKeywordRecord} decrementBy - * @property {() => void} clear */ ``` diff --git a/packages/zoe/src/contractFacet/types-ambient.d.ts b/packages/zoe/src/contractFacet/types-ambient.d.ts index b483bdfedc8..bcac00c752c 100644 --- a/packages/zoe/src/contractFacet/types-ambient.d.ts +++ b/packages/zoe/src/contractFacet/types-ambient.d.ts @@ -24,11 +24,6 @@ type ZCF> = { * - atomically reallocate amounts among seats. */ atomicRearrange: (transfers: TransferPart[]) => void; - /** - * - reallocate amounts among seats. - * @deprecated Use atomicRearrange instead. - */ - reallocate: Reallocate; /** * - check * whether a keyword is valid and unique and could be added in @@ -86,34 +81,6 @@ type ZCF> = { getInstance: () => Instance; }; -/** - * @deprecated Use atomicRearrange instead - * - * The contract can reallocate over seats, which commits the staged - * allocation for each seat. On commit, the staged allocation becomes - * the current allocation and the staged allocation is deleted. - * - * The reallocation will only succeed if the reallocation 1) conserves - * rights (the amounts specified have the same total value as the - * current total amount), and 2) is 'offer-safe' for all parties - * involved. All seats that have staged allocations must be included - * as arguments to `reallocate`, or an error is thrown. Additionally, - * an error is thrown if any seats included in `reallocate` do not - * have a staged allocation. - * - * The reallocation is partial, meaning that it applies only to the - * seats passed in as arguments. By induction, if rights conservation - * and offer safety hold before, they will hold after a safe - * reallocation, even though we only re-validate for the seats whose - * allocations will change. Since rights are conserved for the change, - * overall rights will be unchanged, and a reallocation can only - * effect offer safety for seats whose allocations change. - */ -type Reallocate = ( - seat1: ZCFSeat, - seat2: ZCFSeat, - ...seatRest: Array -) => void; type TransferPart = [ fromSeat?: ZCFSeat, toSeat?: ZCFSeat, @@ -184,31 +151,7 @@ type ZCFSeat = import('@endo/pass-style').RemotableObject & { brand?: B, ) => B extends Brand ? Amount : Amount; getCurrentAllocation: () => Allocation; - /** - * @deprecated Use atomicRearrange instead - */ - getStagedAllocation: () => Allocation; - /** - * @deprecated Use atomicRearrange instead - */ - hasStagedAllocation: () => boolean; isOfferSafe: (newAllocation: Allocation) => boolean; - /** - * @deprecated Use atomicRearrange instead - */ - incrementBy: ( - amountKeywordRecord: AmountKeywordRecord, - ) => AmountKeywordRecord; - /** - * @deprecated Use atomicRearrange instead - */ - decrementBy: ( - amountKeywordRecord: AmountKeywordRecord, - ) => AmountKeywordRecord; - /** - * @deprecated Use atomicRearrange instead - */ - clear: () => void; }; type ZcfSeatKit = { zcfSeat: ZCFSeat; diff --git a/packages/zoe/src/contractFacet/zcfMint.js b/packages/zoe/src/contractFacet/zcfMint.js index 1c07c0d0dc4..3fad8229c59 100644 --- a/packages/zoe/src/contractFacet/zcfMint.js +++ b/packages/zoe/src/contractFacet/zcfMint.js @@ -87,12 +87,6 @@ export const prepareZcMint = ( gains, ); - // Increment the stagedAllocation if it exists so that the - // stagedAllocation is kept up to the currentAllocation - if (zcfSeat.hasStagedAllocation()) { - zcfSeat.incrementBy(gains); - } - // Offer safety should never be able to be violated here, as // we are adding assets. However, we keep this check so that // all reallocations are covered by offer safety checks, and @@ -129,12 +123,6 @@ export const prepareZcMint = ( zcfSeat.isOfferSafe(allocationMinusLosses) || Fail`The allocation after burning losses ${allocationMinusLosses} for the zcfSeat was not offer safe`; - // Decrement the stagedAllocation if it exists so that the - // stagedAllocation is kept up to the currentAllocation - if (zcfSeat.hasStagedAllocation()) { - zcfSeat.decrementBy(losses); - } - // No effects above, apart from decrementBy. Note COMMIT POINT within // reallocator.reallocate(). The following two steps *should* be // committed atomically, but it is not a disaster if they are diff --git a/packages/zoe/src/contractFacet/zcfSeat.js b/packages/zoe/src/contractFacet/zcfSeat.js index 1e304abdfa3..d1fe42844e0 100644 --- a/packages/zoe/src/contractFacet/zcfSeat.js +++ b/packages/zoe/src/contractFacet/zcfSeat.js @@ -5,7 +5,6 @@ import { prepareExoClass, prepareExoClassKit, provide, - provideDurableMapStore, provideDurableWeakMapStore, } from '@agoric/vat-data'; import { AmountMath } from '@agoric/ertp'; @@ -13,8 +12,6 @@ import { initEmpty, M } from '@agoric/store'; import { isOfferSafe } from './offerSafety.js'; import { assertRightsConserved } from './rightsConservation.js'; -import { addToAllocation, subtractFromAllocation } from './allocationMath.js'; -import { coerceAmountKeywordRecord } from '../cleanProposal.js'; import { AmountKeywordRecordShape, SeatDataShape, @@ -41,11 +38,11 @@ export const createSeatManager = ( ) => { /** @type {WeakMapStore} */ let activeZCFSeats = provideDurableWeakMapStore(zcfBaggage, 'activeZCFSeats'); - /** @type {MapStore} */ - const zcfSeatToStagedAllocations = provideDurableMapStore( - zcfBaggage, - 'zcfSeatToStagedAllocations', - ); + + // Removed. See #6679 + if (zcfBaggage.has('zcfSeatToStagedAllocations')) { + zcfBaggage.delete('zcfSeatToStagedAllocations'); + } /** @type {WeakMapStore} */ let zcfSeatToSeatHandle = provideDurableWeakMapStore( @@ -84,64 +81,6 @@ export const createSeatManager = ( return activeZCFSeats.get(zcfSeat); }; - /** - * @param {ZCFSeat} zcfSeat - * @returns {void} - */ - const commitStagedAllocation = zcfSeat => { - // By this point, we have checked that the zcfSeat is a key in - // activeZCFSeats and in zcfSeatToStagedAllocations. - activeZCFSeats.set(zcfSeat, zcfSeat.getStagedAllocation()); - zcfSeatToStagedAllocations.delete(zcfSeat); - }; - - /** - * @param {ZCFSeat} zcfSeat - * @returns {Allocation} - */ - const hasStagedAllocation = zcfSeatToStagedAllocations.has; - - /** - * Get the stagedAllocation. If one does not exist, return the - * currentAllocation. We return the currentAllocation in this case - * so that downstream users do not have to check whether the - * stagedAllocation is defined before adding to it or subtracting - * from it. To check whether a stagedAllocation exists, use - * `hasStagedAllocation` - * - * @param {ZCFSeat} zcfSeat - * @returns {Allocation} - */ - const getStagedAllocation = zcfSeat => { - if (zcfSeatToStagedAllocations.has(zcfSeat)) { - return zcfSeatToStagedAllocations.get(zcfSeat); - } else { - return activeZCFSeats.get(zcfSeat); - } - }; - - const assertStagedAllocation = zcfSeat => { - hasStagedAllocation(zcfSeat) || - Fail`Reallocate failed because a seat had no staged allocation. Please add or subtract from the seat and then reallocate.`; - }; - - const setStagedAllocation = (zcfSeat, newStagedAllocation) => { - if (zcfSeatToStagedAllocations.has(zcfSeat)) { - zcfSeatToStagedAllocations.set(zcfSeat, newStagedAllocation); - } else { - zcfSeatToStagedAllocations.init(zcfSeat, newStagedAllocation); - } - }; - - /** @param {ZCFSeat} zcfSeat */ - const assertNoStagedAllocation = zcfSeat => { - if (hasStagedAllocation(zcfSeat)) { - Fail`The seat could not be exited with a staged but uncommitted allocation: ${getStagedAllocation( - zcfSeat, - )}. Please reallocate over this seat or clear the staged allocation.`; - } - }; - const ZCFSeatI = M.interface('ZCFSeat', {}, { sloppy: true }); const makeZCFSeatInternal = prepareExoClass( @@ -163,7 +102,6 @@ export const createSeatManager = ( exit(completion) { const { self } = this; assertActive(self); - assertNoStagedAllocation(self); doExitSeat(self); void E(zoeInstanceAdmin).exitSeat( zcfSeatToSeatHandle.get(self), @@ -221,10 +159,6 @@ export const createSeatManager = ( const { self } = this; return getCurrentAllocation(self); }, - getStagedAllocation() { - const { self } = this; - return getStagedAllocation(self); - }, isOfferSafe(newAllocation) { const { state, self } = this; assertActive(self); @@ -236,53 +170,6 @@ export const createSeatManager = ( return isOfferSafe(state.proposal, reallocation); }, - /** - * @deprecated switch to zcf.atomicRearrange() - * @param {AmountKeywordRecord} amountKeywordRecord - */ - incrementBy(amountKeywordRecord) { - const { self } = this; - assertActive(self); - amountKeywordRecord = coerceAmountKeywordRecord( - amountKeywordRecord, - getAssetKindByBrand, - ); - setStagedAllocation( - self, - addToAllocation(getStagedAllocation(self), amountKeywordRecord), - ); - return amountKeywordRecord; - }, - /** - * @deprecated switch to zcf.atomicRearrange() - * @param {AmountKeywordRecord} amountKeywordRecord - */ - decrementBy(amountKeywordRecord) { - const { self } = this; - assertActive(self); - amountKeywordRecord = coerceAmountKeywordRecord( - amountKeywordRecord, - getAssetKindByBrand, - ); - setStagedAllocation( - self, - subtractFromAllocation( - getStagedAllocation(self), - amountKeywordRecord, - ), - ); - return amountKeywordRecord; - }, - clear() { - const { self } = this; - if (zcfSeatToStagedAllocations.has(self)) { - zcfSeatToStagedAllocations.delete(self); - } - }, - hasStagedAllocation() { - const { self } = this; - return hasStagedAllocation(self); - }, }, ); @@ -296,9 +183,6 @@ export const createSeatManager = ( seatManager: M.interface('ZcfSeatManager', { makeZCFSeat: M.call(SeatDataShape).returns(M.remotable('zcfSeat')), atomicRearrange: M.call(M.arrayOf(TransferPartShape)).returns(), - reallocate: M.call(M.remotable('zcfSeat'), M.remotable('zcfSeat')) - .rest(M.arrayOf(M.remotable('zcfSeat'))) - .returns(), dropAllReferences: M.call().returns(), }), zcfMintReallocator: M.interface('MintReallocator', { @@ -350,8 +234,6 @@ export const createSeatManager = ( // ////// All Seats are active ///////////////////////////////// for (const [seat] of newAllocations) { assertActive(seat); - !seat.hasStagedAllocation() || - Fail`Cannot mix atomicRearrange with seat stagings: ${seat}`; zcfSeatToSeatHandle.has(seat) || Fail`The seat ${seat} was not recognized`; } @@ -410,69 +292,6 @@ export const createSeatManager = ( throw err; } }, - reallocate(/** @type {ZCFSeat[]} */ ...seats) { - for (const seat of seats) { - assertActive(seat); - assertStagedAllocation(seat); - } - - // Ensure that rights are conserved overall. - const flattenAllocations = allocations => - allocations.flatMap(Object.values); - const previousAllocations = seats.map(seat => - seat.getCurrentAllocation(), - ); - const previousAmounts = flattenAllocations(previousAllocations); - const newAllocations = seats.map(seat => seat.getStagedAllocation()); - const newAmounts = flattenAllocations(newAllocations); - - assertRightsConserved(previousAmounts, newAmounts); - - // Ensure that offer safety holds. - for (const seat of seats) { - isOfferSafe(seat.getProposal(), seat.getStagedAllocation()) || - Fail`Offer safety was violated by the proposed allocation: ${seat.getStagedAllocation()}. Proposal was ${seat.getProposal()}`; - } - - // Keep track of seats used so far in this call, to prevent aliasing. - const zcfSeatsSoFar = new Set(); - - for (const seat of seats) { - zcfSeatToSeatHandle.has(seat) || - Fail`The seat ${seat} was not recognized`; - !zcfSeatsSoFar.has(seat) || - Fail`Seat (${seat}) was already an argument to reallocate`; - zcfSeatsSoFar.add(seat); - } - - try { - // No side effects above. All conditions checked which could have - // caused us to reject this reallocation. - // COMMIT POINT - // All the effects below must succeed "atomically". Scare quotes because - // the eventual send at the bottom is part of this "atomicity" even - // though its effects happen later. The send occurs in the order of - // updates from zcf to zoe, its effects must occur immediately in zoe - // on reception, and must not fail. - // - // Commit the staged allocations (currentAllocation is replaced - // for each of the seats) and inform Zoe of the - // newAllocation. - - for (const seat of seats) { - commitStagedAllocation(seat); - } - const seatHandleAllocations = seats.map(seat => { - const seatHandle = zcfSeatToSeatHandle.get(seat); - return { seatHandle, allocation: seat.getCurrentAllocation() }; - }); - - E(zoeInstanceAdmin).replaceAllocations(seatHandleAllocations); - } catch (err) { - shutdownWithFailure(err); - throw err; - } - }, dropAllReferences() { activeZCFSeats = replaceDurableWeakMapStore( zcfBaggage, @@ -485,9 +304,9 @@ export const createSeatManager = ( }, }, zcfMintReallocator: { - // Unlike the zcf.reallocate method, this one does not check + // Unlike the zcf.atomicRearrange method, this one does not check // conservation, and so can be used internally for reallocations that - // violate conservation. + // violate conservation, like minting and burning. reallocate(zcfSeat, newAllocation) { try { // COMMIT POINT diff --git a/packages/zoe/src/contractFacet/zcfZygote.js b/packages/zoe/src/contractFacet/zcfZygote.js index a63a25e9fbb..eb11510211f 100644 --- a/packages/zoe/src/contractFacet/zcfZygote.js +++ b/packages/zoe/src/contractFacet/zcfZygote.js @@ -283,7 +283,6 @@ export const makeZCFZygote = async ( /** @type {ZCF} */ const zcf = prepareExo(zcfBaggage, 'zcf', ZcfI, { atomicRearrange: transfers => seatManager.atomicRearrange(transfers), - reallocate: (...seats) => seatManager.reallocate(...seats), assertUniqueKeyword: kwd => getInstanceRecHolder().assertUniqueKeyword(kwd), saveIssuer: async (issuerP, keyword) => { // TODO: The checks of the keyword for uniqueness are diff --git a/packages/zoe/src/internal-types.js b/packages/zoe/src/internal-types.js index 59a55eab4df..8fef1fe51cf 100644 --- a/packages/zoe/src/internal-types.js +++ b/packages/zoe/src/internal-types.js @@ -276,7 +276,6 @@ /** * @typedef {object} ZcfSeatManager * @property {MakeZCFSeat} makeZCFSeat - * @property {Reallocate} reallocate * @property {DropAllReferences} dropAllReferences */ diff --git a/packages/zoe/test/unitTests/zcf/allStagedSeatsUsed.test.js b/packages/zoe/test/unitTests/zcf/allStagedSeatsUsed.test.js deleted file mode 100644 index c2f1d1fa351..00000000000 --- a/packages/zoe/test/unitTests/zcf/allStagedSeatsUsed.test.js +++ /dev/null @@ -1,93 +0,0 @@ -import { test } from '@agoric/swingset-vat/tools/prepare-test-env-ava.js'; - -import { AssetKind, AmountMath } from '@agoric/ertp'; -import { makeOffer } from '../makeOffer.js'; - -import { setup } from '../setupBasicMints.js'; - -import { setupZCFTest } from './setupZcfTest.js'; - -test(`allStagedSeatsUsed should not be asserted`, async t => { - const { moolaKit, moola } = setup(); - const { zoe, zcf } = await setupZCFTest({ - Moola: moolaKit.issuer, - }); - - const { zcfSeat: zcfSeat1 } = await makeOffer( - zoe, - zcf, - harden({ give: { B: moola(3n) } }), - harden({ B: moolaKit.mint.mintPayment(moola(3n)) }), - ); - - const { zcfSeat: zcfSeat2 } = await makeOffer( - zoe, - zcf, - harden({ give: { B: moola(3n) } }), - harden({ B: moolaKit.mint.mintPayment(moola(3n)) }), - ); - - zcfSeat1.incrementBy(zcfSeat2.decrementBy(harden({ B: moola(2n) }))); - // Seats have staged allocations - t.true(zcfSeat1.hasStagedAllocation()); - - const zcfMint = await zcf.makeZCFMint( - 'Token', - AssetKind.NAT, - harden({ - decimalPlaces: 6, - }), - ); - - const { brand: tokenBrand } = zcfMint.getIssuerRecord(); - - const zcfSeat3 = zcfMint.mintGains( - harden({ - MyToken: AmountMath.make(tokenBrand, 100n), - }), - ); - - // This test was failing due to this bug: https://github.com/Agoric/agoric-sdk/issues/3613 - t.deepEqual(zcfSeat3.getCurrentAllocation(), { - MyToken: AmountMath.make(tokenBrand, 100n), - }); -}); - -test(`allStagedSeatsUsed should be asserted`, async t => { - const { moolaKit, moola } = setup(); - const { zoe, zcf } = await setupZCFTest({ - Moola: moolaKit.issuer, - }); - - const { zcfSeat: zcfSeat1 } = await makeOffer( - zoe, - zcf, - harden({ give: { B: moola(3n) } }), - harden({ B: moolaKit.mint.mintPayment(moola(3n)) }), - ); - - const { zcfSeat: zcfSeat2 } = await makeOffer( - zoe, - zcf, - harden({ give: { B: moola(3n) } }), - harden({ B: moolaKit.mint.mintPayment(moola(3n)) }), - ); - - const { zcfSeat: zcfSeat3 } = await makeOffer( - zoe, - zcf, - harden({ give: { B: moola(3n) } }), - harden({ B: moolaKit.mint.mintPayment(moola(3n)) }), - ); - - zcfSeat1.incrementBy(harden(zcfSeat2.decrementBy(harden({ B: moola(2n) })))); - - zcfSeat3.incrementBy(harden({ B: moola(3n) })); - - t.true(zcfSeat3.hasStagedAllocation()); - - // zcfSeat3 has a staged allocation but was not included in reallocate. - // This is now legal, so we now test that this reallocate does - // not throw. - t.notThrows(() => zcf.reallocate(zcfSeat1, zcfSeat2)); -}); diff --git a/packages/zoe/test/unitTests/zcf/reallocate-empty.test.js b/packages/zoe/test/unitTests/zcf/reallocate-empty.test.js deleted file mode 100644 index a6615352273..00000000000 --- a/packages/zoe/test/unitTests/zcf/reallocate-empty.test.js +++ /dev/null @@ -1,63 +0,0 @@ -import { test } from '@agoric/swingset-vat/tools/prepare-test-env-ava.js'; - -import { AmountMath, AssetKind } from '@agoric/ertp'; - -import { setupZCFTest } from './setupZcfTest.js'; - -test(`zcf.reallocate introducing new empty amount`, async t => { - const { zcf } = await setupZCFTest(); - const { zcfSeat: zcfSeat1 } = zcf.makeEmptySeatKit(); - const { zcfSeat: zcfSeat2 } = zcf.makeEmptySeatKit(); - const zcfMint = await zcf.makeZCFMint('RUN'); - const { brand } = zcfMint.getIssuerRecord(); - - // Get the amount allocated on zcfSeat1. It is empty for the RUN brand. - const allocation = zcfSeat1.getAmountAllocated('RUN', brand); - t.true(AmountMath.isEmpty(allocation)); - - const empty = AmountMath.makeEmpty(brand, AssetKind.NAT); - - // decrementBy empty does not throw, and does not add a keyword - zcfSeat1.decrementBy(harden({ RUN: empty })); - t.deepEqual(zcfSeat1.getStagedAllocation(), {}); - - // Try to incrementBy empty. This succeeds, and the keyword is added - // with an empty amount. - zcfSeat1.incrementBy(harden({ RUN: empty })); - zcfSeat2.incrementBy(harden({ RUN: empty })); - - zcf.reallocate(zcfSeat1, zcfSeat2); - - t.deepEqual(zcfSeat1.getStagedAllocation(), { - RUN: empty, - }); - t.deepEqual(zcfSeat2.getStagedAllocation(), { - RUN: empty, - }); -}); - -test(`zcf.reallocate undefined`, async t => { - const { zcf } = await setupZCFTest(); - const { zcfSeat: zcfSeat1 } = zcf.makeEmptySeatKit(); - const { zcfSeat: zcfSeat2 } = zcf.makeEmptySeatKit(); - - // @ts-expect-error Deliberate wrong type for testing - t.throws(() => zcf.reallocate(zcfSeat1, zcfSeat2, undefined), { - message: / - Must be a remotable/, - }); -}); - -test(`zcf.reallocate unstaged`, async t => { - const { zcf } = await setupZCFTest(); - const { zcfSeat: zcfSeat1 } = zcf.makeEmptySeatKit(); - const { zcfSeat: zcfSeat2 } = zcf.makeEmptySeatKit(); - - const zcfMint = await zcf.makeZCFMint('RUN'); - const { brand } = zcfMint.getIssuerRecord(); - const empty = AmountMath.makeEmpty(brand, AssetKind.NAT); - zcfSeat1.incrementBy(harden({ RUN: empty })); - t.throws(() => zcf.reallocate(zcfSeat1, zcfSeat2), { - message: - 'Reallocate failed because a seat had no staged allocation. Please add or subtract from the seat and then reallocate.', - }); -}); diff --git a/packages/zoe/test/unitTests/zcf/reallocateForZCFMint.test.js b/packages/zoe/test/unitTests/zcf/reallocateForZCFMint.test.js deleted file mode 100644 index d4bc1eba07b..00000000000 --- a/packages/zoe/test/unitTests/zcf/reallocateForZCFMint.test.js +++ /dev/null @@ -1,217 +0,0 @@ -import { test } from '@agoric/swingset-vat/tools/prepare-test-env-ava.js'; - -import { AssetKind, AmountMath } from '@agoric/ertp'; -import { makeOffer } from '../makeOffer.js'; - -import { setup } from '../setupBasicMints.js'; - -import { setupZCFTest } from './setupZcfTest.js'; - -// Test that `zcfSeat.incrementBy()` and `zcfSeat.decrementBy()` can -// be interleaved at any point with `zcfMint.mintGains()` and -// `zcfMint.burnLosses()` with no problems. This test only performs a -// subset of all possible interleavings, but it should cover a fair amount -// -// Order of calls: -// 1. zcfSeat2.decrementBy with non-zcfMint token -// 2. zcfMint.mintGains on zcfSeat2 -// 3. zcfMint.mintGains on zcfSeat1 -// 4. zcfSeat1.incrementBy non-zcfMint token -// 5. reallocate(zcfSeat1, zcfSeat2) -// 6. zcfSeat1.decrementBy zcfMint token and non-zcfMint token -// 7. zcfMint.burnLosses on zcfSeat1 -// 8. zcfMint.burnLosses on zcfSeat2 -// 9. zcfSeat2.incrementBy zcfMint token and non-zcfMint token -// 10. zcfMint.mintGains on zcfSeat2 -// 11 reallocate(zcfSeat1, zcfSeat2) - -test(`stagedAllocations safely interleave with zcfMint calls`, async t => { - const { moolaKit, moola } = setup(); - const { zoe, zcf } = await setupZCFTest({ - Moola: moolaKit.issuer, - }); - - // Make zcfSeat1 - const { zcfSeat: zcfSeat1 } = await makeOffer( - zoe, - zcf, - harden({ give: { B: moola(3n) } }), - harden({ B: moolaKit.mint.mintPayment(moola(3n)) }), - ); - - // Make zcfSeat2 - const { zcfSeat: zcfSeat2 } = await makeOffer( - zoe, - zcf, - harden({ give: { B: moola(3n) } }), - harden({ B: moolaKit.mint.mintPayment(moola(3n)) }), - ); - - // Make ZCFMint - const zcfMint = await zcf.makeZCFMint( - 'Token', - AssetKind.NAT, - harden({ - decimalPlaces: 6, - }), - ); - const { brand: tokenBrand } = zcfMint.getIssuerRecord(); - - // Decrement zcfSeat2 by non-zcfMintToken - zcfSeat2.decrementBy(harden({ B: moola(2n) })); - t.true(zcfSeat2.hasStagedAllocation()); - t.deepEqual(zcfSeat2.getStagedAllocation(), { B: moola(1n) }); - t.deepEqual(zcfSeat2.getCurrentAllocation(), { B: moola(3n) }); - - // Mint gains to zcfSeat2 - zcfMint.mintGains( - harden({ - MyToken: AmountMath.make(tokenBrand, 100n), - }), - zcfSeat2, - ); - // zcfSeat2's staged allocation and the current allocation should - // include the newly minted tokens. Staged allocations completely - // replace the old allocations, so it is important that anything - // added to the current allocation also gets added to any - // in-progress staged allocation. - t.deepEqual(zcfSeat2.getStagedAllocation(), { - B: moola(1n), - MyToken: AmountMath.make(tokenBrand, 100n), - }); - t.deepEqual(zcfSeat2.getCurrentAllocation(), { - B: moola(3n), - MyToken: AmountMath.make(tokenBrand, 100n), - }); - - // Mint gains to zcfSeat1 - zcfMint.mintGains( - harden({ - OtherTokens: AmountMath.make(tokenBrand, 50n), - }), - zcfSeat1, - ); - // zcfSeat1 has no staged allocation, but the current - // allocation should have changed to include the minted tokens - t.false(zcfSeat1.hasStagedAllocation()); - t.deepEqual(zcfSeat1.getCurrentAllocation(), { - B: moola(3n), - OtherTokens: AmountMath.make(tokenBrand, 50n), - }); - - // zcfSeat1.incrementBy non-zcfMint token - zcfSeat1.incrementBy(harden({ B: moola(2n) })); - // Both the staged allocation and the current allocation show the OtherTokens - t.deepEqual(zcfSeat1.getStagedAllocation(), { - B: moola(5n), - OtherTokens: AmountMath.make(tokenBrand, 50n), - }); - t.deepEqual(zcfSeat1.getCurrentAllocation(), { - B: moola(3n), - OtherTokens: AmountMath.make(tokenBrand, 50n), - }); - - // Reallocate - zcf.reallocate(zcfSeat1, zcfSeat2); - t.false(zcfSeat1.hasStagedAllocation()); - t.false(zcfSeat2.hasStagedAllocation()); - t.deepEqual(zcfSeat1.getCurrentAllocation(), { - B: moola(5n), - OtherTokens: AmountMath.make(tokenBrand, 50n), - }); - t.deepEqual(zcfSeat2.getCurrentAllocation(), { - B: moola(1n), - MyToken: AmountMath.make(tokenBrand, 100n), - }); - - // Test burnLosses interleaving - - // zcfSeat1 decrementBy both zcfMint token and non-zcfMint token - zcfSeat1.decrementBy( - harden({ - OtherTokens: AmountMath.make(tokenBrand, 5n), - B: moola(1n), - }), - ); - - // zcfMint.burnLosses on zcfSeat1 - zcfMint.burnLosses( - harden({ OtherTokens: AmountMath.make(tokenBrand, 7n) }), - zcfSeat1, - ); - // The zcfMint losses are subtracted from both the currentAllocation and the - // stagedAllocation, but currentAllocation does not include the - // stagedAllocations, and will not until zcf.reallocate is called. - t.deepEqual(zcfSeat1.getCurrentAllocation(), { - B: moola(5n), // no change since reallocate - OtherTokens: AmountMath.make(tokenBrand, 43n), - }); - t.deepEqual(zcfSeat1.getStagedAllocation(), { - B: moola(4n), - OtherTokens: AmountMath.make(tokenBrand, 38n), // includes decrementBy and burnLosses - }); - - // zcfMint.burnLosses on zcfSeat2 - zcfMint.burnLosses( - harden({ - MyToken: AmountMath.make(tokenBrand, 17n), - }), - zcfSeat2, - ); - t.deepEqual(zcfSeat2.getCurrentAllocation(), { - B: moola(1n), - MyToken: AmountMath.make(tokenBrand, 83n), - }); - t.false(zcfSeat2.hasStagedAllocation()); - - // zcfSeat2.incrementBy - zcfSeat2.incrementBy( - harden({ - OtherTokens: AmountMath.make(tokenBrand, 5n), // let's keep this keyword separate even though we don't have to - B: moola(1n), - }), - ); - t.deepEqual(zcfSeat2.getCurrentAllocation(), { - B: moola(1n), - MyToken: AmountMath.make(tokenBrand, 83n), - }); - t.deepEqual(zcfSeat2.getStagedAllocation(), { - B: moola(2n), - MyToken: AmountMath.make(tokenBrand, 83n), - OtherTokens: AmountMath.make(tokenBrand, 5n), - }); - - // zcfMint.mintGains on zcfSeat2 - zcfMint.mintGains( - harden({ - AnotherOne: AmountMath.make(tokenBrand, 2n), - }), - zcfSeat2, - ); - t.deepEqual(zcfSeat2.getCurrentAllocation(), { - B: moola(1n), - MyToken: AmountMath.make(tokenBrand, 83n), - AnotherOne: AmountMath.make(tokenBrand, 2n), - }); - t.deepEqual(zcfSeat2.getStagedAllocation(), { - B: moola(2n), - MyToken: AmountMath.make(tokenBrand, 83n), - OtherTokens: AmountMath.make(tokenBrand, 5n), - AnotherOne: AmountMath.make(tokenBrand, 2n), - }); - - // One last reallocate - zcf.reallocate(zcfSeat1, zcfSeat2); - t.deepEqual(zcfSeat2.getCurrentAllocation(), { - B: moola(2n), - MyToken: AmountMath.make(tokenBrand, 83n), - OtherTokens: AmountMath.make(tokenBrand, 5n), - AnotherOne: AmountMath.make(tokenBrand, 2n), - }); - t.deepEqual(zcfSeat1.getCurrentAllocation(), { - B: moola(4n), - OtherTokens: AmountMath.make(tokenBrand, 38n), - }); - t.false(zcfSeat1.hasStagedAllocation()); - t.false(zcfSeat2.hasStagedAllocation()); -}); diff --git a/packages/zoe/test/unitTests/zcf/zcf.test.js b/packages/zoe/test/unitTests/zcf/zcf.test.js index 89af906268f..d6a7d967e89 100644 --- a/packages/zoe/test/unitTests/zcf/zcf.test.js +++ b/packages/zoe/test/unitTests/zcf/zcf.test.js @@ -592,53 +592,6 @@ test(`zcf.makeZCFMint - burnLosses - seat exited`, async t => { ); }); -test('burnLosses - offer safety violation no staged allocation', async t => { - const { zcf } = await setupZCFTest(); - const doubloonMint = await zcf.makeZCFMint('Doubloons'); - const yenMint = await zcf.makeZCFMint('Yen'); - const { brand: doubloonBrand } = doubloonMint.getIssuerRecord(); - const { brand: yenBrand } = yenMint.getIssuerRecord(); - const yenAmount = AmountMath.make(yenBrand, 100n); - const proposal = harden({ - give: { DownPayment: yenAmount }, - want: { Bonus: AmountMath.make(doubloonBrand, 1_000_000n) }, - }); - const { zcfSeat: mintSeat, userSeat: payoutSeat } = zcf.makeEmptySeatKit(); - yenMint.mintGains( - harden({ - Cost: yenAmount, - }), - mintSeat, - ); - mintSeat.exit(); - const payout = await E(payoutSeat).getPayout('Cost'); - const payment = { DownPayment: payout }; - - const { zcfSeat } = await makeOffer( - zcf.getZoeService(), - zcf, - proposal, - payment, - ); - - zcfSeat.incrementBy({ SidePayment: AmountMath.make(yenBrand, 50n) }); - const staged = zcfSeat.getStagedAllocation(); - - t.throws( - () => - yenMint.burnLosses( - { DownPayment: AmountMath.make(yenBrand, 50n) }, - zcfSeat, - ), - { - message: - /The allocation after burning losses .* for the zcfSeat was not offer safe/, - }, - ); - t.truthy(zcfSeat.hasStagedAllocation()); - t.deepEqual(zcfSeat.getStagedAllocation().DownPayment, staged.DownPayment); -}); - test(`zcf.makeZCFMint - displayInfo`, async t => { const { zcf } = await setupZCFTest(); const zcfMint = await zcf.makeZCFMint( @@ -694,15 +647,10 @@ test(`zcfSeat from zcf.makeEmptySeatKit - only these properties exist`, async t 'fail', 'getAmountAllocated', 'getCurrentAllocation', - 'getStagedAllocation', 'getSubscriber', 'getProposal', 'hasExited', 'isOfferSafe', - 'incrementBy', - 'decrementBy', - 'clear', - 'hasStagedAllocation', ]; const { zcf } = await setupZCFTest(); const makeZCFSeat = () => zcf.makeEmptySeatKit().zcfSeat; @@ -863,26 +811,6 @@ test(`zcfSeat.getAmountAllocated from zcf.makeEmptySeatKit`, async t => { }); }); -test(`zcfSeat.incrementBy, decrementBy, zcf.reallocate from zcf.makeEmptySeatKit`, async t => { - const { zcf } = await setupZCFTest(); - const { zcfSeat: zcfSeat1 } = zcf.makeEmptySeatKit(); - const { zcfSeat: zcfSeat2 } = zcf.makeEmptySeatKit(); - - const issuerRecord1 = await allocateEasy(zcf, 'Stuff', zcfSeat1, 'A', 6n); - const six = AmountMath.make(issuerRecord1.brand, 6n); - zcfSeat1.decrementBy(harden({ A: six })); - zcfSeat2.incrementBy(harden({ B: six })); - - zcf.reallocate(zcfSeat1, zcfSeat2); - - t.deepEqual(zcfSeat1.getCurrentAllocation(), { - A: AmountMath.make(issuerRecord1.brand, 0n), - }); - t.deepEqual(zcfSeat2.getCurrentAllocation(), { - B: AmountMath.make(issuerRecord1.brand, 6n), - }); -}); - test(`userSeat.getProposal from zcf.makeEmptySeatKit`, async t => { const { zcf } = await setupZCFTest(); const makeUserSeat = async () => zcf.makeEmptySeatKit().userSeat; @@ -1000,176 +928,6 @@ test(`userSeat.getPayout() should throw from zcf.makeEmptySeatKit`, async t => { }); }); -test(`zcf.reallocate < 2 seats`, async t => { - const { zcf } = await setupZCFTest(); - // @ts-expect-error deliberate invalid arguments for testing - t.throws(() => zcf.reallocate(), { - message: - 'In "reallocate" method of (ZcfSeatManager seatManager): Expected at least 2 arguments: []', - }); -}); - -test(`zcf.reallocate 3 seats, rights conserved`, async t => { - const { moolaKit, simoleanKit, moola, simoleans } = setup(); - const { zoe, zcf } = await setupZCFTest({ - Moola: moolaKit.issuer, - Simoleans: simoleanKit.issuer, - }); - - const { zcfSeat: zcfSeat1 } = await makeOffer( - zoe, - zcf, - harden({ want: { A: simoleans(2n) }, give: { B: moola(3n) } }), - harden({ B: moolaKit.mint.mintPayment(moola(3n)) }), - ); - const { zcfSeat: zcfSeat2 } = await makeOffer( - zoe, - zcf, - harden({ - want: { Whatever: moola(2n) }, - give: { Whatever2: simoleans(2n) }, - }), - harden({ Whatever2: simoleanKit.mint.mintPayment(simoleans(2n)) }), - ); - - const { zcfSeat: zcfSeat3 } = await makeOffer( - zoe, - zcf, - harden({ - want: { Whatever: moola(1n) }, - }), - ); - zcfSeat1.decrementBy(harden({ B: moola(3n) })); - zcfSeat3.incrementBy(harden({ Whatever: moola(1n) })); - zcfSeat2.incrementBy(harden({ Whatever: moola(2n) })); - - zcfSeat2.decrementBy(harden({ Whatever2: simoleans(2n) })); - zcfSeat1.incrementBy(harden({ A: simoleans(2n) })); - - zcf.reallocate(zcfSeat1, zcfSeat2, zcfSeat3); - t.deepEqual(zcfSeat1.getCurrentAllocation(), { - A: simoleans(2n), - B: moola(0n), - }); - - t.deepEqual(zcfSeat2.getCurrentAllocation(), { - Whatever: moola(2n), - Whatever2: simoleans(0n), - }); - - t.deepEqual(zcfSeat3.getCurrentAllocation(), { - Whatever: moola(1n), - }); -}); - -test(`zcf.reallocate 3 seats, some not staged`, async t => { - const { moolaKit, simoleanKit, moola, simoleans } = setup(); - const { zoe, zcf } = await setupZCFTest({ - Moola: moolaKit.issuer, - Simoleans: simoleanKit.issuer, - }); - - const { zcfSeat: zcfSeat1 } = await makeOffer( - zoe, - zcf, - harden({ want: { A: simoleans(2n) }, give: { B: moola(3n) } }), - harden({ B: moolaKit.mint.mintPayment(moola(3n)) }), - ); - const { zcfSeat: zcfSeat2 } = await makeOffer( - zoe, - zcf, - harden({ - want: { Whatever: moola(2n) }, - give: { Whatever2: simoleans(2n) }, - }), - harden({ Whatever2: simoleanKit.mint.mintPayment(simoleans(2n)) }), - ); - - const { zcfSeat: zcfSeat3 } = await makeOffer( - zoe, - zcf, - harden({ - want: { Whatever: moola(1n) }, - }), - ); - - zcfSeat1.incrementBy( - harden({ - A: simoleans(100n), - }), - ); - - t.throws(() => zcf.reallocate(zcfSeat1, zcfSeat2, zcfSeat3), { - message: - 'Reallocate failed because a seat had no staged allocation. Please add or subtract from the seat and then reallocate.', - }); - - t.deepEqual(zcfSeat1.getCurrentAllocation(), { - A: simoleans(0n), - B: moola(3n), - }); - t.deepEqual(zcfSeat2.getCurrentAllocation(), { - Whatever: moola(0n), - Whatever2: simoleans(2n), - }); - t.deepEqual(zcfSeat3.getCurrentAllocation(), { Whatever: moola(0n) }); -}); - -test(`zcf.reallocate 3 seats, rights NOT conserved`, async t => { - const { moolaKit, simoleanKit, moola, simoleans } = setup(); - const { zoe, zcf } = await setupZCFTest({ - Moola: moolaKit.issuer, - Simoleans: simoleanKit.issuer, - }); - - const { zcfSeat: zcfSeat1 } = await makeOffer( - zoe, - zcf, - harden({ want: { A: simoleans(2n) }, give: { B: moola(3n) } }), - harden({ B: moolaKit.mint.mintPayment(moola(3n)) }), - ); - const { zcfSeat: zcfSeat2 } = await makeOffer( - zoe, - zcf, - harden({ - want: { Whatever: moola(2n) }, - give: { Whatever2: simoleans(2n) }, - }), - harden({ Whatever2: simoleanKit.mint.mintPayment(simoleans(2n)) }), - ); - - const { zcfSeat: zcfSeat3 } = await makeOffer( - zoe, - zcf, - harden({ - want: { Whatever: moola(1n) }, - }), - ); - - zcfSeat2.decrementBy(harden({ Whatever2: simoleans(2n) })); - // This does not conserve rights in the slightest - zcfSeat1.incrementBy( - harden({ - A: simoleans(100n), - }), - ); - zcfSeat3.incrementBy(harden({ Whatever: moola(1n) })); - - t.throws(() => zcf.reallocate(zcfSeat1, zcfSeat2, zcfSeat3), { - message: /rights were not conserved for brand .*/, - }); - - t.deepEqual(zcfSeat1.getCurrentAllocation(), { - A: simoleans(0n), - B: moola(3n), - }); - t.deepEqual(zcfSeat2.getCurrentAllocation(), { - Whatever: moola(0n), - Whatever2: simoleans(2n), - }); - t.deepEqual(zcfSeat3.getCurrentAllocation(), { Whatever: moola(0n) }); -}); - test(`zcf.shutdown - userSeat exits`, async t => { const { zoe, zcf } = await setupZCFTest(); const { userSeat } = await makeOffer(zoe, zcf); From 7854c0af351e70ee12391941ea4e75edb52a0bae Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Tue, 21 Jan 2025 16:49:05 -0800 Subject: [PATCH 2/6] refactor: satisfies() no longer needs zcf param --- .../zoe/src/contractSupport/zoeHelpers.js | 4 +- .../contractSupport/zoeHelpers.test.js | 48 ++++--------------- 2 files changed, 12 insertions(+), 40 deletions(-) diff --git a/packages/zoe/src/contractSupport/zoeHelpers.js b/packages/zoe/src/contractSupport/zoeHelpers.js index 45a0c5c5732..69abe7d96d0 100644 --- a/packages/zoe/src/contractSupport/zoeHelpers.js +++ b/packages/zoe/src/contractSupport/zoeHelpers.js @@ -36,12 +36,12 @@ export const assertIssuerKeywords = (zcf, expected) => { * false and 1 for true. When multiples are introduced, any * positive return value will mean true. * - * @param {ZCF} zcf + * @param {any} _ignored no longer used. * @param {ZcfSeatPartial} seat * @param {AmountKeywordRecord} update * @returns {0|1} */ -export const satisfies = (zcf, seat, update) => { +export const satisfies = (_ignored, seat, update) => { const currentAllocation = seat.getCurrentAllocation(); const newAllocation = { ...currentAllocation, ...update }; const proposal = seat.getProposal(); diff --git a/packages/zoe/test/unitTests/contractSupport/zoeHelpers.test.js b/packages/zoe/test/unitTests/contractSupport/zoeHelpers.test.js index 03f7e19a100..7965ce0f4e1 100644 --- a/packages/zoe/test/unitTests/contractSupport/zoeHelpers.test.js +++ b/packages/zoe/test/unitTests/contractSupport/zoeHelpers.test.js @@ -1,7 +1,6 @@ import { test } from '@agoric/swingset-vat/tools/prepare-test-env-ava.js'; import { Far } from '@endo/marshal'; -import { makeScalarMapStore } from '@agoric/store'; import { setup } from '../setupBasicMints.js'; import { @@ -16,27 +15,6 @@ test('ZoeHelpers messages', t => { ); }); -function makeMockTradingZcfBuilder() { - const offers = makeScalarMapStore('offerHandle'); - const allocs = makeScalarMapStore('offerHandle'); - const reallocatedStagings = []; - - return Far('mockTradingZcfBuilder', { - addOffer: (keyword, offer) => offers.init(keyword, offer), - addAllocation: (keyword, alloc) => allocs.init(keyword, alloc), - /** @returns {ZCF} */ - build: () => - Far('mockZCF', { - // @ts-expect-error mock - getZoeService: () => {}, - reallocate: (...seatStagings) => { - reallocatedStagings.push(...seatStagings); - }, - getReallocatedStagings: () => reallocatedStagings, - }), - }); -} - test('ZoeHelpers satisfies blank proposal', t => { const { moola } = setup(); /** @type {ZCFSeat} */ @@ -45,10 +23,8 @@ test('ZoeHelpers satisfies blank proposal', t => { getCurrentAllocation: () => harden({ Asset: moola(10n) }), getProposal: () => harden({}), }); - const mockZCFBuilder = makeMockTradingZcfBuilder(); - const mockZCF = mockZCFBuilder.build(); t.truthy( - satisfies(mockZCF, fakeZcfSeat, { Gift: moola(3n) }), + satisfies({}, fakeZcfSeat, { Gift: moola(3n) }), `giving anything to a blank proposal is satisifying`, ); }); @@ -61,29 +37,27 @@ test('ZoeHelpers satisfies simple proposal', t => { getCurrentAllocation: () => harden({ Asset: moola(10n) }), getProposal: () => harden({ want: { Desire: moola(30n) } }), }); - const mockZCFBuilder = makeMockTradingZcfBuilder(); - const mockZCF = mockZCFBuilder.build(); t.falsy( - satisfies(mockZCF, fakeZcfSeat, { Desire: moola(3n) }), + satisfies({}, fakeZcfSeat, { Desire: moola(3n) }), `giving less than specified to a proposal is not satisifying`, ); t.falsy( - satisfies(mockZCF, fakeZcfSeat, { Gift: moola(3n) }), + satisfies({}, fakeZcfSeat, { Gift: moola(3n) }), `giving other than what's specified to a proposal is not satisifying`, ); t.truthy( - satisfies(mockZCF, fakeZcfSeat, { Desire: moola(30n) }), + satisfies({}, fakeZcfSeat, { Desire: moola(30n) }), `giving exactly what's proposed is satisifying`, ); t.truthy( - satisfies(mockZCF, fakeZcfSeat, { + satisfies({}, fakeZcfSeat, { Desire: moola(30n), Gift: simoleans(1n), }), `giving extras beyond what's proposed is satisifying`, ); t.truthy( - satisfies(mockZCF, fakeZcfSeat, { Desire: moola(40n) }), + satisfies({}, fakeZcfSeat, { Desire: moola(40n) }), `giving more than what's proposed is satisifying`, ); }); @@ -97,22 +71,20 @@ test('ZoeHelpers satisfies() with give', t => { getProposal: () => harden({ give: { Charge: moola(30n) }, want: { Desire: bucks(5n) } }), }); - const mockZCFBuilder = makeMockTradingZcfBuilder(); - const mockZCF = mockZCFBuilder.build(); t.falsy( - satisfies(mockZCF, fakeZcfSeat, { Charge: moola(0n), Desire: bucks(1n) }), + satisfies({}, fakeZcfSeat, { Charge: moola(0n), Desire: bucks(1n) }), `providing neither give nor want is not satisfying`, ); t.falsy( - satisfies(mockZCF, fakeZcfSeat, { Charge: moola(30n) }), + satisfies({}, fakeZcfSeat, { Charge: moola(30n) }), `providing less than what's wanted is not satisfying`, ); t.truthy( - satisfies(mockZCF, fakeZcfSeat, { Charge: moola(0n), Desire: bucks(40n) }), + satisfies({}, fakeZcfSeat, { Charge: moola(0n), Desire: bucks(40n) }), `providing more than what's wanted is satisfying`, ); t.truthy( - satisfies(mockZCF, fakeZcfSeat, { Desire: bucks(40n), Charge: moola(3n) }), + satisfies({}, fakeZcfSeat, { Desire: bucks(40n), Charge: moola(3n) }), `providing what's wanted makes it possible to reduce give`, ); }); From 24475c6c218eea055392ad7d933eb602744b20bf Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Wed, 22 Jan 2025 10:09:45 -0800 Subject: [PATCH 3/6] test: drop zcfProbe test --- .../test/bootstrapTests/zcf-upgrade.test.ts | 124 -------------- .../test/bootstrapTests/zcfProbe.contract.js | 154 ------------------ 2 files changed, 278 deletions(-) delete mode 100644 packages/boot/test/bootstrapTests/zcf-upgrade.test.ts delete mode 100644 packages/boot/test/bootstrapTests/zcfProbe.contract.js diff --git a/packages/boot/test/bootstrapTests/zcf-upgrade.test.ts b/packages/boot/test/bootstrapTests/zcf-upgrade.test.ts deleted file mode 100644 index 8dc3ddb07a0..00000000000 --- a/packages/boot/test/bootstrapTests/zcf-upgrade.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { eventLoopIteration } from '@agoric/internal/src/testing-utils.js'; -import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js'; -import bundleSource from '@endo/bundle-source'; - -import path from 'path'; - -import { makeAgoricNamesRemotesFromFakeStorage } from '@agoric/vats/tools/board-utils.js'; -import type { TestFn } from 'ava'; -import { matchAmount, makeSwingsetTestKit } from '../../tools/supports.js'; -import { makeZoeDriver } from '../../tools/drivers.js'; - -const dirname = path.dirname(new URL(import.meta.url).pathname); - -const ZCF_PROBE_SRC = './zcfProbe.contract.js'; - -/** - * @file Bootstrap test of upgrading ZCF to support atomicRearrange internally. - * - * The goal is to tell Zoe about a new version of ZCF that it should use when - * starting new contracts. Zoe wasn't previously configurable for that, so a - * prerequisite was to upgrade Zoe to a version that could have its ZCF - * updated. To test that we install a contract that can detect the variation - * among zcf versions, and run it before, in the middle and after the - * upgrades. - * - * 0. add a contract that can report on the state of ZCF's support for different - * versions of reallocation: staging, helper, and internal. - * 1. put new Zoe & ZCF bundles on chain - * 2. upgrade Zoe; return a new facet that supports ZCF update - * 3. tell Zoe to use new ZCF - * 4. restart the new contract; verify that the behavior is unchanged. - * 5. null upgrade the contract; verify that zcf supports internal rearrange. - * 6. [optional] fully upgrade the contract; verify that it works - */ - -export const makeZoeTestContext = async t => { - console.time('ZoeTestContext'); - const swingsetTestKit = await makeSwingsetTestKit(t.log, undefined, { - configSpecifier: '@agoric/vm-config/decentral-demo-config.json', - }); - - const { runUtils } = swingsetTestKit; - console.timeLog('DefaultTestContext', 'swingsetTestKit'); - const { EV } = runUtils; - - await eventLoopIteration(); - - // We don't need vaults, but this gets the brand, which is checked somewhere - // Wait for ATOM to make it into agoricNames - await EV.vat('bootstrap').consumeItem('vaultFactoryKit'); - console.timeLog('DefaultTestContext', 'vaultFactoryKit'); - - // has to be late enough for agoricNames data to have been published - const agoricNamesRemotes = makeAgoricNamesRemotesFromFakeStorage( - swingsetTestKit.storage, - ); - console.timeLog('DefaultTestContext', 'agoricNamesRemotes'); - - const zoeDriver = await makeZoeDriver(swingsetTestKit); - console.timeLog('DefaultTestContext', 'walletFactoryDriver'); - - console.timeEnd('DefaultTestContext'); - - return { - ...swingsetTestKit, - agoricNamesRemotes, - zoeDriver, - }; -}; - -const test = anyTest as TestFn>>; - -test.before(async t => { - t.context = await makeZoeTestContext(t); -}); - -test.after.always(t => t.context.shutdown?.()); - -test('run restart-vats proposal', async t => { - const { controller, buildProposal, evalProposal, zoeDriver } = t.context; - const { EV } = t.context.runUtils; - - const source = `${dirname}/${ZCF_PROBE_SRC}`; - const zcfProbeBundle = await bundleSource(source); - await controller.validateAndInstallBundle(zcfProbeBundle); - // This test self-sufficiently builds all the artifacts it needs. The test in - // .../packages/deployment/upgradeTest/upgrade-test-scripts/unreleased-upgrade/zoe-upgrade/ - // needs a bundled copy of ./zcfProbe.contract.js as of the final commit that will be - // installed on-chain. Uncomment the following line and add - // `import fs from "fs";` to generate a bundle of the contract. - // fs.writeFileSync('bundles/prober-contract-bundle.json', JSON.stringify(zcfProbeBundle)); - - const brandRecord = await zoeDriver.instantiateProbeContract(zcfProbeBundle); - const { brand, issuer } = brandRecord; - - t.deepEqual(await zoeDriver.verifyRealloc(), {}); - - const ducats = await zoeDriver.faucet(); - const initialAmount = await EV(issuer).getAmountOf(ducats); - - const beforeResult = await zoeDriver.probeReallocation(initialAmount, ducats); - t.true(beforeResult.stagingResult); - t.true(beforeResult.helperResult); - // In this version of the test, we're upgrading from new ZCF to new ZCF - t.true(beforeResult.internalResult); - matchAmount(t, (await zoeDriver.verifyRealloc()).Ducats, brand, 3n); - - t.log('building proposal'); - // /////// Upgrading //////////////////////////////// - await evalProposal( - buildProposal('@agoric/builders/scripts/vats/replace-zoe.js'), - ); - - t.log('upgrade zoe&zcf proposal executed'); - await zoeDriver.upgradeProbe(zcfProbeBundle); - const nextDucats = beforeResult.leftoverPayments.Ducats; - const nextAmount = await EV(issuer).getAmountOf(nextDucats); - - const afterResult = await zoeDriver.probeReallocation(nextAmount, nextDucats); - t.true(afterResult.stagingResult); - t.true(afterResult.helperResult); - t.true(afterResult.internalResult); - matchAmount(t, (await zoeDriver.verifyRealloc()).Ducats, brand, 6n); -}); diff --git a/packages/boot/test/bootstrapTests/zcfProbe.contract.js b/packages/boot/test/bootstrapTests/zcfProbe.contract.js deleted file mode 100644 index 326cb73ef5f..00000000000 --- a/packages/boot/test/bootstrapTests/zcfProbe.contract.js +++ /dev/null @@ -1,154 +0,0 @@ -import { makeTracer } from '@agoric/internal'; -import { E } from '@endo/far'; -import { - atomicRearrange, - provideAll, -} from '@agoric/zoe/src/contractSupport/index.js'; -import { M, prepareExoClass, provide } from '@agoric/vat-data'; -import { AmountMath } from '@agoric/ertp'; - -const trace = makeTracer('ZCF Probe'); - -const ZcfProbeI = M.interface('ZCF Probe', { - makeProbeHelperInvitation: M.call().returns(M.promise()), - makeProbeInternalInvitation: M.call().returns(M.promise()), - makeProbeStagingInvitation: M.call().returns(M.promise()), - getAllocation: M.call().returns(M.any()), - makeFaucetInvitation: M.call().returns(M.promise()), -}); - -// /** @type {ContractMeta} */ -// export const meta = { upgradability: 'canUpgrade' }; -// harden(meta); - -/** - * @param {ZCF} zcf - * @param {{ storageNode: StorageNode }} privateArgs - * @param {import('@agoric/vat-data').Baggage} baggage - */ -export const start = async (zcf, privateArgs, baggage) => { - const { probeMint } = await provideAll(baggage, { - probeMint: () => zcf.makeZCFMint('Ducats'), - }); - - const storageNode = privateArgs?.storageNode; - const makeZcfProbe = await prepareExoClass( - baggage, - 'zcfProbe', - ZcfProbeI, - () => ({ - stashSeat: zcf.makeEmptySeatKit().zcfSeat, - probeMint: null, - }), - { - makeProbeHelperInvitation() { - const { stashSeat } = this.state; - - const probeHelper = seat => { - trace('ProbeHelper'); - const originalAlloc = seat.getCurrentAllocation().Ducats; - const one = AmountMath.make(originalAlloc.brand, 1n); - let result; - try { - atomicRearrange(zcf, harden([[seat, stashSeat, { Ducats: one }]])); - - // the helper can fail silently if the most recent version of the - // library calls zcf.atomicRearrange() directly while running with a - // zcf that doesn't yet have atomicRearrange. This can happen when - // the prober bundle is built in a later version and run under - // docker in an earlier version. - result = !AmountMath.isEqual( - originalAlloc, - stashSeat.getCurrentAllocation().Ducats, - ); - } catch (e) { - result = false; - } - - seat.exit(); - return result; - }; - - return zcf.makeInvitation(probeHelper, 'probe helper'); - }, - makeProbeInternalInvitation() { - const { stashSeat } = this.state; - const probeInternal = seat => { - trace('ProbeIntrinsics'); - const originalAlloc = seat.getCurrentAllocation().Ducats; - const one = AmountMath.make(originalAlloc.brand, 1n); - let result; - try { - zcf.atomicRearrange(harden([[seat, stashSeat, { Ducats: one }]])); - result = true; - } catch (e) { - result = false; - } - - seat.clear(); - seat.exit(); - - trace('Intrinsics', result); - // write to vstorage so a test can detect it. - if (storageNode) { - void E(storageNode).setValue(`${result}`); - } - - return result; - }; - - return zcf.makeInvitation(probeInternal, 'probe intrinsic'); - }, - makeProbeStagingInvitation() { - const { stashSeat } = this.state; - - const probeStaging = seat => { - trace('ProbeStaging'); - - const originalAlloc = seat.getCurrentAllocation().Ducats; - const one = AmountMath.make(originalAlloc.brand, 1n); - let result; - try { - stashSeat.incrementBy(seat.decrementBy({ Ducats: one })); - zcf.reallocate(seat, stashSeat); - result = true; - } catch (e) { - seat.clear(); - stashSeat.clear(); - result = false; - } - - seat.exit(); - return result; - }; - - return zcf.makeInvitation(probeStaging, 'probe staging'); - }, - getAllocation() { - const { stashSeat } = this.state; - trace('getAllocation'); - - return stashSeat.getCurrentAllocation(); - }, - makeFaucetInvitation() { - return zcf.makeInvitation(async seat => { - trace('faucet'); - const { brand } = await probeMint.getIssuerRecord(); - - await probeMint.mintGains( - { Ducats: AmountMath.make(brand, 16n) }, - seat, - ); - seat.exit(); - return 'minted 16n Ducats'; - }, 'faucet'); - }, - }, - ); - - const probe = await provide(baggage, 'probe', () => makeZcfProbe()); - return harden({ - creatorFacet: probe, - }); -}; -harden(start); From e46d1028a7b63ef82195625f401faa760a31f09a Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Wed, 22 Jan 2025 10:32:58 -0800 Subject: [PATCH 4/6] docs: update references to zcf.reallocate() in docs --- packages/zoe/docs/seats.md | 5 ---- packages/zoe/docs/zcf-reallocate-flow.png | Bin 24569 -> 0 bytes packages/zoe/docs/zcf-reallocate-flow.puml | 26 -------------------- packages/zoe/docs/zoe-catalog-of-storage.md | 4 +-- 4 files changed, 2 insertions(+), 33 deletions(-) delete mode 100644 packages/zoe/docs/zcf-reallocate-flow.png delete mode 100644 packages/zoe/docs/zcf-reallocate-flow.puml diff --git a/packages/zoe/docs/seats.md b/packages/zoe/docs/seats.md index 4e09fc9752e..d42f3742ebe 100644 --- a/packages/zoe/docs/seats.md +++ b/packages/zoe/docs/seats.md @@ -13,11 +13,6 @@ __ZCFSeat.exit() Flow:__ ![ZCFSeat Exit Flow](./zcf-seat-exit-flow.png) -__ZCF.reallocate() Flow:__ - -![ZCF Reallocate Flow](./zcf-reallocate-flow.png) - - ## UserSeat The `UserSeat` is what is returned when a user calls diff --git a/packages/zoe/docs/zcf-reallocate-flow.png b/packages/zoe/docs/zcf-reallocate-flow.png deleted file mode 100644 index 29525f493f4a04ccee17f5f16ed4f392629f5042..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24569 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 { - object UserSeat - UserSeat : tryExit() - UserSeat : ... - - object ZoeSeatAdmin - ZoeSeatAdmin : exit() - ZoeSeatAdmin : replaceAllocation() -} - -package ZCF <> { - object ZCFSeat - ZCFSeat : exit() - ZCFSeat : ... - - object ZCFSeatAdmin - ZCFSeatAdmin : commit() - -} - -ZCFSeat --|> ZCFSeatAdmin : 2) looked up in internal map -ZCFSeatAdmin --|> ZoeSeatAdmin : 3) commit() -ZoeSeatAdmin --|> ZoeSeatAdmin : 4) replaceAllocation() -@enduml \ No newline at end of file diff --git a/packages/zoe/docs/zoe-catalog-of-storage.md b/packages/zoe/docs/zoe-catalog-of-storage.md index afee6d5374a..d6948ccf137 100644 --- a/packages/zoe/docs/zoe-catalog-of-storage.md +++ b/packages/zoe/docs/zoe-catalog-of-storage.md @@ -187,8 +187,8 @@ seats. ### sumsByBrand - Store from `@agoric/store` -Two of these are created in every call to `zcf.reallocate`, and then -are immediately dropped. +One of these is created in every call to `zcf.atomicRearrange`, and then +is immediately dropped. **Expected cardinality**: One key per brand of allocation reallocated over. From da28af3eb3772f875ef7785e8b7e6f3f2a964eba Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Wed, 22 Jan 2025 10:37:14 -0800 Subject: [PATCH 5/6] docs: drop obsolete comment references to incrementBy, decrementBy --- packages/zoe/src/contractFacet/zcfMint.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/zoe/src/contractFacet/zcfMint.js b/packages/zoe/src/contractFacet/zcfMint.js index 3fad8229c59..dc2c37012d5 100644 --- a/packages/zoe/src/contractFacet/zcfMint.js +++ b/packages/zoe/src/contractFacet/zcfMint.js @@ -93,7 +93,7 @@ export const prepareZcMint = ( // that any bug within Zoe that may affect this is caught. zcfSeat.isOfferSafe(allocationPlusGains) || Fail`The allocation after minting gains ${allocationPlusGains} for the zcfSeat was not offer safe`; - // No effects above, apart from incrementBy. Note COMMIT POINT within + // No effects above, Note COMMIT POINT within // reallocator.reallocate(). The following two steps *should* be // committed atomically, but it is not a disaster if they are // not. If we minted only, no one would ever get those @@ -123,7 +123,7 @@ export const prepareZcMint = ( zcfSeat.isOfferSafe(allocationMinusLosses) || Fail`The allocation after burning losses ${allocationMinusLosses} for the zcfSeat was not offer safe`; - // No effects above, apart from decrementBy. Note COMMIT POINT within + // No effects above, Note COMMIT POINT within // reallocator.reallocate(). The following two steps *should* be // committed atomically, but it is not a disaster if they are // not. If we only commit the allocationMinusLosses no one would From b6231b664813ca36b3206aef15d46ba2500dcc28 Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Wed, 22 Jan 2025 10:38:25 -0800 Subject: [PATCH 6/6] test: update obsolete reallocate() call in test --- packages/smart-wallet/test/gameAssetContract.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/smart-wallet/test/gameAssetContract.js b/packages/smart-wallet/test/gameAssetContract.js index 356ec3f4236..de605f19588 100644 --- a/packages/smart-wallet/test/gameAssetContract.js +++ b/packages/smart-wallet/test/gameAssetContract.js @@ -48,10 +48,13 @@ export const start = async zcf => { // We use the deprecated stage/reallocate API // so that we can test this with the version of zoe on mainnet1B. - playerSeat.decrementBy(gameSeat.incrementBy(give)); const tmp = mint.mintGains(want); - playerSeat.incrementBy(tmp.decrementBy(want)); - zcf.reallocate(playerSeat, tmp, gameSeat); + zcf.atomicRearrange( + harden([ + [playerSeat, gameSeat, give], + [tmp, playerSeat, want], + ]), + ); playerSeat.exit(true); return 'welcome to the game'; };