Skip to content

Commit

Permalink
refactor: move atomicRearrange into Zcf
Browse files Browse the repository at this point in the history
This is the next step of #6577 (atomic reallocations for
contracts). It adds native support in ZCF for atomicReallocate, which
makes it possible to update contracts from the helper to use the
ZCF-native approach, which is a prerequisite for removing the old
hazardous staging-based approach.

This PR only affects ZCF, though it updates types referenced from
inter-protocol contracts. This is required in order to keep the
mono-repo consistent, and has no run-time impact on the contracts,
even if changes are pushed to some live contracts even before the ZOE
change takes effect.

The sample contracts in Zoe have also been updated to use the new
API. These do not affect the interprotocol contracts. The only zoe
contract referenced by inter-protocol contracts is
scaledPriceAuthority, which is not changed here.

The new code was tested with versions of the inter-protocol contracts
updated to use the ZCF-native API. Those contracts were reverted to
their old form before merging this PR since the contracts should be
updated separately from Zoe. None of the contracts should be updated
to use the new reallocation API until this PR is running on
chain. They can be individually updated once this code is in place.
  • Loading branch information
Chris-Hibbert committed Jun 8, 2023
1 parent 2043349 commit b6e326b
Show file tree
Hide file tree
Showing 24 changed files with 516 additions and 73 deletions.
4 changes: 2 additions & 2 deletions packages/inter-protocol/src/auction/auctioneer.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ const distributeProportionalShares = (

const collShare = makeRatioFromAmounts(unsoldCollateral, totalCollDeposited);
const currShare = makeRatioFromAmounts(proceeds, totalCollDeposited);
/** @type {import('@agoric/zoe/src/contractSupport/atomicTransfer.js').TransferPart[]} */
/** @type {TransferPart[]} */
const transfers = [];
let proceedsLeft = proceeds;
let collateralLeft = unsoldCollateral;
Expand Down Expand Up @@ -255,7 +255,7 @@ export const distributeProportionalSharesWithLimits = (
// collateral to reach their share. Then see what's left, and allocate it
// among the remaining depositors. Escape to distributeProportionalShares if
// anything doesn't work.
/** @type {import('@agoric/zoe/src/contractSupport/atomicTransfer.js').TransferPart[]} */
/** @type {TransferPart[]} */
const transfers = [];
let proceedsLeft = proceeds;
let collateralLeft = unsoldCollateral;
Expand Down
2 changes: 1 addition & 1 deletion packages/inter-protocol/src/vaultFactory/liquidation.js
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ export const getLiquidatableVaults = (
const { zcfSeat: liqSeat } = zcf.makeEmptySeatKit();
let totalDebt = AmountMath.makeEmpty(debtBrand);
let totalCollateral = AmountMath.makeEmpty(collateralBrand);
/** @type {import('@agoric/zoe/src/contractSupport/atomicTransfer.js').TransferPart[]} */
/** @type {TransferPart[]} */
const transfers = [];

for (const vault of vaultsToLiquidate.values()) {
Expand Down
2 changes: 1 addition & 1 deletion packages/inter-protocol/src/vaultFactory/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
* @param {ZCFSeat} mintReceiver
* @param {Amount<'nat'>} toMint
* @param {Amount<'nat'>} fee
* @param {import('@agoric/zoe/src/contractSupport/atomicTransfer.js').TransferPart[]} transfers
* @param {TransferPart[]} transfers
* @returns {void}
*/

Expand Down
2 changes: 1 addition & 1 deletion packages/inter-protocol/src/vaultFactory/vault.js
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ export const prepareVault = (baggage, makeRecorderKit, zcf) => {

const giveMintedTaken = AmountMath.subtract(fp.give.Minted, surplus);

/** @type {import('@agoric/zoe/src/contractSupport/atomicTransfer.js').TransferPart[]} */
/** @type {TransferPart[]} */
const transfers = harden([
[clientSeat, vaultSeat, { Collateral: fp.give.Collateral }],
[vaultSeat, clientSeat, { Collateral: fp.want.Collateral }],
Expand Down
2 changes: 1 addition & 1 deletion packages/inter-protocol/src/vaultFactory/vaultDirector.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ const prepareVaultDirector = (
mintAndTransfer: (mintReceiver, toMint, fee, nonMintTransfers) => {
const kept = AmountMath.subtract(toMint, fee);
debtMint.mintGains(harden({ Minted: toMint }), mintSeat);
/** @type {import('@agoric/zoe/src/contractSupport/atomicTransfer.js').TransferPart[]} */
/** @type {TransferPart[]} */
const transfers = [
...nonMintTransfers,
[mintSeat, rewardPoolSeat, { Minted: fee }],
Expand Down
2 changes: 1 addition & 1 deletion packages/inter-protocol/src/vaultFactory/vaultManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,7 @@ export const prepareVaultManagerKit = (
if (plan.transfersToVault.length > 0) {
const transfers = plan.transfersToVault.map(
([vaultIndex, amounts]) =>
/** @type {import('@agoric/zoe/src/contractSupport/atomicTransfer.js').TransferPart} */ ([
/** @type {TransferPart} */ ([
liqSeat,
vaultsInPlan[vaultIndex].getVaultSeat(),
amounts,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
/** @file DEPRECATED use the vault test driver instead */
import { AmountMath, makeIssuerKit } from '@agoric/ertp';

import { assert } from '@agoric/assert';
import { makePublishKit, observeNotifier } from '@agoric/notifier';
import {
makeFakeMarshaller,
makeFakeStorage,
} from '@agoric/notifier/tools/testSupports.js';
import {
atomicRearrange,
prepareRecorderKit,
unitAmount,
} from '@agoric/zoe/src/contractSupport/index.js';
Expand Down Expand Up @@ -101,14 +99,14 @@ export async function start(zcf, privateArgs, baggage) {
const mintAndTransfer = (mintReceiver, toMint, fee, nonMintTransfers) => {
const kept = AmountMath.subtract(toMint, fee);
stableMint.mintGains(harden({ Minted: toMint }), mintSeat);
/** @type {import('@agoric/zoe/src/contractSupport/atomicTransfer.js').TransferPart[]} */
/** @type {TransferPart[]} */
const transfers = [
...nonMintTransfers,
[mintSeat, vaultFactorySeat, { Minted: fee }],
[mintSeat, mintReceiver, { Minted: kept }],
];
try {
atomicRearrange(zcf, harden(transfers));
zcf.atomicRearrange(harden(transfers));
} catch (e) {
console.error('mintAndTransfer caught', e);
stableMint.burnLosses(harden({ Minted: toMint }), mintSeat);
Expand Down
104 changes: 104 additions & 0 deletions packages/zoe/src/contractFacet/reallocate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { makeScalarMapStore } from '@agoric/vat-data';

import { assertRightsConserved } from './rightsConservation.js';
import { addToAllocation, subtractFromAllocation } from './allocationMath.js';

const { Fail } = assert;

/** @typedef {Array<AmountKeywordRecord>} TransactionList */

/**
* Convert from a list of transfer descriptions ([fromSeat, toSeat, fromAmount,
* toAmount], with many parts optional) to a list of resulting allocations for
* each of the seats mentioned.
*
* @param {Array<TransferPart>} transfers
* @returns {[ZCFSeat,AmountKeywordRecord][]}
*/
export const makeAllocationMap = transfers => {
/** @type {MapStore<ZCFSeat, [TransactionList, TransactionList]>} */
const allocations = makeScalarMapStore();

const getAllocations = seat => {
if (allocations.has(seat)) {
return allocations.get(seat);
}

/** @type {[TransactionList, TransactionList]} */
const pair = [[], []];
allocations.init(seat, pair);
return pair;
};

const updateAllocations = (seat, newAllocation) => {
allocations.set(seat, newAllocation);
};

const decrementAllocation = (seat, decrement) => {
const [incr, decr] = getAllocations(seat);

const newDecr = [...decr, decrement];
updateAllocations(seat, [incr, newDecr]);
};

const incrementAllocation = (seat, increment) => {
const [incr, decr] = getAllocations(seat);

const newIncr = [...incr, increment];
updateAllocations(seat, [newIncr, decr]);
};

for (const [
fromSeat = undefined,
toSeat = undefined,
fromAmounts = undefined,
toAmounts = undefined,
] of transfers) {
if (fromSeat) {
if (!fromAmounts) {
throw Fail`Transfer from ${fromSeat} must say how much`;
}
decrementAllocation(fromSeat, fromAmounts);
if (toSeat) {
// Conserved transfer between seats
if (toAmounts) {
// distinct amounts, so we check conservation.
assertRightsConserved(
Object.values(fromAmounts),
Object.values(toAmounts),
);
incrementAllocation(toSeat, toAmounts);
} else {
// fromAmounts will be used for toAmounts as well
incrementAllocation(toSeat, fromAmounts);
}
} else {
// Transfer only from fromSeat
!toAmounts ||
Fail`Transfer without toSeat cannot have toAmounts ${toAmounts}`;
}
} else {
toSeat || Fail`Transfer must have at least one of fromSeat or toSeat`;
// Transfer only to toSeat
!fromAmounts ||
Fail`Transfer without fromSeat cannot have fromAmounts ${fromAmounts}`;
toAmounts || Fail`Transfer to ${toSeat} must say how much`;
incrementAllocation(toSeat, toAmounts);
}
}

/** @type {[ZCFSeat,AmountKeywordRecord][]} */
const resultingAllocations = [];
for (const seat of allocations.keys()) {
const [incrList, decrList] = getAllocations(seat);
let newAlloc = seat.getCurrentAllocation();
for (const incr of incrList) {
newAlloc = addToAllocation(newAlloc, incr);
}
for (const decr of decrList) {
newAlloc = subtractFromAllocation(newAlloc, decr);
}
resultingAllocations.push([seat, newAlloc]);
}
return resultingAllocations;
};
25 changes: 23 additions & 2 deletions packages/zoe/src/contractFacet/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
* synchronously from within the contract, and usually is referred to
* in code as zcf.
*
* @property {(transfers: TransferPart[]) => void} atomicRearrange - atomically reallocate amounts among seats.
* @property {Reallocate} reallocate - reallocate amounts among seats.
* Deprecated: Use atomicRearrange instead.
* @property {(keyword: Keyword) => void} assertUniqueKeyword - check
Expand Down Expand Up @@ -59,6 +60,9 @@
*/

/**
* @deprecated reallocate() will be supported until at least 2023/09/01. It may
* be removed without further warning any time after 2023/11/01.
*
* @typedef {(seat1: ZCFSeat, seat2: ZCFSeat, ...seatRest:
* Array<ZCFSeat>) => void} Reallocate
*
Expand All @@ -83,6 +87,15 @@
* effect offer safety for seats whose allocations change.
*/

/**
* @typedef {[
* fromSeat?: ZCFSeat,
* toSeat?: ZCFSeat,
* fromAmounts?: AmountKeywordRecord,
* toAmounts?: AmountKeywordRecord
* ]} TransferPart
*/

/**
* @callback SaveIssuer
*
Expand Down Expand Up @@ -184,6 +197,14 @@
* @returns {Amount<any>}
*/

/**
* @deprecated Use atomicRearrange instead
*
* @callback DeprecatedIncrementDecrementBy
* @param {AmountKeywordRecord} amountKeywordRecord
* @returns {AmountKeywordRecord}
*/

/**
* @typedef {object} ZCFSeat
* @property {(completion?: Completion) => void} exit
Expand All @@ -198,9 +219,9 @@
* @property {() => boolean} hasStagedAllocation
* Deprecated: Use atomicRearrange instead
* @property {(newAllocation: Allocation) => boolean} isOfferSafe
* @property {(amountKeywordRecord: AmountKeywordRecord) => AmountKeywordRecord} incrementBy
* @property {DeprecatedIncrementDecrementBy} incrementBy
* Deprecated: Use atomicRearrange instead
* @property {(amountKeywordRecord: AmountKeywordRecord) => AmountKeywordRecord} decrementBy
* @property {DeprecatedIncrementDecrementBy} decrementBy
* Deprecated: Use atomicRearrange instead
* @property {() => void} clear
* Deprecated: Use atomicRearrange instead
Expand Down
Loading

0 comments on commit b6e326b

Please sign in to comment.