Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(ertp): ertp on zones #7116

Merged
merged 3 commits into from
Feb 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/ERTP/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@agoric/notifier": "^0.6.2",
"@agoric/store": "^0.9.2",
"@agoric/vat-data": "^0.5.2",
"@agoric/zone": "^0.2.2",
"@endo/eventual-send": "^1.1.2",
"@endo/far": "^1.0.4",
"@endo/marshal": "^1.3.0",
Expand Down
7 changes: 7 additions & 0 deletions packages/ERTP/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,10 @@
export * from './amountMath.js';
export * from './issuerKit.js';
export * from './typeGuards.js';

/**
* Importing Baggage from `@agoric/ertp` is deprecated. Import Baggage from
* `@agoric/vat-data` instead
*
* @typedef {import('@agoric/vat-data').Baggage} Baggage
*/
25 changes: 12 additions & 13 deletions packages/ERTP/src/issuerKit.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,14 @@
import { assert } from '@agoric/assert';
import { assertPattern } from '@agoric/store';
import { makeScalarBigMapStore } from '@agoric/vat-data';
import { makeDurableZone } from '@agoric/zone/durable.js';

import { AssetKind, assertAssetKind } from './amountMath.js';
import { coerceDisplayInfo } from './displayInfo.js';
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 */

/**
* @template {AssetKind} K
* @typedef {object} IssuerRecord
Expand All @@ -28,7 +25,7 @@ import './types-ambient.js';
*
* @template {AssetKind} K
* @param {IssuerRecord<K>} issuerRecord
* @param {Baggage} issuerBaggage
* @param {import('@agoric/zone').Zone} issuerZone
* @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
Expand All @@ -40,7 +37,7 @@ import './types-ambient.js';
*/
const setupIssuerKit = (
{ name, assetKind, displayInfo, elementShape },
issuerBaggage,
issuerZone,
optShutdownWithFailure = undefined,
) => {
assert.typeof(name, 'string');
Expand All @@ -60,7 +57,7 @@ const setupIssuerKit = (
/** @type {PaymentLedger<K>} */
// @ts-expect-error could be instantiated with different subtype of AssetKind
const { issuer, mint, brand, mintRecoveryPurse } = preparePaymentLedger(
issuerBaggage,
issuerZone,
name,
assetKind,
cleanDisplayInfo,
Expand All @@ -86,7 +83,7 @@ const INSTANCE_KEY = 'issuer';
* make a new one.
*
* @template {AssetKind} K
* @param {Baggage} issuerBaggage
* @param {import('@agoric/vat-data').Baggage} issuerBaggage
* @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
Expand All @@ -101,14 +98,15 @@ export const upgradeIssuerKit = (
optShutdownWithFailure = undefined,
) => {
const issuerRecord = issuerBaggage.get(INSTANCE_KEY);
return setupIssuerKit(issuerRecord, issuerBaggage, optShutdownWithFailure);
const issuerZone = makeDurableZone(issuerBaggage);
erights marked this conversation as resolved.
Show resolved Hide resolved
return setupIssuerKit(issuerRecord, issuerZone, optShutdownWithFailure);
};
harden(upgradeIssuerKit);

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

Expand Down Expand Up @@ -142,7 +140,7 @@ export const hasIssuer = baggage => baggage.has(INSTANCE_KEY);
* basic fungible tokens.
*
* `displayInfo` gives information to the UI on how to display the amount.
* @param {Baggage} issuerBaggage
* @param {import('@agoric/vat-data').Baggage} issuerBaggage
* @param {string} name
* @param {K} [assetKind]
* @param {AdditionalDisplayInfo} [displayInfo]
Expand All @@ -167,7 +165,8 @@ export const makeDurableIssuerKit = (
) => {
const issuerData = harden({ name, assetKind, displayInfo, elementShape });
issuerBaggage.init(INSTANCE_KEY, issuerData);
return setupIssuerKit(issuerData, issuerBaggage, optShutdownWithFailure);
const issuerZone = makeDurableZone(issuerBaggage);
erights marked this conversation as resolved.
Show resolved Hide resolved
return setupIssuerKit(issuerData, issuerZone, optShutdownWithFailure);
};
harden(makeDurableIssuerKit);

Expand All @@ -187,7 +186,7 @@ harden(makeDurableIssuerKit);
* basic fungible tokens.
*
* `displayInfo` gives information to the UI on how to display the amount.
* @param {Baggage} issuerBaggage
* @param {import('@agoric/vat-data').Baggage} issuerBaggage
* @param {string} name
* @param {K} [assetKind]
* @param {AdditionalDisplayInfo} [displayInfo]
Expand Down
4 changes: 2 additions & 2 deletions packages/ERTP/src/mathHelpers/copyBagMathHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import {
} from '@agoric/store';
import '../types-ambient.js';

/** @type {CopyBag} */
/** @type {import('@endo/patterns').CopyBag} */
const empty = makeCopyBag([]);

/** @type {MathHelpers<CopyBag>} */
/** @type {MathHelpers<import('@endo/patterns').CopyBag>} */
export const copyBagMathHelpers = harden({
doCoerce: bag => {
mustMatch(bag, M.bag(), 'bag of amount');
Expand Down
18 changes: 5 additions & 13 deletions packages/ERTP/src/payment.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
// @jessie-check

import { initEmpty } from '@agoric/store';
import { prepareExoClass } from '@agoric/vat-data';

/** @typedef {import('@endo/patterns').MethodGuard} MethodGuard */
/**
* @template {Record<string | symbol, MethodGuard>} [T=Record<string | symbol, MethodGuard>]
* @typedef {import('@endo/patterns').InterfaceGuard<T>} InterfaceGuard
*/
/** @typedef {import('@agoric/vat-data').Baggage} Baggage */

// TODO Type InterfaceGuard better than InterfaceGuard<any>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need help. Finally resorted to any after everything else I tried failed. I don't understand why this problem arose in this PR but not prior to this PR.

I got rid of all the @typedefs in case it could help with this, which it did not. But I left the non-@typedef form in anyway because it is now our recommended practice. Preserves type info better in the IDE.

/**
* @template {AssetKind} K
* @param {Baggage} issuerBaggage
* @param {import('@agoric/zone').Zone} issuerZone
* @param {string} name
* @param {Brand<K>} brand
* @param {InterfaceGuard} PaymentI
* @param {import('@endo/patterns').InterfaceGuard<any>} PaymentI
* @returns {() => Payment<K>}
*/
export const preparePaymentKind = (issuerBaggage, name, brand, PaymentI) => {
const makePayment = prepareExoClass(
issuerBaggage,
export const preparePaymentKind = (issuerZone, name, brand, PaymentI) => {
const makePayment = issuerZone.exoClass(
`${name} payment`,
PaymentI,
initEmpty,
Expand Down
64 changes: 34 additions & 30 deletions packages/ERTP/src/paymentLedger.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,13 @@
/* eslint-disable no-use-before-define */
import { isPromise } from '@endo/promise-kit';
import { mustMatch, M, keyEQ } from '@agoric/store';
import {
provideDurableWeakMapStore,
prepareExo,
provide,
} from '@agoric/vat-data';
import { AmountMath } from './amountMath.js';
import { preparePaymentKind } from './payment.js';
import { preparePurseKind } from './purse.js';

import '@agoric/store/exported.js';
import { BrandI, makeIssuerInterfaces } from './typeGuards.js';

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

const { details: X, quote: q, Fail } = assert;

/**
Expand Down Expand Up @@ -74,7 +67,7 @@ const amountShapeFromElementShape = (brand, assetKind, elementShape) => {
* minting and transfer authority originates here.
*
* @template {AssetKind} K
* @param {Baggage} issuerBaggage
* @param {import('@agoric/zone').Zone} issuerZone
erights marked this conversation as resolved.
Show resolved Hide resolved
* @param {string} name
* @param {K} assetKind
* @param {DisplayInfo<K>} displayInfo
Expand All @@ -83,16 +76,20 @@ const amountShapeFromElementShape = (brand, assetKind, elementShape) => {
* @returns {PaymentLedger<K>}
*/
export const preparePaymentLedger = (
issuerBaggage,
issuerZone,
name,
assetKind,
displayInfo,
elementShape,
optShutdownWithFailure = undefined,
) => {
/** @type {Brand<K>} */
// @ts-expect-error XXX callWhen
const brand = prepareExo(issuerBaggage, `${name} brand`, BrandI, {
// Should be
// at-ts-expect-error XXX callWhen
// but ran into the usual disagreement between local lint and CI
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
// @ts-ignore
const brand = issuerZone.exo(`${name} brand`, BrandI, {
isMyIssuer(allegedIssuer) {
// BrandI delays calling this method until `allegedIssuer` is a Remotable
return allegedIssuer === issuer;
Expand Down Expand Up @@ -121,7 +118,7 @@ export const preparePaymentLedger = (
amountShape,
);

const makePayment = preparePaymentKind(issuerBaggage, name, brand, PaymentI);
const makePayment = preparePaymentKind(issuerZone, name, brand, PaymentI);

/** @type {ShutdownWithFailure} */
const shutdownLedgerWithFailure = reason => {
Expand All @@ -139,11 +136,9 @@ export const preparePaymentLedger = (
};

/** @type {WeakMapStore<Payment, Amount>} */
const paymentLedger = provideDurableWeakMapStore(
issuerBaggage,
'paymentLedger',
{ valueShape: amountShape },
);
const paymentLedger = issuerZone.weakMapStore('paymentLedger', {
valueShape: amountShape,
});

/**
* A withdrawn live payment is associated with the recovery set of the purse
Expand All @@ -164,10 +159,7 @@ export const preparePaymentLedger = (
*
* @type {WeakMapStore<Payment, SetStore<Payment>>}
*/
const paymentRecoverySets = provideDurableWeakMapStore(
issuerBaggage,
'paymentRecoverySets',
);
const paymentRecoverySets = issuerZone.weakMapStore('paymentRecoverySets');

/**
* To maintain the invariants listed in the `paymentRecoverySets` comment,
Expand Down Expand Up @@ -293,7 +285,7 @@ export const preparePaymentLedger = (
/** @type {() => Purse<K>} */
// @ts-expect-error type parameter confusion
const makeEmptyPurse = preparePurseKind(
issuerBaggage,
issuerZone,
name,
assetKind,
brand,
Expand All @@ -305,8 +297,12 @@ export const preparePaymentLedger = (
);

/** @type {Issuer<K>} */
// @ts-expect-error cast due to callWhen discrepancy
const issuer = prepareExo(issuerBaggage, `${name} issuer`, IssuerI, {
// Should be
// at-ts-expect-error cast due to callWhen discrepancy
// but ran into the usual disagreement between local lint and CI
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
// @ts-ignore
const issuer = issuerZone.exo(`${name} issuer`, IssuerI, {
getBrand() {
return brand;
},
Expand Down Expand Up @@ -359,20 +355,28 @@ export const preparePaymentLedger = (
* Because the `mintRecoveryPurse` is placed in baggage, even if the caller of
* `makeIssuerKit` drops it on the floor, it can still be recovered in an
* emergency upgrade.
*
* @type {Purse<K>}
*/
const mintRecoveryPurse = provide(issuerBaggage, 'mintRecoveryPurse', () =>
makeEmptyPurse(),
// Should be
// at-ts-expect-error checked cast
// but ran into the usual disagreement between local lint and IDE lint.
// Don't know yet about lint under CI.
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
// @ts-ignore
const mintRecoveryPurse = /** @type {Purse<K>} */ (
issuerZone.makeOnce('mintRecoveryPurse', () => makeEmptyPurse())
);

/** @type {Mint<K>} */
const mint = prepareExo(issuerBaggage, `${name} mint`, MintI, {
const mint = issuerZone.exo(`${name} mint`, MintI, {
getIssuer() {
return issuer;
},
mintPayment(newAmount) {
// @ts-expect-error checked cast
// Should be
// at-ts-expect-error checked cast
// but ran into the usual disagreement between local lint and CI
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
// @ts-ignore
newAmount = coerce(newAmount);
mustMatch(newAmount, amountShape, 'minted amount');
// `rawPayment` is not associated with any recovery set, and
Expand Down
22 changes: 8 additions & 14 deletions packages/ERTP/src/purse.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,27 @@
import { M } from '@agoric/store';
import { prepareExoClassKit, makeScalarBigSetStore } from '@agoric/vat-data';
import { AmountMath } from './amountMath.js';
import { makeTransientNotifierKit } from './transientNotifier.js';
import { makeAmountStore } from './amountStore.js';

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

const { Fail } = assert;

// TODO Type InterfaceGuard better than InterfaceGuard<any>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need help. Finally resorted to any after everything else I tried failed. I don't understand why this problem arose in this PR but not prior to this PR.

I got rid of all the @typedefs in case it could help with this, which it did not. But I left the non-@typedef form in anyway because it is now our recommended practice. Preserves type info better in the IDE.

/**
* @param {Baggage} issuerBaggage
* @param {import('@agoric/zone').Zone} issuerZone
* @param {string} name
* @param {AssetKind} assetKind
* @param {Brand} brand
* @param {{
* purse: InterfaceGuard;
* depositFacet: InterfaceGuard;
* purse: import('@endo/patterns').InterfaceGuard<any>;
* depositFacet: import('@endo/patterns').InterfaceGuard<any>;
* }} PurseIKit
* @param {{
* depositInternal: any;
* withdrawInternal: any;
* }} purseMethods
*/
export const preparePurseKind = (
issuerBaggage,
issuerZone,
name,
assetKind,
brand,
Expand All @@ -36,6 +32,7 @@ export const preparePurseKind = (

// Note: Virtual for high cardinality, but *not* durable, and so
// broken across an upgrade.
// TODO propagate zonifying to notifiers, maybe?
const { provideNotifier, update: updateBalance } = makeTransientNotifierKit();

// - This kind is a pair of purse and depositFacet that have a 1:1
Expand All @@ -45,17 +42,14 @@ export const preparePurseKind = (
// that created depositFacet as needed. But this approach ensures a constant
// identity for the facet and exercises the multi-faceted object style.
const { depositInternal, withdrawInternal } = purseMethods;
const makePurseKit = prepareExoClassKit(
issuerBaggage,
const makePurseKit = issuerZone.exoClassKit(
`${name} Purse`,
PurseIKit,
() => {
const currentBalance = AmountMath.makeEmpty(brand, assetKind);

/** @type {SetStore<Payment>} */
const recoverySet = makeScalarBigSetStore('recovery set', {
durable: true,
});
const recoverySet = issuerZone.detached().setStore('recovery set');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does detached() do? Is there any documentation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

detached() is how you ask that a new one always be made, even if it is a durable zone. Without detach(), issuerZone.setStore('recovery set') would reuse a 'recovery set' entry in issuerZone if there is one, and register the newly made one there otherwise. IOW, detached() is the new way to say make*SetStore rather than provide*SetStore.

@michaelfig , I leave the documentation question to you. Thanks.


return {
currentBalance,
Expand Down
Loading
Loading