Skip to content

Commit

Permalink
refactor(ertp): reallyPrepareIssuerKit
Browse files Browse the repository at this point in the history
  • Loading branch information
erights committed Nov 6, 2023
1 parent 97f26d4 commit 71cf064
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 97 deletions.
114 changes: 109 additions & 5 deletions packages/ERTP/src/issuerKit.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { preparePaymentLedger } from './paymentLedger.js';

import './types-ambient.js';

// TODO Why does TypeScript lose the `MapStore` typing of `Baggage` here, even
// though it knows the correct type at the exporting `@agoric/vat-data`
/** @typedef {import('@agoric/vat-data').Baggage} Baggage */

/**
Expand All @@ -22,6 +24,8 @@ import './types-ambient.js';
*/

/**
* Used _only_ internally, to make a new issuerKit or to revive an old one.
*
* @template {AssetKind} K
* @param {IssuerRecord<K>} issuerRecord
* @param {Baggage} issuerBaggage
Expand Down Expand Up @@ -78,6 +82,9 @@ harden(setupIssuerKit);
const INSTANCE_KEY = 'issuer';

/**
* Used _only_ to upgrade a predecessor issuerKit. Use `makeDurableIssuerKit` to
* make a new one.
*
* @template {AssetKind} K
* @param {Baggage} issuerBaggage
* @param {ShutdownWithFailure} [optShutdownWithFailure] If this issuer fails in
Expand All @@ -89,26 +96,51 @@ const INSTANCE_KEY = 'issuer';
* https://github.com/Agoric/agoric-sdk/issues/3434
* @returns {IssuerKit<K>}
*/
export const prepareIssuerKit = (
export const upgradeIssuerKit = (
issuerBaggage,
optShutdownWithFailure = undefined,
) => {
const issuerRecord = issuerBaggage.get(INSTANCE_KEY);
return setupIssuerKit(issuerRecord, issuerBaggage, optShutdownWithFailure);
};
harden(prepareIssuerKit);
harden(upgradeIssuerKit);

/**
* Does baggage already have an issuer from prepareIssuerKit()? That is: does it
* have the relevant keys defined?
* Confusingly, `prepareIssuerKit` was the original name for `upgradeIssuerKit`,
* even though it is used only to upgrade a predecessor issuerKit. Use
* `makeDurableIssuerKit` to make a new one.
*
* @deprecated Use `upgradeIssuerKit` instead if that's what you want. Or
* `reallyPrepareIssuerKit` if you want the behavior that should have been
* bound to this name.
*/
export const prepareIssuerKit = upgradeIssuerKit;

/**
* Does baggage already have an issuerKit?
*
* @param {Baggage} baggage
*/
export const hasIssuer = baggage => baggage.has(INSTANCE_KEY);

/** @typedef {Partial<{ elementShape: Pattern }>} IssuerOptionsRecord */
/**
* `elementShape`, may only be present for collection-style amounts. If present,
* it is a `Pattern` that every element of this issuerKits's amounts must
* satisfy. For example, the Zoe Invitation issuerKit uses an elementShape
* describing the invitation details for an individual invitation. An invitation
* purse or payment has an amount that can only be a set of these. (Though
* typically, the amount of an invitation payment is a singleton set. Such a
* payment is often referred to in the singular as "an invitation".)
*
* @typedef {Partial<{
* elementShape: Pattern;
* }>} IssuerOptionsRecord
*/

/**
* Used _only_ to make a _new_ durable issuer, i.e., the initial incarnation of
* that issuer.
*
* @template {AssetKind} K The name becomes part of the brand in asset
* descriptions. The name is useful for debugging and double-checking
* assumptions, but should not be trusted wrt any external namespace. For
Expand Down Expand Up @@ -151,6 +183,78 @@ export const makeDurableIssuerKit = (
harden(makeDurableIssuerKit);

/**
* What _should_ have been named `prepareIssuerKit`. Used to either revive a
* predecessor issuerKit, or to make a new durable one if it is absent, and to
* place it in baggage for the next successor.
*
* @template {AssetKind} K The name becomes part of the brand in asset
* descriptions. The name is useful for debugging and double-checking
* assumptions, but should not be trusted wrt any external namespace. For
* example, anyone could create a new issuer kit with name 'BTC', but it is
* not bitcoin or even related. It is only the name according to that issuer
* and brand.
*
* The assetKind will be used to import a specific mathHelpers from the
* mathHelpers library. For example, natMathHelpers, the default, is used for
* basic fungible tokens.
*
* `displayInfo` gives information to the UI on how to display the amount.
* @param {Baggage} issuerBaggage
* @param {string} name
* @param {K} [assetKind]
* @param {AdditionalDisplayInfo} [displayInfo]
* @param {ShutdownWithFailure} [optShutdownWithFailure] If this issuer fails in
* the middle of an atomic action (which btw should never happen), it
* potentially leaves its ledger in a corrupted state. If this function was
* provided, then the failed atomic action will call it, so that some larger
* unit of computation, like the enclosing vat, can be shutdown before
* anything else is corrupted by that corrupted state. See
* https://github.com/Agoric/agoric-sdk/issues/3434
* @param {IssuerOptionsRecord} [options]
* @returns {IssuerKit<K>}
*/
export const reallyPrepareIssuerKit = (
issuerBaggage,
name,
// @ts-expect-error K could be instantiated with a different subtype of AssetKind
assetKind = AssetKind.NAT,
displayInfo = harden({}),
optShutdownWithFailure = undefined,
options = {},
) => {
if (hasIssuer(issuerBaggage)) {
const { elementShape: _ = undefined } = options;
const issuerKit = upgradeIssuerKit(issuerBaggage, optShutdownWithFailure);

// TODO check consistency with name, assetKind, displayInfo, elementShape.
// Consistency either means that these are the same, or that they differ
// in a direction we are prepared to upgrade.

// @ts-expect-error Type parameter confusion.
return issuerKit;
} else {
const issuerKit = makeDurableIssuerKit(
issuerBaggage,
name,
assetKind,
displayInfo,
optShutdownWithFailure,
options,
);
return issuerKit;
}
};
harden(reallyPrepareIssuerKit);

/**
* Used _only_ to make a new issuerKit that is effectively non-durable. This is
* currently done by making a durable one in a baggage not reachable from
* anywhere. TODO Once rebuilt on zones, this should instead just build on the
* virtual zone. See https://github.com/Agoric/agoric-sdk/pull/7116
*
* Currently used for testing only. Should probably continue to be used for
* testing only.
*
* @template {AssetKind} [K='nat'] The name becomes part of the brand in asset
* descriptions. The name is useful for debugging and double-checking
* assumptions, but should not be trusted wrt any external namespace. For
Expand Down
2 changes: 2 additions & 0 deletions packages/ERTP/src/paymentLedger.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,8 @@ export const preparePaymentLedger = (
return payment;
};

/** @type {() => Purse<K>} */
// @ts-expect-error type parameter confusion
const makeEmptyPurse = preparePurseKind(
issuerBaggage,
name,
Expand Down
18 changes: 18 additions & 0 deletions packages/ERTP/src/purse.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,26 @@ import { prepareExoClassKit, makeScalarBigSetStore } from '@agoric/vat-data';
import { AmountMath } from './amountMath.js';
import { makeTransientNotifierKit } from './transientNotifier.js';

// TODO `InterfaceGuard` type parameter
/** @typedef {import('@endo/patterns').InterfaceGuard} InterfaceGuard */
/** @typedef {import('@agoric/vat-data').Baggage} Baggage */

const { Fail } = assert;

/**
* @param {Baggage} issuerBaggage
* @param {string} name
* @param {AssetKind} assetKind
* @param {Brand} brand
* @param {{
* purse: InterfaceGuard;
* depositFacet: InterfaceGuard;
* }} PurseIKit
* @param {{
* depositInternal: any;
* withdrawInternal: any;
* }} purseMethods
*/
export const preparePurseKind = (
issuerBaggage,
name,
Expand Down
29 changes: 21 additions & 8 deletions packages/ERTP/src/types-ambient.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
/**
* @callback IssuerIsLive Return true if the payment continues to exist.
*
* If the payment is a promise, the operation will proceed upon resolution.
* If the payment is a promise, the operation will proceed upon fulfillment.
* @param {ERef<Payment>} payment
* @returns {Promise<boolean>}
*/
Expand All @@ -125,18 +125,25 @@
* Because the payment is not trusted, we cannot call a method on it directly,
* and must use the issuer instead.
*
* If the payment is a promise, the operation will proceed upon resolution.
* If the payment is a promise, the operation will proceed upon fulfillment.
* @param {ERef<Payment>} payment
* @returns {Promise<Amount<K>>}
*/

/**
* @callback IssuerBurn Burn all of the digital assets in the payment.
* `optAmount` is optional. If `optAmount` is present, the code will insist
* that the amount of the digital assets in the payment is equal to
* `optAmount`, to prevent sending the wrong payment and other confusion.
* `optAmountShape` is optional. If the `optAmountShape` pattern is present,
* the amount of the digital assets in the payment must match
* `optAmountShape`, to prevent sending the wrong payment and other
* confusion.
*
* If the payment is a promise, the operation will proceed upon resolution.
* If the payment is a promise, the operation will proceed upon fulfillment.
*
* As always with optional `Pattern` arguments, keep in mind that technically
* the value `undefined` itself is a valid `Key` and therefore a valid
* `Pattern`. But in optional pattern position, a top level `undefined` will
* be interpreted as absence. If you want to express a `Pattern` that will
* match only `undefined`, use `M.undefined()` instead.
* @param {ERef<Payment>} payment
* @param {Pattern} [optAmountShape]
* @returns {Promise<Amount>}
Expand Down Expand Up @@ -171,7 +178,8 @@
* @template {AssetKind} [K=AssetKind]
* @typedef {object} PaymentLedger
* @property {Mint<K>} mint
* @property {Purse<K>} mintRecoveryPurse
* @property {Purse<K>} mintRecoveryPurse Useful only to get the recovery set
* associated with minted payments that are still live.
* @property {Issuer<K>} issuer
* @property {Brand<K>} brand
*/
Expand All @@ -180,7 +188,8 @@
* @template {AssetKind} [K=AssetKind]
* @typedef {object} IssuerKit
* @property {Mint<K>} mint
* @property {Purse<K>} mintRecoveryPurse
* @property {Purse<K>} mintRecoveryPurse Useful only to get the recovery set
* associated with minted payments that are still live.
* @property {Issuer<K>} issuer
* @property {Brand<K>} brand
* @property {DisplayInfo} displayInfo
Expand Down Expand Up @@ -211,6 +220,8 @@
* Payment containing newly minted amount.
*/

// /////////////////////////// Purse / Payment /////////////////////////////////

/**
* @callback DepositFacetReceive
* @param {Payment} payment
Expand Down Expand Up @@ -298,6 +309,8 @@
* be treated with suspicion and verified elsewhere.
*/

// /////////////////////////// MathHelpers /////////////////////////////////////

/**
* @template {AmountValue} V
* @typedef {object} MathHelpers All of the difference in how digital asset
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import {
AssetKind,
makeDurableIssuerKit,
prepareIssuerKit,
upgradeIssuerKit,
} from '../../../src/index.js';

export const prepareErtpService = (baggage, exitVatWithFailure) => {
Expand Down Expand Up @@ -36,7 +36,7 @@ export const prepareErtpService = (baggage, exitVatWithFailure) => {
});

for (const issuerBaggage of issuerBaggageSet.values()) {
prepareIssuerKit(issuerBaggage, exitVatWithFailure);
upgradeIssuerKit(issuerBaggage, exitVatWithFailure);
}

return ertpService;
Expand Down
16 changes: 8 additions & 8 deletions packages/inter-protocol/src/price/fluxAggregatorContract.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
// @jessie-check

import {
hasIssuer,
makeDurableIssuerKit,
prepareIssuerKit,
} from '@agoric/ertp';
import { reallyPrepareIssuerKit } from '@agoric/ertp';
import { handleParamGovernance } from '@agoric/governance';
import { makeTracer, StorageNodeShape } from '@agoric/internal';
import { prepareDurablePublishKit } from '@agoric/notifier';
Expand Down Expand Up @@ -72,9 +68,13 @@ export const start = async (zcf, privateArgs, baggage) => {

// xxx uses contract baggage as issuerBagage, assumes one issuer in this contract
/** @type {import('./roundsManager.js').QuoteKit} */
const quoteIssuerKit = hasIssuer(baggage)
? prepareIssuerKit(baggage)
: makeDurableIssuerKit(baggage, 'quote', 'set');
const quoteIssuerKit = reallyPrepareIssuerKit(
baggage,
'quote',
'set',
undefined,
undefined,
);

const {
highPrioritySendersManager,
Expand Down
14 changes: 3 additions & 11 deletions packages/vats/src/mintHolder.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
// @ts-check
// @jessie-check

import {
hasIssuer,
makeDurableIssuerKit,
prepareIssuerKit,
} from '@agoric/ertp';
import { reallyPrepareIssuerKit } from '@agoric/ertp';

/** @typedef {import('@agoric/vat-data').Baggage} Baggage */

Expand All @@ -26,12 +22,8 @@ import {
* @param {Baggage} baggage
*/
function provideIssuerKit(zcf, baggage) {
if (!hasIssuer(baggage)) {
const { keyword, assetKind, displayInfo } = zcf.getTerms();
return makeDurableIssuerKit(baggage, keyword, assetKind, displayInfo);
} else {
return prepareIssuerKit(baggage);
}
const { keyword, assetKind, displayInfo } = zcf.getTerms();
return reallyPrepareIssuerKit(baggage, keyword, assetKind, displayInfo);
}

/** @type {ContractMeta} */
Expand Down
Loading

0 comments on commit 71cf064

Please sign in to comment.