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); 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'; }; 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..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 @@ -67,12 +62,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/docs/zcf-reallocate-flow.png b/packages/zoe/docs/zcf-reallocate-flow.png deleted file mode 100644 index 29525f493f4..00000000000 Binary files a/packages/zoe/docs/zcf-reallocate-flow.png and /dev/null differ diff --git a/packages/zoe/docs/zcf-reallocate-flow.puml b/packages/zoe/docs/zcf-reallocate-flow.puml deleted file mode 100644 index c9dde0db61d..00000000000 --- a/packages/zoe/docs/zcf-reallocate-flow.puml +++ /dev/null @@ -1,26 +0,0 @@ -@startuml ZCF.reallocate() flow - -package ZoeService <> { - 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. 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..dc2c37012d5 100644 --- a/packages/zoe/src/contractFacet/zcfMint.js +++ b/packages/zoe/src/contractFacet/zcfMint.js @@ -87,19 +87,13 @@ 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 // 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 @@ -129,13 +123,7 @@ 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 + // 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 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/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/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/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`, ); }); 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);