Skip to content

Commit

Permalink
refactor: Durability for ERTP (#5283)
Browse files Browse the repository at this point in the history
Co-authored-by: Chris Hibbert <hibbert@agoric.com>
  • Loading branch information
erights and Chris-Hibbert authored Jul 8, 2022
1 parent 4cca744 commit eab27e4
Show file tree
Hide file tree
Showing 47 changed files with 923 additions and 487 deletions.
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/swingset-vat": "^0.28.0",
"@agoric/vat-data": "^0.3.1",
"@endo/eventual-send": "^0.15.5",
"@endo/far": "^0.2.5",
"@endo/marshal": "^0.6.9",
"@endo/promise-kit": "^0.2.43"
},
Expand Down
81 changes: 0 additions & 81 deletions packages/ERTP/src/brand.js

This file was deleted.

165 changes: 108 additions & 57 deletions packages/ERTP/src/issuerKit.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,54 +3,37 @@

import { assert } from '@agoric/assert';
import { assertPattern } from '@agoric/store';
import { makeScalarBigMapStore } from '@agoric/vat-data';

import { AssetKind, assertAssetKind } from './amountMath.js';
import { coerceDisplayInfo } from './displayInfo.js';
import { makeBrand } from './brand.js';
import { makePaymentLedger } from './paymentLedger.js';
import { vivifyPaymentLedger } from './paymentLedger.js';

import './types.js';

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

/**
* @template {AssetKind} K
* The allegedName becomes part of the brand in asset descriptions. The
* allegedName doesn't have to be a string, but it will only be used for
* its value. The allegedName is useful for debugging and double-checking
* assumptions, but should not be trusted.
*
* 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 {string} allegedName
* @param {K} [assetKind=AssetKind.NAT]
* @param {AdditionalDisplayInfo} [displayInfo={}]
* @param {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
* 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 {Partial<{elementSchema: Pattern}>} [options]
* @returns {{
* mint: Mint<K>,
* issuer: Issuer<K>,
* brand: Brand<K>,
* displayInfo: DisplayInfo,
* }}
* @returns {IssuerKit<K>}
*/
const makeIssuerKit = (
allegedName,
// @ts-expect-error K could be instantiated with a different subtype of AssetKind
assetKind = AssetKind.NAT,
displayInfo = harden({}),
export const vivifyIssuerKit = (
issuerBaggage,
optShutdownWithFailure = undefined,
{ elementSchema = undefined } = {},
) => {
assert.typeof(allegedName, 'string');
const name = issuerBaggage.get('name');
const assetKind = issuerBaggage.get('assetKind');
const displayInfo = issuerBaggage.get('displayInfo');
const elementSchema = issuerBaggage.get('elementSchema');
assert.typeof(name, 'string');
assertAssetKind(assetKind);

// Add assetKind to displayInfo, or override if present
Expand All @@ -63,32 +46,13 @@ const makeIssuerKit = (
assertPattern(elementSchema);
}

/**
* We can define this function to use the in-scope `issuer` variable
* before that variable is initialized, as long as the variable is
* initialized before the function is called.
*
* @param {Issuer} allegedIssuer
* @returns {boolean}
*/
// eslint-disable-next-line no-use-before-define
const isMyIssuerNow = allegedIssuer => allegedIssuer === issuer;

const brand = makeBrand(
allegedName,
isMyIssuerNow,
cleanDisplayInfo,
assetKind,
elementSchema,
);

// Attenuate the powerful authority to mint and change balances
const { issuer, mint } = makePaymentLedger(
allegedName,
brand,
const { issuer, mint, brand } = vivifyPaymentLedger(
issuerBaggage,
name,
assetKind,
cleanDisplayInfo,
brand.getAmountSchema(),
elementSchema,
optShutdownWithFailure,
);

Expand All @@ -99,9 +63,96 @@ const makeIssuerKit = (
displayInfo: cleanDisplayInfo,
});
};
harden(vivifyIssuerKit);

harden(makeIssuerKit);

export { makeIssuerKit };
/**
* @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=AssetKind.NAT]
* @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 {Partial<{elementSchema: Pattern}>} [options]
* @returns {IssuerKit<K>}
*/
export const makeDurableIssuerKit = (
issuerBaggage,
name,
// @ts-expect-error K could be instantiated with a different subtype of AssetKind
assetKind = AssetKind.NAT,
displayInfo = harden({}),
optShutdownWithFailure = undefined,
{ elementSchema = undefined } = {},
) => {
issuerBaggage.init('name', name);
issuerBaggage.init('assetKind', assetKind);
issuerBaggage.init('displayInfo', displayInfo);
issuerBaggage.init('elementSchema', elementSchema);
return vivifyIssuerKit(issuerBaggage, optShutdownWithFailure);
};
harden(makeDurableIssuerKit);

/** @typedef {ReturnType<typeof makeIssuerKit>} IssuerKit */
/**
* @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 {string} name
* @param {K} [assetKind=AssetKind.NAT]
* @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 {Partial<{elementSchema: Pattern}>} [options]
* @returns {IssuerKit<K>}
*/
export const makeIssuerKit = (
name,
// @ts-expect-error K could be instantiated with a different subtype of AssetKind
assetKind = AssetKind.NAT,
displayInfo = harden({}),
optShutdownWithFailure = undefined,
{ elementSchema = undefined } = {},
) =>
makeDurableIssuerKit(
makeScalarBigMapStore('dropped issuer kit', { durable: true }),
name,
assetKind,
displayInfo,
optShutdownWithFailure,
{ elementSchema },
);
harden(makeIssuerKit);
19 changes: 12 additions & 7 deletions packages/ERTP/src/payment.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
// @ts-check

import { defineKind } from '@agoric/vat-data';
import { defineDurableKind, provideKindHandle } from '@agoric/vat-data';

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

/**
* @template {AssetKind} K
* @param {string} allegedName
* @param {Brand<K>} brand
* @param {Baggage} issuerBaggage
* @param {string} name
* @param {() => Brand<K>} getBrand must not be called before the issuerKit is
* created
* @returns {() => Payment<K>}
*/
export const definePaymentKind = (allegedName, brand) => {
const makePayment = defineKind(`${allegedName} payment`, () => ({}), {
getAllegedBrand: () => brand,
export const vivifyPaymentKind = (issuerBaggage, name, getBrand) => {
const paymentKindHandle = provideKindHandle(issuerBaggage, `${name} payment`);
const makePayment = defineDurableKind(paymentKindHandle, () => ({}), {
getAllegedBrand: getBrand,
});
return makePayment;
};
harden(definePaymentKind);
harden(vivifyPaymentKind);
Loading

0 comments on commit eab27e4

Please sign in to comment.