From 7a9266eed231220e2e37bf4c38b0ea8b2092928e Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Tue, 28 May 2019 14:42:59 -0700 Subject: [PATCH 01/18] merge conflicts resolved --- collections/EMap.js | 199 ++++++++++++++++++ collections/ESet.js | 195 +++++++++++++++++ collections/PrivateName.js | 58 +++++ collections/allSettled.js | 37 ++++ collections/insist.js | 38 ++++ collections/sameStructure.js | 247 ++++++++++++++++++++++ demo/contractHost/assays.js | 314 ++++++++++++++++++++++++++++ demo/contractHost/bootstrap.js | 290 ++++++++++++++++++------- demo/contractHost/chit.js | 185 ++++++++++++++++ demo/contractHost/coveredCall.js | 55 +++++ demo/contractHost/escrow.js | 119 +++++++---- demo/contractHost/issuers.chainmail | 56 +++++ demo/contractHost/issuers.js | 292 ++++++++++++++++++++++++++ demo/contractHost/vat-alice.js | 276 +++++++++++++++++------- demo/contractHost/vat-bob.js | 161 +++++++------- demo/contractHost/vat-fred.js | 150 +++++++++++++ demo/contractHost/vat-host.js | 148 +------------ demo/contractHost/vat-mint.js | 81 +------ 18 files changed, 2411 insertions(+), 490 deletions(-) create mode 100644 collections/EMap.js create mode 100644 collections/ESet.js create mode 100644 collections/PrivateName.js create mode 100644 collections/allSettled.js create mode 100644 collections/insist.js create mode 100644 collections/sameStructure.js create mode 100644 demo/contractHost/assays.js create mode 100644 demo/contractHost/chit.js create mode 100644 demo/contractHost/coveredCall.js create mode 100644 demo/contractHost/issuers.chainmail create mode 100644 demo/contractHost/issuers.js create mode 100644 demo/contractHost/vat-fred.js diff --git a/collections/EMap.js b/collections/EMap.js new file mode 100644 index 00000000000..c540f0600a4 --- /dev/null +++ b/collections/EMap.js @@ -0,0 +1,199 @@ +// Copyright (C) 2019 Agoric, under Apache License 2.0 + +import harden from '@agoric/harden'; + +import { makePrivateName } from './PrivateName'; +import { insist } from './insist'; + +// Maps from EMaps to encapsulated Maps. All lookups from this table +// are only queries. (Except for the one in the FlexMap constructor) +const hiddenEMap = makePrivateName(); + +// Abstract superclass with query-only methods. +class EMap { + constructor(optIterable = undefined) { + insist(new.target !== EMap)`\ +EMap is abstract`; + const newHidden = new Map(optIterable); + hiddenEMap.init(this, newHidden); + } + + snapshot() { + // copy + // eslint-disable-next-line no-use-before-define + return new FixedMap(hiddenEMap.get(this)); + } + + diverge() { + // copy + // eslint-disable-next-line no-use-before-define + return new FlexMap(hiddenEMap.get(this)); + } + + readOnlyView() { + // eslint-disable-next-line no-use-before-define + const result = new InternalReadOnlyMap(); + // Share the hidden map itself, but the readOnlyView only grants + // the ability to query it. + hiddenEMap.init(result, hiddenEMap.get(this)); + return result; + } + + // Forward query protocol from Map + + keys() { + return hiddenEMap.get(this).keys(); + } + + values() { + return hiddenEMap.get(this).values(); + } + + entries() { + return hiddenEMap.get(this).entries(); + } + + [Symbol.iterator]() { + return hiddenEMap.get(this)[Symbol.iterator](); + } + + forEach(callback) { + return hiddenEMap.get(this).forEach(callback); + } + + get(member) { + return hiddenEMap.get(this).get(member); + } + + has(member) { + return hiddenEMap.get(this).has(member); + } + + get size() { + return hiddenEMap.get(this).size; + } +} +harden(EMap); + +// Guarantees that the map contents is stable. +// TODO: Somehow arrange for this to be pass-by-copy-ish. +class FixedMap extends EMap { + constructor(optIterable = undefined) { + insist(new.target === FixedMap)`\ +FixedMap is final`; + super(optIterable); + harden(this); + } + + // override + snapshot() { + return this; + } + + // override + readOnlyView() { + return this; + } +} +harden(FixedMap); + +// Maps from FlexMaps to encapsulated Maps, a subset of +// hiddenEMap. Lookups from this table can mutate. +const hiddenFlexMap = makePrivateName(); + +// Supports mutation. +class FlexMap extends EMap { + constructor(optIterable = undefined) { + insist(new.target === FlexMap)`\ +FlexMap is final`; + super(optIterable); + // Be very scared of the following line, since it looks up on + // hiddenEMap for purposes of enabling mutation. We assume it is + // safe because the `new.target` insist check above ensures this + // constructor is being called as-if directly with `new`. We say + // "as-if" because it might be invoked by `Reflect.construct`, but + // only in an equivalent manner. + hiddenFlexMap.init(this, hiddenEMap.get(this)); + harden(this); + } + + // Like snapshot() except that this FlexMap loses ownership and + // becomes useless. + takeSnapshot() { + const hiddenMap = hiddenFlexMap.get(this); + + // Ideally we'd delete, as we would from a WeakMap. However, + // PrivateName, to emulate class private names, has no delete. + // hiddenFlexMap.delete(this); + // hiddenEMap.delete(this); + hiddenFlexMap.set(this, null); + hiddenEMap.set(this, null); + + const result = new FixedMap(); + hiddenEMap.init(result, hiddenMap); + return result; + } + + // Like diverge() except that this FlexMap loses ownership and + // becomes useless. + takeDiverge() { + const hiddenMap = hiddenFlexMap.get(this); + + // Ideally we'd delete, as we would from a WeakMap. However, + // PrivateName, to emulate class private names, has no delete. + // hiddenFlexMap.delete(this); + // hiddenEMap.delete(this); + hiddenFlexMap.set(this, null); + hiddenEMap.set(this, null); + + const result = new FlexMap(); + hiddenEMap.init(result, hiddenMap); + hiddenFlexMap.init(result, hiddenMap); + return result; + } + + // Forward update protocol from Map + + set(k, v) { + return hiddenFlexMap.get(this).set(k, v); + } + + clear() { + return hiddenFlexMap.get(this).clear(); + } + + delete(m) { + return hiddenFlexMap.get(this).delete(m); + } +} +harden(FlexMap); + +// The constructor for internal use only. The rest of the class is +// available from the pseudo-constructor ReadOnlyMap. +class InternalReadOnlyMap extends EMap { + constructor() { + super(); + harden(this); + } + + // override + readOnlyView() { + return this; + } +} + +// Fake constructor becomes the public identity of the class. +// Guarantee that an instance of ReadOnlyMap does not provide the +// ability to modify. +function ReadOnlyMap() { + insist(new.target === ReadOnlyMap)`\ +ReadOnlyMap is final`; + insist(false)`\ +Use readOnlyView() to view an existing EMap`; +} +Object.setPrototypeOf(ReadOnlyMap, EMap); +ReadOnlyMap.prototype = InternalReadOnlyMap.prototype; +ReadOnlyMap.prototype.constructor = ReadOnlyMap; +harden(ReadOnlyMap); + +export { EMap, FixedMap, FlexMap, ReadOnlyMap }; diff --git a/collections/ESet.js b/collections/ESet.js new file mode 100644 index 00000000000..99eddf7b12a --- /dev/null +++ b/collections/ESet.js @@ -0,0 +1,195 @@ +// Copyright (C) 2019 Agoric, under Apache License 2.0 + +import harden from '@agoric/harden'; + +import { makePrivateName } from './PrivateName'; +import { insist } from './insist'; + +// Maps from ESets to encapsulated Sets. All lookups from this table +// are only queries. (Except for the one in the FlexSet constructor) +const hiddenESet = makePrivateName(); + +// Abstract superclass with query-only methods. +class ESet { + constructor(optIterable = undefined) { + insist(new.target !== ESet)`\ +ESet is abstract`; + const newHidden = new Set(optIterable); + hiddenESet.init(this, newHidden); + } + + snapshot() { + // copy + // eslint-disable-next-line no-use-before-define + return new FixedSet(hiddenESet.get(this)); + } + + diverge() { + // copy + // eslint-disable-next-line no-use-before-define + return new FlexSet(hiddenESet.get(this)); + } + + readOnlyView() { + // eslint-disable-next-line no-use-before-define + const result = new InternalReadOnlySet(); + // Share the hidden set itself, but the readOnlyView only grants + // the ability to query it. + hiddenESet.init(result, hiddenESet.get(this)); + return result; + } + + // Forward query protocol from Set + + keys() { + return hiddenESet.get(this).keys(); + } + + values() { + return hiddenESet.get(this).values(); + } + + entries() { + return hiddenESet.get(this).entries(); + } + + [Symbol.iterator]() { + return hiddenESet.get(this)[Symbol.iterator](); + } + + forEach(callback) { + return hiddenESet.get(this).forEach(callback); + } + + has(member) { + return hiddenESet.get(this).has(member); + } + + get size() { + return hiddenESet.get(this).size; + } +} +harden(ESet); + +// Guarantees that the set contents is stable. +// TODO: Somehow arrange for this to be pass-by-copy-ish. +class FixedSet extends ESet { + constructor(optIterable = undefined) { + insist(new.target === FixedSet)`\ +FixedSet is final`; + super(optIterable); + harden(this); + } + + // override + snapshot() { + return this; + } + + // override + readOnlyView() { + return this; + } +} +harden(FixedSet); + +// Maps from FlexSets to encapsulated Sets, a subset of +// hiddenESet. Lookups from this table can mutate. +const hiddenFlexSet = makePrivateName(); + +// Supports mutation. +class FlexSet extends ESet { + constructor(optIterable = undefined) { + insist(new.target === FlexSet)`\ +FlexSet is final`; + super(optIterable); + // Be very scared of the following line, since it looks up on + // hiddenESet for purposes of enabling mutation. We assume it is + // safe because the `new.target` insist check above ensures this + // constructor is being called as-if directly with `new`. We say + // "as-if" because it might be invoked by `Reflect.construct`, but + // only in an equivalent manner. + hiddenFlexSet.init(this, hiddenESet.get(this)); + harden(this); + } + + // Like snapshot() except that this FlexSet loses ownership and + // becomes useless. + takeSnapshot() { + const hiddenSet = hiddenFlexSet.get(this); + + // Ideally we'd delete, as we would from a WeakMap. However, + // PrivateName, to emulate class private names, has no delete. + // hiddenFlexSet.delete(this); + // hiddenESet.delete(this); + hiddenFlexSet.set(this, null); + hiddenESet.set(this, null); + + const result = new FixedSet(); + hiddenESet.init(result, hiddenSet); + return result; + } + + // Like diverge() except that this FlexSet loses ownership and + // becomes useless. + takeDiverge() { + const hiddenSet = hiddenFlexSet.get(this); + + // Ideally we'd delete, as we would from a WeakMap. However, + // PrivateName, to emulate class private names, has no delete. + // hiddenFlexSet.delete(this); + // hiddenESet.delete(this); + hiddenFlexSet.set(this, null); + hiddenESet.set(this, null); + + const result = new FlexSet(); + hiddenESet.init(result, hiddenSet); + hiddenFlexSet.init(result, hiddenSet); + return result; + } + + // Forward update protocol from Set + + add(m) { + return hiddenFlexSet.get(this).add(m); + } + + clear() { + return hiddenFlexSet.get(this).clear(); + } + + delete(m) { + return hiddenFlexSet.get(this).delete(m); + } +} +harden(FlexSet); + +// The constructor for internal use only. The rest of the class is +// available from the pseudo-constructor ReadOnlySet. +class InternalReadOnlySet extends ESet { + constructor() { + super(); + harden(this); + } + + // override + readOnlyView() { + return this; + } +} + +// Fake constructor becomes the public identity of the class. +// Guarantee that an instance of ReadOnlySet does not provide the +// ability to modify. +function ReadOnlySet() { + insist(new.target === ReadOnlySet)`\ +ReadOnlySet is final`; + insist(false)`\ +Use readOnlyView() to view an existing ESet`; +} +Object.setPrototypeOf(ReadOnlySet, ESet); +ReadOnlySet.prototype = InternalReadOnlySet.prototype; +ReadOnlySet.prototype.constructor = ReadOnlySet; +harden(ReadOnlySet); + +export { ESet, FixedSet, FlexSet, ReadOnlySet }; diff --git a/collections/PrivateName.js b/collections/PrivateName.js new file mode 100644 index 00000000000..031ef9f2726 --- /dev/null +++ b/collections/PrivateName.js @@ -0,0 +1,58 @@ +// Copyright (C) 2019 Agoric, uner Apache license 2.0 + +import harden from '@agoric/harden'; + +import { insist } from './insist'; + +function makePrivateName(...args) { + const wm = new WeakMap(...args); + return harden({ + has(key) { + return wm.has(key); + }, + init(key, value) { + insist(!wm.has(key))`\ +key already registered: ${key}`; + wm.set(key, value); + }, + get(key) { + insist(wm.has(key))`\ +key not found: ${key}`; + return wm.get(key); + }, + set(key, value) { + insist(wm.has(key))`\ +key not found: ${key}`; + wm.set(key, value); + }, + }); +} +harden(makePrivateName); + +const bootPN = makePrivateName(); + +class PrivateName { + constructor(...args) { + bootPN.init(this, makePrivateName(...args)); + harden(this); + } + + has(key) { + return bootPN.get(this).has(key); + } + + init(key, value) { + bootPN.get(this).init(key, value); + } + + get(key) { + return bootPN.get(this).get(key); + } + + set(key, value) { + return bootPN.get(this).set(key, value); + } +} +harden(PrivateName); + +export { makePrivateName, PrivateName }; diff --git a/collections/allSettled.js b/collections/allSettled.js new file mode 100644 index 00000000000..c56f8564456 --- /dev/null +++ b/collections/allSettled.js @@ -0,0 +1,37 @@ +import harden from '@agoric/harden'; + +import makePromise from '../src/kernel/makePromise'; + +// TODO Reconcile with spec of Promise.allSettled +function allSettled(promises) { + promises = [...promises]; + const len = promises.length; + if (len === 0) { + return []; + } + const result = makePromise(); + const list = []; + let count = len; + for (let i = 0; i < len; i += 1) { + Promise.resolve(promises[i]).then( + v => { + list[i] = v; + count -= 1; + if (count === 0) { + result.res(list); + } + }, + _ => { + list[i] = promises[i]; + count -= 1; + if (count === 0) { + result.res(list); + } + }, + ); + } + return result.p; +} +harden(allSettled); + +export { allSettled }; diff --git a/collections/insist.js b/collections/insist.js new file mode 100644 index 00000000000..b670cdd76f2 --- /dev/null +++ b/collections/insist.js @@ -0,0 +1,38 @@ +// Copyright (C) 2019 Agoric, under Apache License 2.0 + +import harden from '@agoric/harden'; + +// Insist that expr is truthy with a tagged template literal like +// insist(expr)`....` +// If expr is falsy, then the template contents are reported to the +// console and also in a thrown error. +// +// The literal portions of the template are assumed non-sensitive, as +// are the typeof types of the substitution values. These are +// assembles into the thrown error message. The actual contents of the +// substitution values are assumed sensitive, to be revealed to the +// console only. We assume only the virtual platform's owner can read +// what is written to the console, where the owner is in a privileged +// position over computation running on that platform. +function insist(flag) { + function tag(template, ...args) { + if (flag) { + return; + } + const interleaved = [template[0]]; + const parts = [template[0]]; + for (let i = 0; i < args.length; i += 1) { + interleaved.push(args[i], template[i + 1]); + parts.push('(a ', typeof args[i], ')', template[i + 1]); + } + if (args.length >= 1) { + parts.push('\nSee console for error data.'); + } + console.error(...interleaved); + throw new Error(parts.join('')); + } + return harden(tag); +} +harden(insist); + +export { insist }; diff --git a/collections/sameStructure.js b/collections/sameStructure.js new file mode 100644 index 00000000000..fcbb4f5e534 --- /dev/null +++ b/collections/sameStructure.js @@ -0,0 +1,247 @@ +import harden from '@agoric/harden'; + +import { insist } from './insist'; +import { passStyleOf } from '../src/kernel/marshal'; + +// Shim of Object.fromEntries from +// https://github.com/tc39/proposal-object-from-entries/blob/master/polyfill.js +function ObjectFromEntries(iter) { + const obj = {}; + + for (const pair of iter) { + if (Object(pair) !== pair) { + throw new TypeError('iterable for fromEntries should yield objects'); + } + + // Consistency with Map: contract is that entry has "0" and "1" keys, not + // that it is an array or iterable. + + const { '0': key, '1': val } = pair; + + Object.defineProperty(obj, key, { + configurable: true, + enumerable: true, + writable: true, + value: val, + }); + } + + return obj; +} + +// A *passable* is something that may be mashalled. It consists of a +// graph of pass-by-copy data terminating in leaves of passable +// non-pass-by-copy data. These leaves may be promises, or +// pass-by-presence objects. A *comparable* is a passable whose leaves +// contain no promises. Two comparables can be synchronously compared +// for structural equivalence. +// +// TODO: Currently, all algorithms here treat the pass-by-copy +// superstructure as a tree. This means that dags are unwound at +// potentially exponential code, and cycles cause failure to +// terminate. We must fix both problems, making all these algorthms +// graph-aware. + +// We say that a function *reveals* an X when it returns either an X +// or a promise for an X. + +// Given a passable, reveal a corresponding comparable, where each +// leaf promise of the passable has been replaced with its +// corresponding comparable. +function allComparable(passable) { + const passStyle = passStyleOf(passable); + switch (passStyle) { + case 'null': + case 'undefined': + case 'string': + case 'boolean': + case 'number': + case 'symbol': + case 'bigint': + case 'presence': + case 'copyError': { + return passable; + } + case 'promise': { + return passable.then(nonp => allComparable(nonp)); + } + case 'copyArray': { + const valPs = passable.map(p => allComparable(p)); + return Promise.all(valPs).then(vals => harden(vals)); + } + case 'copyRecord': { + const names = Object.getOwnPropertyNames(passable); + const valPs = names.map(name => allComparable(passable[name])); + return Promise.all(valPs).then(vals => + harden(ObjectFromEntries(vals.map((val, i) => [names[i], val]))), + ); + } + default: { + throw new TypeError(`unrecognized passStyle ${passStyle}`); + } + } +} +harden(allComparable); + +// Are left and right structurally equivalent comparables? This +// compares pass-by-copy data deeply until non-pass-by-copy values are +// reached. The non-pass-by-copy values at the leaves of the +// comparison may only be pass-by-presence objects. If they are +// anything else, including promises, throw an error. +// +// Pass-by-presence objects compare identities. + +function sameStructure(left, right) { + const leftStyle = passStyleOf(left); + const rightStyle = passStyleOf(right); + insist(leftStyle !== 'promise')`\ +Cannot structurally compare promises: ${left}`; + insist(rightStyle !== 'promise')`\ +Cannot structurally compare promises: ${right}`; + + if (leftStyle !== rightStyle) { + return false; + } + switch (leftStyle) { + case 'null': + case 'undefined': + case 'string': + case 'boolean': + case 'number': + case 'symbol': + case 'bigint': + case 'presence': { + return Object.is(left, right); + } + case 'copyRecord': + case 'copyArray': { + const leftNames = Object.getOwnPropertyNames(left); + const rightNames = Object.getOwnPropertyNames(right); + if (leftNames.length !== rightNames.length) { + return false; + } + for (const name of leftNames) { + // TODO: Better hasOwnProperty check + if (!Object.getOwnPropertyDescriptor(right, name)) { + return false; + } + // TODO: Make cycle tolerant + if (!sameStructure(left[name], right[name])) { + return false; + } + } + return true; + } + case 'copyError': { + return left.name === right.name && left.message === right.message; + } + default: { + throw new TypeError(`unrecognized passStyle ${leftStyle}`); + } + } +} +harden(sameStructure); + +function pathStr(path) { + if (path === null) { + return 'top'; + } + const [base, index] = path; + let i = index; + const baseStr = pathStr(base); + if (typeof i === 'string' && /^[a-zA-Z]\w*$/.test(i)) { + return `${baseStr}.${i}`; + } + if (typeof i === 'string' && `${+i}` === i) { + i = +i; + } + return `${baseStr}[${JSON.stringify(i)}]`; +} + +// TODO: Reduce redundancy between sameStructure and +// mustBeSameStructureInternal +function mustBeSameStructureInternal(left, right, message, path) { + function complain(problem) { + const template = harden([ + `${message}: ${problem} at ${pathStr(path)}: (`, + ') vs (', + ')', + ]); + insist(false)(template, left, right); + } + + const leftStyle = passStyleOf(left); + const rightStyle = passStyleOf(right); + if (leftStyle === 'promise') { + complain('Promise on left'); + } + if (rightStyle === 'promise') { + complain('Promise on right'); + } + + if (leftStyle !== rightStyle) { + complain('different passing style'); + } + switch (leftStyle) { + case 'null': + case 'undefined': + case 'string': + case 'boolean': + case 'number': + case 'symbol': + case 'bigint': + case 'presence': { + if (!Object.is(left, right)) { + complain('different'); + } + break; + } + case 'copyRecord': + case 'copyArray': { + const leftNames = Object.getOwnPropertyNames(left); + const rightNames = Object.getOwnPropertyNames(right); + if (leftNames.length !== rightNames.length) { + complain(`${leftNames.length} vs ${rightNames.length} own properties`); + } + for (const name of leftNames) { + // TODO: Better hasOwnProperty check + if (!Object.getOwnPropertyDescriptor(right, name)) { + complain(`${name} not found on right`); + } + // TODO: Make cycle tolerant + mustBeSameStructureInternal(left[name], right[name], message, [ + path, + name, + ]); + } + break; + } + case 'copyError': { + if (left.name !== right.name) { + complain(`different error name: ${left.name} vs ${right.name}`); + } + if (left.message !== right.message) { + complain( + `different error message: ${left.message} vs ${right.message}`, + ); + } + break; + } + default: { + complain(`unrecognized passStyle ${leftStyle}`); + break; + } + } +} +function mustBeSameStructure(left, right, message) { + mustBeSameStructureInternal(left, right, `${message}`, null); +} +harden(mustBeSameStructure); + +// If `val` would be a valid input to `sameStructure`, return +// normally. Otherwise error. +function mustBeComparable(val) { + mustBeSameStructure(val, val, 'not comparable'); +} + +export { allComparable, sameStructure, mustBeSameStructure, mustBeComparable }; diff --git a/demo/contractHost/assays.js b/demo/contractHost/assays.js new file mode 100644 index 00000000000..9334413edd6 --- /dev/null +++ b/demo/contractHost/assays.js @@ -0,0 +1,314 @@ +// Copyright (C) 2019 Agoric, under Apache License 2.0 + +import Nat from '@agoric/nat'; +import harden from '@agoric/harden'; + +import { insist } from '../../collections/insist'; +import { + sameStructure, + mustBeSameStructure, + mustBeComparable, +} from '../../collections/sameStructure'; + +// This assays.js module treats labels as black boxes. It is not aware +// of issuers, and so can handle labels whose issuers are merely +// presences of remote issuers. + +// Return an assay, which makes amounts, validates amounts, and +// provides set operations over amounts. An amount is a pass-by-copy +// description of some set of erights. An amount has a label and a +// quantity. All amounts made by the same assay have the same label +// but differ in quantity. +// +// An assay is pass-by-presence, but is not designed to be usefully +// passed. Rather, we expect each vat that needs to operate on amounts +// will have its own local assay to do so. +// +// The default assay makes the default kind of amount. The default +// kind of amount is a labeled natural number describing a quantity of +// fungible erights. The label describes what kinds of rights these +// are. This is a form of labeled unit, as in unit typing. +function makeNatAssay(label) { + mustBeComparable(label); + + // memoize well formedness check of amounts + const brand = new WeakSet(); + + const assay = harden({ + getLabel() { + return label; + }, + + // Given the raw quantity that this kind of amount would label, return + // an amount so labeling that quantity. + make(allegedQuantity) { + const amount = harden({ label, quantity: Nat(allegedQuantity) }); + brand.add(amount); + return amount; + }, + + // Is this an amount object made by this assay? If so, return + // it. Otherwise error. + vouch(amount) { + insist(brand.has(amount))`\ +Unrecognized amount: ${amount}`; + return amount; + }, + + // Is this like an amount object made by this assay, such as one + // received by pass-by-copy from an otherwise-identical remote + // amount? On success, return an amount object made by this + // assay. Otherwise error. + // + // Until we have good support for pass-by-construction, the full + // assay style is too awkward to use remotely. See + // mintTestAssay. So coerce also accepts a bare number which it + // will coerce to a labeled number via assay.make. + coerce(allegedAmount) { + if (typeof allegedAmount === 'number') { + // Will throw on inappropriate number + return assay.make(allegedAmount); + } + if (brand.has(allegedAmount)) { + return allegedAmount; + } + const { label: allegedLabel, quantity } = allegedAmount; + mustBeSameStructure(label, allegedLabel, 'Unrecognized label'); + // Will throw on inappropriate quantity + return assay.make(quantity); + }, + + // Return the raw quantity that this amount labels. + quantity(amount) { + return assay.vouch(amount).quantity; + }, + + // Represents the empty set of erights, i.e., no erights + empty() { + return assay.make(0); + }, + + isEmpty(amount) { + return assay.quantity(amount) === 0; + }, + + // Set inclusion of erights. + // Does the set of erights described by `leftAmount` include all + // the erights described by `rightAmount`? + includes(leftAmount, rightAmount) { + return assay.quantity(leftAmount) >= assay.quantity(rightAmount); + }, + + // Set union of erights. + // Describe all the erights described by `leftAmount` and those + // described by `rightAmount`. + with(leftAmount, rightAmount) { + return assay.make( + assay.quantity(leftAmount) + assay.quantity(rightAmount), + ); + }, + + // Covering set subtraction of erights. + // If leftAmount does not include rightAmount, error. + // Describe the erights described by `leftAmount` and not described + // by `rightAmount`. + without(leftAmount, rightAmount) { + return assay.make( + assay.quantity(leftAmount) - assay.quantity(rightAmount), + ); + }, + }); + return assay; +} +harden(makeNatAssay); + +// A meta assay wraps those base assays returned by +// baseLabelToAssayFn. A meta amount's quantity is a base amount, or +// null for empty. Thus, different meta amounts that have the same +// meta label can contain different meta quantities, each of whom is a +// base amount with a different base label. The "single" qualifier +// here is for the restriction that a metaSingleAssay cannot combine +// base amounts with different base labels. +// +// TODO: Before we can make a more general meta assay, we need to +// recognize a ConstMap as a pass-by-copy object. Once we have that, +// we can have a meta amount be a ConstMap from base labels to base +// amounts. +// +// Since an empty meta amount has a null quantity rather than a base +// amount, it has no corresponding base assay. +function makeMetaSingleAssayMaker(baseLabelToAssayFn) { + function makeMetaSingleAssay(metaLabel) { + mustBeComparable(metaLabel); + + // memoize well formedness check of meta amounts. + const metaBrand = new WeakSet(); + + const metaEmptyAmount = harden({ label: metaLabel, quantity: null }); + metaBrand.add(metaEmptyAmount); + + const metaAssay = harden({ + getLabel() { + return metaLabel; + }, + + // Given the raw quantity that this kind of amount would label, return + // an amount so labeling that quantity. + make(allegedBaseAmount) { + if (allegedBaseAmount === null) { + return metaEmptyAmount; + } + const baseAssay = baseLabelToAssayFn(allegedBaseAmount.label); + insist(baseAssay !== undefined)`\ +base label not found ${allegedBaseAmount}`; + const baseAmount = baseAssay.make(allegedBaseAmount.quantity); + if (baseAssay.isEmpty(baseAmount)) { + return metaEmptyAmount; + } + const metaAmount = harden({ label: metaLabel, quantity: baseAmount }); + metaBrand.add(metaAmount); + return metaAmount; + }, + + // Is this an amount object made by this assay? If so, return + // it. Otherwise error. + vouch(metaAmount) { + insist(metaBrand.has(metaAmount))`\ +Unrecognized metaAmount: ${metaAmount}`; + return metaAmount; + }, + + // Is this like an amount object made by this assay, such as one + // received by pass-by-copy from an otherwise-identical remote + // amount? On success, return an amount object made by this + // assay. Otherwise error. + // + // Until we have good support for pass-by-construction, the full + // assay style is too awkward to use remotely. See + // mintTestAssay. So coerce also accepts a bare number which it + // will coerce to a labeled number via metaAssay.make. + coerce(allegedMetaAmount) { + if (metaBrand.has(allegedMetaAmount)) { + return allegedMetaAmount; + } + const { + label: allegedMetaLabel, + quantity: allegedBaseAmount, + } = allegedMetaAmount; + mustBeSameStructure( + metaLabel, + allegedMetaLabel, + 'Unrecognized meta label', + ); + // Will throw on inappropriate quantity + return metaAssay.make(allegedBaseAmount); + }, + + // Return the raw quantity that this meta amount labels. This + // will be either null or a base amount with a label recognized + // by baseLabelToAssayFn. + quantity(metaAmount) { + return metaAssay.vouch(metaAmount).quantity; + }, + + // The meta empty amount has a quantity of null, rather than a + // base amount. + empty() { + return metaEmptyAmount; + }, + + isEmpty(metaAmount) { + const baseAmount = metaAssay.quantity(metaAmount); + if (baseAmount === null) { + insist(metaAmount === metaEmptyAmount)`\ +The empty meta amount should be unique`; + return true; + } + const baseAssay = baseLabelToAssayFn(baseAmount.label); + insist(!baseAssay.isEmpty(baseAmount))`\ +Empty base amount should be canonicalized as a null meta quantity`; + return false; + }, + + // Set inclusion of erights. + // Does the set of erights described by `leftAmount` include all + // the erights described by `rightAmount`? + includes(leftMetaAmount, rightMetaAmount) { + if (metaAssay.isEmpty(rightMetaAmount)) { + return true; + } + if (metaAssay.isEmpty(leftMetaAmount)) { + return false; + } + const leftBaseAmount = leftMetaAmount.quantity; + const leftBaseLabel = leftBaseAmount.label; + const rightBaseAmount = rightMetaAmount.quantity; + const rightBaseLabel = rightBaseAmount.label; + + if (!sameStructure(leftBaseLabel, rightBaseLabel)) { + return false; + } + const baseAssay = baseLabelToAssayFn(leftBaseLabel); + return baseAssay.includes(leftBaseAmount, rightBaseAmount); + }, + + // Set union of erights. + // Describe all the erights described by `leftAmount` and those + // described by `rightAmount`. + with(leftMetaAmount, rightMetaAmount) { + if (metaAssay.isEmpty(leftMetaAmount)) { + return rightMetaAmount; + } + if (metaAssay.isEmpty(rightMetaAmount)) { + return leftMetaAmount; + } + const leftBaseAmount = leftMetaAmount.quantity; + const leftBaseLabel = leftBaseAmount.label; + const rightBaseAmount = rightMetaAmount.quantity; + const rightBaseLabel = rightBaseAmount.label; + + mustBeSameStructure( + leftBaseLabel, + rightBaseLabel, + 'Cannot combine different base rights', + ); + const baseAssay = baseLabelToAssayFn(leftBaseLabel); + + return metaAssay.make(baseAssay.with(leftBaseAmount, rightBaseAmount)); + }, + + // Covering set subtraction of erights. + // If leftAmount does not include rightAmount, error. + // Describe the erights described by `leftAmount` and not described + // by `rightAmount`. + without(leftMetaAmount, rightMetaAmount) { + if (metaAssay.isEmpty(rightMetaAmount)) { + return leftMetaAmount; + } + insist(!metaAssay.isEmpty(leftMetaAmount))`\ +empty left meta assay does not include ${rightMetaAmount}`; + + const leftBaseAmount = leftMetaAmount.quantity; + const leftBaseLabel = leftBaseAmount.label; + const rightBaseAmount = rightMetaAmount.quantity; + const rightBaseLabel = rightBaseAmount.label; + + mustBeSameStructure( + leftBaseLabel, + rightBaseLabel, + 'Cannot subtract different base rights', + ); + const baseAssay = baseLabelToAssayFn(leftBaseLabel); + + return metaAssay.make( + baseAssay.without(leftBaseAmount, rightBaseAmount), + ); + }, + }); + return metaAssay; + } + return harden(makeMetaSingleAssay); +} +harden(makeMetaSingleAssayMaker); + +export { makeNatAssay, makeMetaSingleAssayMaker }; diff --git a/demo/contractHost/bootstrap.js b/demo/contractHost/bootstrap.js index 09a0d46a2bd..f702602a526 100644 --- a/demo/contractHost/bootstrap.js +++ b/demo/contractHost/bootstrap.js @@ -1,58 +1,96 @@ -// Copyright (C) 2011 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - * @fileoverview Test simple contract code - * @requires define - */ +// Copyright (C) 2019 Agoric, under Apache License 2.0 import harden from '@agoric/harden'; +import { insist } from '../../collections/insist'; + function build(E, log) { - function mintTest(mint) { - log('starting mintTest'); - const mP = E(mint).makeMint(); - const alicePurseP = E(mP).mint(1000, 'alice'); - const mIssuerP = E(alicePurseP).getIssuer(); - const depositPurseP = E(mIssuerP).makeEmptyPurse('deposit'); - const v = E(depositPurseP).deposit(50, alicePurseP); // TODO: was .fork() hack - // TODO: no longer true "this ordering should be guaranteed by the fact - // that this is all in the same Flow" - const aBal = v.then(_ => E(alicePurseP).getBalance()); - const dBal = v.then(_ => E(depositPurseP).getBalance()); - Promise.all([aBal, dBal]).then(bals => { - log('++ balances:', bals); - log('++ DONE'); + function showPaymentBalance(name, paymentP) { + E(paymentP) + .getXferBalance() + .then(amount => log(name, ' xfer balance ', amount)); + } + function showPurseBalances(name, purseP) { + E(purseP) + .getXferBalance() + .then(amount => log(name, ' xfer balance ', amount)); + E(purseP) + .getUseBalance() + .then(amount => log(name, ' use balance ', amount)); + } + + /* + const fakeNowTimer = harden({ + delayUntil(deadline, resolution = undefined) { + log(`Pretend ${deadline} passed`); + return E.resolve(resolution); + }, + }); + */ + const fakeNeverTimer = harden({ + delayUntil(deadline, _resolution = undefined) { + log(`Pretend ${deadline} never happens`); + return new Promise(_r => {}); + }, + }); + + // This is written in the full assay style, where bare number + // objects are never used in lieu of full amount objects. This has + // the virtue of unit typing, where 3 dollars cannot be confused + // with 3 seconds. + function mintTestAssay(mint) { + log('starting mintTestAssay'); + const mMintP = E(mint).makeMint('bucks'); + const mIssuerP = E(mMintP).getIssuer(); + E.resolve(mIssuerP).then(issuer => { + // By using an unforgeable issuer presence and a pass-by-copy + // description together as a unit label, we check that both + // agree. The veracity of the description is, however, only as + // good as the issuer doing the check. + const label = harden({ issuer, description: 'bucks' }); + const bucks1000 = harden({ label, quantity: 1000 }); + const bucks50 = harden({ label, quantity: 50 }); + + const alicePurseP = E(mMintP).mint(bucks1000, 'alice'); + const paymentP = E(alicePurseP).withdraw(bucks50); + E.resolve(paymentP).then(_ => { + showPurseBalances('alice', alicePurseP); + showPaymentBalance('payment', paymentP); + }); + }); + } + + // Uses raw numbers rather than amounts. Until we have support for + // pass-by-presence, the full assay style shown in mintTestAssay is + // too awkward. + function mintTestNumber(mint) { + log('starting mintTestNumber'); + const mMintP = E(mint).makeMint('quatloos'); + + const alicePurseP = E(mMintP).mint(1000, 'alice'); + const paymentP = E(alicePurseP).withdraw(50); + E.resolve(paymentP).then(_ => { + showPurseBalances('alice', alicePurseP); + showPaymentBalance('payment', paymentP); }); } function trivialContractTest(host) { log('starting trivialContractTest'); - function trivContract(_whiteP, _blackP) { - return 8; + function trivContract(terms, chitMaker) { + return chitMaker.make('foo', 8); } const contractSrc = `${trivContract}`; - const tokensP = E(host).setup(contractSrc); + const fooChitP = E(host).start(contractSrc, 'foo terms'); + + showPaymentBalance('foo', fooChitP); - const whiteTokenP = tokensP.then(tokens => tokens[0]); - E(host).play(whiteTokenP, contractSrc, 0, {}); + const eightP = E(host).redeem(fooChitP); - const blackTokenP = tokensP.then(tokens => tokens[1]); - const eightP = E(host).play(blackTokenP, contractSrc, 1, {}); eightP.then(res => { + showPaymentBalance('foo', fooChitP); log('++ eightP resolved to', res, '(should be 8)'); if (res !== 8) { throw new Error(`eightP resolved to ${res}, not 8`); @@ -63,77 +101,163 @@ function build(E, log) { } function betterContractTestAliceFirst(mint, alice, bob) { - const moneyMintP = E(mint).makeMint(); + const moneyMintP = E(mint).makeMint('moola'); const aliceMoneyPurseP = E(moneyMintP).mint(1000); const bobMoneyPurseP = E(moneyMintP).mint(1001); - const stockMintP = E(mint).makeMint(); + const stockMintP = E(mint).makeMint('Tyrell'); const aliceStockPurseP = E(stockMintP).mint(2002); const bobStockPurseP = E(stockMintP).mint(2003); - const aliceP = E(alice).init(aliceMoneyPurseP, aliceStockPurseP); - /* eslint-disable-next-line no-unused-vars */ - const bobP = E(bob).init(bobMoneyPurseP, bobStockPurseP); - - const ifItFitsP = E(aliceP).payBobWell(bob); - ifItFitsP.then( - res => { - log('++ ifItFitsP done:', res); - log('++ DONE'); - }, - rej => log('++ ifItFitsP failed', rej), + const aliceP = E(alice).init( + fakeNeverTimer, + aliceMoneyPurseP, + aliceStockPurseP, ); - return ifItFitsP; + const bobP = E(bob).init(fakeNeverTimer, bobMoneyPurseP, bobStockPurseP); + return Promise.all([aliceP, bobP]).then(_ => { + const ifItFitsP = E(aliceP).payBobWell(bob); + ifItFitsP.then( + res => { + log('++ ifItFitsP done:', res); + log('++ DONE'); + }, + rej => log('++ ifItFitsP failed', rej), + ); + return ifItFitsP; + }); } - function betterContractTestBobFirst(mint, alice, bob, bobLies = false) { - const moneyMintP = E(mint).makeMint(); + function betterContractTestBobFirst(mint, alice, bob) { + const moneyMintP = E(mint).makeMint('clams'); const aliceMoneyPurseP = E(moneyMintP).mint(1000, 'aliceMainMoney'); const bobMoneyPurseP = E(moneyMintP).mint(1001, 'bobMainMoney'); - const stockMintP = E(mint).makeMint(); + const stockMintP = E(mint).makeMint('fudco'); const aliceStockPurseP = E(stockMintP).mint(2002, 'aliceMainStock'); const bobStockPurseP = E(stockMintP).mint(2003, 'bobMainStock'); - const aliceP = E(alice).init(aliceMoneyPurseP, aliceStockPurseP); - const bobP = E(bob).init(bobMoneyPurseP, bobStockPurseP); - - if (bobLies) { + const aliceP = E(alice).init( + fakeNeverTimer, + aliceMoneyPurseP, + aliceStockPurseP, + ); + const bobP = E(bob).init(fakeNeverTimer, bobMoneyPurseP, bobStockPurseP); + return Promise.all([aliceP, bobP]).then(_ => { E(bobP) - .tradeWell(aliceP, true) + .tradeWell(aliceP, false) .then( res => { + showPurseBalances('alice money', aliceMoneyPurseP); + showPurseBalances('alice stock', aliceStockPurseP); + showPurseBalances('bob money', bobMoneyPurseP); + showPurseBalances('bob stock', bobStockPurseP); log('++ bobP.tradeWell done:', res); + log('++ DONE'); }, rej => { - if (rej.message.startsWith('unexpected contract')) { - log('++ DONE'); - } else { - log('++ bobP.tradeWell error:', rej); - } + log('++ bobP.tradeWell error:', rej); }, ); - } else { + }); + } + + function coveredCallTest(mint, alice, bob) { + const moneyMintP = E(mint).makeMint('smackers'); + const aliceMoneyPurseP = E(moneyMintP).mint(1000, 'aliceMainMoney'); + const bobMoneyPurseP = E(moneyMintP).mint(1001, 'bobMainMoney'); + + const stockMintP = E(mint).makeMint('yoyodyne'); + const aliceStockPurseP = E(stockMintP).mint(2002, 'aliceMainStock'); + const bobStockPurseP = E(stockMintP).mint(2003, 'bobMainStock'); + + const aliceP = E(alice).init( + fakeNeverTimer, + aliceMoneyPurseP, + aliceStockPurseP, + ); + const bobP = E(bob).init(fakeNeverTimer, bobMoneyPurseP, bobStockPurseP); + return Promise.all([aliceP, bobP]).then(_ => { E(bobP) - .tradeWell(aliceP, false) + .offerAliceOption(aliceP, false) .then( res => { - log('++ bobP.tradeWell done:', res); + showPurseBalances('alice money', aliceMoneyPurseP); + showPurseBalances('alice stock', aliceStockPurseP); + showPurseBalances('bob money', bobMoneyPurseP); + showPurseBalances('bob stock', bobStockPurseP); + log('++ bobP.offerAliceOption done:', res); log('++ DONE'); }, rej => { - log('++ bobP.tradeWell error:', rej); + log('++ bobP.offerAliceOption error:', rej); }, ); - } - // return E(aliceP).tradeWell(bobP); + }); + } + + function coveredCallSaleTest(mint, alice, bob, fred) { + const doughMintP = E(mint).makeMint('dough'); + const aliceDoughPurseP = E(doughMintP).mint(1000, 'aliceDough'); + const bobDoughPurseP = E(doughMintP).mint(1001, 'bobDough'); + const fredDoughPurseP = E(doughMintP).mint(1002, 'fredDough'); + + const stockMintP = E(mint).makeMint('wonka'); + const aliceStockPurseP = E(stockMintP).mint(2002, 'aliceMainStock'); + const bobStockPurseP = E(stockMintP).mint(2003, 'bobMainStock'); + const fredStockPurseP = E(stockMintP).mint(2004, 'fredMainStock'); + + const finMintP = E(mint).makeMint('fins'); + const aliceFinPurseP = E(finMintP).mint(3000, 'aliceFins'); + const fredFinPurseP = E(finMintP).mint(3001, 'fredFins'); + + const aliceP = E(alice).init( + fakeNeverTimer, + aliceDoughPurseP, + aliceStockPurseP, + aliceFinPurseP, + fred, + ); + const bobP = E(bob).init(fakeNeverTimer, bobDoughPurseP, bobStockPurseP); + /* eslint-disable-next-line no-unused-vars */ + const fredP = E(fred).init( + fakeNeverTimer, + fredDoughPurseP, + fredStockPurseP, + fredFinPurseP, + ); + return Promise.all([aliceP, bobP, fredP]).then(_ => { + E(bobP) + .offerAliceOption(aliceP) + .then( + res => { + showPurseBalances('alice dough', aliceDoughPurseP); + showPurseBalances('alice stock', aliceStockPurseP); + showPurseBalances('alice fins', aliceFinPurseP); + + showPurseBalances('bob dough', bobDoughPurseP); + showPurseBalances('bob stock', bobStockPurseP); + + showPurseBalances('fred dough', fredDoughPurseP); + showPurseBalances('fred stock', fredStockPurseP); + showPurseBalances('fred fins', fredFinPurseP); + + log('++ bobP.offerAliceOption done:', res); + log('++ DONE'); + }, + rej => { + log('++ bobP.offerAliceOption error:', rej); + }, + ); + }); } const obj0 = { async bootstrap(argv, vats) { switch (argv[0]) { case 'mint': { - return mintTest(vats.mint); + mintTestAssay(vats.mint); + return mintTestNumber(vats.mint); } case 'trivial': { const host = await E(vats.host).makeHost(); @@ -151,21 +275,30 @@ function build(E, log) { const bob = await E(vats.bob).makeBob(host); return betterContractTestBobFirst(vats.mint, alice, bob); } - case 'bob-first-lies': { + case 'covered-call': { const host = await E(vats.host).makeHost(); const alice = await E(vats.alice).makeAlice(host); const bob = await E(vats.bob).makeBob(host); - return betterContractTestBobFirst(vats.mint, alice, bob, true); + return coveredCallTest(vats.mint, alice, bob); + } + case 'covered-call-sale': { + const host = await E(vats.host).makeHost(); + const alice = await E(vats.alice).makeAlice(host); + const bob = await E(vats.bob).makeBob(host); + const fred = await E(vats.fred).makeFred(host); + return coveredCallSaleTest(vats.mint, alice, bob, fred); + } + default: { + throw new Error(`unrecognized argument value ${argv[0]}`); } - default: - throw new Error('unrecognized argument value'); } }, }; return harden(obj0); } +harden(build); -export default function setup(syscall, state, helpers) { +function setup(syscall, state, helpers) { function log(what) { helpers.log(what); console.log(what); @@ -178,3 +311,4 @@ export default function setup(syscall, state, helpers) { helpers.vatID, ); } +export default harden(setup); diff --git a/demo/contractHost/chit.js b/demo/contractHost/chit.js new file mode 100644 index 00000000000..ed77888e8d3 --- /dev/null +++ b/demo/contractHost/chit.js @@ -0,0 +1,185 @@ +// Copyright (C) 2019 Agoric, under Apache License 2.0 + +// Chit === Contract Host Issuer Token + +import Nat from '@agoric/nat'; +import harden from '@agoric/harden'; +import evaluate from '@agoric/evaluate'; + +import { allSettled } from '../../collections/allSettled'; +import { insist } from '../../collections/insist'; +import { allComparable } from '../../collections/sameStructure'; +import { makeNatAssay } from './assays'; +import { makeMetaIssuerController } from './issuers'; +import makePromise from '../../src/kernel/makePromise'; + +function makeContractHost(E) { + // Maps from seat identity to seats + const seats = new WeakMap(); + + const controller = makeMetaIssuerController('contract host'); + const metaIssuer = controller.getMetaIssuer(); + const metaAssay = metaIssuer.getAssay(); + + function redeem(allegedChitPayment) { + const allegedMetaAmount = allegedChitPayment.getXferBalance(); + const metaAmount = metaAssay.vouch(allegedMetaAmount); + insist(!metaAssay.isEmpty(metaAmount))`\ +No chits left`; + const baseAmount = metaAssay.quantity(metaAmount); + const seatIdentity = baseAmount.label.identity; + insist(seats.has(seatIdentity))`\ +Not a registered chit seat identity ${seatIdentity}`; + return E.resolve(metaIssuer.slash(metaAmount, allegedChitPayment)).then(_ => + seats.get(seatIdentity), + ); + } + + // The contract host is designed to have a long-lived credible + // identity. + // + // TODO: The contract host `start` method should spin off a new vat + // for each new contract instance. + const contractHost = harden({ + getChitIssuer() { + return controller.getMetaIssuer(); + }, + + // The `contractSrc` is code for a contract function parameterized + // by `terms` and `chitMaker`. `start` evaluates this code, + // calls that function to start the contract, and returns whatever + // the contract returns. + start(contractSrc, termsP) { + contractSrc = `${contractSrc}`; + const contract = evaluate(contractSrc, { + Nat, + harden, + console, + E, + makePromise, + }); + + return E.resolve(allComparable(termsP)).then(terms => { + const chitMaker = harden({ + // Used by the contract to make chits for credibly + // participating in the contract. The returned chit can be + // redeemed for this seat. The chitMaker contributes the + // description `{ contractSrc, terms, seatDesc }`. If this + // contract host redeems a chit, then the contractSrc and + // terms are accurate. The seatDesc is according to that + // contractSrc code. + make(seatDesc, seat, name = 'a chit payment') { + const baseDescription = harden({ + contractSrc, + terms, + seatDesc, + }); + // Note that an empty object is pass-by-presence, and so + // has an unforgeable identity. + const seatIdentity = harden({}); + const baseLabel = harden({ + identity: seatIdentity, + description: baseDescription, + }); + const baseAssay = makeNatAssay(baseLabel); + const baseAmount = baseAssay.make(1); + controller.register(baseAssay); + seats.set(seatIdentity, seat); + const metaOneAmount = metaAssay.make(baseAmount); + // This should be the only use of the meta mint, to make a + // meta purse whose quantity is one unit of a base amount + // for a unique base label. This meta purse makes the + // returned meta payment, and then the empty meta purse is + // dropped, in the sense that it becomes inaccessible. But + // it is not yet collectable. Until the returned payment + // is deposited, it will retain the metaPurse, as the + // metaPurse contains the usage rights. + const metaPurse = controller + .getMetaMint() + .mint(metaOneAmount, name); + return metaPurse.withdrawAll(name); + }, + redeem, + }); + return contract(terms, chitMaker); + }); + }, + + // If this is a chit payment made by a chitMaker of this contract + // host, redeem it for the associated seat. Else error. Redeeming + // consumes the chit payment and also transfers the use rights. + redeem(allegedChitPaymentP) { + return E.resolve(allegedChitPaymentP).then(allegedChitPayment => { + return redeem(allegedChitPayment); + }); + }, + }); + return contractHost; +} +harden(makeContractHost); + +function exchangeChitAmount( + chitIssuerP, + seatIdentityP, + contractSrc, + terms, + seatIndex, + giveAmount, + takeAmount, +) { + const passable = harden({ + label: { + issuer: chitIssuerP, + description: 'contract host', + }, + quantity: { + label: { + identity: seatIdentityP, + description: { + contractSrc, + terms, + seatDesc: [seatIndex, giveAmount, takeAmount], + }, + }, + quantity: 1, + }, + }); + const comparableP = allComparable(passable); + /* + E.resolve(comparableP).then(comparable => + console.log('\n####\n(', passable, ')\n####\n(', comparable, ')\n####\n'), + ); + */ + return comparableP; +} +harden(exchangeChitAmount); + +function makeCollect(E) { + function collect(seatP, winPurseP, refundPurseP, name = 'collecting') { + const results = harden([ + E(seatP) + .getWinnings() + .then(winnings => E(winPurseP).depositAll(winnings)), + // TODO Bug if replace the comma above with the uncommented out + // ".then(_ => undefined)," below, somehow we end up trying to + // marshal an array with holes, rather than an array with + // undefined elements. This remains true whether we use + // Promise.all or allSettled + /* .then(_ => undefined), */ + E(seatP) + .getRefund() + .then(refund => refund && E(refundPurseP).depositAll(refund)), + ]); + const doneP = allSettled(results); + E.resolve(doneP).then(([wins, refs]) => { + console.log(`${name} wins: `, wins, `refs: `, refs); + }); + // Use Promise.all here rather than allSettled in order to + // propagate rejection. + return Promise.all(results); + } + return harden(collect); +} +harden(makeCollect); + +export { makeContractHost, exchangeChitAmount, makeCollect }; diff --git a/demo/contractHost/coveredCall.js b/demo/contractHost/coveredCall.js new file mode 100644 index 00000000000..7ff8922a857 --- /dev/null +++ b/demo/contractHost/coveredCall.js @@ -0,0 +1,55 @@ +/* global E */ +// Copyright (C) 2019 Agoric, under Apache License 2.0 + +import harden from '@agoric/harden'; + +import { escrowExchange } from './escrow'; + +function coveredCall(terms, chitMaker) { + const [moneyNeeded, stockNeeded, timerP, deadline] = terms; + + const [aliceChit, bobChit] = escrowExchange( + [moneyNeeded, stockNeeded], + chitMaker, + ); + + const aliceEscrowSeatP = chitMaker.redeem(aliceChit); + const bobEscrowSeatP = chitMaker.redeem(bobChit); + + // Seats + + E(timerP) + .delayUntil(deadline) + .then(_ => E(bobEscrowSeatP).cancel('expired')); + + const bobSeat = harden({ + offer(stockPayment) { + const sIssuer = stockNeeded.label.issuer; + return E(sIssuer) + .getExclusive(stockNeeded, stockPayment, 'prePay') + .then(prePayment => { + E(bobEscrowSeatP).offer(prePayment); + return chitMaker.make( + ['holder', moneyNeeded, stockNeeded], + aliceEscrowSeatP, + ); + }); + }, + getWinnings() { + return E(bobEscrowSeatP).getWinnings(); + }, + getRefund() { + return E(bobEscrowSeatP).getRefund(); + }, + }); + + return chitMaker.make(['writer', stockNeeded, moneyNeeded], bobSeat); +} + +const coveredCallSrc = `\ +(function() { + ${escrowExchange} + return (${coveredCall}); +}())`; + +export { coveredCall, coveredCallSrc }; diff --git a/demo/contractHost/escrow.js b/demo/contractHost/escrow.js index 0edfba1e620..b9dce974a22 100644 --- a/demo/contractHost/escrow.js +++ b/demo/contractHost/escrow.js @@ -1,49 +1,96 @@ -/* global require E */ - -export default function escrowExchange(a, b) { - /* eslint-disable-next-line global-require */ - const harden = require('@agoric/harden'); - - function join(xP, yP) { - return Promise.all([xP, yP]).then(([x, y]) => { - if (Object.is(x, y)) { - return x; - } - throw new Error('not the same'); - }); - } +/* global E makePromise */ +// Copyright (C) 2019 Agoric, under Apache License 2.0 + +import harden from '@agoric/harden'; + +// For clarity, the code below internally speaks of a scenario is +// which Alice is trading some of her money for some of Bob's +// stock. However, for generality, the API does not expose names like +// "alice", "bob", "money", or "stock". Rather, Alice and Bob are +// players 0 and 1. Money are the rights transfered from player 0 to +// 1, and Stock are the rights transfered from 1 to 0. + +function escrowExchange(terms, chitMaker) { + const [moneyNeeded, stockNeeded] = terms; - // a from Alice , b from Bob - function makeTransfer(srcPurseP, dstPurseP, amount) { - const issuerP = join(E(srcPurseP).getIssuer(), E(dstPurseP).getIssuer()); - const escrowPurseP = E(issuerP).makeEmptyPurse('escrow'); + function makeTransfer(amount, srcPaymentP) { + const { issuer } = amount.label; + const escrowP = E(issuer).getExclusive(amount, srcPaymentP, 'escrow'); + const winnings = makePromise(); + const refund = makePromise(); return harden({ phase1() { - return E(escrowPurseP).deposit(amount, srcPurseP); + return escrowP; }, phase2() { - return E(dstPurseP).deposit(amount, escrowPurseP); + winnings.res(escrowP); + refund.res(null); }, - abort() { - return E(srcPurseP).deposit(amount, escrowPurseP); + abort(reason) { + winnings.reject(reason); + refund.res(escrowP); + }, + getWinnings() { + return winnings.p; + }, + getRefund() { + return refund.p; }, }); } - function failOnly(cancellationP) { - return Promise.resolve(cancellationP).then(cancellation => { - throw cancellation; - }); - } + // Promise wiring + + const moneyPayment = makePromise(); + const moneyTransfer = makeTransfer(moneyNeeded, moneyPayment.p); - const aT = makeTransfer(a.moneySrcP, b.moneyDstP, b.moneyNeeded); - const bT = makeTransfer(b.stockSrcP, a.stockDstP, a.stockNeeded); - return Promise.race([ - Promise.all([aT.phase1(), bT.phase1()]), - failOnly(a.cancellationP), - failOnly(b.cancellationP), - ]).then( - _x => Promise.all([aT.phase2(), bT.phase2()]), - _ex => Promise.all([aT.abort(), bT.abort()]), + const stockPayment = makePromise(); + const stockTransfer = makeTransfer(stockNeeded, stockPayment.p); + + // TODO Use cancellation tokens instead. + const aliceCancel = makePromise(); + const bobCancel = makePromise(); + + // Set it all in motion optimistically. + + const decisionP = Promise.race([ + Promise.all([moneyTransfer.phase1(), stockTransfer.phase1()]), + aliceCancel.p, + bobCancel.p, + ]); + decisionP.then( + _ => { + moneyTransfer.phase2(); + stockTransfer.phase2(); + }, + reason => { + moneyTransfer.abort(reason); + stockTransfer.abort(reason); + }, ); + + // Seats + + const aliceSeat = harden({ + offer: moneyPayment.res, + cancel: aliceCancel.reject, + getWinnings: stockTransfer.getWinnings, + getRefund: moneyTransfer.getRefund, + }); + + const bobSeat = harden({ + offer: stockPayment.res, + cancel: bobCancel.reject, + getWinnings: moneyTransfer.getWinnings, + getRefund: stockTransfer.getRefund, + }); + + return harden([ + chitMaker.make([0, moneyNeeded, stockNeeded], aliceSeat), + chitMaker.make([1, stockNeeded, moneyNeeded], bobSeat), + ]); } + +const escrowExchangeSrc = `(${escrowExchange})`; + +export { escrowExchange, escrowExchangeSrc }; diff --git a/demo/contractHost/issuers.chainmail b/demo/contractHost/issuers.chainmail new file mode 100644 index 00000000000..109b81ee4e1 --- /dev/null +++ b/demo/contractHost/issuers.chainmail @@ -0,0 +1,56 @@ +class Assay[Quantity] { + type Label = { issuer: Issuer, description }; + type Amount = { label: Label, quantity: Quantity }; + + getLabel() ::Label; + make(allegedQuantity ?Quantity) ::Amount; + vouch(amount ?Amount) ::Amount + coerce(amountLike ?Amount) ::Amount; + quantity(amount ?Amount) ::Quantity; + empty() ::Amount; + isEmpty(amount ?Amount) ::boolean; + includes(leftAmount ?Amount, rightAmount ?Amount) ::boolean; + with(leftAmount ?Amount, rightAmount ?Amount) ::Amount; + without(leftAmount ?Amount, rightAmount ?Amount) ::Amount; +} +makeNatAssay(label ::Label) ::Assay; +makeMetaSingleAssayMaker( + baseLabelToAssayFn ::(Label -> Assay)) ::(Label -> Assay); + +class Issuer[Assay] { + type Amount = Assay.Amount; + getLabel() ::{ issuer ::Issuer, description }; + getAssay() ::Assay; + makeEmptyPurse(name ?String) ::Purse; + + class Mint { + getIssuer() Issuer; + mint(initialBalance ?Amount, name ?String) ::Purse; + } + class Payment { + getIssuer() ::Issuer; + getXferBalance() ::Amount; + } + class Purse { + getIssuer() ::Issuer; + getXferBalance() ::Amount; + getUseBalance() ::Amount; + deposit(amount ?Amount, srcPaymentP ?reveal[Promise]) ::Amount; + withdraw(amount ?Amount, name ?String) ::Purse; + } +} +makeMint(description, makeAssay ::(Label -> Assay)) ::Issuer[Assay].Mint; + +class Peg[RemoteIssuer, LocalIssuer] { + getLocalIssuer() ::LocalIssuer; + getRemoteIssuer() ::reveal; + retain(remoteAmount ?reveal, + remotePaymentP ?reveal, + name ?String) ::LocalIssuer.Payment; + redeem(localAmount ?LocalIssuer.Amount, + localPayment ?LocalIssuer.Payment, + name ?String) ::reveal; +} +makePeg(E, + remoteIssuerP ?reveal[Issuer], + makeAssay ::(Label -> Assay)) ::Peg; diff --git a/demo/contractHost/issuers.js b/demo/contractHost/issuers.js new file mode 100644 index 00000000000..b744a21477c --- /dev/null +++ b/demo/contractHost/issuers.js @@ -0,0 +1,292 @@ +// Copyright (C) 2019 Agoric, under Apache License 2.0 + +import harden from '@agoric/harden'; + +import { makePrivateName } from '../../collections/PrivateName'; +import { insist } from '../../collections/insist'; +import { makeNatAssay, makeMetaSingleAssayMaker } from './assays'; +import { mustBeSameStructure } from '../../collections/sameStructure'; + +function makeMint(description, makeAssay = makeNatAssay) { + insist(description)`\ +Description must be truthy: ${description}`; + + // Map from purse or payment to the transfer rights it currently + // holds. Transfer rights can move via payments, or they can cause a + // transfer of both the transfer and use rights by depositing it + // into a purse. + const xferRights = makePrivateName(); + + // Map from purse to useRights, where useRights do not include the + // right to transfer. Creating a payment moves some xferRights into the + // payment, but no useRights. Depositing a payment into another + // purse transfers both the xferRights and the useRights. + const useRights = makePrivateName(); + + // Map from payment to the home purse the payment came from. When the + // payment is deposited elsewhere, useRights are transfered from the + // home purse to the destination purse. + const homePurses = makePrivateName(); + + // src is a purse or payment. Return a fresh payment. One internal + // function used for both cases, since they are so similar. + function takePayment(amount, isPurse, src, _name) { + // eslint-disable-next-line no-use-before-define + amount = assay.coerce(amount); + _name = `${_name}`; + if (isPurse) { + insist(useRights.has(src))`\ +Purse expected: ${src}`; + } else { + insist(homePurses.has(src))`\ +Payment expected: ${src}`; + } + const srcOldXferAmount = xferRights.get(src); + // eslint-disable-next-line no-use-before-define + const srcNewXferAmount = assay.without(srcOldXferAmount, amount); + + // ///////////////// commit point ////////////////// + // All queries above passed with no side effects. + // During side effects below, any early exits should be made into + // fatal turn aborts. + + const payment = harden({ + getIssuer() { + // eslint-disable-next-line no-use-before-define + return issuer; + }, + getXferBalance() { + return xferRights.get(payment); + }, + }); + xferRights.set(src, srcNewXferAmount); + xferRights.init(payment, amount); + const homePurse = isPurse ? src : homePurses.get(src); + homePurses.init(payment, homePurse); + return payment; + } + + const issuer = harden({ + getLabel() { + // eslint-disable-next-line no-use-before-define + return assay.getLabel(); + }, + + getAssay() { + // eslint-disable-next-line no-use-before-define + return assay; + }, + + makeEmptyPurse(name = 'a purse') { + // eslint-disable-next-line no-use-before-define + return mint.mint(assay.empty(), name); // mint and issuer call each other + }, + + getExclusive(amount, srcPaymentP, name = 'a payment') { + return Promise.resolve(srcPaymentP).then(srcPayment => + takePayment(amount, false, srcPayment, name), + ); + }, + + getExclusiveAll(srcPaymentP, name = 'a payment') { + return Promise.resolve(srcPaymentP).then(srcPayment => + takePayment(xferRights.get(srcPayment), false, srcPayment, name), + ); + }, + + slash(amount, srcPaymentP) { + // We deposit the alleged payment, rather than just doing a get + // exclusive on it, in order to consume the usage erights as well. + const sinkPurse = issuer.makeEmptyPurse('sink purse'); + return sinkPurse.deposit(amount, srcPaymentP); + }, + + slashAll(srcPaymentP) { + const sinkPurse = issuer.makeEmptyPurse('sink purse'); + return sinkPurse.depositAll(srcPaymentP); + }, + }); + + const label = harden({ issuer, description }); + + const assay = makeAssay(label); + + function depositInto(purse, amount, srcPayment) { + amount = assay.coerce(amount); + const purseOldXferAmount = xferRights.get(purse); + const srcOldXferAmount = xferRights.get(srcPayment); + // Also checks that the union is representable + const purseNewXferAmount = assay.with(purseOldXferAmount, amount); + const srcNewXferAmount = assay.without(srcOldXferAmount, amount); + + const homePurse = homePurses.get(srcPayment); + const purseOldUseAmount = useRights.get(purse); + const homeOldUseAmount = useRights.get(homePurse); + // Also checks that the union is representable + const purseNewUseAmount = assay.with(purseOldUseAmount, amount); + const homeNewUseAmount = assay.without(homeOldUseAmount, amount); + + // ///////////////// commit point ////////////////// + // All queries above passed with no side effects. + // During side effects below, any early exits should be made into + // fatal turn aborts. + + xferRights.set(srcPayment, srcNewXferAmount); + xferRights.set(purse, purseNewXferAmount); + useRights.set(homePurse, homeNewUseAmount); + useRights.set(purse, purseNewUseAmount); + + return amount; + } + + const mint = harden({ + getIssuer() { + return issuer; + }, + mint(initialBalance, _name = 'a purse') { + initialBalance = assay.coerce(initialBalance); + _name = `${_name}`; + + const purse = harden({ + getIssuer() { + return issuer; + }, + getXferBalance() { + return xferRights.get(purse); + }, + getUseBalance() { + return useRights.get(purse); + }, + deposit(amount, srcPaymentP) { + return Promise.resolve(srcPaymentP).then(srcPayment => { + return depositInto(purse, amount, srcPayment); + }); + }, + depositAll(srcPaymentP) { + return Promise.resolve(srcPaymentP).then(srcPayment => { + return depositInto(purse, xferRights.get(srcPayment), srcPayment); + }); + }, + withdraw(amount, name = 'a withdrawal payment') { + return takePayment(amount, true, purse, name); + }, + withdrawAll(name = 'a withdrawal payment') { + return takePayment(xferRights.get(purse), true, purse, name); + }, + }); + xferRights.init(purse, initialBalance); + useRights.init(purse, initialBalance); + return purse; + }, + }); + return mint; +} +harden(makeMint); + +// Makes a meta issuer issuing rights represented by registered base +// assays. +// +// An empty meta purse or meta payment is not specific to a base +// assay. Its balance is the empty meta amount which has a null meta +// quantity. Non-empty ones have a meta amount whose quantity is a +// base amount of some registered base assay, which cannot be combined +// with amounts of other base assays. (This is the "single" +// restriction of makeMetaSingleAssayMaker.) +// +// Base assays should be registered as soon as they are made, so that +// there is no observable state change from not being registered to +// being registered. +function makeMetaIssuerController(description) { + const baseIdentityToAssay = new WeakMap(); + function baseLabelToAssayFn(baseLabel) { + const baseAssay = baseIdentityToAssay.get(baseLabel.identity); + insist(baseAssay !== undefined)`\ +Label identity not found ${baseLabel}.identity === ${baseLabel.identity}`; + mustBeSameStructure(baseAssay.getLabel(), baseLabel, `Labels don't match`); + return baseAssay; + } + const makeMetaAssay = makeMetaSingleAssayMaker(baseLabelToAssayFn); + const metaMint = makeMint(description, makeMetaAssay); + const metaIssuer = metaMint.getIssuer(); + + const controller = harden({ + getMetaMint() { + return metaMint; + }, + getMetaIssuer() { + return metaIssuer; + }, + register(baseAssay) { + baseIdentityToAssay.set(baseAssay.getLabel().identity, baseAssay); + }, + }); + return controller; +} +harden(makeMetaIssuerController); + +// Creates a local issuer that locally represents a remotely issued +// currency. Returns a promise for a peg object that asynchonously +// converts between the two. The local currency is synchronously +// transferable locally. +function makePeg(E, remoteIssuerP, makeAssay = makeNatAssay) { + const remoteLabelP = E(remoteIssuerP).getLabel(); + + // The remoteLabel is a local copy of the remote pass-by-copy + // label. It has a presence of the remote issuer and a copy of the + // description. + return Promise.resolve(remoteLabelP).then(remoteLabel => { + // Retaining remote currency deposits it in here. + // Redeeming local currency withdraws remote from here. + const backingPurseP = E(remoteIssuerP).makeEmptyPurse('backing'); + + const { description } = remoteLabel; + const localMint = makeMint(description, makeAssay); + const localIssuer = localMint.getIssuer(); + const localLabel = localIssuer.getLabel(); + + function localAmountOf(remoteAmount) { + return harden({ + label: localLabel, + quantity: remoteAmount.quantity, + }); + } + + function remoteAmountOf(localAmount) { + return harden({ + label: remoteLabel, + quantity: localAmount.quantity, + }); + } + + return harden({ + getLocalIssuer() { + return localIssuer; + }, + + getRemoteIssuer() { + return remoteIssuerP; + }, + + retainAll(remotePaymentP, name = 'backed') { + return E(backingPurseP) + .depositAll(remotePaymentP) + .then(remoteAmount => + localMint + .mint(localAmountOf(remoteAmount), `${name} purse`) + .withdrawAll(name), + ); + }, + + redeemAll(localPayment, name = 'redeemed') { + return localIssuer + .slashAll(localPayment) + .then(localAmount => + E(backingPurseP).withdraw(remoteAmountOf(localAmount), name), + ); + }, + }); + }); +} +harden(makePeg); + +export { makeMint, makeMetaIssuerController, makePeg }; diff --git a/demo/contractHost/vat-alice.js b/demo/contractHost/vat-alice.js index 86cec4526e9..93e0a9034d3 100644 --- a/demo/contractHost/vat-alice.js +++ b/demo/contractHost/vat-alice.js @@ -1,112 +1,233 @@ -// Copyright (C) 2013 Google Inc. -// Copyright (C) 2018 Agoric -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* eslint-disable-next-line global-require, import/no-extraneous-dependencies */ +// Copyright (C) 2013 Google Inc, under Apache License 2.0 +// Copyright (C) 2018 Agoric, under Apache License 2.0 + import harden from '@agoric/harden'; -import escrowExchange from './escrow'; + +import { insist } from '../../collections/insist'; +import { escrowExchangeSrc } from './escrow'; +import { coveredCallSrc } from './coveredCall'; +import { exchangeChitAmount, makeCollect } from './chit'; function makeAlice(E, host, log) { - const escrowSrc = `(${escrowExchange})`; + const collect = makeCollect(E); + + function showPaymentBalance(name, paymentP) { + E(paymentP) + .getXferBalance() + .then(amount => log(name, ' xfer balance ', amount)); + } let initialized = false; + let timerP; + let chitIssuerP; + let myMoneyPurseP; - let myMoneyIssuerP; - /* eslint-disable-next-line no-unused-vars */ + let moneyIssuerP; + let myStockPurseP; - let myStockIssuerP; + let stockIssuerP; + + let myOptFinPurseP; + let optFinIssuerP; + + let optFredP; + + function init( + timer, + myMoneyPurse, + myStockPurse, + myOptFinPurse = undefined, + optFred = undefined, + ) { + timerP = E.resolve(timer); + chitIssuerP = E(host).getChitIssuer(); + + myMoneyPurseP = E.resolve(myMoneyPurse); + moneyIssuerP = E(myMoneyPurseP).getIssuer(); + + myStockPurseP = E.resolve(myStockPurse); + stockIssuerP = E(myStockPurseP).getIssuer(); + + if (myOptFinPurse) { + myOptFinPurseP = E.resolve(myOptFinPurse); + optFinIssuerP = E(myOptFinPurseP).getIssuer(); + } + optFredP = optFred; - function init(myMoneyPurse, myStockPurse) { initialized = true; - myMoneyPurseP = Promise.resolve(myMoneyPurse); - myMoneyIssuerP = E(myMoneyPurse).getIssuer(); - myStockPurseP = Promise.resolve(myStockPurse); - myStockIssuerP = E(myStockPurse).getIssuer(); // eslint-disable-next-line no-use-before-define return alice; // alice and init use each other } - const check = (_allegedSrc, _allegedSide) => { - // for testing purposes, alice and bob are willing to play - // any side of any contract, so that the failure we're testing - // is in the contractHost's checking - }; - const alice = harden({ init, payBobWell(bob) { - if (!initialized) { - log('++ ERR: payBobWell called before init()'); - } - const paymentP = E(myMoneyIssuerP).makeEmptyPurse(); - const ackP = E(paymentP).deposit(10, myMoneyPurseP); - return ackP.then(_ => E(bob).buy('shoe', paymentP)); + log('++ alice.payBobWell starting'); + insist(initialized)`\ +ERR: alice.payBobWell called before init()`; + + const paymentP = E(myMoneyPurseP).withdraw(10); + return E(bob).buy('shoe', paymentP); }, - payBobBadly1(bob) { - if (!initialized) { - log('++ ERR: payBobBadly1 called before init()'); - } - const payment = harden({ deposit(_amount, _src) {} }); - return E(bob).buy('shoe', payment); + + invite(allegedChitPaymentP) { + log('++ alice.invite starting'); + insist(initialized)`\ +ERR: alice.invite called before init()`; + + showPaymentBalance('alice chit', allegedChitPaymentP); + + const allegedMetaAmountP = E(allegedChitPaymentP).getXferBalance(); + + const verifiedChitP = E.resolve(allegedMetaAmountP).then( + allegedMetaAmount => { + const clams10 = harden({ + label: { + issuer: moneyIssuerP, + description: 'clams', + }, + quantity: 10, + }); + const fudco7 = harden({ + label: { + issuer: stockIssuerP, + description: 'fudco', + }, + quantity: 7, + }); + + const metaOneAmountP = exchangeChitAmount( + chitIssuerP, + allegedMetaAmount.quantity.label.identity, + escrowExchangeSrc, + [clams10, fudco7], + 0, + clams10, + fudco7, + ); + + return E.resolve(metaOneAmountP).then(metaOneAmount => + E(chitIssuerP).getExclusive( + metaOneAmount, + allegedChitPaymentP, + 'verified chit', + ), + ); + }, + ); + + showPaymentBalance('verified chit', verifiedChitP); + + const seatP = E(host).redeem(verifiedChitP); + const moneyPaymentP = E(myMoneyPurseP).withdraw(10); + E(seatP).offer(moneyPaymentP); + return collect(seatP, myStockPurseP, myMoneyPurseP, 'alice escrow'); }, - payBobBadly2(bob) { - if (!initialized) { - log('++ ERR: payBobBadly2 called before init()'); + + acceptOption(allegedChitPaymentP) { + if (optFredP) { + return alice.acceptOptionForFred(allegedChitPaymentP); } - const paymentP = E(myMoneyIssuerP).makeEmptyPurse(); - const ackP = E(paymentP).deposit(5, myMoneyPurseP); - return ackP.then(_ => E(bob).buy('shoe', paymentP)); + return alice.acceptOptionDirectly(allegedChitPaymentP); }, - tradeWell(bob) { - if (!initialized) { - log('++ ERR: tradeWell called before init()'); - } - const tokensP = E(host).setup(escrowSrc); - const aliceTokenP = tokensP.then(tokens => tokens[0]); - const bobTokenP = tokensP.then(tokens => tokens[1]); - E(bob).invite(bobTokenP, escrowSrc, 1); - return E(alice).invite(aliceTokenP, escrowSrc, 0); + acceptOptionDirectly(allegedChitPaymentP) { + log('++ alice.acceptOptionDirectly starting'); + insist(initialized)`\ +ERR: alice.acceptOptionDirectly called before init()`; + + showPaymentBalance('alice chit', allegedChitPaymentP); + + const allegedMetaAmountP = E(allegedChitPaymentP).getXferBalance(); + + const verifiedChitP = E.resolve(allegedMetaAmountP).then( + allegedMetaAmount => { + const smackers10 = harden({ + label: { + issuer: moneyIssuerP, + description: 'smackers', + }, + quantity: 10, + }); + const yoyodyne7 = harden({ + label: { + issuer: stockIssuerP, + description: 'yoyodyne', + }, + quantity: 7, + }); + + const metaOneAmountP = exchangeChitAmount( + chitIssuerP, + allegedMetaAmount.quantity.label.identity, + coveredCallSrc, + [smackers10, yoyodyne7, timerP, 'singularity'], + 'holder', + smackers10, + yoyodyne7, + ); + + return E.resolve(metaOneAmountP).then(metaOneAmount => + E(chitIssuerP).getExclusive( + metaOneAmount, + allegedChitPaymentP, + 'verified chit', + ), + ); + }, + ); + + showPaymentBalance('verified chit', verifiedChitP); + + const seatP = E(host).redeem(verifiedChitP); + const moneyPaymentP = E(myMoneyPurseP).withdraw(10); + E(seatP).offer(moneyPaymentP); + return collect(seatP, myStockPurseP, myMoneyPurseP, 'alice option'); }, - invite(tokenP, allegedSrc, allegedSide) { - if (!initialized) { - log('++ ERR: invite called before init()'); - } + acceptOptionForFred(allegedChitPaymentP) { + log('++ alice.acceptOptionForFred starting'); + insist(initialized)`\ +ERR: alice.acceptOptionForFred called before init()`; + + const finNeededP = E(E(optFinIssuerP).getAssay()).make(55); + const chitNeededP = E(allegedChitPaymentP).getXferBalance(); - check(allegedSrc, allegedSide); + const termsP = harden([finNeededP, chitNeededP]); + const chitsP = E(host).start(escrowExchangeSrc, termsP); + const fredChitP = chitsP.then(chits => chits[0]); + const aliceForFredChitP = chitsP.then(chits => chits[1]); + const doneP = Promise.all([ + E(optFredP).acceptOptionOffer(fredChitP), + E(alice).completeOptionsSale(aliceForFredChitP, allegedChitPaymentP), + ]); + doneP.then( + _res => log('++ alice.acceptOptionForFred done'), + rej => log('++ alice.acceptOptionForFred reject: ', rej), + ); + return doneP; + }, - // eslint-disable-next-line no-unused-vars - let cancel; - const a = harden({ - moneySrcP: E(myMoneyIssuerP).makeEmptyPurse('aliceMoneySrc'), - stockDstP: E(myStockIssuerP).makeEmptyPurse('aliceStockDst'), - stockNeeded: 7, - cancellationP: new Promise(r => (cancel = r)), - }); - const ackP = E(a.moneySrcP).deposit(10, myMoneyPurseP); + completeOptionsSale(aliceForFredChitP, allegedChitPaymentP) { + log('++ alice.completeOptionsSale starting'); + insist(initialized)`\ +ERR: alice.completeOptionsSale called before init()`; - const doneP = ackP.then(_ => - E(host).play(tokenP, allegedSrc, allegedSide, a), + const aliceForFredSeatP = E(host).redeem(aliceForFredChitP); + E(aliceForFredSeatP).offer(allegedChitPaymentP); + const myChitPurseP = E(chitIssuerP).makeEmptyPurse(); + return collect( + aliceForFredSeatP, + myOptFinPurseP, + myChitPurseP, + 'alice options sale', ); - return doneP.then(_ => E(a.stockDstP).getBalance()); }, }); return alice; } -export default function setup(syscall, state, helpers) { +function setup(syscall, state, helpers) { function log(what) { helpers.log(what); console.log(what); @@ -119,3 +240,4 @@ export default function setup(syscall, state, helpers) { }), ); } +export default harden(setup); diff --git a/demo/contractHost/vat-bob.js b/demo/contractHost/vat-bob.js index 76d87ceda20..23f5314364c 100644 --- a/demo/contractHost/vat-bob.js +++ b/demo/contractHost/vat-bob.js @@ -1,49 +1,46 @@ -// Copyright (C) 2013 Google Inc. -// Copyright (C) 2018 Agoric -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* eslint-disable-next-line global-require, import/no-extraneous-dependencies */ +// Copyright (C) 2013 Google Inc, under Apache License 2.0 +// Copyright (C) 2018 Agoric, under Apache License 2.0 + import harden from '@agoric/harden'; -import escrowExchange from './escrow'; -function makeBob(E, host, log) { - const escrowSrc = `(${escrowExchange})`; +import { insist } from '../../collections/insist'; +import { escrowExchangeSrc } from './escrow'; +import { coveredCallSrc } from './coveredCall'; +import { makeCollect } from './chit'; + +function makeBob(E, host) { + const collect = makeCollect(E); let initialized = false; + let timerP; + let myMoneyPurseP; - let myMoneyIssuerP; + let moneyIssuerP; + let moneyNeededP; + let myStockPurseP; - let myStockIssuerP; + let stockIssuerP; + let stockNeededP; + + function init(timer, myMoneyPurse, myStockPurse) { + timerP = E.resolve(timer); + + myMoneyPurseP = E.resolve(myMoneyPurse); + moneyIssuerP = E(myMoneyPurseP).getIssuer(); + moneyNeededP = E(E(moneyIssuerP).getAssay()).make(10); + + myStockPurseP = E.resolve(myStockPurse); + stockIssuerP = E(myStockPurseP).getIssuer(); + stockNeededP = E(E(stockIssuerP).getAssay()).make(7); - function init(myMoneyPurse, myStockPurse) { initialized = true; - myMoneyPurseP = Promise.resolve(myMoneyPurse); - myMoneyIssuerP = E(myMoneyPurse).getIssuer(); - myStockPurseP = Promise.resolve(myStockPurse); - myStockIssuerP = E(myStockPurse).getIssuer(); - /* eslint-disable-next-line no-use-before-define */ + // eslint-disable-next-line no-use-before-define return bob; // bob and init use each other } - const check = (_allegedSrc, _allegedSide) => { - // for testing purposes, alice and bob are willing to play - // any side of any contract, so that the failure we're testing - // is in the contractHost's checking - }; - const bob = harden({ init, + /** * This is not an imperative to Bob to buy something but rather * the opposite. It is a request by a client to buy something from @@ -51,9 +48,9 @@ function makeBob(E, host, log) { * is a bit confusing here. */ buy(desc, paymentP) { - if (!initialized) { - log('++ ERR: buy called before init()'); - } + insist(initialized)`\ +ERR: buy called before init()`; + /* eslint-disable-next-line no-unused-vars */ let amount; let good; @@ -70,29 +67,26 @@ function makeBob(E, host, log) { } return E(myMoneyPurseP) - .deposit(10, paymentP) + .deposit(amount, paymentP) .then(_ => good); }, - tradeWell(alice, bobLies = false) { + tradeWell(alice) { log('++ bob.tradeWell starting'); - if (!initialized) { - log('++ ERR: tradeWell called before init()'); - } - const tokensP = E(host).setup(escrowSrc); - const aliceTokenP = tokensP.then(tokens => tokens[0]); - const bobTokenP = tokensP.then(tokens => tokens[1]); - let escrowSrcWeTellAlice = escrowSrc; - if (bobLies) { - escrowSrcWeTellAlice += 'NOT'; - } + insist(initialized)`\ +ERR: tradeWell called before init()`; + + const termsP = harden([moneyNeededP, stockNeededP]); + const chitsP = E(host).start(escrowExchangeSrc, termsP); + const aliceChitP = chitsP.then(chits => chits[0]); + const bobChitP = chitsP.then(chits => chits[1]); const doneP = Promise.all([ - E(alice).invite(aliceTokenP, escrowSrcWeTellAlice, 0), - E(bob).invite(bobTokenP, escrowSrc, 1), + E(alice).invite(aliceChitP), + E(bob).invite(bobChitP), ]); doneP.then( _res => log('++ bob.tradeWell done'), - rej => log('++ bob.tradeWell reject', rej), + rej => log('++ bob.tradeWell reject: ', rej), ); return doneP; }, @@ -102,42 +96,46 @@ function makeBob(E, host, log) { * this object, asking it to join in a contract instance. It is not * requesting that this object invite anything. */ - invite(tokenP, allegedSrc, allegedSide) { - if (!initialized) { - log('++ ERR: invite called before init()'); - } - log('++ bob.invite start'); - check(allegedSrc, allegedSide); - log('++ bob.invite passed check'); - /* eslint-disable-next-line no-unused-vars */ - let cancel; - const b = harden({ - stockSrcP: E(myStockIssuerP).makeEmptyPurse('bobStockSrc'), - moneyDstP: E(myMoneyIssuerP).makeEmptyPurse('bobMoneyDst'), - moneyNeeded: 10, - cancellationP: new Promise(r => (cancel = r)), - }); - const ackP = E(b.stockSrcP).deposit(7, myStockPurseP); - - const doneP = ackP.then(_ => { - log('++ bob.invite ackP'); - return E(host).play(tokenP, allegedSrc, allegedSide, b); - }); - return doneP.then( - _ => { - log('++ bob.invite doneP'); - return E(b.moneyDstP).getBalance(); - }, - rej => { - log('++ bob.invite doneP reject', rej); - }, + invite(chitP) { + insist(initialized)`\ +ERR: invite called before init()`; + + const seatP = E(host).redeem(chitP); + const stockPaymentP = E(myStockPurseP).withdraw(7); + E(seatP).offer(stockPaymentP); + return collect(seatP, myMoneyPurseP, myStockPurseP, 'bob escrow'); + }, + + offerAliceOption(alice) { + log('++ bob.offerAliceOption starting'); + insist(initialized)`\ +ERR: offerAliceOption called before init()`; + + const termsP = harden([ + moneyNeededP, + stockNeededP, + timerP, + 'singularity', + ]); + const bobChitP = E(host).start(coveredCallSrc, termsP); + const bobSeatP = E(host).redeem(bobChitP); + const stockPaymentP = E(myStockPurseP).withdraw(7); + const aliceChitP = E(bobSeatP).offer(stockPaymentP); + const doneP = Promise.all([ + E(alice).acceptOption(aliceChitP), + collect(bobSeatP, myMoneyPurseP, myStockPurseP, 'bob option'), + ]); + doneP.then( + _res => log('++ bob.offerAliceOption done'), + rej => log('++ bob.offerAliceOption reject: ', rej), ); + return doneP; }, }); return bob; } -export default function setup(syscall, state, helpers) { +function setup(syscall, state, helpers) { function log(what) { helpers.log(what); console.log(what); @@ -150,3 +148,4 @@ export default function setup(syscall, state, helpers) { }), ); } +export default harden(setup); diff --git a/demo/contractHost/vat-fred.js b/demo/contractHost/vat-fred.js new file mode 100644 index 00000000000..ef9b2a82bdf --- /dev/null +++ b/demo/contractHost/vat-fred.js @@ -0,0 +1,150 @@ +// Copyright (C) 2013 Google Inc, under Apache License 2.0 +// Copyright (C) 2018 Agoric, under Apache License 2.0 + +import harden from '@agoric/harden'; + +import { allComparable } from '../../collections/sameStructure'; +import { insist } from '../../collections/insist'; +import { escrowExchangeSrc } from './escrow'; +import { coveredCallSrc } from './coveredCall'; +import { exchangeChitAmount, makeCollect } from './chit'; + +function makeFred(E, host) { + const collect = makeCollect(E); + + let initialized = false; + let timerP; + let chitIssuerP; + + let myMoneyPurseP; + let moneyIssuerP; + + let myStockPurseP; + let stockIssuerP; + + let myFinPurseP; + let finIssuerP; + + function init(timer, myMoneyPurse, myStockPurse, myFinPurse) { + timerP = E.resolve(timer); + chitIssuerP = E(host).getChitIssuer(); + + myMoneyPurseP = E.resolve(myMoneyPurse); + moneyIssuerP = E(myMoneyPurseP).getIssuer(); + + myStockPurseP = E.resolve(myStockPurse); + stockIssuerP = E(myStockPurseP).getIssuer(); + + myFinPurseP = E.resolve(myFinPurse); + finIssuerP = E(myFinPurseP).getIssuer(); + + initialized = true; + // eslint-disable-next-line no-use-before-define + return fred; // fred and init use each other + } + + const fred = harden({ + init, + acceptOptionOffer(allegedChitPaymentP) { + console.log('++ fred.acceptOptionOffer starting'); + insist(initialized)`\ +ERR: fred.acceptOptionOffer called before init()`; + + const dough10 = harden({ + label: { + issuer: moneyIssuerP, + description: 'dough', + }, + quantity: 10, + }); + const wonka7 = harden({ + label: { + issuer: stockIssuerP, + description: 'wonka', + }, + quantity: 7, + }); + const fin55 = harden({ + label: { + issuer: finIssuerP, + description: 'fins', + }, + quantity: 55, + }); + + const allegedMetaAmountP = E(allegedChitPaymentP).getXferBalance(); + + const verifiedChitP = E.resolve(allegedMetaAmountP).then( + allegedMetaAmount => { + const allegedBaseOptionsChitIssuer = + allegedMetaAmount.quantity.label.description.terms[1]; + + const metaOptionAmountP = exchangeChitAmount( + chitIssuerP, + allegedBaseOptionsChitIssuer.quantity.label.identity, + coveredCallSrc, + [dough10, wonka7, timerP, 'singularity'], + 'holder', + dough10, + wonka7, + ); + + const metaOptionSaleAmountP = exchangeChitAmount( + chitIssuerP, + allegedMetaAmount.quantity.label.identity, + escrowExchangeSrc, + [fin55, metaOptionAmountP], + 0, + fin55, + metaOptionAmountP, + ); + + return E.resolve(metaOptionSaleAmountP).then(metaOptionSaleAmount => + E(chitIssuerP).getExclusive( + metaOptionSaleAmount, + allegedChitPaymentP, + 'verified chit', + ), + ); + }, + ); + const seatP = E(host).redeem(verifiedChitP); + const finPaymentP = E(myFinPurseP).withdraw(55); + E(seatP).offer(finPaymentP); + const optionChitPurseP = E(chitIssuerP).makeEmptyPurse(); + const gotOptionP = collect( + seatP, + optionChitPurseP, + myFinPurseP, + 'fred buys escrowed option', + ); + return E.resolve(gotOptionP).then(_ => { + // Fred bought the option. Now fred tries to exercise the option. + const optionChitPayment = E(optionChitPurseP).withdrawAll(); + const optionSeatP = E(host).redeem(optionChitPayment); + return E.resolve(allComparable(dough10)).then(d10 => { + const doughPaymentP = E(myMoneyPurseP).withdraw(d10); + E(optionSeatP).offer(doughPaymentP); + return collect( + optionSeatP, + myStockPurseP, + myMoneyPurseP, + 'fred exercises option, buying stock', + ); + }); + }); + }, + }); + return fred; +} + +function setup(syscall, state, helpers) { + return helpers.makeLiveSlots(syscall, state, E => + harden({ + makeFred(host) { + return harden(makeFred(E, host)); + }, + }), + ); +} +export default harden(setup); diff --git a/demo/contractHost/vat-host.js b/demo/contractHost/vat-host.js index dc88f6506c8..1813eac5e75 100644 --- a/demo/contractHost/vat-host.js +++ b/demo/contractHost/vat-host.js @@ -1,158 +1,20 @@ -// Copyright (C) 2012 Google Inc. -// Copyright (C) 2018 Agoric -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Copyright (C) 2018 Agoric, under Apache License 2.0 -/** - * @fileoverview Simple AMD module exports a {@code makeContractHost} - * function, which makes a contract host, which makes and runs a - * contract. Requires SES and its simple AMD loader. - * @requires define, WeakMap, Q, cajaVM - * @author Mark S. Miller erights@gmail.com - */ - -/** - * A contract host as a mutually trusted third party for honestly - * running whatever smart contract code its customers agree on. - * - *

When mutually suspicious parties wish to engage in a joint - * contract, if they can agree on a contract host to be run the - * following code honestly, agree on the code that represents their - * smart contract, and agree on which side of the contract they each - * play, then they can validly engage in the contract. - * - *

The {@code contractSrc} is assumed to be the source code for a - * closed SES function, where each of the parties to the contract is - * supposed to provide their respective fulfilled arguments. Once - * all these arguments are fulfilled, then the contract is run. - * - *

There are two "roles" for participating in the protocol: - * contract initiator, who calls the contract host's {@code setup} - * method, and contract participants, who call the contract host's - * {@code play} method. For example, let's say the contract in - * question is the board manager for playing chess. The initiator - * instantiates a new chess game, whose board manager is a two - * argument function, where argument zero is provided by the player - * playing "white" and argument one is provided by the player - * playing "black". - * - *

The {@code setup} method returns an array of tokens, one per - * argument, where each token represents the exclusive right to - * provide that argument. The initiator would then distribute these - * tokens to each of the players, together with the alleged source - * for the game they would be playing, and their alleged side, i.e., - * which argument position they are responsible for providing. - * - *

- *   // Contract initiator
- *   var tokensP = Q(contractHostP).invoke('setup', chessSrc);
- *   var whiteTokenP = Q(tokensP).get(0);
- *   var blackTokenP = Q(tokensP).get(1);
- *   Q(whitePlayer).invoke('invite', whiteTokenP, chessSrc, 0);
- *   Q(blackPlayer).invoke('invite', blackTokenP, chessSrc, 1);
- * 
- * - *

Each player, on receiving the token, alleged game source, and - * alleged argument index, would first decide (e.g., with the {@code - * check} function below) whether this is a game they would be - * interested in playing. If so, the redeem the token to - * start playing their side of the game -- but only if the contract - * host verifies that they are playing the side of the game that - * they think they are. - * - *

- *   // Contract participant
- *   function invite(tokenP, allegedChessSrc, allegedSide) {
- *     check(allegedChessSrc, allegedSide);
- *     var outcomeP = Q(contractHostP).invoke(
- *         'play', tokenP, allegedChessSrc, allegedSide, arg);
- *   }
- * 
- */ - -/* eslint-disable-next-line global-require, import/no-extraneous-dependencies */ import harden from '@agoric/harden'; -import evaluate from '@agoric/evaluate'; -import makePromise from '../../src/kernel/makePromise'; - -function makeHost(E) { - const m = new WeakMap(); - return harden({ - setup(contractSrc) { - contractSrc = `${contractSrc}`; - const tokens = []; - const argPs = []; - const { p: resultP, res: resolve } = makePromise(); - // let resolve; - // const f = new Flow(); - // const resultP = f.makeVow(r => (resolve = r)); - const contract = evaluate(contractSrc, { - // Flow, - // Vow, - console, - require, - E, - }); - // console.log(`confined contract is ${typeof contract} ${contract}`); - - const addParam = (i, token) => { - tokens[i] = token; - // let resolveArg; - // argPs[i] = f.makeVow(r => (resolveArg = r)); - const p = makePromise(); - const resolveArg = p.res; - argPs[i] = p.p; - m.set(token, (allegedSrc, allegedI, arg) => { - if (contractSrc !== allegedSrc) { - throw new Error(`unexpected contract: ${contractSrc}`); - } - if (i !== allegedI) { - throw new Error(`unexpected side: ${i}`); - } - m.delete(token); - resolveArg(arg); - return resultP; - }); - }; - for (let i = 0; i < contract.length; i += 1) { - addParam(i, harden({})); - } - resolve( - Promise.all(argPs).then(args => { - return contract(...args); - }), - ); - return harden(tokens); - }, - play(tokenP, allegedSrc, allegedI, arg) { - return Promise.resolve(tokenP).then(token => { - return m.get(token)(allegedSrc, allegedI, arg); - }); - }, - }); -} +import { makeContractHost } from './chit'; -export default function setup(syscall, state, helpers) { +function setup(syscall, state, helpers) { return helpers.makeLiveSlots( syscall, state, E => harden({ makeHost() { - return harden(makeHost(E)); + return harden(makeContractHost(E)); }, }), helpers.vatID, ); } +export default harden(setup); diff --git a/demo/contractHost/vat-mint.js b/demo/contractHost/vat-mint.js index c86154233dd..b5ec19f0e86 100644 --- a/demo/contractHost/vat-mint.js +++ b/demo/contractHost/vat-mint.js @@ -1,85 +1,15 @@ -// Copyright (C) 2012 Google Inc. -// Copyright (C) 2018 Agoric -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Copyright (C) 2019 Agoric, under Apache License 2.0 -import Nat from '@agoric/nat'; import harden from '@agoric/harden'; -function build(_E, log) { - let debugCounter = 0; - - function makeMint() { - log(`makeMint`); - // Map from purse or payment to balance - const ledger = new WeakMap(); - - const issuer = harden({ - makeEmptyPurse(name) { - log(`makeEmptyPurse(${name})`); - // eslint-disable-next-line no-use-before-define - return mint(0, name); // mint and issuer call each other - }, - }); - - const mint = (initialBalance, name) => { - const purse = harden({ - getBalance() { - log(`getBalance`, ledger.get(purse)); - return ledger.get(purse); - }, - getIssuer() { - return issuer; - }, - deposit(amount, srcP) { - amount = Nat(amount); - debugCounter += 1; - const c = debugCounter; - log(`deposit[${name}]#${c}: bal=${ledger.get(purse)} amt=${amount}`); - return Promise.resolve(srcP).then(src => { - log( - ` dep[${name}]#${c} (post-P): bal=${ledger.get( - purse, - )} amt=${amount}`, - ); - const myOldBal = Nat(ledger.get(purse)); - const srcOldBal = Nat(ledger.get(src)); - Nat(myOldBal + amount); - const srcNewBal = Nat(srcOldBal - amount); - // ///////////////// commit point ////////////////// - // All queries above passed with no side effects. - // During side effects below, any early exits should be made into - // fatal turn aborts. - ledger.set(src, srcNewBal); - // In case purse and src are the same, add to purse's updated - // balance rather than myOldBal above. The current balance must be - // >= 0 and <= myOldBal, so no additional Nat test is needed. - // This is good because we're after the commit point, where no - // non-fatal errors are allowed. - ledger.set(purse, ledger.get(purse) + amount); - }); - }, - }); - ledger.set(purse, initialBalance); - return purse; - }; - return harden({ mint }); - } +import { makeMint } from './issuers'; +function build(_E, log) { return harden({ makeMint }); } +harden(build); -export default function setup(syscall, state, helpers) { +function setup(syscall, state, helpers) { function log(what) { helpers.log(what); console.log(what); @@ -91,3 +21,4 @@ export default function setup(syscall, state, helpers) { helpers.vatID, ); } +export default harden(setup); From 784e5dd21e247178c586ce73d28a97f8b85d6732 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Tue, 28 May 2019 14:49:21 -0700 Subject: [PATCH 02/18] demo/contractHost seems to work --- demo/contractHost/bootstrap.js | 2 -- demo/contractHost/vat-bob.js | 2 +- demo/contractHost/vat-mint.js | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/demo/contractHost/bootstrap.js b/demo/contractHost/bootstrap.js index f702602a526..ae1bc5fac1d 100644 --- a/demo/contractHost/bootstrap.js +++ b/demo/contractHost/bootstrap.js @@ -2,8 +2,6 @@ import harden from '@agoric/harden'; -import { insist } from '../../collections/insist'; - function build(E, log) { function showPaymentBalance(name, paymentP) { E(paymentP) diff --git a/demo/contractHost/vat-bob.js b/demo/contractHost/vat-bob.js index 23f5314364c..08f923a533a 100644 --- a/demo/contractHost/vat-bob.js +++ b/demo/contractHost/vat-bob.js @@ -8,7 +8,7 @@ import { escrowExchangeSrc } from './escrow'; import { coveredCallSrc } from './coveredCall'; import { makeCollect } from './chit'; -function makeBob(E, host) { +function makeBob(E, host, log) { const collect = makeCollect(E); let initialized = false; diff --git a/demo/contractHost/vat-mint.js b/demo/contractHost/vat-mint.js index b5ec19f0e86..83fe07eb5a7 100644 --- a/demo/contractHost/vat-mint.js +++ b/demo/contractHost/vat-mint.js @@ -4,7 +4,7 @@ import harden from '@agoric/harden'; import { makeMint } from './issuers'; -function build(_E, log) { +function build(_E, _log) { return harden({ makeMint }); } harden(build); From 7bf21223f5794bf318eaf502307bcd0786b97c88 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Tue, 28 May 2019 16:02:49 -0700 Subject: [PATCH 03/18] contract tests from master branch commented out for now --- test/test-demos.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/test-demos.js b/test/test-demos.js index 3c42e85c5ab..a4d2bad0be9 100644 --- a/test/test-demos.js +++ b/test/test-demos.js @@ -63,6 +63,9 @@ test('run encouragementBotComms Demo without SES', async t => { t.end(); }); +/* +TODO get these tests working again! + test('run contractHost Demo --mint with SES', async t => { const dump = await main(true, 'demo/contractHost', ['mint']); t.deepEquals(dump.log, [ @@ -276,3 +279,5 @@ test('run contractHost Demo --bob-first-lies without SES', async t => { ]); t.end(); }); + +*/ From b4fc81983fae01500a68ec8c764458165f309ef0 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Fri, 31 May 2019 15:29:23 -0700 Subject: [PATCH 04/18] now works again with kernel log change --- demo/contractHost/bootstrap.js | 6 +- demo/contractHost/vat-alice.js | 6 +- demo/contractHost/vat-bob.js | 6 +- demo/contractHost/vat-fred.js | 10 +- demo/contractHost/vat-mint.js | 6 +- test/test-demos.js | 277 ++++++++++++++++++--------------- 6 files changed, 168 insertions(+), 143 deletions(-) diff --git a/demo/contractHost/bootstrap.js b/demo/contractHost/bootstrap.js index ae1bc5fac1d..dd4b33b2b5e 100644 --- a/demo/contractHost/bootstrap.js +++ b/demo/contractHost/bootstrap.js @@ -297,9 +297,9 @@ function build(E, log) { harden(build); function setup(syscall, state, helpers) { - function log(what) { - helpers.log(what); - console.log(what); + function log(...args) { + helpers.log(...args); + console.log(...args); } log(`=> setup called`); return helpers.makeLiveSlots( diff --git a/demo/contractHost/vat-alice.js b/demo/contractHost/vat-alice.js index 93e0a9034d3..98e63b05921 100644 --- a/demo/contractHost/vat-alice.js +++ b/demo/contractHost/vat-alice.js @@ -228,9 +228,9 @@ ERR: alice.completeOptionsSale called before init()`; } function setup(syscall, state, helpers) { - function log(what) { - helpers.log(what); - console.log(what); + function log(...args) { + helpers.log(...args); + console.log(...args); } return helpers.makeLiveSlots(syscall, state, E => harden({ diff --git a/demo/contractHost/vat-bob.js b/demo/contractHost/vat-bob.js index 08f923a533a..136a83da3ea 100644 --- a/demo/contractHost/vat-bob.js +++ b/demo/contractHost/vat-bob.js @@ -136,9 +136,9 @@ ERR: offerAliceOption called before init()`; } function setup(syscall, state, helpers) { - function log(what) { - helpers.log(what); - console.log(what); + function log(...args) { + helpers.log(...args); + console.log(...args); } return helpers.makeLiveSlots(syscall, state, E => harden({ diff --git a/demo/contractHost/vat-fred.js b/demo/contractHost/vat-fred.js index ef9b2a82bdf..d2454484399 100644 --- a/demo/contractHost/vat-fred.js +++ b/demo/contractHost/vat-fred.js @@ -9,7 +9,7 @@ import { escrowExchangeSrc } from './escrow'; import { coveredCallSrc } from './coveredCall'; import { exchangeChitAmount, makeCollect } from './chit'; -function makeFred(E, host) { +function makeFred(E, host, log) { const collect = makeCollect(E); let initialized = false; @@ -46,7 +46,7 @@ function makeFred(E, host) { const fred = harden({ init, acceptOptionOffer(allegedChitPaymentP) { - console.log('++ fred.acceptOptionOffer starting'); + log('++ fred.acceptOptionOffer starting'); insist(initialized)`\ ERR: fred.acceptOptionOffer called before init()`; @@ -139,10 +139,14 @@ ERR: fred.acceptOptionOffer called before init()`; } function setup(syscall, state, helpers) { + function log(...args) { + helpers.log(...args); + console.log(...args); + } return helpers.makeLiveSlots(syscall, state, E => harden({ makeFred(host) { - return harden(makeFred(E, host)); + return harden(makeFred(E, host, log)); }, }), ); diff --git a/demo/contractHost/vat-mint.js b/demo/contractHost/vat-mint.js index 83fe07eb5a7..a0b6a20e4d7 100644 --- a/demo/contractHost/vat-mint.js +++ b/demo/contractHost/vat-mint.js @@ -10,9 +10,9 @@ function build(_E, _log) { harden(build); function setup(syscall, state, helpers) { - function log(what) { - helpers.log(what); - console.log(what); + function log(...args) { + helpers.log(...args); + console.log(...args); } return helpers.makeLiveSlots( syscall, diff --git a/test/test-demos.js b/test/test-demos.js index a4d2bad0be9..23fd8d1265a 100644 --- a/test/test-demos.js +++ b/test/test-demos.js @@ -63,22 +63,18 @@ test('run encouragementBotComms Demo without SES', async t => { t.end(); }); -/* -TODO get these tests working again! - test('run contractHost Demo --mint with SES', async t => { const dump = await main(true, 'demo/contractHost', ['mint']); t.deepEquals(dump.log, [ '=> setup called', - 'starting mintTest', - 'makeMint', - 'makeEmptyPurse(deposit)', - 'deposit[deposit]#1: bal=0 amt=50', - ' dep[deposit]#1 (post-P): bal=0 amt=50', - 'getBalance', - 'getBalance', - '++ balances:', - '++ DONE', + 'starting mintTestAssay', + 'starting mintTestNumber', + 'alice xfer balance {"label":{"issuer":{},"description":"quatloos"},"quantity":950}', + 'alice use balance {"label":{"issuer":{},"description":"quatloos"},"quantity":1000}', + 'payment xfer balance {"label":{"issuer":{},"description":"quatloos"},"quantity":50}', + 'alice xfer balance {"label":{"issuer":{},"description":"bucks"},"quantity":950}', + 'alice use balance {"label":{"issuer":{},"description":"bucks"},"quantity":1000}', + 'payment xfer balance {"label":{"issuer":{},"description":"bucks"},"quantity":50}', ]); t.end(); }); @@ -87,15 +83,14 @@ test('run contractHost Demo --mint without SES', async t => { const dump = await main(false, 'demo/contractHost', ['mint']); t.deepEquals(dump.log, [ '=> setup called', - 'starting mintTest', - 'makeMint', - 'makeEmptyPurse(deposit)', - 'deposit[deposit]#1: bal=0 amt=50', - ' dep[deposit]#1 (post-P): bal=0 amt=50', - 'getBalance', - 'getBalance', - '++ balances:', - '++ DONE', + 'starting mintTestAssay', + 'starting mintTestNumber', + 'alice xfer balance {"label":{"issuer":{},"description":"quatloos"},"quantity":950}', + 'alice use balance {"label":{"issuer":{},"description":"quatloos"},"quantity":1000}', + 'payment xfer balance {"label":{"issuer":{},"description":"quatloos"},"quantity":50}', + 'alice xfer balance {"label":{"issuer":{},"description":"bucks"},"quantity":950}', + 'alice use balance {"label":{"issuer":{},"description":"bucks"},"quantity":1000}', + 'payment xfer balance {"label":{"issuer":{},"description":"bucks"},"quantity":50}', ]); t.end(); }); @@ -105,8 +100,10 @@ test('run contractHost Demo --trivial with SES', async t => { t.deepEquals(dump.log, [ '=> setup called', 'starting trivialContractTest', - '++ eightP resolved to', + 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"function trivCo...foo\', 8);\\n }","terms":"foo terms","seatDesc":"foo"}},"quantity":1}}', + '++ eightP resolved to8(should be 8)', '++ DONE', + 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":null}', ]); t.end(); }); @@ -116,8 +113,10 @@ test('run contractHost Demo --trivial without SES', async t => { t.deepEquals(dump.log, [ '=> setup called', 'starting trivialContractTest', - '++ eightP resolved to', + 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"function trivCo...foo\', 8);\\n }","terms":"foo terms","seatDesc":"foo"}},"quantity":1}}', + '++ eightP resolved to8(should be 8)', '++ DONE', + 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":null}', ]); t.end(); }); @@ -126,14 +125,8 @@ test('run contractHost Demo --alice-first with SES', async t => { const dump = await main(true, 'demo/contractHost', ['alice-first']); t.deepEquals(dump.log, [ '=> setup called', - 'makeMint', - 'makeMint', - 'makeEmptyPurse(undefined)', - 'deposit[undefined]#1: bal=0 amt=10', - ' dep[undefined]#1 (post-P): bal=0 amt=10', - 'deposit[undefined]#2: bal=1001 amt=10', - ' dep[undefined]#2 (post-P): bal=1001 amt=10', - '++ ifItFitsP done:', + '++ alice.payBobWell starting', + '++ ifItFitsP done:If it fits, ware it.', '++ DONE', ]); t.end(); @@ -143,14 +136,8 @@ test('run contractHost Demo --alice-first without SES', async t => { const dump = await main(false, 'demo/contractHost', ['alice-first']); t.deepEquals(dump.log, [ '=> setup called', - 'makeMint', - 'makeMint', - 'makeEmptyPurse(undefined)', - 'deposit[undefined]#1: bal=0 amt=10', - ' dep[undefined]#1 (post-P): bal=0 amt=10', - 'deposit[undefined]#2: bal=1001 amt=10', - ' dep[undefined]#2 (post-P): bal=1001 amt=10', - '++ ifItFitsP done:', + '++ alice.payBobWell starting', + '++ ifItFitsP done:If it fits, ware it.', '++ DONE', ]); t.end(); @@ -160,36 +147,21 @@ test('run contractHost Demo --bob-first with SES', async t => { const dump = await main(true, 'demo/contractHost', ['bob-first']); t.deepEquals(dump.log, [ '=> setup called', - 'makeMint', - 'makeMint', '++ bob.tradeWell starting', - '++ bob.invite start', - '++ bob.invite passed check', - 'makeEmptyPurse(bobMoneyDst)', - 'makeEmptyPurse(bobStockSrc)', - 'makeEmptyPurse(aliceMoneySrc)', - 'makeEmptyPurse(aliceStockDst)', - 'deposit[bobStockSrc]#1: bal=0 amt=7', - 'deposit[aliceMoneySrc]#2: bal=0 amt=10', - ' dep[bobStockSrc]#1 (post-P): bal=0 amt=7', - ' dep[aliceMoneySrc]#2 (post-P): bal=0 amt=10', - '++ bob.invite ackP', - 'makeEmptyPurse(escrow)', - 'makeEmptyPurse(escrow)', - 'deposit[escrow]#3: bal=0 amt=10', - 'deposit[escrow]#4: bal=0 amt=7', - ' dep[escrow]#3 (post-P): bal=0 amt=10', - ' dep[escrow]#4 (post-P): bal=0 amt=7', - 'deposit[bobMoneyDst]#5: bal=0 amt=10', - 'deposit[aliceStockDst]#6: bal=0 amt=7', - ' dep[bobMoneyDst]#5 (post-P): bal=0 amt=10', - ' dep[aliceStockDst]#6 (post-P): bal=0 amt=7', - '++ bob.invite doneP', - 'getBalance', - 'getBalance', + '++ alice.invite starting', + 'alice chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', + 'verified chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', '++ bob.tradeWell done', - '++ bobP.tradeWell done:', + '++ bobP.tradeWell done:[[{"label":{"issuer":{},"description":"fudco"},"quantity":7},null],[{"label":{"issuer":{},"description":"clams"},"quantity":10},null]]', '++ DONE', + 'alice money xfer balance {"label":{"issuer":{},"description":"clams"},"quantity":990}', + 'alice money use balance {"label":{"issuer":{},"description":"clams"},"quantity":990}', + 'alice stock xfer balance {"label":{"issuer":{},"description":"fudco"},"quantity":2009}', + 'alice stock use balance {"label":{"issuer":{},"description":"fudco"},"quantity":2009}', + 'bob money xfer balance {"label":{"issuer":{},"description":"clams"},"quantity":1011}', + 'bob money use balance {"label":{"issuer":{},"description":"clams"},"quantity":1011}', + 'bob stock xfer balance {"label":{"issuer":{},"description":"fudco"},"quantity":1996}', + 'bob stock use balance {"label":{"issuer":{},"description":"fudco"},"quantity":1996}', ]); t.end(); }); @@ -198,86 +170,135 @@ test('run contractHost Demo --bob-first without SES', async t => { const dump = await main(false, 'demo/contractHost', ['bob-first']); t.deepEquals(dump.log, [ '=> setup called', - 'makeMint', - 'makeMint', '++ bob.tradeWell starting', - '++ bob.invite start', - '++ bob.invite passed check', - 'makeEmptyPurse(bobMoneyDst)', - 'makeEmptyPurse(bobStockSrc)', - 'makeEmptyPurse(aliceMoneySrc)', - 'makeEmptyPurse(aliceStockDst)', - 'deposit[bobStockSrc]#1: bal=0 amt=7', - 'deposit[aliceMoneySrc]#2: bal=0 amt=10', - ' dep[bobStockSrc]#1 (post-P): bal=0 amt=7', - ' dep[aliceMoneySrc]#2 (post-P): bal=0 amt=10', - '++ bob.invite ackP', - 'makeEmptyPurse(escrow)', - 'makeEmptyPurse(escrow)', - 'deposit[escrow]#3: bal=0 amt=10', - 'deposit[escrow]#4: bal=0 amt=7', - ' dep[escrow]#3 (post-P): bal=0 amt=10', - ' dep[escrow]#4 (post-P): bal=0 amt=7', - 'deposit[bobMoneyDst]#5: bal=0 amt=10', - 'deposit[aliceStockDst]#6: bal=0 amt=7', - ' dep[bobMoneyDst]#5 (post-P): bal=0 amt=10', - ' dep[aliceStockDst]#6 (post-P): bal=0 amt=7', - '++ bob.invite doneP', - 'getBalance', - 'getBalance', + '++ alice.invite starting', + 'alice chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', + 'verified chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', '++ bob.tradeWell done', - '++ bobP.tradeWell done:', + '++ bobP.tradeWell done:[[{"label":{"issuer":{},"description":"fudco"},"quantity":7},null],[{"label":{"issuer":{},"description":"clams"},"quantity":10},null]]', '++ DONE', + 'alice money xfer balance {"label":{"issuer":{},"description":"clams"},"quantity":990}', + 'alice money use balance {"label":{"issuer":{},"description":"clams"},"quantity":990}', + 'alice stock xfer balance {"label":{"issuer":{},"description":"fudco"},"quantity":2009}', + 'alice stock use balance {"label":{"issuer":{},"description":"fudco"},"quantity":2009}', + 'bob money xfer balance {"label":{"issuer":{},"description":"clams"},"quantity":1011}', + 'bob money use balance {"label":{"issuer":{},"description":"clams"},"quantity":1011}', + 'bob stock xfer balance {"label":{"issuer":{},"description":"fudco"},"quantity":1996}', + 'bob stock use balance {"label":{"issuer":{},"description":"fudco"},"quantity":1996}', ]); t.end(); }); -test('run contractHost Demo --bob-first-lies with SES', async t => { - const dump = await main(true, 'demo/contractHost', ['bob-first-lies']); +test('run contractHost Demo --covered-call with SES', async t => { + const dump = await main(true, 'demo/contractHost', ['covered-call']); t.deepEquals(dump.log, [ '=> setup called', - 'makeMint', - 'makeMint', - '++ bob.tradeWell starting', - '++ bob.invite start', - '++ bob.invite passed check', - 'makeEmptyPurse(bobMoneyDst)', - 'makeEmptyPurse(bobStockSrc)', - 'makeEmptyPurse(aliceMoneySrc)', - 'makeEmptyPurse(aliceStockDst)', - 'deposit[bobStockSrc]#1: bal=0 amt=7', - 'deposit[aliceMoneySrc]#2: bal=0 amt=10', - ' dep[bobStockSrc]#1 (post-P): bal=0 amt=7', - ' dep[aliceMoneySrc]#2 (post-P): bal=0 amt=10', - '++ bob.invite ackP', - '++ bob.tradeWell reject', + '++ bob.offerAliceOption starting', + '++ alice.acceptOptionDirectly starting', + 'Pretend singularity never happens', + 'alice chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', + 'verified chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', + '++ bob.offerAliceOption done', + '++ bobP.offerAliceOption done:[[{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},null],[{"label":{"issuer":{},"description":"smackers"},"quantity":10},null]]', '++ DONE', + 'alice money xfer balance {"label":{"issuer":{},"description":"smackers"},"quantity":990}', + 'alice money use balance {"label":{"issuer":{},"description":"smackers"},"quantity":990}', + 'alice stock xfer balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":2009}', + 'alice stock use balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":2009}', + 'bob money xfer balance {"label":{"issuer":{},"description":"smackers"},"quantity":1011}', + 'bob money use balance {"label":{"issuer":{},"description":"smackers"},"quantity":1011}', + 'bob stock xfer balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":1996}', + 'bob stock use balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":1996}', ]); t.end(); }); -test('run contractHost Demo --bob-first-lies without SES', async t => { - const dump = await main(false, 'demo/contractHost', ['bob-first-lies']); +test('run contractHost Demo --covered-call without SES', async t => { + const dump = await main(false, 'demo/contractHost', ['covered-call']); t.deepEquals(dump.log, [ '=> setup called', - 'makeMint', - 'makeMint', - '++ bob.tradeWell starting', - '++ bob.invite start', - '++ bob.invite passed check', - 'makeEmptyPurse(bobMoneyDst)', - 'makeEmptyPurse(bobStockSrc)', - 'makeEmptyPurse(aliceMoneySrc)', - 'makeEmptyPurse(aliceStockDst)', - 'deposit[bobStockSrc]#1: bal=0 amt=7', - 'deposit[aliceMoneySrc]#2: bal=0 amt=10', - ' dep[bobStockSrc]#1 (post-P): bal=0 amt=7', - ' dep[aliceMoneySrc]#2 (post-P): bal=0 amt=10', - '++ bob.invite ackP', - '++ bob.tradeWell reject', + '++ bob.offerAliceOption starting', + '++ alice.acceptOptionDirectly starting', + 'Pretend singularity never happens', + 'alice chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', + 'verified chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', + '++ bob.offerAliceOption done', + '++ bobP.offerAliceOption done:[[{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},null],[{"label":{"issuer":{},"description":"smackers"},"quantity":10},null]]', + '++ DONE', + 'alice money xfer balance {"label":{"issuer":{},"description":"smackers"},"quantity":990}', + 'alice money use balance {"label":{"issuer":{},"description":"smackers"},"quantity":990}', + 'alice stock xfer balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":2009}', + 'alice stock use balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":2009}', + 'bob money xfer balance {"label":{"issuer":{},"description":"smackers"},"quantity":1011}', + 'bob money use balance {"label":{"issuer":{},"description":"smackers"},"quantity":1011}', + 'bob stock xfer balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":1996}', + 'bob stock use balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":1996}', + ]); + t.end(); +}); + +test('run contractHost Demo --covered-call-sale with SES', async t => { + const dump = await main(true, 'demo/contractHost', ['covered-call-sale']); + t.deepEquals(dump.log, [ + '=> setup called', + '++ bob.offerAliceOption starting', + '++ alice.acceptOptionForFred starting', + '++ alice.completeOptionsSale starting', + '++ fred.acceptOptionOffer starting', + 'Pretend singularity never happens', + '++ alice.acceptOptionForFred done', + '++ bob.offerAliceOption done', + '++ bobP.offerAliceOption done:[[[{"label":{"issuer":{},"description":"wonka"},"quantity":7},null],[{"label":{"issuer":{},"description":"fins"},"quantity":55},null]],[{"label":{"issuer":{},"description":"dough"},"quantity":10},null]]', '++ DONE', + 'alice dough xfer balance {"label":{"issuer":{},"description":"dough"},"quantity":1000}', + 'alice dough use balance {"label":{"issuer":{},"description":"dough"},"quantity":1000}', + 'alice stock xfer balance {"label":{"issuer":{},"description":"wonka"},"quantity":2002}', + 'alice stock use balance {"label":{"issuer":{},"description":"wonka"},"quantity":2002}', + 'alice fins xfer balance {"label":{"issuer":{},"description":"fins"},"quantity":3055}', + 'alice fins use balance {"label":{"issuer":{},"description":"fins"},"quantity":3055}', + 'bob dough xfer balance {"label":{"issuer":{},"description":"dough"},"quantity":1011}', + 'bob dough use balance {"label":{"issuer":{},"description":"dough"},"quantity":1011}', + 'bob stock xfer balance {"label":{"issuer":{},"description":"wonka"},"quantity":1996}', + 'bob stock use balance {"label":{"issuer":{},"description":"wonka"},"quantity":1996}', + 'fred dough xfer balance {"label":{"issuer":{},"description":"dough"},"quantity":992}', + 'fred dough use balance {"label":{"issuer":{},"description":"dough"},"quantity":992}', + 'fred stock xfer balance {"label":{"issuer":{},"description":"wonka"},"quantity":2011}', + 'fred stock use balance {"label":{"issuer":{},"description":"wonka"},"quantity":2011}', + 'fred fins xfer balance {"label":{"issuer":{},"description":"fins"},"quantity":2946}', + 'fred fins use balance {"label":{"issuer":{},"description":"fins"},"quantity":2946}', ]); t.end(); }); -*/ +test('run contractHost Demo --covered-call-sale without SES', async t => { + const dump = await main(false, 'demo/contractHost', ['covered-call-sale']); + t.deepEquals(dump.log, [ + '=> setup called', + '++ bob.offerAliceOption starting', + '++ alice.acceptOptionForFred starting', + '++ alice.completeOptionsSale starting', + '++ fred.acceptOptionOffer starting', + 'Pretend singularity never happens', + '++ alice.acceptOptionForFred done', + '++ bob.offerAliceOption done', + '++ bobP.offerAliceOption done:[[[{"label":{"issuer":{},"description":"wonka"},"quantity":7},null],[{"label":{"issuer":{},"description":"fins"},"quantity":55},null]],[{"label":{"issuer":{},"description":"dough"},"quantity":10},null]]', + '++ DONE', + 'alice dough xfer balance {"label":{"issuer":{},"description":"dough"},"quantity":1000}', + 'alice dough use balance {"label":{"issuer":{},"description":"dough"},"quantity":1000}', + 'alice stock xfer balance {"label":{"issuer":{},"description":"wonka"},"quantity":2002}', + 'alice stock use balance {"label":{"issuer":{},"description":"wonka"},"quantity":2002}', + 'alice fins xfer balance {"label":{"issuer":{},"description":"fins"},"quantity":3055}', + 'alice fins use balance {"label":{"issuer":{},"description":"fins"},"quantity":3055}', + 'bob dough xfer balance {"label":{"issuer":{},"description":"dough"},"quantity":1011}', + 'bob dough use balance {"label":{"issuer":{},"description":"dough"},"quantity":1011}', + 'bob stock xfer balance {"label":{"issuer":{},"description":"wonka"},"quantity":1996}', + 'bob stock use balance {"label":{"issuer":{},"description":"wonka"},"quantity":1996}', + 'fred dough xfer balance {"label":{"issuer":{},"description":"dough"},"quantity":992}', + 'fred dough use balance {"label":{"issuer":{},"description":"dough"},"quantity":992}', + 'fred stock xfer balance {"label":{"issuer":{},"description":"wonka"},"quantity":2011}', + 'fred stock use balance {"label":{"issuer":{},"description":"wonka"},"quantity":2011}', + 'fred fins xfer balance {"label":{"issuer":{},"description":"fins"},"quantity":2946}', + 'fred fins use balance {"label":{"issuer":{},"description":"fins"},"quantity":2946}', + ]); + t.end(); +}); From 5401790ff8bb79a0f37acfd74ca052c70c95e4c7 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Tue, 28 May 2019 17:47:06 -0700 Subject: [PATCH 05/18] consolidate with and without ses demo tests --- test/test-demos.js | 196 ++++++--------------------------------------- 1 file changed, 26 insertions(+), 170 deletions(-) diff --git a/test/test-demos.js b/test/test-demos.js index 23fd8d1265a..4f5ffbbf8c6 100644 --- a/test/test-demos.js +++ b/test/test-demos.js @@ -1,18 +1,20 @@ import { test } from 'tape-promise/tape'; import { loadBasedir, buildVatController } from '../src/index'; -async function main(withSES, basedir, argv) { - const config = await loadBasedir(basedir); - const ldSrcPath = require.resolve('../src/devices/loopbox-src'); - config.devices = [['loopbox', ldSrcPath, {}]]; - - const controller = await buildVatController(config, withSES, argv); - await controller.run(); - return controller.dump(); +async function main(withSESflags, basedir, argv) { + for (const withSES of withSESflags) { + const config = await loadBasedir(basedir); + const ldSrcPath = require.resolve('../src/devices/loopbox-src'); + config.devices = [['loopbox', ldSrcPath, {}]]; + + const controller = await buildVatController(config, withSES, argv); + await controller.run(); + return controller.dump(); + } } -test('run encouragementBot Demo with SES', async t => { - const dump = await main(true, 'demo/encouragementBot', []); +test('run encouragementBot Demo', async t => { + const dump = await main([true, false], 'demo/encouragementBot', []); t.deepEquals(dump.log, [ '=> setup called', '=> user.talkToBot is called with encouragementBot', @@ -23,20 +25,8 @@ test('run encouragementBot Demo with SES', async t => { t.end(); }); -test('run encouragementBot Demo without SES', async t => { - const dump = await main(false, 'demo/encouragementBot', []); - t.deepEquals(dump.log, [ - '=> setup called', - '=> user.talkToBot is called with encouragementBot', - '=> encouragementBot.encourageMe got the name: user', - "=> the promise given by the call to user.talkToBot resolved to 'Thanks for the setup. I sure hope I get some encouragement...'", - '=> user receives the encouragement: user, you are awesome, keep it up!', - ]); - t.end(); -}); - -test('run encouragementBotComms Demo with SES', async t => { - const dump = await main(true, 'demo/encouragementBotComms', []); +test('run encouragementBotComms Demo', async t => { + const dump = await main([true, false], 'demo/encouragementBotComms', []); t.deepEquals(dump.log, [ '=> setup called', 'addEgress called with sender user, index 0, valslot [object Object]', @@ -49,38 +39,8 @@ test('run encouragementBotComms Demo with SES', async t => { t.end(); }); -test('run encouragementBotComms Demo without SES', async t => { - const dump = await main(false, 'demo/encouragementBotComms'); - t.deepEquals(dump.log, [ - '=> setup called', - 'addEgress called with sender user, index 0, valslot [object Object]', - 'addIngress called with machineName bot, index 0', - '=> user.talkToBot is called with bot', - "=> the promise given by the call to user.talkToBot resolved to 'Thanks for the setup. I sure hope I get some encouragement...'", - '=> encouragementBot.encourageMe got the name: user', - '=> user receives the encouragement: user, you are awesome, keep it up!', - ]); - t.end(); -}); - -test('run contractHost Demo --mint with SES', async t => { - const dump = await main(true, 'demo/contractHost', ['mint']); - t.deepEquals(dump.log, [ - '=> setup called', - 'starting mintTestAssay', - 'starting mintTestNumber', - 'alice xfer balance {"label":{"issuer":{},"description":"quatloos"},"quantity":950}', - 'alice use balance {"label":{"issuer":{},"description":"quatloos"},"quantity":1000}', - 'payment xfer balance {"label":{"issuer":{},"description":"quatloos"},"quantity":50}', - 'alice xfer balance {"label":{"issuer":{},"description":"bucks"},"quantity":950}', - 'alice use balance {"label":{"issuer":{},"description":"bucks"},"quantity":1000}', - 'payment xfer balance {"label":{"issuer":{},"description":"bucks"},"quantity":50}', - ]); - t.end(); -}); - -test('run contractHost Demo --mint without SES', async t => { - const dump = await main(false, 'demo/contractHost', ['mint']); +test('run contractHost Demo --mint', async t => { + const dump = await main([true, false], 'demo/contractHost', ['mint']); t.deepEquals(dump.log, [ '=> setup called', 'starting mintTestAssay', @@ -95,8 +55,8 @@ test('run contractHost Demo --mint without SES', async t => { t.end(); }); -test('run contractHost Demo --trivial with SES', async t => { - const dump = await main(true, 'demo/contractHost', ['trivial']); +test('run contractHost Demo --trivial', async t => { + const dump = await main([true, false], 'demo/contractHost', ['trivial']); t.deepEquals(dump.log, [ '=> setup called', 'starting trivialContractTest', @@ -108,21 +68,8 @@ test('run contractHost Demo --trivial with SES', async t => { t.end(); }); -test('run contractHost Demo --trivial without SES', async t => { - const dump = await main(false, 'demo/contractHost', ['trivial']); - t.deepEquals(dump.log, [ - '=> setup called', - 'starting trivialContractTest', - 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"function trivCo...foo\', 8);\\n }","terms":"foo terms","seatDesc":"foo"}},"quantity":1}}', - '++ eightP resolved to8(should be 8)', - '++ DONE', - 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":null}', - ]); - t.end(); -}); - -test('run contractHost Demo --alice-first with SES', async t => { - const dump = await main(true, 'demo/contractHost', ['alice-first']); +test('run contractHost Demo --alice-first', async t => { + const dump = await main([true, false], 'demo/contractHost', ['alice-first']); t.deepEquals(dump.log, [ '=> setup called', '++ alice.payBobWell starting', @@ -132,19 +79,8 @@ test('run contractHost Demo --alice-first with SES', async t => { t.end(); }); -test('run contractHost Demo --alice-first without SES', async t => { - const dump = await main(false, 'demo/contractHost', ['alice-first']); - t.deepEquals(dump.log, [ - '=> setup called', - '++ alice.payBobWell starting', - '++ ifItFitsP done:If it fits, ware it.', - '++ DONE', - ]); - t.end(); -}); - -test('run contractHost Demo --bob-first with SES', async t => { - const dump = await main(true, 'demo/contractHost', ['bob-first']); +test('run contractHost Demo --bob-first', async t => { + const dump = await main([true, false], 'demo/contractHost', ['bob-first']); t.deepEquals(dump.log, [ '=> setup called', '++ bob.tradeWell starting', @@ -166,31 +102,8 @@ test('run contractHost Demo --bob-first with SES', async t => { t.end(); }); -test('run contractHost Demo --bob-first without SES', async t => { - const dump = await main(false, 'demo/contractHost', ['bob-first']); - t.deepEquals(dump.log, [ - '=> setup called', - '++ bob.tradeWell starting', - '++ alice.invite starting', - 'alice chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', - 'verified chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', - '++ bob.tradeWell done', - '++ bobP.tradeWell done:[[{"label":{"issuer":{},"description":"fudco"},"quantity":7},null],[{"label":{"issuer":{},"description":"clams"},"quantity":10},null]]', - '++ DONE', - 'alice money xfer balance {"label":{"issuer":{},"description":"clams"},"quantity":990}', - 'alice money use balance {"label":{"issuer":{},"description":"clams"},"quantity":990}', - 'alice stock xfer balance {"label":{"issuer":{},"description":"fudco"},"quantity":2009}', - 'alice stock use balance {"label":{"issuer":{},"description":"fudco"},"quantity":2009}', - 'bob money xfer balance {"label":{"issuer":{},"description":"clams"},"quantity":1011}', - 'bob money use balance {"label":{"issuer":{},"description":"clams"},"quantity":1011}', - 'bob stock xfer balance {"label":{"issuer":{},"description":"fudco"},"quantity":1996}', - 'bob stock use balance {"label":{"issuer":{},"description":"fudco"},"quantity":1996}', - ]); - t.end(); -}); - -test('run contractHost Demo --covered-call with SES', async t => { - const dump = await main(true, 'demo/contractHost', ['covered-call']); +test('run contractHost Demo --covered-call', async t => { + const dump = await main([true, false], 'demo/contractHost', ['covered-call']); t.deepEquals(dump.log, [ '=> setup called', '++ bob.offerAliceOption starting', @@ -213,65 +126,8 @@ test('run contractHost Demo --covered-call with SES', async t => { t.end(); }); -test('run contractHost Demo --covered-call without SES', async t => { - const dump = await main(false, 'demo/contractHost', ['covered-call']); - t.deepEquals(dump.log, [ - '=> setup called', - '++ bob.offerAliceOption starting', - '++ alice.acceptOptionDirectly starting', - 'Pretend singularity never happens', - 'alice chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', - 'verified chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', - '++ bob.offerAliceOption done', - '++ bobP.offerAliceOption done:[[{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},null],[{"label":{"issuer":{},"description":"smackers"},"quantity":10},null]]', - '++ DONE', - 'alice money xfer balance {"label":{"issuer":{},"description":"smackers"},"quantity":990}', - 'alice money use balance {"label":{"issuer":{},"description":"smackers"},"quantity":990}', - 'alice stock xfer balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":2009}', - 'alice stock use balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":2009}', - 'bob money xfer balance {"label":{"issuer":{},"description":"smackers"},"quantity":1011}', - 'bob money use balance {"label":{"issuer":{},"description":"smackers"},"quantity":1011}', - 'bob stock xfer balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":1996}', - 'bob stock use balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":1996}', - ]); - t.end(); -}); - -test('run contractHost Demo --covered-call-sale with SES', async t => { - const dump = await main(true, 'demo/contractHost', ['covered-call-sale']); - t.deepEquals(dump.log, [ - '=> setup called', - '++ bob.offerAliceOption starting', - '++ alice.acceptOptionForFred starting', - '++ alice.completeOptionsSale starting', - '++ fred.acceptOptionOffer starting', - 'Pretend singularity never happens', - '++ alice.acceptOptionForFred done', - '++ bob.offerAliceOption done', - '++ bobP.offerAliceOption done:[[[{"label":{"issuer":{},"description":"wonka"},"quantity":7},null],[{"label":{"issuer":{},"description":"fins"},"quantity":55},null]],[{"label":{"issuer":{},"description":"dough"},"quantity":10},null]]', - '++ DONE', - 'alice dough xfer balance {"label":{"issuer":{},"description":"dough"},"quantity":1000}', - 'alice dough use balance {"label":{"issuer":{},"description":"dough"},"quantity":1000}', - 'alice stock xfer balance {"label":{"issuer":{},"description":"wonka"},"quantity":2002}', - 'alice stock use balance {"label":{"issuer":{},"description":"wonka"},"quantity":2002}', - 'alice fins xfer balance {"label":{"issuer":{},"description":"fins"},"quantity":3055}', - 'alice fins use balance {"label":{"issuer":{},"description":"fins"},"quantity":3055}', - 'bob dough xfer balance {"label":{"issuer":{},"description":"dough"},"quantity":1011}', - 'bob dough use balance {"label":{"issuer":{},"description":"dough"},"quantity":1011}', - 'bob stock xfer balance {"label":{"issuer":{},"description":"wonka"},"quantity":1996}', - 'bob stock use balance {"label":{"issuer":{},"description":"wonka"},"quantity":1996}', - 'fred dough xfer balance {"label":{"issuer":{},"description":"dough"},"quantity":992}', - 'fred dough use balance {"label":{"issuer":{},"description":"dough"},"quantity":992}', - 'fred stock xfer balance {"label":{"issuer":{},"description":"wonka"},"quantity":2011}', - 'fred stock use balance {"label":{"issuer":{},"description":"wonka"},"quantity":2011}', - 'fred fins xfer balance {"label":{"issuer":{},"description":"fins"},"quantity":2946}', - 'fred fins use balance {"label":{"issuer":{},"description":"fins"},"quantity":2946}', - ]); - t.end(); -}); - -test('run contractHost Demo --covered-call-sale without SES', async t => { - const dump = await main(false, 'demo/contractHost', ['covered-call-sale']); +test('run contractHost Demo --covered-call-sale', async t => { + const dump = await main([true, false], 'demo/contractHost', ['covered-call-sale']); t.deepEquals(dump.log, [ '=> setup called', '++ bob.offerAliceOption starting', From 7ddfb03a47347d439e8741dea942128d0070889b Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Tue, 28 May 2019 23:23:35 -0700 Subject: [PATCH 06/18] consolidate ses and non-ses tests correctly --- test/test-demos.js | 368 ++++++++++++++++++++++++++------------------- 1 file changed, 215 insertions(+), 153 deletions(-) diff --git a/test/test-demos.js b/test/test-demos.js index 4f5ffbbf8c6..8b43127820a 100644 --- a/test/test-demos.js +++ b/test/test-demos.js @@ -1,160 +1,222 @@ import { test } from 'tape-promise/tape'; import { loadBasedir, buildVatController } from '../src/index'; -async function main(withSESflags, basedir, argv) { - for (const withSES of withSESflags) { - const config = await loadBasedir(basedir); - const ldSrcPath = require.resolve('../src/devices/loopbox-src'); - config.devices = [['loopbox', ldSrcPath, {}]]; - - const controller = await buildVatController(config, withSES, argv); - await controller.run(); - return controller.dump(); - } +async function main(withSES, basedir, argv) { + const config = await loadBasedir(basedir); + const ldSrcPath = require.resolve('../src/devices/loopbox-src'); + config.devices = [['loopbox', ldSrcPath, {}]]; + + const controller = await buildVatController(config, withSES, argv); + await controller.run(); + return controller.dump(); } -test('run encouragementBot Demo', async t => { - const dump = await main([true, false], 'demo/encouragementBot', []); - t.deepEquals(dump.log, [ - '=> setup called', - '=> user.talkToBot is called with encouragementBot', - '=> encouragementBot.encourageMe got the name: user', - "=> the promise given by the call to user.talkToBot resolved to 'Thanks for the setup. I sure hope I get some encouragement...'", - '=> user receives the encouragement: user, you are awesome, keep it up!', - ]); - t.end(); -}); - -test('run encouragementBotComms Demo', async t => { - const dump = await main([true, false], 'demo/encouragementBotComms', []); - t.deepEquals(dump.log, [ - '=> setup called', - 'addEgress called with sender user, index 0, valslot [object Object]', - 'addIngress called with machineName bot, index 0', - '=> user.talkToBot is called with bot', - "=> the promise given by the call to user.talkToBot resolved to 'Thanks for the setup. I sure hope I get some encouragement...'", - '=> encouragementBot.encourageMe got the name: user', - '=> user receives the encouragement: user, you are awesome, keep it up!', - ]); - t.end(); -}); - -test('run contractHost Demo --mint', async t => { - const dump = await main([true, false], 'demo/contractHost', ['mint']); - t.deepEquals(dump.log, [ - '=> setup called', - 'starting mintTestAssay', - 'starting mintTestNumber', - 'alice xfer balance {"label":{"issuer":{},"description":"quatloos"},"quantity":950}', - 'alice use balance {"label":{"issuer":{},"description":"quatloos"},"quantity":1000}', - 'payment xfer balance {"label":{"issuer":{},"description":"quatloos"},"quantity":50}', - 'alice xfer balance {"label":{"issuer":{},"description":"bucks"},"quantity":950}', - 'alice use balance {"label":{"issuer":{},"description":"bucks"},"quantity":1000}', - 'payment xfer balance {"label":{"issuer":{},"description":"bucks"},"quantity":50}', - ]); - t.end(); -}); - -test('run contractHost Demo --trivial', async t => { - const dump = await main([true, false], 'demo/contractHost', ['trivial']); - t.deepEquals(dump.log, [ - '=> setup called', - 'starting trivialContractTest', - 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"function trivCo...foo\', 8);\\n }","terms":"foo terms","seatDesc":"foo"}},"quantity":1}}', - '++ eightP resolved to8(should be 8)', - '++ DONE', - 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":null}', - ]); - t.end(); -}); - -test('run contractHost Demo --alice-first', async t => { - const dump = await main([true, false], 'demo/contractHost', ['alice-first']); - t.deepEquals(dump.log, [ - '=> setup called', - '++ alice.payBobWell starting', - '++ ifItFitsP done:If it fits, ware it.', - '++ DONE', - ]); - t.end(); -}); - -test('run contractHost Demo --bob-first', async t => { - const dump = await main([true, false], 'demo/contractHost', ['bob-first']); - t.deepEquals(dump.log, [ - '=> setup called', - '++ bob.tradeWell starting', - '++ alice.invite starting', - 'alice chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', - 'verified chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', - '++ bob.tradeWell done', - '++ bobP.tradeWell done:[[{"label":{"issuer":{},"description":"fudco"},"quantity":7},null],[{"label":{"issuer":{},"description":"clams"},"quantity":10},null]]', - '++ DONE', - 'alice money xfer balance {"label":{"issuer":{},"description":"clams"},"quantity":990}', - 'alice money use balance {"label":{"issuer":{},"description":"clams"},"quantity":990}', - 'alice stock xfer balance {"label":{"issuer":{},"description":"fudco"},"quantity":2009}', - 'alice stock use balance {"label":{"issuer":{},"description":"fudco"},"quantity":2009}', - 'bob money xfer balance {"label":{"issuer":{},"description":"clams"},"quantity":1011}', - 'bob money use balance {"label":{"issuer":{},"description":"clams"},"quantity":1011}', - 'bob stock xfer balance {"label":{"issuer":{},"description":"fudco"},"quantity":1996}', - 'bob stock use balance {"label":{"issuer":{},"description":"fudco"},"quantity":1996}', - ]); - t.end(); -}); - -test('run contractHost Demo --covered-call', async t => { - const dump = await main([true, false], 'demo/contractHost', ['covered-call']); - t.deepEquals(dump.log, [ - '=> setup called', - '++ bob.offerAliceOption starting', - '++ alice.acceptOptionDirectly starting', - 'Pretend singularity never happens', - 'alice chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', - 'verified chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', - '++ bob.offerAliceOption done', - '++ bobP.offerAliceOption done:[[{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},null],[{"label":{"issuer":{},"description":"smackers"},"quantity":10},null]]', - '++ DONE', - 'alice money xfer balance {"label":{"issuer":{},"description":"smackers"},"quantity":990}', - 'alice money use balance {"label":{"issuer":{},"description":"smackers"},"quantity":990}', - 'alice stock xfer balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":2009}', - 'alice stock use balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":2009}', - 'bob money xfer balance {"label":{"issuer":{},"description":"smackers"},"quantity":1011}', - 'bob money use balance {"label":{"issuer":{},"description":"smackers"},"quantity":1011}', - 'bob stock xfer balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":1996}', - 'bob stock use balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":1996}', - ]); - t.end(); -}); - -test('run contractHost Demo --covered-call-sale', async t => { - const dump = await main([true, false], 'demo/contractHost', ['covered-call-sale']); - t.deepEquals(dump.log, [ - '=> setup called', - '++ bob.offerAliceOption starting', - '++ alice.acceptOptionForFred starting', - '++ alice.completeOptionsSale starting', - '++ fred.acceptOptionOffer starting', - 'Pretend singularity never happens', - '++ alice.acceptOptionForFred done', - '++ bob.offerAliceOption done', - '++ bobP.offerAliceOption done:[[[{"label":{"issuer":{},"description":"wonka"},"quantity":7},null],[{"label":{"issuer":{},"description":"fins"},"quantity":55},null]],[{"label":{"issuer":{},"description":"dough"},"quantity":10},null]]', - '++ DONE', - 'alice dough xfer balance {"label":{"issuer":{},"description":"dough"},"quantity":1000}', - 'alice dough use balance {"label":{"issuer":{},"description":"dough"},"quantity":1000}', - 'alice stock xfer balance {"label":{"issuer":{},"description":"wonka"},"quantity":2002}', - 'alice stock use balance {"label":{"issuer":{},"description":"wonka"},"quantity":2002}', - 'alice fins xfer balance {"label":{"issuer":{},"description":"fins"},"quantity":3055}', - 'alice fins use balance {"label":{"issuer":{},"description":"fins"},"quantity":3055}', - 'bob dough xfer balance {"label":{"issuer":{},"description":"dough"},"quantity":1011}', - 'bob dough use balance {"label":{"issuer":{},"description":"dough"},"quantity":1011}', - 'bob stock xfer balance {"label":{"issuer":{},"description":"wonka"},"quantity":1996}', - 'bob stock use balance {"label":{"issuer":{},"description":"wonka"},"quantity":1996}', - 'fred dough xfer balance {"label":{"issuer":{},"description":"dough"},"quantity":992}', - 'fred dough use balance {"label":{"issuer":{},"description":"dough"},"quantity":992}', - 'fred stock xfer balance {"label":{"issuer":{},"description":"wonka"},"quantity":2011}', - 'fred stock use balance {"label":{"issuer":{},"description":"wonka"},"quantity":2011}', - 'fred fins xfer balance {"label":{"issuer":{},"description":"fins"},"quantity":2946}', - 'fred fins use balance {"label":{"issuer":{},"description":"fins"},"quantity":2946}', - ]); +const encouragementBotGolden = [ + '=> setup called', + '=> user.talkToBot is called with encouragementBot', + '=> encouragementBot.encourageMe got the name: user', + "=> the promise given by the call to user.talkToBot resolved to 'Thanks for the setup. I sure hope I get some encouragement...'", + '=> user receives the encouragement: user, you are awesome, keep it up!', +]; + +test('run encouragementBot Demo with SES', async t => { + const dump = await main(true, 'demo/encouragementBot', []); + t.deepEquals(dump.log, encouragementBotGolden); + t.end(); +}); + +test('run encouragementBot Demo without SES', async t => { + const dump = await main(false, 'demo/encouragementBot', []); + t.deepEquals(dump.log, encouragementBotGolden); + t.end(); +}); + +const encouragementBotCommsGolden = [ + '=> setup called', + 'addEgress called with sender user, index 0, valslot [object Object]', + 'addIngress called with machineName bot, index 0', + '=> user.talkToBot is called with bot', + "=> the promise given by the call to user.talkToBot resolved to 'Thanks for the setup. I sure hope I get some encouragement...'", + '=> encouragementBot.encourageMe got the name: user', + '=> user receives the encouragement: user, you are awesome, keep it up!', +]; + +test('run encouragementBotComms Demo with SES', async t => { + const dump = await main(true, 'demo/encouragementBotComms', []); + t.deepEquals(dump.log, encouragementBotCommsGolden); + t.end(); +}); + +test('run encouragementBotComms Demo without SES', async t => { + const dump = await main(false, 'demo/encouragementBotComms'); + t.deepEquals(dump.log, encouragementBotCommsGolden); + t.end(); +}); + +const contractMintGolden = [ + '=> setup called', + 'starting mintTestAssay', + 'starting mintTestNumber', + 'alice xfer balance {"label":{"issuer":{},"description":"quatloos"},"quantity":950}', + 'alice use balance {"label":{"issuer":{},"description":"quatloos"},"quantity":1000}', + 'payment xfer balance {"label":{"issuer":{},"description":"quatloos"},"quantity":50}', + 'alice xfer balance {"label":{"issuer":{},"description":"bucks"},"quantity":950}', + 'alice use balance {"label":{"issuer":{},"description":"bucks"},"quantity":1000}', + 'payment xfer balance {"label":{"issuer":{},"description":"bucks"},"quantity":50}', +]; + +test('run contractHost Demo --mint with SES', async t => { + const dump = await main(true, 'demo/contractHost', ['mint']); + t.deepEquals(dump.log, contractMintGolden); + t.end(); +}); + +test('run contractHost Demo --mint without SES', async t => { + const dump = await main(false, 'demo/contractHost', ['mint']); + t.deepEquals(dump.log, contractMintGolden); + t.end(); +}); + +const contractTrivialGolden = [ + '=> setup called', + 'starting trivialContractTest', + 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"function trivCo...foo\', 8);\\n }","terms":"foo terms","seatDesc":"foo"}},"quantity":1}}', + '++ eightP resolved to8(should be 8)', + '++ DONE', + 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":null}', +]; + +test('run contractHost Demo --trivial with SES', async t => { + const dump = await main(true, 'demo/contractHost', ['trivial']); + t.deepEquals(dump.log, contractTrivialGolden); + t.end(); +}); + +test('run contractHost Demo --trivial without SES', async t => { + const dump = await main(false, 'demo/contractHost', ['trivial']); + t.deepEquals(dump.log, contractTrivialGolden); + t.end(); +}); + +const contractAliceFirstGolden = [ + '=> setup called', + '++ alice.payBobWell starting', + '++ ifItFitsP done:If it fits, ware it.', + '++ DONE', +]; + +test('run contractHost Demo --alice-first with SES', async t => { + const dump = await main(true, 'demo/contractHost', ['alice-first']); + t.deepEquals(dump.log, contractAliceFirstGolden); + t.end(); +}); + +test('run contractHost Demo --alice-first without SES', async t => { + const dump = await main(false, 'demo/contractHost', ['alice-first']); + t.deepEquals(dump.log, contractAliceFirstGolden); + t.end(); +}); + +const contractBobFirstGolden = [ + '=> setup called', + '++ bob.tradeWell starting', + '++ alice.invite starting', + 'alice chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', + 'verified chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', + '++ bob.tradeWell done', + '++ bobP.tradeWell done:[[{"label":{"issuer":{},"description":"fudco"},"quantity":7},null],[{"label":{"issuer":{},"description":"clams"},"quantity":10},null]]', + '++ DONE', + 'alice money xfer balance {"label":{"issuer":{},"description":"clams"},"quantity":990}', + 'alice money use balance {"label":{"issuer":{},"description":"clams"},"quantity":990}', + 'alice stock xfer balance {"label":{"issuer":{},"description":"fudco"},"quantity":2009}', + 'alice stock use balance {"label":{"issuer":{},"description":"fudco"},"quantity":2009}', + 'bob money xfer balance {"label":{"issuer":{},"description":"clams"},"quantity":1011}', + 'bob money use balance {"label":{"issuer":{},"description":"clams"},"quantity":1011}', + 'bob stock xfer balance {"label":{"issuer":{},"description":"fudco"},"quantity":1996}', + 'bob stock use balance {"label":{"issuer":{},"description":"fudco"},"quantity":1996}', +]; + +test('run contractHost Demo --bob-first with SES', async t => { + const dump = await main(true, 'demo/contractHost', ['bob-first']); + t.deepEquals(dump.log, contractBobFirstGolden); + t.end(); +}); + +test('run contractHost Demo --bob-first without SES', async t => { + const dump = await main(false, 'demo/contractHost', ['bob-first']); + t.deepEquals(dump.log, contractBobFirstGolden); + t.end(); +}); + +const contractCoveredCallGolden = [ + '=> setup called', + '++ bob.offerAliceOption starting', + '++ alice.acceptOptionDirectly starting', + 'Pretend singularity never happens', + 'alice chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', + 'verified chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', + '++ bob.offerAliceOption done', + '++ bobP.offerAliceOption done:[[{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},null],[{"label":{"issuer":{},"description":"smackers"},"quantity":10},null]]', + '++ DONE', + 'alice money xfer balance {"label":{"issuer":{},"description":"smackers"},"quantity":990}', + 'alice money use balance {"label":{"issuer":{},"description":"smackers"},"quantity":990}', + 'alice stock xfer balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":2009}', + 'alice stock use balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":2009}', + 'bob money xfer balance {"label":{"issuer":{},"description":"smackers"},"quantity":1011}', + 'bob money use balance {"label":{"issuer":{},"description":"smackers"},"quantity":1011}', + 'bob stock xfer balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":1996}', + 'bob stock use balance {"label":{"issuer":{},"description":"yoyodyne"},"quantity":1996}', +]; + +test('run contractHost Demo --covered-call with SES', async t => { + const dump = await main(true, 'demo/contractHost', ['covered-call']); + t.deepEquals(dump.log, contractCoveredCallGolden); + t.end(); +}); + +test('run contractHost Demo --covered-call without SES', async t => { + const dump = await main(false, 'demo/contractHost', ['covered-call']); + t.deepEquals(dump.log, contractCoveredCallGolden); + t.end(); +}); + +const contractCoveredCallSaleGolden = [ + '=> setup called', + '++ bob.offerAliceOption starting', + '++ alice.acceptOptionForFred starting', + '++ alice.completeOptionsSale starting', + '++ fred.acceptOptionOffer starting', + 'Pretend singularity never happens', + '++ alice.acceptOptionForFred done', + '++ bob.offerAliceOption done', + '++ bobP.offerAliceOption done:[[[{"label":{"issuer":{},"description":"wonka"},"quantity":7},null],[{"label":{"issuer":{},"description":"fins"},"quantity":55},null]],[{"label":{"issuer":{},"description":"dough"},"quantity":10},null]]', + '++ DONE', + 'alice dough xfer balance {"label":{"issuer":{},"description":"dough"},"quantity":1000}', + 'alice dough use balance {"label":{"issuer":{},"description":"dough"},"quantity":1000}', + 'alice stock xfer balance {"label":{"issuer":{},"description":"wonka"},"quantity":2002}', + 'alice stock use balance {"label":{"issuer":{},"description":"wonka"},"quantity":2002}', + 'alice fins xfer balance {"label":{"issuer":{},"description":"fins"},"quantity":3055}', + 'alice fins use balance {"label":{"issuer":{},"description":"fins"},"quantity":3055}', + 'bob dough xfer balance {"label":{"issuer":{},"description":"dough"},"quantity":1011}', + 'bob dough use balance {"label":{"issuer":{},"description":"dough"},"quantity":1011}', + 'bob stock xfer balance {"label":{"issuer":{},"description":"wonka"},"quantity":1996}', + 'bob stock use balance {"label":{"issuer":{},"description":"wonka"},"quantity":1996}', + 'fred dough xfer balance {"label":{"issuer":{},"description":"dough"},"quantity":992}', + 'fred dough use balance {"label":{"issuer":{},"description":"dough"},"quantity":992}', + 'fred stock xfer balance {"label":{"issuer":{},"description":"wonka"},"quantity":2011}', + 'fred stock use balance {"label":{"issuer":{},"description":"wonka"},"quantity":2011}', + 'fred fins xfer balance {"label":{"issuer":{},"description":"fins"},"quantity":2946}', + 'fred fins use balance {"label":{"issuer":{},"description":"fins"},"quantity":2946}', +]; + +test('run contractHost Demo --covered-call-sale with SES', async t => { + const dump = await main(true, 'demo/contractHost', ['covered-call-sale']); + t.deepEquals(dump.log, contractCoveredCallSaleGolden); + t.end(); +}); + +test('run contractHost Demo --covered-call-sale without SES', async t => { + const dump = await main(false, 'demo/contractHost', ['covered-call-sale']); + t.deepEquals(dump.log, contractCoveredCallSaleGolden); t.end(); }); From 53a3c01f7d3510989c0029ab0bbea9269260e10c Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Tue, 28 May 2019 23:36:58 -0700 Subject: [PATCH 07/18] missing log data for testing --- demo/contractHost/chit.js | 4 ++-- demo/contractHost/vat-alice.js | 2 +- demo/contractHost/vat-bob.js | 2 +- demo/contractHost/vat-fred.js | 2 +- test/test-demos.js | 8 ++++++++ 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/demo/contractHost/chit.js b/demo/contractHost/chit.js index ed77888e8d3..559bc8887b3 100644 --- a/demo/contractHost/chit.js +++ b/demo/contractHost/chit.js @@ -154,7 +154,7 @@ function exchangeChitAmount( } harden(exchangeChitAmount); -function makeCollect(E) { +function makeCollect(E, log) { function collect(seatP, winPurseP, refundPurseP, name = 'collecting') { const results = harden([ E(seatP) @@ -172,7 +172,7 @@ function makeCollect(E) { ]); const doneP = allSettled(results); E.resolve(doneP).then(([wins, refs]) => { - console.log(`${name} wins: `, wins, `refs: `, refs); + log(`${name} wins: `, wins, ` refs: `, refs); }); // Use Promise.all here rather than allSettled in order to // propagate rejection. diff --git a/demo/contractHost/vat-alice.js b/demo/contractHost/vat-alice.js index 98e63b05921..b1fdd5b4695 100644 --- a/demo/contractHost/vat-alice.js +++ b/demo/contractHost/vat-alice.js @@ -9,7 +9,7 @@ import { coveredCallSrc } from './coveredCall'; import { exchangeChitAmount, makeCollect } from './chit'; function makeAlice(E, host, log) { - const collect = makeCollect(E); + const collect = makeCollect(E, log); function showPaymentBalance(name, paymentP) { E(paymentP) diff --git a/demo/contractHost/vat-bob.js b/demo/contractHost/vat-bob.js index 136a83da3ea..7d53f6e1898 100644 --- a/demo/contractHost/vat-bob.js +++ b/demo/contractHost/vat-bob.js @@ -9,7 +9,7 @@ import { coveredCallSrc } from './coveredCall'; import { makeCollect } from './chit'; function makeBob(E, host, log) { - const collect = makeCollect(E); + const collect = makeCollect(E, log); let initialized = false; let timerP; diff --git a/demo/contractHost/vat-fred.js b/demo/contractHost/vat-fred.js index d2454484399..0f095602ed1 100644 --- a/demo/contractHost/vat-fred.js +++ b/demo/contractHost/vat-fred.js @@ -10,7 +10,7 @@ import { coveredCallSrc } from './coveredCall'; import { exchangeChitAmount, makeCollect } from './chit'; function makeFred(E, host, log) { - const collect = makeCollect(E); + const collect = makeCollect(E, log); let initialized = false; let timerP; diff --git a/test/test-demos.js b/test/test-demos.js index 8b43127820a..f9dc150753d 100644 --- a/test/test-demos.js +++ b/test/test-demos.js @@ -123,6 +123,8 @@ const contractBobFirstGolden = [ '++ alice.invite starting', 'alice chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', 'verified chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', + 'bob escrow wins: {"label":{"issuer":{},"description":"clams"},"quantity":10} refs: null', + 'alice escrow wins: {"label":{"issuer":{},"description":"fudco"},"quantity":7} refs: null', '++ bob.tradeWell done', '++ bobP.tradeWell done:[[{"label":{"issuer":{},"description":"fudco"},"quantity":7},null],[{"label":{"issuer":{},"description":"clams"},"quantity":10},null]]', '++ DONE', @@ -155,6 +157,8 @@ const contractCoveredCallGolden = [ 'Pretend singularity never happens', 'alice chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', 'verified chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', + 'alice option wins: {"label":{"issuer":{},"description":"yoyodyne"},"quantity":7} refs: null', + 'bob option wins: {"label":{"issuer":{},"description":"smackers"},"quantity":10} refs: null', '++ bob.offerAliceOption done', '++ bobP.offerAliceOption done:[[{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},null],[{"label":{"issuer":{},"description":"smackers"},"quantity":10},null]]', '++ DONE', @@ -187,6 +191,10 @@ const contractCoveredCallSaleGolden = [ '++ alice.completeOptionsSale starting', '++ fred.acceptOptionOffer starting', 'Pretend singularity never happens', + 'alice options sale wins: {"label":{"issuer":{},"description":"fins"},"quantity":55} refs: null', + 'fred buys escrowed option wins: {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"dough"},"quantity":10},{"label":{"issuer":{},"description":"wonka"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"dough"},"quantity":10},{"label":{"issuer":{},"description":"wonka"},"quantity":7}]}},"quantity":1}} refs: null', + 'fred exercises option, buying stock wins: {"label":{"issuer":{},"description":"wonka"},"quantity":7} refs: null', + 'bob option wins: {"label":{"issuer":{},"description":"dough"},"quantity":10} refs: null', '++ alice.acceptOptionForFred done', '++ bob.offerAliceOption done', '++ bobP.offerAliceOption done:[[[{"label":{"issuer":{},"description":"wonka"},"quantity":7},null],[{"label":{"issuer":{},"description":"fins"},"quantity":55},null]],[{"label":{"issuer":{},"description":"dough"},"quantity":10},null]]', From 24c3e42e6f65dae4224d7873369c19f75885a1dd Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Wed, 29 May 2019 10:12:36 -0700 Subject: [PATCH 08/18] chit renamed to invite --- demo/contractHost/bootstrap.js | 12 +-- .../contractHost/{chit.js => contractHost.js} | 50 ++++++----- demo/contractHost/coveredCall.js | 14 +-- demo/contractHost/escrow.js | 6 +- demo/contractHost/vat-alice.js | 86 +++++++++---------- demo/contractHost/vat-bob.js | 31 +++---- demo/contractHost/vat-fred.js | 40 ++++----- demo/contractHost/vat-host.js | 2 +- test/test-demos.js | 10 +-- 9 files changed, 122 insertions(+), 129 deletions(-) rename demo/contractHost/{chit.js => contractHost.js} (80%) diff --git a/demo/contractHost/bootstrap.js b/demo/contractHost/bootstrap.js index dd4b33b2b5e..61f95955401 100644 --- a/demo/contractHost/bootstrap.js +++ b/demo/contractHost/bootstrap.js @@ -76,19 +76,19 @@ function build(E, log) { function trivialContractTest(host) { log('starting trivialContractTest'); - function trivContract(terms, chitMaker) { - return chitMaker.make('foo', 8); + function trivContract(terms, inviteMaker) { + return inviteMaker.make('foo', 8); } const contractSrc = `${trivContract}`; - const fooChitP = E(host).start(contractSrc, 'foo terms'); + const fooInviteP = E(host).start(contractSrc, 'foo terms'); - showPaymentBalance('foo', fooChitP); + showPaymentBalance('foo', fooInviteP); - const eightP = E(host).redeem(fooChitP); + const eightP = E(host).redeem(fooInviteP); eightP.then(res => { - showPaymentBalance('foo', fooChitP); + showPaymentBalance('foo', fooInviteP); log('++ eightP resolved to', res, '(should be 8)'); if (res !== 8) { throw new Error(`eightP resolved to ${res}, not 8`); diff --git a/demo/contractHost/chit.js b/demo/contractHost/contractHost.js similarity index 80% rename from demo/contractHost/chit.js rename to demo/contractHost/contractHost.js index 559bc8887b3..c3248903d46 100644 --- a/demo/contractHost/chit.js +++ b/demo/contractHost/contractHost.js @@ -1,7 +1,5 @@ // Copyright (C) 2019 Agoric, under Apache License 2.0 -// Chit === Contract Host Issuer Token - import Nat from '@agoric/nat'; import harden from '@agoric/harden'; import evaluate from '@agoric/evaluate'; @@ -21,16 +19,16 @@ function makeContractHost(E) { const metaIssuer = controller.getMetaIssuer(); const metaAssay = metaIssuer.getAssay(); - function redeem(allegedChitPayment) { - const allegedMetaAmount = allegedChitPayment.getXferBalance(); + function redeem(allegedInvitePayment) { + const allegedMetaAmount = allegedInvitePayment.getXferBalance(); const metaAmount = metaAssay.vouch(allegedMetaAmount); insist(!metaAssay.isEmpty(metaAmount))`\ -No chits left`; +No invites left`; const baseAmount = metaAssay.quantity(metaAmount); const seatIdentity = baseAmount.label.identity; insist(seats.has(seatIdentity))`\ -Not a registered chit seat identity ${seatIdentity}`; - return E.resolve(metaIssuer.slash(metaAmount, allegedChitPayment)).then(_ => +Not a registered invite seat identity ${seatIdentity}`; + return E.resolve(metaIssuer.slash(metaAmount, allegedInvitePayment)).then(_ => seats.get(seatIdentity), ); } @@ -41,12 +39,12 @@ Not a registered chit seat identity ${seatIdentity}`; // TODO: The contract host `start` method should spin off a new vat // for each new contract instance. const contractHost = harden({ - getChitIssuer() { + getInviteIssuer() { return controller.getMetaIssuer(); }, // The `contractSrc` is code for a contract function parameterized - // by `terms` and `chitMaker`. `start` evaluates this code, + // by `terms` and `inviteMaker`. `start` evaluates this code, // calls that function to start the contract, and returns whatever // the contract returns. start(contractSrc, termsP) { @@ -60,15 +58,15 @@ Not a registered chit seat identity ${seatIdentity}`; }); return E.resolve(allComparable(termsP)).then(terms => { - const chitMaker = harden({ - // Used by the contract to make chits for credibly - // participating in the contract. The returned chit can be - // redeemed for this seat. The chitMaker contributes the + const inviteMaker = harden({ + // Used by the contract to make invites for credibly + // participating in the contract. The returned invite can be + // redeemed for this seat. The inviteMaker contributes the // description `{ contractSrc, terms, seatDesc }`. If this - // contract host redeems a chit, then the contractSrc and + // contract host redeems an invite, then the contractSrc and // terms are accurate. The seatDesc is according to that // contractSrc code. - make(seatDesc, seat, name = 'a chit payment') { + make(seatDesc, seat, name = 'an invite payment') { const baseDescription = harden({ contractSrc, terms, @@ -101,16 +99,16 @@ Not a registered chit seat identity ${seatIdentity}`; }, redeem, }); - return contract(terms, chitMaker); + return contract(terms, inviteMaker); }); }, - // If this is a chit payment made by a chitMaker of this contract + // If this is an invite payment made by an inviteMaker of this contract // host, redeem it for the associated seat. Else error. Redeeming - // consumes the chit payment and also transfers the use rights. - redeem(allegedChitPaymentP) { - return E.resolve(allegedChitPaymentP).then(allegedChitPayment => { - return redeem(allegedChitPayment); + // consumes the invite payment and also transfers the use rights. + redeem(allegedInvitePaymentP) { + return E.resolve(allegedInvitePaymentP).then(allegedInvitePayment => { + return redeem(allegedInvitePayment); }); }, }); @@ -118,8 +116,8 @@ Not a registered chit seat identity ${seatIdentity}`; } harden(makeContractHost); -function exchangeChitAmount( - chitIssuerP, +function exchangeInviteAmount( + inviteIssuerP, seatIdentityP, contractSrc, terms, @@ -129,7 +127,7 @@ function exchangeChitAmount( ) { const passable = harden({ label: { - issuer: chitIssuerP, + issuer: inviteIssuerP, description: 'contract host', }, quantity: { @@ -152,7 +150,7 @@ function exchangeChitAmount( */ return comparableP; } -harden(exchangeChitAmount); +harden(exchangeInviteAmount); function makeCollect(E, log) { function collect(seatP, winPurseP, refundPurseP, name = 'collecting') { @@ -182,4 +180,4 @@ function makeCollect(E, log) { } harden(makeCollect); -export { makeContractHost, exchangeChitAmount, makeCollect }; +export { makeContractHost, exchangeInviteAmount, makeCollect }; diff --git a/demo/contractHost/coveredCall.js b/demo/contractHost/coveredCall.js index 7ff8922a857..46da5af2327 100644 --- a/demo/contractHost/coveredCall.js +++ b/demo/contractHost/coveredCall.js @@ -5,16 +5,16 @@ import harden from '@agoric/harden'; import { escrowExchange } from './escrow'; -function coveredCall(terms, chitMaker) { +function coveredCall(terms, inviteMaker) { const [moneyNeeded, stockNeeded, timerP, deadline] = terms; - const [aliceChit, bobChit] = escrowExchange( + const [aliceInvite, bobInvite] = escrowExchange( [moneyNeeded, stockNeeded], - chitMaker, + inviteMaker, ); - const aliceEscrowSeatP = chitMaker.redeem(aliceChit); - const bobEscrowSeatP = chitMaker.redeem(bobChit); + const aliceEscrowSeatP = inviteMaker.redeem(aliceInvite); + const bobEscrowSeatP = inviteMaker.redeem(bobInvite); // Seats @@ -29,7 +29,7 @@ function coveredCall(terms, chitMaker) { .getExclusive(stockNeeded, stockPayment, 'prePay') .then(prePayment => { E(bobEscrowSeatP).offer(prePayment); - return chitMaker.make( + return inviteMaker.make( ['holder', moneyNeeded, stockNeeded], aliceEscrowSeatP, ); @@ -43,7 +43,7 @@ function coveredCall(terms, chitMaker) { }, }); - return chitMaker.make(['writer', stockNeeded, moneyNeeded], bobSeat); + return inviteMaker.make(['writer', stockNeeded, moneyNeeded], bobSeat); } const coveredCallSrc = `\ diff --git a/demo/contractHost/escrow.js b/demo/contractHost/escrow.js index b9dce974a22..1c83b063ab0 100644 --- a/demo/contractHost/escrow.js +++ b/demo/contractHost/escrow.js @@ -10,7 +10,7 @@ import harden from '@agoric/harden'; // players 0 and 1. Money are the rights transfered from player 0 to // 1, and Stock are the rights transfered from 1 to 0. -function escrowExchange(terms, chitMaker) { +function escrowExchange(terms, inviteMaker) { const [moneyNeeded, stockNeeded] = terms; function makeTransfer(amount, srcPaymentP) { @@ -86,8 +86,8 @@ function escrowExchange(terms, chitMaker) { }); return harden([ - chitMaker.make([0, moneyNeeded, stockNeeded], aliceSeat), - chitMaker.make([1, stockNeeded, moneyNeeded], bobSeat), + inviteMaker.make([0, moneyNeeded, stockNeeded], aliceSeat), + inviteMaker.make([1, stockNeeded, moneyNeeded], bobSeat), ]); } diff --git a/demo/contractHost/vat-alice.js b/demo/contractHost/vat-alice.js index b1fdd5b4695..2ac09a506fb 100644 --- a/demo/contractHost/vat-alice.js +++ b/demo/contractHost/vat-alice.js @@ -6,7 +6,7 @@ import harden from '@agoric/harden'; import { insist } from '../../collections/insist'; import { escrowExchangeSrc } from './escrow'; import { coveredCallSrc } from './coveredCall'; -import { exchangeChitAmount, makeCollect } from './chit'; +import { exchangeInviteAmount, makeCollect } from './contractHost'; function makeAlice(E, host, log) { const collect = makeCollect(E, log); @@ -19,7 +19,7 @@ function makeAlice(E, host, log) { let initialized = false; let timerP; - let chitIssuerP; + let inviteIssuerP; let myMoneyPurseP; let moneyIssuerP; @@ -40,7 +40,7 @@ function makeAlice(E, host, log) { optFred = undefined, ) { timerP = E.resolve(timer); - chitIssuerP = E(host).getChitIssuer(); + inviteIssuerP = E(host).getInviteIssuer(); myMoneyPurseP = E.resolve(myMoneyPurse); moneyIssuerP = E(myMoneyPurseP).getIssuer(); @@ -70,16 +70,16 @@ ERR: alice.payBobWell called before init()`; return E(bob).buy('shoe', paymentP); }, - invite(allegedChitPaymentP) { - log('++ alice.invite starting'); + acceptInvite(allegedInvitePaymentP) { + log('++ alice.acceptInvite starting'); insist(initialized)`\ -ERR: alice.invite called before init()`; +ERR: alice.acceptInvite called before init()`; - showPaymentBalance('alice chit', allegedChitPaymentP); + showPaymentBalance('alice invite', allegedInvitePaymentP); - const allegedMetaAmountP = E(allegedChitPaymentP).getXferBalance(); + const allegedMetaAmountP = E(allegedInvitePaymentP).getXferBalance(); - const verifiedChitP = E.resolve(allegedMetaAmountP).then( + const verifiedInviteP = E.resolve(allegedMetaAmountP).then( allegedMetaAmount => { const clams10 = harden({ label: { @@ -96,8 +96,8 @@ ERR: alice.invite called before init()`; quantity: 7, }); - const metaOneAmountP = exchangeChitAmount( - chitIssuerP, + const metaOneAmountP = exchangeInviteAmount( + inviteIssuerP, allegedMetaAmount.quantity.label.identity, escrowExchangeSrc, [clams10, fudco7], @@ -107,40 +107,40 @@ ERR: alice.invite called before init()`; ); return E.resolve(metaOneAmountP).then(metaOneAmount => - E(chitIssuerP).getExclusive( + E(inviteIssuerP).getExclusive( metaOneAmount, - allegedChitPaymentP, - 'verified chit', + allegedInvitePaymentP, + 'verified invite', ), ); }, ); - showPaymentBalance('verified chit', verifiedChitP); + showPaymentBalance('verified invite', verifiedInviteP); - const seatP = E(host).redeem(verifiedChitP); + const seatP = E(host).redeem(verifiedInviteP); const moneyPaymentP = E(myMoneyPurseP).withdraw(10); E(seatP).offer(moneyPaymentP); return collect(seatP, myStockPurseP, myMoneyPurseP, 'alice escrow'); }, - acceptOption(allegedChitPaymentP) { + acceptOption(allegedInvitePaymentP) { if (optFredP) { - return alice.acceptOptionForFred(allegedChitPaymentP); + return alice.acceptOptionForFred(allegedInvitePaymentP); } - return alice.acceptOptionDirectly(allegedChitPaymentP); + return alice.acceptOptionDirectly(allegedInvitePaymentP); }, - acceptOptionDirectly(allegedChitPaymentP) { + acceptOptionDirectly(allegedInvitePaymentP) { log('++ alice.acceptOptionDirectly starting'); insist(initialized)`\ ERR: alice.acceptOptionDirectly called before init()`; - showPaymentBalance('alice chit', allegedChitPaymentP); + showPaymentBalance('alice invite', allegedInvitePaymentP); - const allegedMetaAmountP = E(allegedChitPaymentP).getXferBalance(); + const allegedMetaAmountP = E(allegedInvitePaymentP).getXferBalance(); - const verifiedChitP = E.resolve(allegedMetaAmountP).then( + const verifiedInviteP = E.resolve(allegedMetaAmountP).then( allegedMetaAmount => { const smackers10 = harden({ label: { @@ -157,8 +157,8 @@ ERR: alice.acceptOptionDirectly called before init()`; quantity: 7, }); - const metaOneAmountP = exchangeChitAmount( - chitIssuerP, + const metaOneAmountP = exchangeInviteAmount( + inviteIssuerP, allegedMetaAmount.quantity.label.identity, coveredCallSrc, [smackers10, yoyodyne7, timerP, 'singularity'], @@ -168,38 +168,38 @@ ERR: alice.acceptOptionDirectly called before init()`; ); return E.resolve(metaOneAmountP).then(metaOneAmount => - E(chitIssuerP).getExclusive( + E(inviteIssuerP).getExclusive( metaOneAmount, - allegedChitPaymentP, - 'verified chit', + allegedInvitePaymentP, + 'verified invite', ), ); }, ); - showPaymentBalance('verified chit', verifiedChitP); + showPaymentBalance('verified invite', verifiedInviteP); - const seatP = E(host).redeem(verifiedChitP); + const seatP = E(host).redeem(verifiedInviteP); const moneyPaymentP = E(myMoneyPurseP).withdraw(10); E(seatP).offer(moneyPaymentP); return collect(seatP, myStockPurseP, myMoneyPurseP, 'alice option'); }, - acceptOptionForFred(allegedChitPaymentP) { + acceptOptionForFred(allegedInvitePaymentP) { log('++ alice.acceptOptionForFred starting'); insist(initialized)`\ ERR: alice.acceptOptionForFred called before init()`; const finNeededP = E(E(optFinIssuerP).getAssay()).make(55); - const chitNeededP = E(allegedChitPaymentP).getXferBalance(); + const inviteNeededP = E(allegedInvitePaymentP).getXferBalance(); - const termsP = harden([finNeededP, chitNeededP]); - const chitsP = E(host).start(escrowExchangeSrc, termsP); - const fredChitP = chitsP.then(chits => chits[0]); - const aliceForFredChitP = chitsP.then(chits => chits[1]); + const termsP = harden([finNeededP, inviteNeededP]); + const invitesP = E(host).start(escrowExchangeSrc, termsP); + const fredInviteP = invitesP.then(invites => invites[0]); + const aliceForFredInviteP = invitesP.then(invites => invites[1]); const doneP = Promise.all([ - E(optFredP).acceptOptionOffer(fredChitP), - E(alice).completeOptionsSale(aliceForFredChitP, allegedChitPaymentP), + E(optFredP).acceptOptionOffer(fredInviteP), + E(alice).completeOptionsSale(aliceForFredInviteP, allegedInvitePaymentP), ]); doneP.then( _res => log('++ alice.acceptOptionForFred done'), @@ -208,18 +208,18 @@ ERR: alice.acceptOptionForFred called before init()`; return doneP; }, - completeOptionsSale(aliceForFredChitP, allegedChitPaymentP) { + completeOptionsSale(aliceForFredInviteP, allegedInvitePaymentP) { log('++ alice.completeOptionsSale starting'); insist(initialized)`\ ERR: alice.completeOptionsSale called before init()`; - const aliceForFredSeatP = E(host).redeem(aliceForFredChitP); - E(aliceForFredSeatP).offer(allegedChitPaymentP); - const myChitPurseP = E(chitIssuerP).makeEmptyPurse(); + const aliceForFredSeatP = E(host).redeem(aliceForFredInviteP); + E(aliceForFredSeatP).offer(allegedInvitePaymentP); + const myInvitePurseP = E(inviteIssuerP).makeEmptyPurse(); return collect( aliceForFredSeatP, myOptFinPurseP, - myChitPurseP, + myInvitePurseP, 'alice options sale', ); }, diff --git a/demo/contractHost/vat-bob.js b/demo/contractHost/vat-bob.js index 7d53f6e1898..d2d339917b7 100644 --- a/demo/contractHost/vat-bob.js +++ b/demo/contractHost/vat-bob.js @@ -6,7 +6,7 @@ import harden from '@agoric/harden'; import { insist } from '../../collections/insist'; import { escrowExchangeSrc } from './escrow'; import { coveredCallSrc } from './coveredCall'; -import { makeCollect } from './chit'; +import { makeCollect } from './contractHost'; function makeBob(E, host, log) { const collect = makeCollect(E, log); @@ -77,12 +77,12 @@ ERR: buy called before init()`; ERR: tradeWell called before init()`; const termsP = harden([moneyNeededP, stockNeededP]); - const chitsP = E(host).start(escrowExchangeSrc, termsP); - const aliceChitP = chitsP.then(chits => chits[0]); - const bobChitP = chitsP.then(chits => chits[1]); + const invitesP = E(host).start(escrowExchangeSrc, termsP); + const aliceInviteP = invitesP.then(invites => invites[0]); + const bobInviteP = invitesP.then(invites => invites[1]); const doneP = Promise.all([ - E(alice).invite(aliceChitP), - E(bob).invite(bobChitP), + E(alice).acceptInvite(aliceInviteP), + E(bob).acceptInvite(bobInviteP), ]); doneP.then( _res => log('++ bob.tradeWell done'), @@ -91,16 +91,11 @@ ERR: tradeWell called before init()`; return doneP; }, - /** - * As with 'buy', the naming is awkward. A client is inviting - * this object, asking it to join in a contract instance. It is not - * requesting that this object invite anything. - */ - invite(chitP) { + acceptInvite(inviteP) { insist(initialized)`\ -ERR: invite called before init()`; +ERR: acceptInvite called before init()`; - const seatP = E(host).redeem(chitP); + const seatP = E(host).redeem(inviteP); const stockPaymentP = E(myStockPurseP).withdraw(7); E(seatP).offer(stockPaymentP); return collect(seatP, myMoneyPurseP, myStockPurseP, 'bob escrow'); @@ -117,12 +112,12 @@ ERR: offerAliceOption called before init()`; timerP, 'singularity', ]); - const bobChitP = E(host).start(coveredCallSrc, termsP); - const bobSeatP = E(host).redeem(bobChitP); + const bobInviteP = E(host).start(coveredCallSrc, termsP); + const bobSeatP = E(host).redeem(bobInviteP); const stockPaymentP = E(myStockPurseP).withdraw(7); - const aliceChitP = E(bobSeatP).offer(stockPaymentP); + const aliceInviteP = E(bobSeatP).offer(stockPaymentP); const doneP = Promise.all([ - E(alice).acceptOption(aliceChitP), + E(alice).acceptOption(aliceInviteP), collect(bobSeatP, myMoneyPurseP, myStockPurseP, 'bob option'), ]); doneP.then( diff --git a/demo/contractHost/vat-fred.js b/demo/contractHost/vat-fred.js index 0f095602ed1..cf3ce9e3bfb 100644 --- a/demo/contractHost/vat-fred.js +++ b/demo/contractHost/vat-fred.js @@ -7,14 +7,14 @@ import { allComparable } from '../../collections/sameStructure'; import { insist } from '../../collections/insist'; import { escrowExchangeSrc } from './escrow'; import { coveredCallSrc } from './coveredCall'; -import { exchangeChitAmount, makeCollect } from './chit'; +import { exchangeInviteAmount, makeCollect } from './contractHost'; function makeFred(E, host, log) { const collect = makeCollect(E, log); let initialized = false; let timerP; - let chitIssuerP; + let inviteIssuerP; let myMoneyPurseP; let moneyIssuerP; @@ -27,7 +27,7 @@ function makeFred(E, host, log) { function init(timer, myMoneyPurse, myStockPurse, myFinPurse) { timerP = E.resolve(timer); - chitIssuerP = E(host).getChitIssuer(); + inviteIssuerP = E(host).getInviteIssuer(); myMoneyPurseP = E.resolve(myMoneyPurse); moneyIssuerP = E(myMoneyPurseP).getIssuer(); @@ -45,7 +45,7 @@ function makeFred(E, host, log) { const fred = harden({ init, - acceptOptionOffer(allegedChitPaymentP) { + acceptOptionOffer(allegedInvitePaymentP) { log('++ fred.acceptOptionOffer starting'); insist(initialized)`\ ERR: fred.acceptOptionOffer called before init()`; @@ -72,16 +72,16 @@ ERR: fred.acceptOptionOffer called before init()`; quantity: 55, }); - const allegedMetaAmountP = E(allegedChitPaymentP).getXferBalance(); + const allegedMetaAmountP = E(allegedInvitePaymentP).getXferBalance(); - const verifiedChitP = E.resolve(allegedMetaAmountP).then( + const verifiedInviteP = E.resolve(allegedMetaAmountP).then( allegedMetaAmount => { - const allegedBaseOptionsChitIssuer = + const allegedBaseOptionsInviteIssuer = allegedMetaAmount.quantity.label.description.terms[1]; - const metaOptionAmountP = exchangeChitAmount( - chitIssuerP, - allegedBaseOptionsChitIssuer.quantity.label.identity, + const metaOptionAmountP = exchangeInviteAmount( + inviteIssuerP, + allegedBaseOptionsInviteIssuer.quantity.label.identity, coveredCallSrc, [dough10, wonka7, timerP, 'singularity'], 'holder', @@ -89,8 +89,8 @@ ERR: fred.acceptOptionOffer called before init()`; wonka7, ); - const metaOptionSaleAmountP = exchangeChitAmount( - chitIssuerP, + const metaOptionSaleAmountP = exchangeInviteAmount( + inviteIssuerP, allegedMetaAmount.quantity.label.identity, escrowExchangeSrc, [fin55, metaOptionAmountP], @@ -100,28 +100,28 @@ ERR: fred.acceptOptionOffer called before init()`; ); return E.resolve(metaOptionSaleAmountP).then(metaOptionSaleAmount => - E(chitIssuerP).getExclusive( + E(inviteIssuerP).getExclusive( metaOptionSaleAmount, - allegedChitPaymentP, - 'verified chit', + allegedInvitePaymentP, + 'verified invite', ), ); }, ); - const seatP = E(host).redeem(verifiedChitP); + const seatP = E(host).redeem(verifiedInviteP); const finPaymentP = E(myFinPurseP).withdraw(55); E(seatP).offer(finPaymentP); - const optionChitPurseP = E(chitIssuerP).makeEmptyPurse(); + const optionInvitePurseP = E(inviteIssuerP).makeEmptyPurse(); const gotOptionP = collect( seatP, - optionChitPurseP, + optionInvitePurseP, myFinPurseP, 'fred buys escrowed option', ); return E.resolve(gotOptionP).then(_ => { // Fred bought the option. Now fred tries to exercise the option. - const optionChitPayment = E(optionChitPurseP).withdrawAll(); - const optionSeatP = E(host).redeem(optionChitPayment); + const optionInvitePayment = E(optionInvitePurseP).withdrawAll(); + const optionSeatP = E(host).redeem(optionInvitePayment); return E.resolve(allComparable(dough10)).then(d10 => { const doughPaymentP = E(myMoneyPurseP).withdraw(d10); E(optionSeatP).offer(doughPaymentP); diff --git a/demo/contractHost/vat-host.js b/demo/contractHost/vat-host.js index 1813eac5e75..9a6d5c2a7a8 100644 --- a/demo/contractHost/vat-host.js +++ b/demo/contractHost/vat-host.js @@ -2,7 +2,7 @@ import harden from '@agoric/harden'; -import { makeContractHost } from './chit'; +import { makeContractHost } from './contractHost'; function setup(syscall, state, helpers) { return helpers.makeLiveSlots( diff --git a/test/test-demos.js b/test/test-demos.js index f9dc150753d..00552b123b9 100644 --- a/test/test-demos.js +++ b/test/test-demos.js @@ -120,9 +120,9 @@ test('run contractHost Demo --alice-first without SES', async t => { const contractBobFirstGolden = [ '=> setup called', '++ bob.tradeWell starting', - '++ alice.invite starting', - 'alice chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', - 'verified chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', + '++ alice.acceptInvite starting', + 'alice invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', + 'verified invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', 'bob escrow wins: {"label":{"issuer":{},"description":"clams"},"quantity":10} refs: null', 'alice escrow wins: {"label":{"issuer":{},"description":"fudco"},"quantity":7} refs: null', '++ bob.tradeWell done', @@ -155,8 +155,8 @@ const contractCoveredCallGolden = [ '++ bob.offerAliceOption starting', '++ alice.acceptOptionDirectly starting', 'Pretend singularity never happens', - 'alice chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', - 'verified chit xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', + 'alice invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', + 'verified invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', 'alice option wins: {"label":{"issuer":{},"description":"yoyodyne"},"quantity":7} refs: null', 'bob option wins: {"label":{"issuer":{},"description":"smackers"},"quantity":10} refs: null', '++ bob.offerAliceOption done', From 99c7d81ddef2b22c041a151c021f0fb56bab9c45 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Wed, 29 May 2019 10:23:10 -0700 Subject: [PATCH 09/18] lint-ok --- demo/contractHost/contractHost.js | 4 ++-- demo/contractHost/vat-alice.js | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/demo/contractHost/contractHost.js b/demo/contractHost/contractHost.js index c3248903d46..e9a9b51756a 100644 --- a/demo/contractHost/contractHost.js +++ b/demo/contractHost/contractHost.js @@ -28,8 +28,8 @@ No invites left`; const seatIdentity = baseAmount.label.identity; insist(seats.has(seatIdentity))`\ Not a registered invite seat identity ${seatIdentity}`; - return E.resolve(metaIssuer.slash(metaAmount, allegedInvitePayment)).then(_ => - seats.get(seatIdentity), + return E.resolve(metaIssuer.slash(metaAmount, allegedInvitePayment)).then( + _ => seats.get(seatIdentity), ); } diff --git a/demo/contractHost/vat-alice.js b/demo/contractHost/vat-alice.js index 2ac09a506fb..196edc71b18 100644 --- a/demo/contractHost/vat-alice.js +++ b/demo/contractHost/vat-alice.js @@ -199,7 +199,10 @@ ERR: alice.acceptOptionForFred called before init()`; const aliceForFredInviteP = invitesP.then(invites => invites[1]); const doneP = Promise.all([ E(optFredP).acceptOptionOffer(fredInviteP), - E(alice).completeOptionsSale(aliceForFredInviteP, allegedInvitePaymentP), + E(alice).completeOptionsSale( + aliceForFredInviteP, + allegedInvitePaymentP, + ), ]); doneP.then( _res => log('++ alice.acceptOptionForFred done'), From 24ca0b1d13658f5dbc702a1e855be42b1a8903fb Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Wed, 29 May 2019 11:13:09 -0700 Subject: [PATCH 10/18] curried contractHost start into install and spawn --- demo/contractHost/bootstrap.js | 40 +++++----- demo/contractHost/contractHost.js | 122 ++++++++++++++++-------------- demo/contractHost/vat-alice.js | 4 +- demo/contractHost/vat-bob.js | 4 +- test/test-demos.js | 2 +- 5 files changed, 93 insertions(+), 79 deletions(-) diff --git a/demo/contractHost/bootstrap.js b/demo/contractHost/bootstrap.js index 61f95955401..37b53aefbd1 100644 --- a/demo/contractHost/bootstrap.js +++ b/demo/contractHost/bootstrap.js @@ -4,17 +4,19 @@ import harden from '@agoric/harden'; function build(E, log) { function showPaymentBalance(name, paymentP) { - E(paymentP) + return E(paymentP) .getXferBalance() .then(amount => log(name, ' xfer balance ', amount)); } function showPurseBalances(name, purseP) { - E(purseP) - .getXferBalance() - .then(amount => log(name, ' xfer balance ', amount)); - E(purseP) - .getUseBalance() - .then(amount => log(name, ' use balance ', amount)); + return Promise.all([ + E(purseP) + .getXferBalance() + .then(amount => log(name, ' xfer balance ', amount)), + E(purseP) + .getUseBalance() + .then(amount => log(name, ' use balance ', amount)), + ]); } /* @@ -81,21 +83,21 @@ function build(E, log) { } const contractSrc = `${trivContract}`; - const fooInviteP = E(host).start(contractSrc, 'foo terms'); - - showPaymentBalance('foo', fooInviteP); + const fooInviteP = E(E(host).install(contractSrc)).spawn('foo terms'); - const eightP = E(host).redeem(fooInviteP); + return E.resolve(showPaymentBalance('foo', fooInviteP)).then(_ => { + const eightP = E(host).redeem(fooInviteP); - eightP.then(res => { - showPaymentBalance('foo', fooInviteP); - log('++ eightP resolved to', res, '(should be 8)'); - if (res !== 8) { - throw new Error(`eightP resolved to ${res}, not 8`); - } - log('++ DONE'); + eightP.then(res => { + showPaymentBalance('foo', fooInviteP); + log('++ eightP resolved to ', res, ' (should be 8)'); + if (res !== 8) { + throw new Error(`eightP resolved to ${res}, not 8`); + } + log('++ DONE'); + }); + return eightP; }); - return eightP; } function betterContractTestAliceFirst(mint, alice, bob) { diff --git a/demo/contractHost/contractHost.js b/demo/contractHost/contractHost.js index e9a9b51756a..d44f8699f66 100644 --- a/demo/contractHost/contractHost.js +++ b/demo/contractHost/contractHost.js @@ -35,72 +35,84 @@ Not a registered invite seat identity ${seatIdentity}`; // The contract host is designed to have a long-lived credible // identity. - // - // TODO: The contract host `start` method should spin off a new vat - // for each new contract instance. const contractHost = harden({ getInviteIssuer() { return controller.getMetaIssuer(); }, // The `contractSrc` is code for a contract function parameterized - // by `terms` and `inviteMaker`. `start` evaluates this code, + // by `terms` and `inviteMaker`. `spawn` evaluates this code, // calls that function to start the contract, and returns whatever // the contract returns. - start(contractSrc, termsP) { - contractSrc = `${contractSrc}`; - const contract = evaluate(contractSrc, { - Nat, - harden, - console, - E, - makePromise, - }); + // + // TODO: The `spawn` method should spin off a new vat for each new + // contract instance. In the current single-vat implementation we + // could evaluate the contractSrc to a contract during install + // rather than spawn. However, once we spin off a new vat per + // spawn, we'll need to evaluate per-spawn anyway. Even though we + // do not save on evaluations, this currying enables us to avoid + // re-sending the contract source code, and it enables us to use + // the installation in descriptions rather than the source code + // itself. + install(contractSrc) { + const installation = harden({ + spawn(termsP) { + contractSrc = `${contractSrc}`; + const contract = evaluate(contractSrc, { + Nat, + harden, + console, + E, + makePromise, + }); - return E.resolve(allComparable(termsP)).then(terms => { - const inviteMaker = harden({ - // Used by the contract to make invites for credibly - // participating in the contract. The returned invite can be - // redeemed for this seat. The inviteMaker contributes the - // description `{ contractSrc, terms, seatDesc }`. If this - // contract host redeems an invite, then the contractSrc and - // terms are accurate. The seatDesc is according to that - // contractSrc code. - make(seatDesc, seat, name = 'an invite payment') { - const baseDescription = harden({ - contractSrc, - terms, - seatDesc, - }); - // Note that an empty object is pass-by-presence, and so - // has an unforgeable identity. - const seatIdentity = harden({}); - const baseLabel = harden({ - identity: seatIdentity, - description: baseDescription, + return E.resolve(allComparable(termsP)).then(terms => { + const inviteMaker = harden({ + // Used by the contract to make invites for credibly + // participating in the contract. The returned invite can be + // redeemed for this seat. The inviteMaker contributes the + // description `{ contractSrc, terms, seatDesc }`. If this + // contract host redeems an invite, then the contractSrc and + // terms are accurate. The seatDesc is according to that + // contractSrc code. + make(seatDesc, seat, name = 'an invite payment') { + const baseDescription = harden({ + contractSrc, + terms, + seatDesc, + }); + // Note that an empty object is pass-by-presence, and so + // has an unforgeable identity. + const seatIdentity = harden({}); + const baseLabel = harden({ + identity: seatIdentity, + description: baseDescription, + }); + const baseAssay = makeNatAssay(baseLabel); + const baseAmount = baseAssay.make(1); + controller.register(baseAssay); + seats.set(seatIdentity, seat); + const metaOneAmount = metaAssay.make(baseAmount); + // This should be the only use of the meta mint, to make a + // meta purse whose quantity is one unit of a base amount + // for a unique base label. This meta purse makes the + // returned meta payment, and then the empty meta purse is + // dropped, in the sense that it becomes inaccessible. But + // it is not yet collectable. Until the returned payment + // is deposited, it will retain the metaPurse, as the + // metaPurse contains the usage rights. + const metaPurse = controller + .getMetaMint() + .mint(metaOneAmount, name); + return metaPurse.withdrawAll(name); + }, + redeem, }); - const baseAssay = makeNatAssay(baseLabel); - const baseAmount = baseAssay.make(1); - controller.register(baseAssay); - seats.set(seatIdentity, seat); - const metaOneAmount = metaAssay.make(baseAmount); - // This should be the only use of the meta mint, to make a - // meta purse whose quantity is one unit of a base amount - // for a unique base label. This meta purse makes the - // returned meta payment, and then the empty meta purse is - // dropped, in the sense that it becomes inaccessible. But - // it is not yet collectable. Until the returned payment - // is deposited, it will retain the metaPurse, as the - // metaPurse contains the usage rights. - const metaPurse = controller - .getMetaMint() - .mint(metaOneAmount, name); - return metaPurse.withdrawAll(name); - }, - redeem, - }); - return contract(terms, inviteMaker); + return contract(terms, inviteMaker); + }); + }, }); + return installation; }, // If this is an invite payment made by an inviteMaker of this contract diff --git a/demo/contractHost/vat-alice.js b/demo/contractHost/vat-alice.js index 196edc71b18..826bd733886 100644 --- a/demo/contractHost/vat-alice.js +++ b/demo/contractHost/vat-alice.js @@ -12,7 +12,7 @@ function makeAlice(E, host, log) { const collect = makeCollect(E, log); function showPaymentBalance(name, paymentP) { - E(paymentP) + return E(paymentP) .getXferBalance() .then(amount => log(name, ' xfer balance ', amount)); } @@ -194,7 +194,7 @@ ERR: alice.acceptOptionForFred called before init()`; const inviteNeededP = E(allegedInvitePaymentP).getXferBalance(); const termsP = harden([finNeededP, inviteNeededP]); - const invitesP = E(host).start(escrowExchangeSrc, termsP); + const invitesP = E(E(host).install(escrowExchangeSrc)).spawn(termsP); const fredInviteP = invitesP.then(invites => invites[0]); const aliceForFredInviteP = invitesP.then(invites => invites[1]); const doneP = Promise.all([ diff --git a/demo/contractHost/vat-bob.js b/demo/contractHost/vat-bob.js index d2d339917b7..a80117538bd 100644 --- a/demo/contractHost/vat-bob.js +++ b/demo/contractHost/vat-bob.js @@ -77,7 +77,7 @@ ERR: buy called before init()`; ERR: tradeWell called before init()`; const termsP = harden([moneyNeededP, stockNeededP]); - const invitesP = E(host).start(escrowExchangeSrc, termsP); + const invitesP = E(E(host).install(escrowExchangeSrc)).spawn(termsP); const aliceInviteP = invitesP.then(invites => invites[0]); const bobInviteP = invitesP.then(invites => invites[1]); const doneP = Promise.all([ @@ -112,7 +112,7 @@ ERR: offerAliceOption called before init()`; timerP, 'singularity', ]); - const bobInviteP = E(host).start(coveredCallSrc, termsP); + const bobInviteP = E(E(host).install(coveredCallSrc)).spawn(termsP); const bobSeatP = E(host).redeem(bobInviteP); const stockPaymentP = E(myStockPurseP).withdraw(7); const aliceInviteP = E(bobSeatP).offer(stockPaymentP); diff --git a/test/test-demos.js b/test/test-demos.js index 00552b123b9..dccecc1af1c 100644 --- a/test/test-demos.js +++ b/test/test-demos.js @@ -81,7 +81,7 @@ const contractTrivialGolden = [ '=> setup called', 'starting trivialContractTest', 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"function trivCo...foo\', 8);\\n }","terms":"foo terms","seatDesc":"foo"}},"quantity":1}}', - '++ eightP resolved to8(should be 8)', + '++ eightP resolved to 8 (should be 8)', '++ DONE', 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":null}', ]; From 40365f5b2a1f82c86d86d62a6ca778ebef40417a Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Wed, 29 May 2019 16:56:58 -0700 Subject: [PATCH 11/18] simpler seatDesc --- demo/contractHost/contractHost.js | 6 ++---- demo/contractHost/coveredCall.js | 7 ++----- demo/contractHost/escrow.js | 4 ++-- demo/contractHost/vat-alice.js | 6 +----- demo/contractHost/vat-fred.js | 6 +----- test/test-demos.js | 10 +++++----- 6 files changed, 13 insertions(+), 26 deletions(-) diff --git a/demo/contractHost/contractHost.js b/demo/contractHost/contractHost.js index d44f8699f66..f71d6e38f82 100644 --- a/demo/contractHost/contractHost.js +++ b/demo/contractHost/contractHost.js @@ -133,9 +133,7 @@ function exchangeInviteAmount( seatIdentityP, contractSrc, terms, - seatIndex, - giveAmount, - takeAmount, + seatDesc, ) { const passable = harden({ label: { @@ -148,7 +146,7 @@ function exchangeInviteAmount( description: { contractSrc, terms, - seatDesc: [seatIndex, giveAmount, takeAmount], + seatDesc, }, }, quantity: 1, diff --git a/demo/contractHost/coveredCall.js b/demo/contractHost/coveredCall.js index 46da5af2327..3cc56c5502f 100644 --- a/demo/contractHost/coveredCall.js +++ b/demo/contractHost/coveredCall.js @@ -29,10 +29,7 @@ function coveredCall(terms, inviteMaker) { .getExclusive(stockNeeded, stockPayment, 'prePay') .then(prePayment => { E(bobEscrowSeatP).offer(prePayment); - return inviteMaker.make( - ['holder', moneyNeeded, stockNeeded], - aliceEscrowSeatP, - ); + return inviteMaker.make('holder', aliceEscrowSeatP); }); }, getWinnings() { @@ -43,7 +40,7 @@ function coveredCall(terms, inviteMaker) { }, }); - return inviteMaker.make(['writer', stockNeeded, moneyNeeded], bobSeat); + return inviteMaker.make('writer', bobSeat); } const coveredCallSrc = `\ diff --git a/demo/contractHost/escrow.js b/demo/contractHost/escrow.js index 1c83b063ab0..cac53ec4714 100644 --- a/demo/contractHost/escrow.js +++ b/demo/contractHost/escrow.js @@ -86,8 +86,8 @@ function escrowExchange(terms, inviteMaker) { }); return harden([ - inviteMaker.make([0, moneyNeeded, stockNeeded], aliceSeat), - inviteMaker.make([1, stockNeeded, moneyNeeded], bobSeat), + inviteMaker.make('left', aliceSeat), + inviteMaker.make('right', bobSeat), ]); } diff --git a/demo/contractHost/vat-alice.js b/demo/contractHost/vat-alice.js index 826bd733886..6bfb9811688 100644 --- a/demo/contractHost/vat-alice.js +++ b/demo/contractHost/vat-alice.js @@ -101,9 +101,7 @@ ERR: alice.acceptInvite called before init()`; allegedMetaAmount.quantity.label.identity, escrowExchangeSrc, [clams10, fudco7], - 0, - clams10, - fudco7, + 'left', ); return E.resolve(metaOneAmountP).then(metaOneAmount => @@ -163,8 +161,6 @@ ERR: alice.acceptOptionDirectly called before init()`; coveredCallSrc, [smackers10, yoyodyne7, timerP, 'singularity'], 'holder', - smackers10, - yoyodyne7, ); return E.resolve(metaOneAmountP).then(metaOneAmount => diff --git a/demo/contractHost/vat-fred.js b/demo/contractHost/vat-fred.js index cf3ce9e3bfb..ee6afe96e3c 100644 --- a/demo/contractHost/vat-fred.js +++ b/demo/contractHost/vat-fred.js @@ -85,8 +85,6 @@ ERR: fred.acceptOptionOffer called before init()`; coveredCallSrc, [dough10, wonka7, timerP, 'singularity'], 'holder', - dough10, - wonka7, ); const metaOptionSaleAmountP = exchangeInviteAmount( @@ -94,9 +92,7 @@ ERR: fred.acceptOptionOffer called before init()`; allegedMetaAmount.quantity.label.identity, escrowExchangeSrc, [fin55, metaOptionAmountP], - 0, - fin55, - metaOptionAmountP, + 'left', ); return E.resolve(metaOptionSaleAmountP).then(metaOptionSaleAmount => diff --git a/test/test-demos.js b/test/test-demos.js index dccecc1af1c..f633c630ce3 100644 --- a/test/test-demos.js +++ b/test/test-demos.js @@ -121,8 +121,8 @@ const contractBobFirstGolden = [ '=> setup called', '++ bob.tradeWell starting', '++ alice.acceptInvite starting', - 'alice invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', - 'verified invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":[0,{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}]}},"quantity":1}}', + 'alice invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":"left"}},"quantity":1}}', + 'verified invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":"left"}},"quantity":1}}', 'bob escrow wins: {"label":{"issuer":{},"description":"clams"},"quantity":10} refs: null', 'alice escrow wins: {"label":{"issuer":{},"description":"fudco"},"quantity":7} refs: null', '++ bob.tradeWell done', @@ -155,8 +155,8 @@ const contractCoveredCallGolden = [ '++ bob.offerAliceOption starting', '++ alice.acceptOptionDirectly starting', 'Pretend singularity never happens', - 'alice invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', - 'verified invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7}]}},"quantity":1}}', + 'alice invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":"holder"}},"quantity":1}}', + 'verified invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":"holder"}},"quantity":1}}', 'alice option wins: {"label":{"issuer":{},"description":"yoyodyne"},"quantity":7} refs: null', 'bob option wins: {"label":{"issuer":{},"description":"smackers"},"quantity":10} refs: null', '++ bob.offerAliceOption done', @@ -192,7 +192,7 @@ const contractCoveredCallSaleGolden = [ '++ fred.acceptOptionOffer starting', 'Pretend singularity never happens', 'alice options sale wins: {"label":{"issuer":{},"description":"fins"},"quantity":55} refs: null', - 'fred buys escrowed option wins: {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"dough"},"quantity":10},{"label":{"issuer":{},"description":"wonka"},"quantity":7},{},"singularity"],"seatDesc":["holder",{"label":{"issuer":{},"description":"dough"},"quantity":10},{"label":{"issuer":{},"description":"wonka"},"quantity":7}]}},"quantity":1}} refs: null', + 'fred buys escrowed option wins: {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"dough"},"quantity":10},{"label":{"issuer":{},"description":"wonka"},"quantity":7},{},"singularity"],"seatDesc":"holder"}},"quantity":1}} refs: null', 'fred exercises option, buying stock wins: {"label":{"issuer":{},"description":"wonka"},"quantity":7} refs: null', 'bob option wins: {"label":{"issuer":{},"description":"dough"},"quantity":10} refs: null', '++ alice.acceptOptionForFred done', From 5dfdd6e821b9da3ec3868b94453a9d6905d89cb7 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Wed, 29 May 2019 19:46:30 -0700 Subject: [PATCH 12/18] installation in descriptions rather than source code --- demo/contractHost/bootstrap.js | 73 ++++++++++++++++++++++++++----- demo/contractHost/contractHost.js | 13 +++--- demo/contractHost/issuers.js | 6 +-- demo/contractHost/vat-alice.js | 16 ++++--- demo/contractHost/vat-bob.js | 19 +++++--- demo/contractHost/vat-fred.js | 20 ++++++--- test/test-demos.js | 12 ++--- 7 files changed, 116 insertions(+), 43 deletions(-) diff --git a/demo/contractHost/bootstrap.js b/demo/contractHost/bootstrap.js index 37b53aefbd1..908a4b30cd3 100644 --- a/demo/contractHost/bootstrap.js +++ b/demo/contractHost/bootstrap.js @@ -2,6 +2,9 @@ import harden from '@agoric/harden'; +import { escrowExchangeSrc } from './escrow'; +import { coveredCallSrc } from './coveredCall'; + function build(E, log) { function showPaymentBalance(name, paymentP) { return E(paymentP) @@ -100,7 +103,10 @@ function build(E, log) { }); } - function betterContractTestAliceFirst(mint, alice, bob) { + function betterContractTestAliceFirst(host, mint, alice, bob) { + const escrowExchangeInstallationP = E(host).install(escrowExchangeSrc); + const coveredCallInstallationP = E(host).install(coveredCallSrc); + const moneyMintP = E(mint).makeMint('moola'); const aliceMoneyPurseP = E(moneyMintP).mint(1000); const bobMoneyPurseP = E(moneyMintP).mint(1001); @@ -110,11 +116,19 @@ function build(E, log) { const bobStockPurseP = E(stockMintP).mint(2003); const aliceP = E(alice).init( + escrowExchangeInstallationP, + coveredCallInstallationP, fakeNeverTimer, aliceMoneyPurseP, aliceStockPurseP, ); - const bobP = E(bob).init(fakeNeverTimer, bobMoneyPurseP, bobStockPurseP); + const bobP = E(bob).init( + escrowExchangeInstallationP, + coveredCallInstallationP, + fakeNeverTimer, + bobMoneyPurseP, + bobStockPurseP, + ); return Promise.all([aliceP, bobP]).then(_ => { const ifItFitsP = E(aliceP).payBobWell(bob); ifItFitsP.then( @@ -128,7 +142,10 @@ function build(E, log) { }); } - function betterContractTestBobFirst(mint, alice, bob) { + function betterContractTestBobFirst(host, mint, alice, bob) { + const escrowExchangeInstallationP = E(host).install(escrowExchangeSrc); + const coveredCallInstallationP = E(host).install(coveredCallSrc); + const moneyMintP = E(mint).makeMint('clams'); const aliceMoneyPurseP = E(moneyMintP).mint(1000, 'aliceMainMoney'); const bobMoneyPurseP = E(moneyMintP).mint(1001, 'bobMainMoney'); @@ -138,11 +155,19 @@ function build(E, log) { const bobStockPurseP = E(stockMintP).mint(2003, 'bobMainStock'); const aliceP = E(alice).init( + escrowExchangeInstallationP, + coveredCallInstallationP, fakeNeverTimer, aliceMoneyPurseP, aliceStockPurseP, ); - const bobP = E(bob).init(fakeNeverTimer, bobMoneyPurseP, bobStockPurseP); + const bobP = E(bob).init( + escrowExchangeInstallationP, + coveredCallInstallationP, + fakeNeverTimer, + bobMoneyPurseP, + bobStockPurseP, + ); return Promise.all([aliceP, bobP]).then(_ => { E(bobP) .tradeWell(aliceP, false) @@ -162,7 +187,10 @@ function build(E, log) { }); } - function coveredCallTest(mint, alice, bob) { + function coveredCallTest(host, mint, alice, bob) { + const escrowExchangeInstallationP = E(host).install(escrowExchangeSrc); + const coveredCallInstallationP = E(host).install(coveredCallSrc); + const moneyMintP = E(mint).makeMint('smackers'); const aliceMoneyPurseP = E(moneyMintP).mint(1000, 'aliceMainMoney'); const bobMoneyPurseP = E(moneyMintP).mint(1001, 'bobMainMoney'); @@ -172,11 +200,19 @@ function build(E, log) { const bobStockPurseP = E(stockMintP).mint(2003, 'bobMainStock'); const aliceP = E(alice).init( + escrowExchangeInstallationP, + coveredCallInstallationP, fakeNeverTimer, aliceMoneyPurseP, aliceStockPurseP, ); - const bobP = E(bob).init(fakeNeverTimer, bobMoneyPurseP, bobStockPurseP); + const bobP = E(bob).init( + escrowExchangeInstallationP, + coveredCallInstallationP, + fakeNeverTimer, + bobMoneyPurseP, + bobStockPurseP, + ); return Promise.all([aliceP, bobP]).then(_ => { E(bobP) .offerAliceOption(aliceP, false) @@ -196,7 +232,10 @@ function build(E, log) { }); } - function coveredCallSaleTest(mint, alice, bob, fred) { + function coveredCallSaleTest(host, mint, alice, bob, fred) { + const escrowExchangeInstallationP = E(host).install(escrowExchangeSrc); + const coveredCallInstallationP = E(host).install(coveredCallSrc); + const doughMintP = E(mint).makeMint('dough'); const aliceDoughPurseP = E(doughMintP).mint(1000, 'aliceDough'); const bobDoughPurseP = E(doughMintP).mint(1001, 'bobDough'); @@ -212,15 +251,25 @@ function build(E, log) { const fredFinPurseP = E(finMintP).mint(3001, 'fredFins'); const aliceP = E(alice).init( + escrowExchangeInstallationP, + coveredCallInstallationP, fakeNeverTimer, aliceDoughPurseP, aliceStockPurseP, aliceFinPurseP, fred, ); - const bobP = E(bob).init(fakeNeverTimer, bobDoughPurseP, bobStockPurseP); + const bobP = E(bob).init( + escrowExchangeInstallationP, + coveredCallInstallationP, + fakeNeverTimer, + bobDoughPurseP, + bobStockPurseP, + ); /* eslint-disable-next-line no-unused-vars */ const fredP = E(fred).init( + escrowExchangeInstallationP, + coveredCallInstallationP, fakeNeverTimer, fredDoughPurseP, fredStockPurseP, @@ -267,26 +316,26 @@ function build(E, log) { const host = await E(vats.host).makeHost(); const alice = await E(vats.alice).makeAlice(host); const bob = await E(vats.bob).makeBob(host); - return betterContractTestAliceFirst(vats.mint, alice, bob); + return betterContractTestAliceFirst(host, vats.mint, alice, bob); } case 'bob-first': { const host = await E(vats.host).makeHost(); const alice = await E(vats.alice).makeAlice(host); const bob = await E(vats.bob).makeBob(host); - return betterContractTestBobFirst(vats.mint, alice, bob); + return betterContractTestBobFirst(host, vats.mint, alice, bob); } case 'covered-call': { const host = await E(vats.host).makeHost(); const alice = await E(vats.alice).makeAlice(host); const bob = await E(vats.bob).makeBob(host); - return coveredCallTest(vats.mint, alice, bob); + return coveredCallTest(host, vats.mint, alice, bob); } case 'covered-call-sale': { const host = await E(vats.host).makeHost(); const alice = await E(vats.alice).makeAlice(host); const bob = await E(vats.bob).makeBob(host); const fred = await E(vats.fred).makeFred(host); - return coveredCallSaleTest(vats.mint, alice, bob, fred); + return coveredCallSaleTest(host, vats.mint, alice, bob, fred); } default: { throw new Error(`unrecognized argument value ${argv[0]}`); diff --git a/demo/contractHost/contractHost.js b/demo/contractHost/contractHost.js index f71d6e38f82..cf363d76c4c 100644 --- a/demo/contractHost/contractHost.js +++ b/demo/contractHost/contractHost.js @@ -4,6 +4,7 @@ import Nat from '@agoric/nat'; import harden from '@agoric/harden'; import evaluate from '@agoric/evaluate'; +import { makePrivateName } from '../../collections/PrivateName'; import { allSettled } from '../../collections/allSettled'; import { insist } from '../../collections/insist'; import { allComparable } from '../../collections/sameStructure'; @@ -13,7 +14,7 @@ import makePromise from '../../src/kernel/makePromise'; function makeContractHost(E) { // Maps from seat identity to seats - const seats = new WeakMap(); + const seats = makePrivateName(); const controller = makeMetaIssuerController('contract host'); const metaIssuer = controller.getMetaIssuer(); @@ -71,13 +72,13 @@ Not a registered invite seat identity ${seatIdentity}`; // Used by the contract to make invites for credibly // participating in the contract. The returned invite can be // redeemed for this seat. The inviteMaker contributes the - // description `{ contractSrc, terms, seatDesc }`. If this + // description `{ installation, terms, seatDesc }`. If this // contract host redeems an invite, then the contractSrc and // terms are accurate. The seatDesc is according to that // contractSrc code. make(seatDesc, seat, name = 'an invite payment') { const baseDescription = harden({ - contractSrc, + installation, terms, seatDesc, }); @@ -91,7 +92,7 @@ Not a registered invite seat identity ${seatIdentity}`; const baseAssay = makeNatAssay(baseLabel); const baseAmount = baseAssay.make(1); controller.register(baseAssay); - seats.set(seatIdentity, seat); + seats.init(seatIdentity, seat); const metaOneAmount = metaAssay.make(baseAmount); // This should be the only use of the meta mint, to make a // meta purse whose quantity is one unit of a base amount @@ -131,7 +132,7 @@ harden(makeContractHost); function exchangeInviteAmount( inviteIssuerP, seatIdentityP, - contractSrc, + installationP, terms, seatDesc, ) { @@ -144,7 +145,7 @@ function exchangeInviteAmount( label: { identity: seatIdentityP, description: { - contractSrc, + installation: installationP, terms, seatDesc, }, diff --git a/demo/contractHost/issuers.js b/demo/contractHost/issuers.js index b744a21477c..0c3c2003aa6 100644 --- a/demo/contractHost/issuers.js +++ b/demo/contractHost/issuers.js @@ -197,11 +197,9 @@ harden(makeMint); // there is no observable state change from not being registered to // being registered. function makeMetaIssuerController(description) { - const baseIdentityToAssay = new WeakMap(); + const baseIdentityToAssay = makePrivateName(); function baseLabelToAssayFn(baseLabel) { const baseAssay = baseIdentityToAssay.get(baseLabel.identity); - insist(baseAssay !== undefined)`\ -Label identity not found ${baseLabel}.identity === ${baseLabel.identity}`; mustBeSameStructure(baseAssay.getLabel(), baseLabel, `Labels don't match`); return baseAssay; } @@ -217,7 +215,7 @@ Label identity not found ${baseLabel}.identity === ${baseLabel.identity}`; return metaIssuer; }, register(baseAssay) { - baseIdentityToAssay.set(baseAssay.getLabel().identity, baseAssay); + baseIdentityToAssay.init(baseAssay.getLabel().identity, baseAssay); }, }); return controller; diff --git a/demo/contractHost/vat-alice.js b/demo/contractHost/vat-alice.js index 6bfb9811688..2bf923ec5d6 100644 --- a/demo/contractHost/vat-alice.js +++ b/demo/contractHost/vat-alice.js @@ -4,8 +4,6 @@ import harden from '@agoric/harden'; import { insist } from '../../collections/insist'; -import { escrowExchangeSrc } from './escrow'; -import { coveredCallSrc } from './coveredCall'; import { exchangeInviteAmount, makeCollect } from './contractHost'; function makeAlice(E, host, log) { @@ -18,6 +16,9 @@ function makeAlice(E, host, log) { } let initialized = false; + + let escrowExchangeInstallationP; + let coveredCallInstallationP; let timerP; let inviteIssuerP; @@ -33,12 +34,16 @@ function makeAlice(E, host, log) { let optFredP; function init( + escrowExchangeInst, + coveredCallInst, timer, myMoneyPurse, myStockPurse, myOptFinPurse = undefined, optFred = undefined, ) { + escrowExchangeInstallationP = escrowExchangeInst; + coveredCallInstallationP = coveredCallInst; timerP = E.resolve(timer); inviteIssuerP = E(host).getInviteIssuer(); @@ -99,7 +104,7 @@ ERR: alice.acceptInvite called before init()`; const metaOneAmountP = exchangeInviteAmount( inviteIssuerP, allegedMetaAmount.quantity.label.identity, - escrowExchangeSrc, + escrowExchangeInstallationP, [clams10, fudco7], 'left', ); @@ -158,7 +163,7 @@ ERR: alice.acceptOptionDirectly called before init()`; const metaOneAmountP = exchangeInviteAmount( inviteIssuerP, allegedMetaAmount.quantity.label.identity, - coveredCallSrc, + coveredCallInstallationP, [smackers10, yoyodyne7, timerP, 'singularity'], 'holder', ); @@ -190,7 +195,8 @@ ERR: alice.acceptOptionForFred called before init()`; const inviteNeededP = E(allegedInvitePaymentP).getXferBalance(); const termsP = harden([finNeededP, inviteNeededP]); - const invitesP = E(E(host).install(escrowExchangeSrc)).spawn(termsP); + // const invitesP = E(E(host).install(escrowExchangeSrc)).spawn(termsP); + const invitesP = E(escrowExchangeInstallationP).spawn(termsP); const fredInviteP = invitesP.then(invites => invites[0]); const aliceForFredInviteP = invitesP.then(invites => invites[1]); const doneP = Promise.all([ diff --git a/demo/contractHost/vat-bob.js b/demo/contractHost/vat-bob.js index a80117538bd..048eb4dafe5 100644 --- a/demo/contractHost/vat-bob.js +++ b/demo/contractHost/vat-bob.js @@ -4,14 +4,15 @@ import harden from '@agoric/harden'; import { insist } from '../../collections/insist'; -import { escrowExchangeSrc } from './escrow'; -import { coveredCallSrc } from './coveredCall'; import { makeCollect } from './contractHost'; function makeBob(E, host, log) { const collect = makeCollect(E, log); let initialized = false; + + let escrowExchangeInstallationP; + let coveredCallInstallationP; let timerP; let myMoneyPurseP; @@ -22,7 +23,15 @@ function makeBob(E, host, log) { let stockIssuerP; let stockNeededP; - function init(timer, myMoneyPurse, myStockPurse) { + function init( + escrowExchangeInst, + coveredCallInst, + timer, + myMoneyPurse, + myStockPurse, + ) { + escrowExchangeInstallationP = escrowExchangeInst; + coveredCallInstallationP = coveredCallInst; timerP = E.resolve(timer); myMoneyPurseP = E.resolve(myMoneyPurse); @@ -77,7 +86,7 @@ ERR: buy called before init()`; ERR: tradeWell called before init()`; const termsP = harden([moneyNeededP, stockNeededP]); - const invitesP = E(E(host).install(escrowExchangeSrc)).spawn(termsP); + const invitesP = E(escrowExchangeInstallationP).spawn(termsP); const aliceInviteP = invitesP.then(invites => invites[0]); const bobInviteP = invitesP.then(invites => invites[1]); const doneP = Promise.all([ @@ -112,7 +121,7 @@ ERR: offerAliceOption called before init()`; timerP, 'singularity', ]); - const bobInviteP = E(E(host).install(coveredCallSrc)).spawn(termsP); + const bobInviteP = E(coveredCallInstallationP).spawn(termsP); const bobSeatP = E(host).redeem(bobInviteP); const stockPaymentP = E(myStockPurseP).withdraw(7); const aliceInviteP = E(bobSeatP).offer(stockPaymentP); diff --git a/demo/contractHost/vat-fred.js b/demo/contractHost/vat-fred.js index ee6afe96e3c..2eda8c2daff 100644 --- a/demo/contractHost/vat-fred.js +++ b/demo/contractHost/vat-fred.js @@ -5,14 +5,15 @@ import harden from '@agoric/harden'; import { allComparable } from '../../collections/sameStructure'; import { insist } from '../../collections/insist'; -import { escrowExchangeSrc } from './escrow'; -import { coveredCallSrc } from './coveredCall'; import { exchangeInviteAmount, makeCollect } from './contractHost'; function makeFred(E, host, log) { const collect = makeCollect(E, log); let initialized = false; + + let escrowExchangeInstallationP; + let coveredCallInstallationP; let timerP; let inviteIssuerP; @@ -25,7 +26,16 @@ function makeFred(E, host, log) { let myFinPurseP; let finIssuerP; - function init(timer, myMoneyPurse, myStockPurse, myFinPurse) { + function init( + escrowExchangeInst, + coveredCallInst, + timer, + myMoneyPurse, + myStockPurse, + myFinPurse, + ) { + escrowExchangeInstallationP = escrowExchangeInst; + coveredCallInstallationP = coveredCallInst; timerP = E.resolve(timer); inviteIssuerP = E(host).getInviteIssuer(); @@ -82,7 +92,7 @@ ERR: fred.acceptOptionOffer called before init()`; const metaOptionAmountP = exchangeInviteAmount( inviteIssuerP, allegedBaseOptionsInviteIssuer.quantity.label.identity, - coveredCallSrc, + coveredCallInstallationP, [dough10, wonka7, timerP, 'singularity'], 'holder', ); @@ -90,7 +100,7 @@ ERR: fred.acceptOptionOffer called before init()`; const metaOptionSaleAmountP = exchangeInviteAmount( inviteIssuerP, allegedMetaAmount.quantity.label.identity, - escrowExchangeSrc, + escrowExchangeInstallationP, [fin55, metaOptionAmountP], 'left', ); diff --git a/test/test-demos.js b/test/test-demos.js index f633c630ce3..aafe701bb7c 100644 --- a/test/test-demos.js +++ b/test/test-demos.js @@ -80,7 +80,7 @@ test('run contractHost Demo --mint without SES', async t => { const contractTrivialGolden = [ '=> setup called', 'starting trivialContractTest', - 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"function trivCo...foo\', 8);\\n }","terms":"foo terms","seatDesc":"foo"}},"quantity":1}}', + 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"installation":{},"terms":"foo terms","seatDesc":"foo"}},"quantity":1}}', '++ eightP resolved to 8 (should be 8)', '++ DONE', 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":null}', @@ -121,8 +121,8 @@ const contractBobFirstGolden = [ '=> setup called', '++ bob.tradeWell starting', '++ alice.acceptInvite starting', - 'alice invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":"left"}},"quantity":1}}', - 'verified invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function escro...Seat),\\n ]);\\n})","terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":"left"}},"quantity":1}}', + 'alice invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"installation":{},"terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":"left"}},"quantity":1}}', + 'verified invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"installation":{},"terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":"left"}},"quantity":1}}', 'bob escrow wins: {"label":{"issuer":{},"description":"clams"},"quantity":10} refs: null', 'alice escrow wins: {"label":{"issuer":{},"description":"fudco"},"quantity":7} refs: null', '++ bob.tradeWell done', @@ -155,8 +155,8 @@ const contractCoveredCallGolden = [ '++ bob.offerAliceOption starting', '++ alice.acceptOptionDirectly starting', 'Pretend singularity never happens', - 'alice invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":"holder"}},"quantity":1}}', - 'verified invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":"holder"}},"quantity":1}}', + 'alice invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"installation":{},"terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":"holder"}},"quantity":1}}', + 'verified invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"installation":{},"terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":"holder"}},"quantity":1}}', 'alice option wins: {"label":{"issuer":{},"description":"yoyodyne"},"quantity":7} refs: null', 'bob option wins: {"label":{"issuer":{},"description":"smackers"},"quantity":10} refs: null', '++ bob.offerAliceOption done', @@ -192,7 +192,7 @@ const contractCoveredCallSaleGolden = [ '++ fred.acceptOptionOffer starting', 'Pretend singularity never happens', 'alice options sale wins: {"label":{"issuer":{},"description":"fins"},"quantity":55} refs: null', - 'fred buys escrowed option wins: {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"contractSrc":"(function() {\\n ...Seat);\\n});\\n}())","terms":[{"label":{"issuer":{},"description":"dough"},"quantity":10},{"label":{"issuer":{},"description":"wonka"},"quantity":7},{},"singularity"],"seatDesc":"holder"}},"quantity":1}} refs: null', + 'fred buys escrowed option wins: {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"installation":{},"terms":[{"label":{"issuer":{},"description":"dough"},"quantity":10},{"label":{"issuer":{},"description":"wonka"},"quantity":7},{},"singularity"],"seatDesc":"holder"}},"quantity":1}} refs: null', 'fred exercises option, buying stock wins: {"label":{"issuer":{},"description":"wonka"},"quantity":7} refs: null', 'bob option wins: {"label":{"issuer":{},"description":"dough"},"quantity":10} refs: null', '++ alice.acceptOptionForFred done', From b14e6073a6ca3688f90693e23b4163380bc29f21 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Wed, 29 May 2019 21:54:53 -0700 Subject: [PATCH 13/18] runs and cleanly fails --- demo/contractHost/assays.js | 299 +++++++++++------------------- demo/contractHost/bootstrap.js | 32 ++-- demo/contractHost/contractHost.js | 132 +++++-------- demo/contractHost/issuers.js | 44 +---- demo/contractHost/vat-alice.js | 99 +++++----- demo/contractHost/vat-fred.js | 80 ++++---- test/test-demos.js | 1 + 7 files changed, 281 insertions(+), 406 deletions(-) diff --git a/demo/contractHost/assays.js b/demo/contractHost/assays.js index 9334413edd6..0303d2114e1 100644 --- a/demo/contractHost/assays.js +++ b/demo/contractHost/assays.js @@ -122,193 +122,116 @@ Unrecognized amount: ${amount}`; } harden(makeNatAssay); -// A meta assay wraps those base assays returned by -// baseLabelToAssayFn. A meta amount's quantity is a base amount, or -// null for empty. Thus, different meta amounts that have the same -// meta label can contain different meta quantities, each of whom is a -// base amount with a different base label. The "single" qualifier -// here is for the restriction that a metaSingleAssay cannot combine -// base amounts with different base labels. -// -// TODO: Before we can make a more general meta assay, we need to -// recognize a ConstMap as a pass-by-copy object. Once we have that, -// we can have a meta amount be a ConstMap from base labels to base -// amounts. -// -// Since an empty meta amount has a null quantity rather than a base -// amount, it has no corresponding base assay. -function makeMetaSingleAssayMaker(baseLabelToAssayFn) { - function makeMetaSingleAssay(metaLabel) { - mustBeComparable(metaLabel); - - // memoize well formedness check of meta amounts. - const metaBrand = new WeakSet(); - - const metaEmptyAmount = harden({ label: metaLabel, quantity: null }); - metaBrand.add(metaEmptyAmount); - - const metaAssay = harden({ - getLabel() { - return metaLabel; - }, - - // Given the raw quantity that this kind of amount would label, return - // an amount so labeling that quantity. - make(allegedBaseAmount) { - if (allegedBaseAmount === null) { - return metaEmptyAmount; - } - const baseAssay = baseLabelToAssayFn(allegedBaseAmount.label); - insist(baseAssay !== undefined)`\ -base label not found ${allegedBaseAmount}`; - const baseAmount = baseAssay.make(allegedBaseAmount.quantity); - if (baseAssay.isEmpty(baseAmount)) { - return metaEmptyAmount; - } - const metaAmount = harden({ label: metaLabel, quantity: baseAmount }); - metaBrand.add(metaAmount); - return metaAmount; - }, - - // Is this an amount object made by this assay? If so, return - // it. Otherwise error. - vouch(metaAmount) { - insist(metaBrand.has(metaAmount))`\ -Unrecognized metaAmount: ${metaAmount}`; - return metaAmount; - }, - - // Is this like an amount object made by this assay, such as one - // received by pass-by-copy from an otherwise-identical remote - // amount? On success, return an amount object made by this - // assay. Otherwise error. - // - // Until we have good support for pass-by-construction, the full - // assay style is too awkward to use remotely. See - // mintTestAssay. So coerce also accepts a bare number which it - // will coerce to a labeled number via metaAssay.make. - coerce(allegedMetaAmount) { - if (metaBrand.has(allegedMetaAmount)) { - return allegedMetaAmount; - } - const { - label: allegedMetaLabel, - quantity: allegedBaseAmount, - } = allegedMetaAmount; - mustBeSameStructure( - metaLabel, - allegedMetaLabel, - 'Unrecognized meta label', - ); - // Will throw on inappropriate quantity - return metaAssay.make(allegedBaseAmount); - }, - - // Return the raw quantity that this meta amount labels. This - // will be either null or a base amount with a label recognized - // by baseLabelToAssayFn. - quantity(metaAmount) { - return metaAssay.vouch(metaAmount).quantity; - }, - - // The meta empty amount has a quantity of null, rather than a - // base amount. - empty() { - return metaEmptyAmount; - }, - - isEmpty(metaAmount) { - const baseAmount = metaAssay.quantity(metaAmount); - if (baseAmount === null) { - insist(metaAmount === metaEmptyAmount)`\ -The empty meta amount should be unique`; - return true; - } - const baseAssay = baseLabelToAssayFn(baseAmount.label); - insist(!baseAssay.isEmpty(baseAmount))`\ -Empty base amount should be canonicalized as a null meta quantity`; - return false; - }, - - // Set inclusion of erights. - // Does the set of erights described by `leftAmount` include all - // the erights described by `rightAmount`? - includes(leftMetaAmount, rightMetaAmount) { - if (metaAssay.isEmpty(rightMetaAmount)) { - return true; - } - if (metaAssay.isEmpty(leftMetaAmount)) { - return false; - } - const leftBaseAmount = leftMetaAmount.quantity; - const leftBaseLabel = leftBaseAmount.label; - const rightBaseAmount = rightMetaAmount.quantity; - const rightBaseLabel = rightBaseAmount.label; - - if (!sameStructure(leftBaseLabel, rightBaseLabel)) { - return false; - } - const baseAssay = baseLabelToAssayFn(leftBaseLabel); - return baseAssay.includes(leftBaseAmount, rightBaseAmount); - }, - - // Set union of erights. - // Describe all the erights described by `leftAmount` and those - // described by `rightAmount`. - with(leftMetaAmount, rightMetaAmount) { - if (metaAssay.isEmpty(leftMetaAmount)) { - return rightMetaAmount; - } - if (metaAssay.isEmpty(rightMetaAmount)) { - return leftMetaAmount; - } - const leftBaseAmount = leftMetaAmount.quantity; - const leftBaseLabel = leftBaseAmount.label; - const rightBaseAmount = rightMetaAmount.quantity; - const rightBaseLabel = rightBaseAmount.label; - - mustBeSameStructure( - leftBaseLabel, - rightBaseLabel, - 'Cannot combine different base rights', - ); - const baseAssay = baseLabelToAssayFn(leftBaseLabel); - - return metaAssay.make(baseAssay.with(leftBaseAmount, rightBaseAmount)); - }, - - // Covering set subtraction of erights. - // If leftAmount does not include rightAmount, error. - // Describe the erights described by `leftAmount` and not described - // by `rightAmount`. - without(leftMetaAmount, rightMetaAmount) { - if (metaAssay.isEmpty(rightMetaAmount)) { - return leftMetaAmount; - } - insist(!metaAssay.isEmpty(leftMetaAmount))`\ -empty left meta assay does not include ${rightMetaAmount}`; - - const leftBaseAmount = leftMetaAmount.quantity; - const leftBaseLabel = leftBaseAmount.label; - const rightBaseAmount = rightMetaAmount.quantity; - const rightBaseLabel = rightBaseAmount.label; - - mustBeSameStructure( - leftBaseLabel, - rightBaseLabel, - 'Cannot subtract different base rights', - ); - const baseAssay = baseLabelToAssayFn(leftBaseLabel); - - return metaAssay.make( - baseAssay.without(leftBaseAmount, rightBaseAmount), - ); - }, - }); - return metaAssay; - } - return harden(makeMetaSingleAssay); +// A uniAssay makes uni amounts, which are either empty or have unique +// descriptions. The quantity must either be null, in which case it is +// empty, or be some truthy comparable value, in which case it +// represents a single unique unit described by that truthy +// quantity. Combining two uni amounts with different truthy +// quantities fails, as they represent non-combinable rights. +function makeUniAssay(label) { + mustBeComparable(label); + + const brand = new WeakSet(); + + const emptyAmount = harden({ label, quantity: null }); + brand.add(emptyAmount); + + const assay = harden({ + getLabel() { + return label; + }, + + make(optDescription) { + if (optDescription === null) { + return emptyAmount; + } + insist(!!optDescription)`\ +Uni description must be either null or truthy ${optDescription}`; + + mustBeComparable(optDescription); + + const amount = harden({ label, quantity: optDescription }); + brand.add(amount); + return amount; + }, + + vouch(amount) { + insist(brand.has(amount))`\ +Unrecognized amount: ${amount}`; + return amount; + }, + + coerce(allegedMetaAmount) { + if (brand.has(allegedMetaAmount)) { + return allegedMetaAmount; + } + const { label: allegedLabel, quantity } = allegedMetaAmount; + mustBeSameStructure(label, allegedLabel, 'Unrecognized label'); + return assay.make(quantity); + }, + + quantity(amount) { + return assay.vouch(amount).quantity; + }, + + empty() { + return emptyAmount; + }, + + isEmpty(amount) { + return assay.quantity(amount) === null; + }, + + includes(leftAmount, rightAmount) { + const leftQuant = assay.quantity(leftAmount); + const rightQuant = assay.quantity(rightAmount); + if (rightQuant === null) { + return true; + } + return sameStructure(leftQuant, rightQuant); + }, + + with(leftAmount, rightAmount) { + const leftQuant = assay.quantity(leftAmount); + const rightQuant = assay.quantity(rightAmount); + if (leftQuant === null) { + return rightAmount; + } + if (rightQuant === null) { + return leftAmount; + } + if (sameStructure(leftQuant, rightQuant)) { + // The "throw" is useless since insist(false) will unconditionally + // throw anyway. Rather, it informs IDEs of this control flow. + throw insist(false)`\ +Even identical non-empty uni amounts cannot be added together ${leftAmount}`; + } else { + // The "throw" is useless since insist(false) will unconditionally + // throw anyway. Rather, it informs IDEs of this control flow. + throw insist(false)`\ +Cannot combine different uni descriptions ${leftAmount} vs ${rightAmount}`; + } + }, + + without(leftAmount, rightAmount) { + const leftQuant = assay.quantity(leftAmount); + const rightQuant = assay.quantity(rightAmount); + if (rightQuant === null) { + return leftAmount; + } + insist(leftQuant !== null)`\ +Empty left does not include ${rightAmount}`; + + mustBeSameStructure( + leftQuant, + rightQuant, + 'Cannot subtract different uni descriptions', + ); + return emptyAmount; + }, + }); + return assay; } -harden(makeMetaSingleAssayMaker); +harden(makeUniAssay); -export { makeNatAssay, makeMetaSingleAssayMaker }; +export { makeNatAssay, makeUniAssay }; diff --git a/demo/contractHost/bootstrap.js b/demo/contractHost/bootstrap.js index 908a4b30cd3..79e0d3f6d93 100644 --- a/demo/contractHost/bootstrap.js +++ b/demo/contractHost/bootstrap.js @@ -86,21 +86,29 @@ function build(E, log) { } const contractSrc = `${trivContract}`; - const fooInviteP = E(E(host).install(contractSrc)).spawn('foo terms'); + const installationP = E(host).install(contractSrc); - return E.resolve(showPaymentBalance('foo', fooInviteP)).then(_ => { - const eightP = E(host).redeem(fooInviteP); + return E(host) + .getInstallationSourceCode(installationP) + .then(src => { + log('Does source ', src, ' match? ', src === contractSrc); - eightP.then(res => { - showPaymentBalance('foo', fooInviteP); - log('++ eightP resolved to ', res, ' (should be 8)'); - if (res !== 8) { - throw new Error(`eightP resolved to ${res}, not 8`); - } - log('++ DONE'); + const fooInviteP = E(installationP).spawn('foo terms'); + + return E.resolve(showPaymentBalance('foo', fooInviteP)).then(_ => { + const eightP = E(host).redeem(fooInviteP); + + eightP.then(res => { + showPaymentBalance('foo', fooInviteP); + log('++ eightP resolved to ', res, ' (should be 8)'); + if (res !== 8) { + throw new Error(`eightP resolved to ${res}, not 8`); + } + log('++ DONE'); + }); + return eightP; + }); }); - return eightP; - }); } function betterContractTestAliceFirst(host, mint, alice, bob) { diff --git a/demo/contractHost/contractHost.js b/demo/contractHost/contractHost.js index cf363d76c4c..0d3a5aa6f3f 100644 --- a/demo/contractHost/contractHost.js +++ b/demo/contractHost/contractHost.js @@ -8,37 +8,37 @@ import { makePrivateName } from '../../collections/PrivateName'; import { allSettled } from '../../collections/allSettled'; import { insist } from '../../collections/insist'; import { allComparable } from '../../collections/sameStructure'; -import { makeNatAssay } from './assays'; -import { makeMetaIssuerController } from './issuers'; +import { makeUniAssay } from './assays'; +import { makeMint } from './issuers'; import makePromise from '../../src/kernel/makePromise'; function makeContractHost(E) { // Maps from seat identity to seats const seats = makePrivateName(); + // from installation to source code string + const installationSources = makePrivateName(); - const controller = makeMetaIssuerController('contract host'); - const metaIssuer = controller.getMetaIssuer(); - const metaAssay = metaIssuer.getAssay(); + const inviteMint = makeMint(makeUniAssay); + const inviteIssuer = inviteMint.getIssuer(); + const inviteAssay = inviteIssuer.getAssay(); function redeem(allegedInvitePayment) { - const allegedMetaAmount = allegedInvitePayment.getXferBalance(); - const metaAmount = metaAssay.vouch(allegedMetaAmount); - insist(!metaAssay.isEmpty(metaAmount))`\ + const allegedInviteAmount = allegedInvitePayment.getXferBalance(); + const inviteAmount = inviteAssay.vouch(allegedInviteAmount); + insist(!inviteAssay.isEmpty(inviteAmount))`\ No invites left`; - const baseAmount = metaAssay.quantity(metaAmount); - const seatIdentity = baseAmount.label.identity; - insist(seats.has(seatIdentity))`\ -Not a registered invite seat identity ${seatIdentity}`; - return E.resolve(metaIssuer.slash(metaAmount, allegedInvitePayment)).then( - _ => seats.get(seatIdentity), - ); + const desc = inviteAssay.quantity(inviteAmount); + const { seatIdentity } = desc; + return E.resolve( + inviteIssuer.slash(inviteAmount, allegedInvitePayment), + ).then(_ => seats.get(seatIdentity)); } // The contract host is designed to have a long-lived credible // identity. const contractHost = harden({ getInviteIssuer() { - return controller.getMetaIssuer(); + return inviteIssuer(); }, // The `contractSrc` is code for a contract function parameterized @@ -70,42 +70,32 @@ Not a registered invite seat identity ${seatIdentity}`; return E.resolve(allComparable(termsP)).then(terms => { const inviteMaker = harden({ // Used by the contract to make invites for credibly - // participating in the contract. The returned invite can be - // redeemed for this seat. The inviteMaker contributes the - // description `{ installation, terms, seatDesc }`. If this - // contract host redeems an invite, then the contractSrc and - // terms are accurate. The seatDesc is according to that + // participating in the contract. The returned invite + // can be redeemed for this seat. The inviteMaker + // contributes the description `{ installation, terms, + // seatIdentity, seatDesc }`. If this contract host + // redeems an invite, then the contractSrc and terms are + // accurate. The seatDesc is according to that // contractSrc code. make(seatDesc, seat, name = 'an invite payment') { - const baseDescription = harden({ + const seatIdentity = harden({}); + const inviteDescription = harden({ installation, terms, + seatIdentity, seatDesc, }); - // Note that an empty object is pass-by-presence, and so - // has an unforgeable identity. - const seatIdentity = harden({}); - const baseLabel = harden({ - identity: seatIdentity, - description: baseDescription, - }); - const baseAssay = makeNatAssay(baseLabel); - const baseAmount = baseAssay.make(1); - controller.register(baseAssay); seats.init(seatIdentity, seat); - const metaOneAmount = metaAssay.make(baseAmount); - // This should be the only use of the meta mint, to make a - // meta purse whose quantity is one unit of a base amount - // for a unique base label. This meta purse makes the - // returned meta payment, and then the empty meta purse is - // dropped, in the sense that it becomes inaccessible. But - // it is not yet collectable. Until the returned payment - // is deposited, it will retain the metaPurse, as the - // metaPurse contains the usage rights. - const metaPurse = controller - .getMetaMint() - .mint(metaOneAmount, name); - return metaPurse.withdrawAll(name); + // This should be the only use of the invite mint, to + // make an invite purse whose quantity describes this + // seat. This invite purse makes the invite payment, + // and then the invite purse is dropped, in the sense that it + // becomes inaccessible. But it is not yet + // collectable. Until the returned invite payment is + // deposited, it will retain the invite purse, as the + // invite purse contains the (uselss in this case) usage rights. + const invitePurse = inviteIssuer.mint(inviteDescription, name); + return invitePurse.withdrawAll(name); }, redeem, }); @@ -113,9 +103,19 @@ Not a registered invite seat identity ${seatIdentity}`; }); }, }); + installationSources.init(installation, contractSrc); return installation; }, + // Verify that this is a genuine installation and show its source + // code. Thus, all genuine installations are transparent if one + // has their contractHost. + getInstallationSourceCode(installationP) { + return E.resolve(installationP).then(installation => + installationSources.get(installation), + ); + }, + // If this is an invite payment made by an inviteMaker of this contract // host, redeem it for the associated seat. Else error. Redeeming // consumes the invite payment and also transfers the use rights. @@ -129,49 +129,15 @@ Not a registered invite seat identity ${seatIdentity}`; } harden(makeContractHost); -function exchangeInviteAmount( - inviteIssuerP, - seatIdentityP, - installationP, - terms, - seatDesc, -) { - const passable = harden({ - label: { - issuer: inviteIssuerP, - description: 'contract host', - }, - quantity: { - label: { - identity: seatIdentityP, - description: { - installation: installationP, - terms, - seatDesc, - }, - }, - quantity: 1, - }, - }); - const comparableP = allComparable(passable); - /* - E.resolve(comparableP).then(comparable => - console.log('\n####\n(', passable, ')\n####\n(', comparable, ')\n####\n'), - ); - */ - return comparableP; -} -harden(exchangeInviteAmount); - function makeCollect(E, log) { function collect(seatP, winPurseP, refundPurseP, name = 'collecting') { const results = harden([ E(seatP) .getWinnings() .then(winnings => E(winPurseP).depositAll(winnings)), - // TODO Bug if replace the comma above with the uncommented out - // ".then(_ => undefined)," below, somehow we end up trying to - // marshal an array with holes, rather than an array with + // TODO Bug if we replace the comma above with the uncommented + // out ".then(_ => undefined)," below, somehow we end up trying + // to marshal an array with holes, rather than an array with // undefined elements. This remains true whether we use // Promise.all or allSettled /* .then(_ => undefined), */ @@ -191,4 +157,4 @@ function makeCollect(E, log) { } harden(makeCollect); -export { makeContractHost, exchangeInviteAmount, makeCollect }; +export { makeContractHost, makeCollect }; diff --git a/demo/contractHost/issuers.js b/demo/contractHost/issuers.js index 0c3c2003aa6..0bf161f80ed 100644 --- a/demo/contractHost/issuers.js +++ b/demo/contractHost/issuers.js @@ -4,8 +4,7 @@ import harden from '@agoric/harden'; import { makePrivateName } from '../../collections/PrivateName'; import { insist } from '../../collections/insist'; -import { makeNatAssay, makeMetaSingleAssayMaker } from './assays'; -import { mustBeSameStructure } from '../../collections/sameStructure'; +import { makeNatAssay } from './assays'; function makeMint(description, makeAssay = makeNatAssay) { insist(description)`\ @@ -183,45 +182,6 @@ Payment expected: ${src}`; } harden(makeMint); -// Makes a meta issuer issuing rights represented by registered base -// assays. -// -// An empty meta purse or meta payment is not specific to a base -// assay. Its balance is the empty meta amount which has a null meta -// quantity. Non-empty ones have a meta amount whose quantity is a -// base amount of some registered base assay, which cannot be combined -// with amounts of other base assays. (This is the "single" -// restriction of makeMetaSingleAssayMaker.) -// -// Base assays should be registered as soon as they are made, so that -// there is no observable state change from not being registered to -// being registered. -function makeMetaIssuerController(description) { - const baseIdentityToAssay = makePrivateName(); - function baseLabelToAssayFn(baseLabel) { - const baseAssay = baseIdentityToAssay.get(baseLabel.identity); - mustBeSameStructure(baseAssay.getLabel(), baseLabel, `Labels don't match`); - return baseAssay; - } - const makeMetaAssay = makeMetaSingleAssayMaker(baseLabelToAssayFn); - const metaMint = makeMint(description, makeMetaAssay); - const metaIssuer = metaMint.getIssuer(); - - const controller = harden({ - getMetaMint() { - return metaMint; - }, - getMetaIssuer() { - return metaIssuer; - }, - register(baseAssay) { - baseIdentityToAssay.init(baseAssay.getLabel().identity, baseAssay); - }, - }); - return controller; -} -harden(makeMetaIssuerController); - // Creates a local issuer that locally represents a remotely issued // currency. Returns a promise for a peg object that asynchonously // converts between the two. The local currency is synchronously @@ -287,4 +247,4 @@ function makePeg(E, remoteIssuerP, makeAssay = makeNatAssay) { } harden(makePeg); -export { makeMint, makeMetaIssuerController, makePeg }; +export { makeMint, makePeg }; diff --git a/demo/contractHost/vat-alice.js b/demo/contractHost/vat-alice.js index 2bf923ec5d6..5ffcc032009 100644 --- a/demo/contractHost/vat-alice.js +++ b/demo/contractHost/vat-alice.js @@ -4,7 +4,7 @@ import harden from '@agoric/harden'; import { insist } from '../../collections/insist'; -import { exchangeInviteAmount, makeCollect } from './contractHost'; +import { makeCollect } from './contractHost'; function makeAlice(E, host, log) { const collect = makeCollect(E, log); @@ -21,6 +21,7 @@ function makeAlice(E, host, log) { let coveredCallInstallationP; let timerP; let inviteIssuerP; + let inviteIssuerLabel; let myMoneyPurseP; let moneyIssuerP; @@ -46,6 +47,10 @@ function makeAlice(E, host, log) { coveredCallInstallationP = coveredCallInst; timerP = E.resolve(timer); inviteIssuerP = E(host).getInviteIssuer(); + inviteIssuerLabel = harden({ + issuer: inviteIssuerP, + description: 'contract host', + }); myMoneyPurseP = E.resolve(myMoneyPurse); moneyIssuerP = E(myMoneyPurseP).getIssuer(); @@ -82,10 +87,10 @@ ERR: alice.acceptInvite called before init()`; showPaymentBalance('alice invite', allegedInvitePaymentP); - const allegedMetaAmountP = E(allegedInvitePaymentP).getXferBalance(); + const allegedInviteAmountP = E(allegedInvitePaymentP).getXferBalance(); - const verifiedInviteP = E.resolve(allegedMetaAmountP).then( - allegedMetaAmount => { + const verifiedInviteP = E.resolve(allegedInviteAmountP).then( + allegedInviteAmount => { const clams10 = harden({ label: { issuer: moneyIssuerP, @@ -101,30 +106,32 @@ ERR: alice.acceptInvite called before init()`; quantity: 7, }); - const metaOneAmountP = exchangeInviteAmount( - inviteIssuerP, - allegedMetaAmount.quantity.label.identity, - escrowExchangeInstallationP, - [clams10, fudco7], - 'left', - ); + const inviteAmount = harden({ + label: inviteIssuerLabel, + quantity: { + installation: escrowExchangeInstallationP, + terms: [clams10, fudco7], + seatIdentity: allegedInviteAmount.quantity.seatIdentity, + seatDesc: 'left', + }, + }); - return E.resolve(metaOneAmountP).then(metaOneAmount => - E(inviteIssuerP).getExclusive( - metaOneAmount, - allegedInvitePaymentP, - 'verified invite', - ), + return E(inviteIssuerP).getExclusive( + inviteAmount, + allegedInvitePaymentP, + 'verified invite', ); }, ); - showPaymentBalance('verified invite', verifiedInviteP); - - const seatP = E(host).redeem(verifiedInviteP); - const moneyPaymentP = E(myMoneyPurseP).withdraw(10); - E(seatP).offer(moneyPaymentP); - return collect(seatP, myStockPurseP, myMoneyPurseP, 'alice escrow'); + return E.resolve( + showPaymentBalance('verified invite', verifiedInviteP), + ).then(_ => { + const seatP = E(host).redeem(verifiedInviteP); + const moneyPaymentP = E(myMoneyPurseP).withdraw(10); + E(seatP).offer(moneyPaymentP); + return collect(seatP, myStockPurseP, myMoneyPurseP, 'alice escrow'); + }); }, acceptOption(allegedInvitePaymentP) { @@ -141,10 +148,10 @@ ERR: alice.acceptOptionDirectly called before init()`; showPaymentBalance('alice invite', allegedInvitePaymentP); - const allegedMetaAmountP = E(allegedInvitePaymentP).getXferBalance(); + const allegedInviteAmountP = E(allegedInvitePaymentP).getXferBalance(); - const verifiedInviteP = E.resolve(allegedMetaAmountP).then( - allegedMetaAmount => { + const verifiedInvitePaymentP = E.resolve(allegedInviteAmountP).then( + allegedInviteAmount => { const smackers10 = harden({ label: { issuer: moneyIssuerP, @@ -160,30 +167,32 @@ ERR: alice.acceptOptionDirectly called before init()`; quantity: 7, }); - const metaOneAmountP = exchangeInviteAmount( - inviteIssuerP, - allegedMetaAmount.quantity.label.identity, - coveredCallInstallationP, - [smackers10, yoyodyne7, timerP, 'singularity'], - 'holder', - ); + const inviteAmount = harden({ + label: inviteIssuerLabel, + quantity: { + installation: coveredCallInstallationP, + terms: [smackers10, yoyodyne7, timerP, 'singularity'], + seatIdentity: allegedInviteAmount.quantity.seatIdentity, + seatDesc: 'holder', + }, + }); - return E.resolve(metaOneAmountP).then(metaOneAmount => - E(inviteIssuerP).getExclusive( - metaOneAmount, - allegedInvitePaymentP, - 'verified invite', - ), + return E(inviteIssuerP).getExclusive( + inviteAmount, + allegedInvitePaymentP, + 'verified invite', ); }, ); - showPaymentBalance('verified invite', verifiedInviteP); - - const seatP = E(host).redeem(verifiedInviteP); - const moneyPaymentP = E(myMoneyPurseP).withdraw(10); - E(seatP).offer(moneyPaymentP); - return collect(seatP, myStockPurseP, myMoneyPurseP, 'alice option'); + return E.resolve( + showPaymentBalance('verified invite', verifiedInvitePaymentP), + ).then(_ => { + const seatP = E(host).redeem(verifiedInvitePaymentP); + const moneyPaymentP = E(myMoneyPurseP).withdraw(10); + E(seatP).offer(moneyPaymentP); + return collect(seatP, myStockPurseP, myMoneyPurseP, 'alice option'); + }); }, acceptOptionForFred(allegedInvitePaymentP) { diff --git a/demo/contractHost/vat-fred.js b/demo/contractHost/vat-fred.js index 2eda8c2daff..e92c84924cc 100644 --- a/demo/contractHost/vat-fred.js +++ b/demo/contractHost/vat-fred.js @@ -5,7 +5,7 @@ import harden from '@agoric/harden'; import { allComparable } from '../../collections/sameStructure'; import { insist } from '../../collections/insist'; -import { exchangeInviteAmount, makeCollect } from './contractHost'; +import { makeCollect } from './contractHost'; function makeFred(E, host, log) { const collect = makeCollect(E, log); @@ -16,6 +16,7 @@ function makeFred(E, host, log) { let coveredCallInstallationP; let timerP; let inviteIssuerP; + let inviteIssuerLabel; let myMoneyPurseP; let moneyIssuerP; @@ -38,6 +39,10 @@ function makeFred(E, host, log) { coveredCallInstallationP = coveredCallInst; timerP = E.resolve(timer); inviteIssuerP = E(host).getInviteIssuer(); + inviteIssuerLabel = harden({ + issuer: inviteIssuerP, + description: 'contract host', + }); myMoneyPurseP = E.resolve(myMoneyPurse); moneyIssuerP = E(myMoneyPurseP).getIssuer(); @@ -55,7 +60,7 @@ function makeFred(E, host, log) { const fred = harden({ init, - acceptOptionOffer(allegedInvitePaymentP) { + acceptOptionOffer(allegedSaleInvitePaymentP) { log('++ fred.acceptOptionOffer starting'); insist(initialized)`\ ERR: fred.acceptOptionOffer called before init()`; @@ -82,52 +87,55 @@ ERR: fred.acceptOptionOffer called before init()`; quantity: 55, }); - const allegedMetaAmountP = E(allegedInvitePaymentP).getXferBalance(); - - const verifiedInviteP = E.resolve(allegedMetaAmountP).then( - allegedMetaAmount => { - const allegedBaseOptionsInviteIssuer = - allegedMetaAmount.quantity.label.description.terms[1]; - - const metaOptionAmountP = exchangeInviteAmount( - inviteIssuerP, - allegedBaseOptionsInviteIssuer.quantity.label.identity, - coveredCallInstallationP, - [dough10, wonka7, timerP, 'singularity'], - 'holder', - ); - - const metaOptionSaleAmountP = exchangeInviteAmount( - inviteIssuerP, - allegedMetaAmount.quantity.label.identity, - escrowExchangeInstallationP, - [fin55, metaOptionAmountP], - 'left', - ); - - return E.resolve(metaOptionSaleAmountP).then(metaOptionSaleAmount => - E(inviteIssuerP).getExclusive( - metaOptionSaleAmount, - allegedInvitePaymentP, - 'verified invite', - ), + const allegedSaleAmountP = E(allegedSaleInvitePaymentP).getXferBalance(); + + const verifiedSaleInvitePaymentP = E.resolve(allegedSaleAmountP).then( + allegedSaleInviteAmount => { + const allegedOptionsInviteAmount = + allegedSaleInviteAmount.quantity.terms[1]; + + const optionsInviteAmount = harden({ + label: inviteIssuerLabel, + quantity: { + installation: coveredCallInstallationP, + terms: [dough10, wonka7, timerP, 'singularity'], + seatIdentity: allegedOptionsInviteAmount.quantity.seatIdentity, + seatDesc: 'holder', + }, + }); + + const saleInviteAmount = harden({ + label: inviteIssuerLabel, + quantity: { + installation: escrowExchangeInstallationP, + terms: [fin55, optionsInviteAmount], + seatIdentity: allegedSaleInviteAmount.quantity.seatIdentity, + seatDesc: 'left', + }, + }); + + return E(inviteIssuerP).getExclusive( + saleInviteAmount, + allegedSaleInvitePaymentP, + 'verified sale invite', ); }, ); - const seatP = E(host).redeem(verifiedInviteP); + + const saleSeatP = E(host).redeem(verifiedSaleInvitePaymentP); const finPaymentP = E(myFinPurseP).withdraw(55); - E(seatP).offer(finPaymentP); + E(saleSeatP).offer(finPaymentP); const optionInvitePurseP = E(inviteIssuerP).makeEmptyPurse(); const gotOptionP = collect( - seatP, + saleSeatP, optionInvitePurseP, myFinPurseP, 'fred buys escrowed option', ); return E.resolve(gotOptionP).then(_ => { // Fred bought the option. Now fred tries to exercise the option. - const optionInvitePayment = E(optionInvitePurseP).withdrawAll(); - const optionSeatP = E(host).redeem(optionInvitePayment); + const optionInvitePaymentP = E(optionInvitePurseP).withdrawAll(); + const optionSeatP = E(host).redeem(optionInvitePaymentP); return E.resolve(allComparable(dough10)).then(d10 => { const doughPaymentP = E(myMoneyPurseP).withdraw(d10); E(optionSeatP).offer(doughPaymentP); diff --git a/test/test-demos.js b/test/test-demos.js index aafe701bb7c..8fc6ed96a41 100644 --- a/test/test-demos.js +++ b/test/test-demos.js @@ -80,6 +80,7 @@ test('run contractHost Demo --mint without SES', async t => { const contractTrivialGolden = [ '=> setup called', 'starting trivialContractTest', + "Does source function trivContract(terms, inviteMaker) {\n return inviteMaker.make('foo', 8);\n } match? true", 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"installation":{},"terms":"foo terms","seatDesc":"foo"}},"quantity":1}}', '++ eightP resolved to 8 (should be 8)', '++ DONE', From d54883a2ec83e94b84eed1b0c921471e4cdc0657 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Thu, 30 May 2019 18:00:49 -0700 Subject: [PATCH 14/18] works again. de-meta-ing assay. much simpler --- demo/contractHost/assays.js | 207 +++++++++++++++--------------- demo/contractHost/contractHost.js | 32 +++-- demo/contractHost/vat-alice.js | 65 ++++++---- demo/contractHost/vat-fred.js | 32 +++-- test/test-demos.js | 12 +- 5 files changed, 191 insertions(+), 157 deletions(-) diff --git a/demo/contractHost/assays.js b/demo/contractHost/assays.js index 0303d2114e1..95b1746a63f 100644 --- a/demo/contractHost/assays.js +++ b/demo/contractHost/assays.js @@ -128,110 +128,117 @@ harden(makeNatAssay); // represents a single unique unit described by that truthy // quantity. Combining two uni amounts with different truthy // quantities fails, as they represent non-combinable rights. -function makeUniAssay(label) { - mustBeComparable(label); - - const brand = new WeakSet(); - - const emptyAmount = harden({ label, quantity: null }); - brand.add(emptyAmount); - - const assay = harden({ - getLabel() { - return label; - }, - - make(optDescription) { - if (optDescription === null) { - return emptyAmount; - } - insist(!!optDescription)`\ -Uni description must be either null or truthy ${optDescription}`; - - mustBeComparable(optDescription); - - const amount = harden({ label, quantity: optDescription }); - brand.add(amount); - return amount; - }, - - vouch(amount) { - insist(brand.has(amount))`\ +function makeUniAssayMaker(descriptionCoercer = d => d) { + function makeUniAssay(label) { + mustBeComparable(label); + + const brand = new WeakSet(); + + const emptyAmount = harden({ label, quantity: null }); + brand.add(emptyAmount); + + const assay = harden({ + getLabel() { + return label; + }, + + make(optDescription) { + if (optDescription === null) { + return emptyAmount; + } + insist(!!optDescription)`\ +Uni optDescription must be either null or truthy ${optDescription}`; + mustBeComparable(optDescription); + + const description = descriptionCoercer(optDescription); + insist(!!description)`\ +Uni description must be truthy ${description}`; + mustBeComparable(description); + + const amount = harden({ label, quantity: description }); + brand.add(amount); + return amount; + }, + + vouch(amount) { + insist(brand.has(amount))`\ Unrecognized amount: ${amount}`; - return amount; - }, - - coerce(allegedMetaAmount) { - if (brand.has(allegedMetaAmount)) { - return allegedMetaAmount; - } - const { label: allegedLabel, quantity } = allegedMetaAmount; - mustBeSameStructure(label, allegedLabel, 'Unrecognized label'); - return assay.make(quantity); - }, - - quantity(amount) { - return assay.vouch(amount).quantity; - }, - - empty() { - return emptyAmount; - }, - - isEmpty(amount) { - return assay.quantity(amount) === null; - }, - - includes(leftAmount, rightAmount) { - const leftQuant = assay.quantity(leftAmount); - const rightQuant = assay.quantity(rightAmount); - if (rightQuant === null) { - return true; - } - return sameStructure(leftQuant, rightQuant); - }, - - with(leftAmount, rightAmount) { - const leftQuant = assay.quantity(leftAmount); - const rightQuant = assay.quantity(rightAmount); - if (leftQuant === null) { - return rightAmount; - } - if (rightQuant === null) { - return leftAmount; - } - if (sameStructure(leftQuant, rightQuant)) { - // The "throw" is useless since insist(false) will unconditionally - // throw anyway. Rather, it informs IDEs of this control flow. - throw insist(false)`\ + return amount; + }, + + coerce(allegedMetaAmount) { + if (brand.has(allegedMetaAmount)) { + return allegedMetaAmount; + } + const { label: allegedLabel, quantity } = allegedMetaAmount; + mustBeSameStructure(label, allegedLabel, 'Unrecognized label'); + return assay.make(quantity); + }, + + quantity(amount) { + return assay.vouch(amount).quantity; + }, + + empty() { + return emptyAmount; + }, + + isEmpty(amount) { + return assay.quantity(amount) === null; + }, + + includes(leftAmount, rightAmount) { + const leftQuant = assay.quantity(leftAmount); + const rightQuant = assay.quantity(rightAmount); + if (rightQuant === null) { + return true; + } + return sameStructure(leftQuant, rightQuant); + }, + + with(leftAmount, rightAmount) { + const leftQuant = assay.quantity(leftAmount); + const rightQuant = assay.quantity(rightAmount); + if (leftQuant === null) { + return rightAmount; + } + if (rightQuant === null) { + return leftAmount; + } + if (sameStructure(leftQuant, rightQuant)) { + // The "throw" is useless since insist(false) will unconditionally + // throw anyway. Rather, it informs IDEs of this control flow. + throw insist(false)`\ Even identical non-empty uni amounts cannot be added together ${leftAmount}`; - } else { - // The "throw" is useless since insist(false) will unconditionally - // throw anyway. Rather, it informs IDEs of this control flow. - throw insist(false)`\ + } else { + // The "throw" is useless since insist(false) will unconditionally + // throw anyway. Rather, it informs IDEs of this control flow. + throw insist(false)`\ Cannot combine different uni descriptions ${leftAmount} vs ${rightAmount}`; - } - }, - - without(leftAmount, rightAmount) { - const leftQuant = assay.quantity(leftAmount); - const rightQuant = assay.quantity(rightAmount); - if (rightQuant === null) { - return leftAmount; - } - insist(leftQuant !== null)`\ + } + }, + + without(leftAmount, rightAmount) { + const leftQuant = assay.quantity(leftAmount); + const rightQuant = assay.quantity(rightAmount); + if (rightQuant === null) { + return leftAmount; + } + insist(leftQuant !== null)`\ Empty left does not include ${rightAmount}`; - mustBeSameStructure( - leftQuant, - rightQuant, - 'Cannot subtract different uni descriptions', - ); - return emptyAmount; - }, - }); - return assay; + mustBeSameStructure( + leftQuant, + rightQuant, + 'Cannot subtract different uni descriptions', + ); + return emptyAmount; + }, + }); + return assay; + } + return harden(makeUniAssay); } -harden(makeUniAssay); +harden(makeUniAssayMaker); -export { makeNatAssay, makeUniAssay }; +export { makeNatAssay, makeUniAssayMaker }; diff --git a/demo/contractHost/contractHost.js b/demo/contractHost/contractHost.js index 0d3a5aa6f3f..dfc8948a9b5 100644 --- a/demo/contractHost/contractHost.js +++ b/demo/contractHost/contractHost.js @@ -7,18 +7,29 @@ import evaluate from '@agoric/evaluate'; import { makePrivateName } from '../../collections/PrivateName'; import { allSettled } from '../../collections/allSettled'; import { insist } from '../../collections/insist'; -import { allComparable } from '../../collections/sameStructure'; -import { makeUniAssay } from './assays'; +import { + mustBeSameStructure, + allComparable, +} from '../../collections/sameStructure'; +import { makeUniAssayMaker } from './assays'; import { makeMint } from './issuers'; import makePromise from '../../src/kernel/makePromise'; function makeContractHost(E) { // Maps from seat identity to seats const seats = makePrivateName(); + // from seat identity to invite description. + const seatDescriptions = makePrivateName(); // from installation to source code string const installationSources = makePrivateName(); - const inviteMint = makeMint(makeUniAssay); + function descriptionCoercer(allegedDescription) { + const seatDesc = seatDescriptions.get(allegedDescription.seatIdentity); + mustBeSameStructure(seatDesc, allegedDescription); + return seatDesc; + } + const makeUniAssay = makeUniAssayMaker(descriptionCoercer); + const inviteMint = makeMint('contract host', makeUniAssay); const inviteIssuer = inviteMint.getIssuer(); const inviteAssay = inviteIssuer.getAssay(); @@ -38,7 +49,7 @@ No invites left`; // identity. const contractHost = harden({ getInviteIssuer() { - return inviteIssuer(); + return inviteIssuer; }, // The `contractSrc` is code for a contract function parameterized @@ -79,22 +90,25 @@ No invites left`; // contractSrc code. make(seatDesc, seat, name = 'an invite payment') { const seatIdentity = harden({}); - const inviteDescription = harden({ + const seatDescription = harden({ installation, terms, seatIdentity, seatDesc, }); seats.init(seatIdentity, seat); + seatDescriptions.init(seatIdentity, seatDescription); + const inviteAmount = inviteAssay.make(seatDescription); // This should be the only use of the invite mint, to // make an invite purse whose quantity describes this // seat. This invite purse makes the invite payment, - // and then the invite purse is dropped, in the sense that it - // becomes inaccessible. But it is not yet + // and then the invite purse is dropped, in the sense + // that it becomes inaccessible. But it is not yet // collectable. Until the returned invite payment is // deposited, it will retain the invite purse, as the - // invite purse contains the (uselss in this case) usage rights. - const invitePurse = inviteIssuer.mint(inviteDescription, name); + // invite purse contains the (uselss in this case) + // usage rights. + const invitePurse = inviteMint.mint(inviteAmount, name); return invitePurse.withdrawAll(name); }, redeem, diff --git a/demo/contractHost/vat-alice.js b/demo/contractHost/vat-alice.js index 5ffcc032009..f6c640da9ea 100644 --- a/demo/contractHost/vat-alice.js +++ b/demo/contractHost/vat-alice.js @@ -4,6 +4,7 @@ import harden from '@agoric/harden'; import { insist } from '../../collections/insist'; +import { allComparable } from '../../collections/sameStructure'; import { makeCollect } from './contractHost'; function makeAlice(E, host, log) { @@ -106,21 +107,25 @@ ERR: alice.acceptInvite called before init()`; quantity: 7, }); - const inviteAmount = harden({ - label: inviteIssuerLabel, - quantity: { - installation: escrowExchangeInstallationP, - terms: [clams10, fudco7], - seatIdentity: allegedInviteAmount.quantity.seatIdentity, - seatDesc: 'left', - }, - }); - - return E(inviteIssuerP).getExclusive( - inviteAmount, - allegedInvitePaymentP, - 'verified invite', + const inviteAmountP = allComparable( + harden({ + label: inviteIssuerLabel, + quantity: { + installation: escrowExchangeInstallationP, + terms: [clams10, fudco7], + seatIdentity: allegedInviteAmount.quantity.seatIdentity, + seatDesc: 'left', + }, + }), ); + + return E.resolve(inviteAmountP).then(inviteAmount => { + return E(inviteIssuerP).getExclusive( + inviteAmount, + allegedInvitePaymentP, + 'verified invite', + ); + }); }, ); @@ -167,21 +172,25 @@ ERR: alice.acceptOptionDirectly called before init()`; quantity: 7, }); - const inviteAmount = harden({ - label: inviteIssuerLabel, - quantity: { - installation: coveredCallInstallationP, - terms: [smackers10, yoyodyne7, timerP, 'singularity'], - seatIdentity: allegedInviteAmount.quantity.seatIdentity, - seatDesc: 'holder', - }, - }); - - return E(inviteIssuerP).getExclusive( - inviteAmount, - allegedInvitePaymentP, - 'verified invite', + const inviteAmountP = allComparable( + harden({ + label: inviteIssuerLabel, + quantity: { + installation: coveredCallInstallationP, + terms: [smackers10, yoyodyne7, timerP, 'singularity'], + seatIdentity: allegedInviteAmount.quantity.seatIdentity, + seatDesc: 'holder', + }, + }), ); + + return E.resolve(inviteAmountP).then(inviteAmount => { + return E(inviteIssuerP).getExclusive( + inviteAmount, + allegedInvitePaymentP, + 'verified invite', + ); + }); }, ); diff --git a/demo/contractHost/vat-fred.js b/demo/contractHost/vat-fred.js index e92c84924cc..ec3cb5737be 100644 --- a/demo/contractHost/vat-fred.js +++ b/demo/contractHost/vat-fred.js @@ -104,21 +104,25 @@ ERR: fred.acceptOptionOffer called before init()`; }, }); - const saleInviteAmount = harden({ - label: inviteIssuerLabel, - quantity: { - installation: escrowExchangeInstallationP, - terms: [fin55, optionsInviteAmount], - seatIdentity: allegedSaleInviteAmount.quantity.seatIdentity, - seatDesc: 'left', - }, - }); - - return E(inviteIssuerP).getExclusive( - saleInviteAmount, - allegedSaleInvitePaymentP, - 'verified sale invite', + const saleInviteAmountP = allComparable( + harden({ + label: inviteIssuerLabel, + quantity: { + installation: escrowExchangeInstallationP, + terms: [fin55, optionsInviteAmount], + seatIdentity: allegedSaleInviteAmount.quantity.seatIdentity, + seatDesc: 'left', + }, + }), ); + + return E.resolve(saleInviteAmountP).then(saleInviteAmount => { + return E(inviteIssuerP).getExclusive( + saleInviteAmount, + allegedSaleInvitePaymentP, + 'verified sale invite', + ); + }); }, ); diff --git a/test/test-demos.js b/test/test-demos.js index 8fc6ed96a41..72f156479ec 100644 --- a/test/test-demos.js +++ b/test/test-demos.js @@ -81,7 +81,7 @@ const contractTrivialGolden = [ '=> setup called', 'starting trivialContractTest', "Does source function trivContract(terms, inviteMaker) {\n return inviteMaker.make('foo', 8);\n } match? true", - 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"installation":{},"terms":"foo terms","seatDesc":"foo"}},"quantity":1}}', + 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"installation":{},"terms":"foo terms","seatIdentity":{},"seatDesc":"foo"}}', '++ eightP resolved to 8 (should be 8)', '++ DONE', 'foo xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":null}', @@ -122,8 +122,8 @@ const contractBobFirstGolden = [ '=> setup called', '++ bob.tradeWell starting', '++ alice.acceptInvite starting', - 'alice invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"installation":{},"terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":"left"}},"quantity":1}}', - 'verified invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"installation":{},"terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatDesc":"left"}},"quantity":1}}', + 'alice invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"installation":{},"terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatIdentity":{},"seatDesc":"left"}}', + 'verified invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"installation":{},"terms":[{"label":{"issuer":{},"description":"clams"},"quantity":10},{"label":{"issuer":{},"description":"fudco"},"quantity":7}],"seatIdentity":{},"seatDesc":"left"}}', 'bob escrow wins: {"label":{"issuer":{},"description":"clams"},"quantity":10} refs: null', 'alice escrow wins: {"label":{"issuer":{},"description":"fudco"},"quantity":7} refs: null', '++ bob.tradeWell done', @@ -156,8 +156,8 @@ const contractCoveredCallGolden = [ '++ bob.offerAliceOption starting', '++ alice.acceptOptionDirectly starting', 'Pretend singularity never happens', - 'alice invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"installation":{},"terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":"holder"}},"quantity":1}}', - 'verified invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"installation":{},"terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatDesc":"holder"}},"quantity":1}}', + 'alice invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"installation":{},"terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatIdentity":{},"seatDesc":"holder"}}', + 'verified invite xfer balance {"label":{"issuer":{},"description":"contract host"},"quantity":{"installation":{},"terms":[{"label":{"issuer":{},"description":"smackers"},"quantity":10},{"label":{"issuer":{},"description":"yoyodyne"},"quantity":7},{},"singularity"],"seatIdentity":{},"seatDesc":"holder"}}', 'alice option wins: {"label":{"issuer":{},"description":"yoyodyne"},"quantity":7} refs: null', 'bob option wins: {"label":{"issuer":{},"description":"smackers"},"quantity":10} refs: null', '++ bob.offerAliceOption done', @@ -193,7 +193,7 @@ const contractCoveredCallSaleGolden = [ '++ fred.acceptOptionOffer starting', 'Pretend singularity never happens', 'alice options sale wins: {"label":{"issuer":{},"description":"fins"},"quantity":55} refs: null', - 'fred buys escrowed option wins: {"label":{"issuer":{},"description":"contract host"},"quantity":{"label":{"identity":{},"description":{"installation":{},"terms":[{"label":{"issuer":{},"description":"dough"},"quantity":10},{"label":{"issuer":{},"description":"wonka"},"quantity":7},{},"singularity"],"seatDesc":"holder"}},"quantity":1}} refs: null', + 'fred buys escrowed option wins: {"label":{"issuer":{},"description":"contract host"},"quantity":{"installation":{},"terms":[{"label":{"issuer":{},"description":"dough"},"quantity":10},{"label":{"issuer":{},"description":"wonka"},"quantity":7},{},"singularity"],"seatIdentity":{},"seatDesc":"holder"}} refs: null', 'fred exercises option, buying stock wins: {"label":{"issuer":{},"description":"wonka"},"quantity":7} refs: null', 'bob option wins: {"label":{"issuer":{},"description":"dough"},"quantity":10} refs: null', '++ alice.acceptOptionForFred done', From 9304e321422c77aec4f8b9bae227a63d1ca0d7e0 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Thu, 30 May 2019 19:13:48 -0700 Subject: [PATCH 15/18] use the scope, luke --- demo/contractHost/bootstrap.js | 77 ++++--- demo/contractHost/vat-alice.js | 390 +++++++++++++++------------------ demo/contractHost/vat-bob.js | 217 ++++++++---------- demo/contractHost/vat-fred.js | 236 +++++++++----------- 4 files changed, 410 insertions(+), 510 deletions(-) diff --git a/demo/contractHost/bootstrap.js b/demo/contractHost/bootstrap.js index 79e0d3f6d93..1858e1a962f 100644 --- a/demo/contractHost/bootstrap.js +++ b/demo/contractHost/bootstrap.js @@ -6,11 +6,17 @@ import { escrowExchangeSrc } from './escrow'; import { coveredCallSrc } from './coveredCall'; function build(E, log) { + // TODO BUG: All callers should wait until settled before doing + // anything that would change the balance before show*Balance* reads + // it. function showPaymentBalance(name, paymentP) { return E(paymentP) .getXferBalance() .then(amount => log(name, ' xfer balance ', amount)); } + // TODO BUG: All callers should wait until settled before doing + // anything that would change the balance before show*Balance* reads + // it. function showPurseBalances(name, purseP) { return Promise.all([ E(purseP) @@ -111,7 +117,7 @@ function build(E, log) { }); } - function betterContractTestAliceFirst(host, mint, alice, bob) { + function betterContractTestAliceFirst(host, mint, aliceI, bobI) { const escrowExchangeInstallationP = E(host).install(escrowExchangeSrc); const coveredCallInstallationP = E(host).install(coveredCallSrc); @@ -123,14 +129,14 @@ function build(E, log) { const aliceStockPurseP = E(stockMintP).mint(2002); const bobStockPurseP = E(stockMintP).mint(2003); - const aliceP = E(alice).init( + const aliceP = E(aliceI).init( escrowExchangeInstallationP, coveredCallInstallationP, fakeNeverTimer, aliceMoneyPurseP, aliceStockPurseP, ); - const bobP = E(bob).init( + const bobP = E(bobI).init( escrowExchangeInstallationP, coveredCallInstallationP, fakeNeverTimer, @@ -138,7 +144,7 @@ function build(E, log) { bobStockPurseP, ); return Promise.all([aliceP, bobP]).then(_ => { - const ifItFitsP = E(aliceP).payBobWell(bob); + const ifItFitsP = E(aliceP).payBobWell(bobP); ifItFitsP.then( res => { log('++ ifItFitsP done:', res); @@ -150,7 +156,7 @@ function build(E, log) { }); } - function betterContractTestBobFirst(host, mint, alice, bob) { + function betterContractTestBobFirst(host, mint, aliceI, bobI) { const escrowExchangeInstallationP = E(host).install(escrowExchangeSrc); const coveredCallInstallationP = E(host).install(coveredCallSrc); @@ -162,14 +168,14 @@ function build(E, log) { const aliceStockPurseP = E(stockMintP).mint(2002, 'aliceMainStock'); const bobStockPurseP = E(stockMintP).mint(2003, 'bobMainStock'); - const aliceP = E(alice).init( + const aliceP = E(aliceI).init( escrowExchangeInstallationP, coveredCallInstallationP, fakeNeverTimer, aliceMoneyPurseP, aliceStockPurseP, ); - const bobP = E(bob).init( + const bobP = E(bobI).init( escrowExchangeInstallationP, coveredCallInstallationP, fakeNeverTimer, @@ -195,7 +201,7 @@ function build(E, log) { }); } - function coveredCallTest(host, mint, alice, bob) { + function coveredCallTest(host, mint, aliceI, bobI) { const escrowExchangeInstallationP = E(host).install(escrowExchangeSrc); const coveredCallInstallationP = E(host).install(coveredCallSrc); @@ -207,14 +213,14 @@ function build(E, log) { const aliceStockPurseP = E(stockMintP).mint(2002, 'aliceMainStock'); const bobStockPurseP = E(stockMintP).mint(2003, 'bobMainStock'); - const aliceP = E(alice).init( + const aliceP = E(aliceI).init( escrowExchangeInstallationP, coveredCallInstallationP, fakeNeverTimer, aliceMoneyPurseP, aliceStockPurseP, ); - const bobP = E(bob).init( + const bobP = E(bobI).init( escrowExchangeInstallationP, coveredCallInstallationP, fakeNeverTimer, @@ -240,7 +246,7 @@ function build(E, log) { }); } - function coveredCallSaleTest(host, mint, alice, bob, fred) { + function coveredCallSaleTest(host, mint, aliceI, bobI, fredI) { const escrowExchangeInstallationP = E(host).install(escrowExchangeSrc); const coveredCallInstallationP = E(host).install(coveredCallSrc); @@ -258,24 +264,14 @@ function build(E, log) { const aliceFinPurseP = E(finMintP).mint(3000, 'aliceFins'); const fredFinPurseP = E(finMintP).mint(3001, 'fredFins'); - const aliceP = E(alice).init( - escrowExchangeInstallationP, - coveredCallInstallationP, - fakeNeverTimer, - aliceDoughPurseP, - aliceStockPurseP, - aliceFinPurseP, - fred, - ); - const bobP = E(bob).init( + const bobP = E(bobI).init( escrowExchangeInstallationP, coveredCallInstallationP, fakeNeverTimer, bobDoughPurseP, bobStockPurseP, ); - /* eslint-disable-next-line no-unused-vars */ - const fredP = E(fred).init( + const fredP = E(fredI).init( escrowExchangeInstallationP, coveredCallInstallationP, fakeNeverTimer, @@ -283,6 +279,15 @@ function build(E, log) { fredStockPurseP, fredFinPurseP, ); + const aliceP = E(aliceI).init( + escrowExchangeInstallationP, + coveredCallInstallationP, + fakeNeverTimer, + aliceDoughPurseP, + aliceStockPurseP, + aliceFinPurseP, + fredP, + ); return Promise.all([aliceP, bobP, fredP]).then(_ => { E(bobP) .offerAliceOption(aliceP) @@ -322,28 +327,28 @@ function build(E, log) { } case 'alice-first': { const host = await E(vats.host).makeHost(); - const alice = await E(vats.alice).makeAlice(host); - const bob = await E(vats.bob).makeBob(host); - return betterContractTestAliceFirst(host, vats.mint, alice, bob); + const aliceI = await E(vats.alice).makeAlice(host); + const bobI = await E(vats.bob).makeBob(host); + return betterContractTestAliceFirst(host, vats.mint, aliceI, bobI); } case 'bob-first': { const host = await E(vats.host).makeHost(); - const alice = await E(vats.alice).makeAlice(host); - const bob = await E(vats.bob).makeBob(host); - return betterContractTestBobFirst(host, vats.mint, alice, bob); + const aliceI = await E(vats.alice).makeAlice(host); + const bobI = await E(vats.bob).makeBob(host); + return betterContractTestBobFirst(host, vats.mint, aliceI, bobI); } case 'covered-call': { const host = await E(vats.host).makeHost(); - const alice = await E(vats.alice).makeAlice(host); - const bob = await E(vats.bob).makeBob(host); - return coveredCallTest(host, vats.mint, alice, bob); + const aliceI = await E(vats.alice).makeAlice(host); + const bobI = await E(vats.bob).makeBob(host); + return coveredCallTest(host, vats.mint, aliceI, bobI); } case 'covered-call-sale': { const host = await E(vats.host).makeHost(); - const alice = await E(vats.alice).makeAlice(host); - const bob = await E(vats.bob).makeBob(host); - const fred = await E(vats.fred).makeFred(host); - return coveredCallSaleTest(host, vats.mint, alice, bob, fred); + const aliceI = await E(vats.alice).makeAlice(host); + const bobI = await E(vats.bob).makeBob(host); + const fredI = await E(vats.fred).makeFred(host); + return coveredCallSaleTest(host, vats.mint, aliceI, bobI, fredI); } default: { throw new Error(`unrecognized argument value ${argv[0]}`); diff --git a/demo/contractHost/vat-alice.js b/demo/contractHost/vat-alice.js index f6c640da9ea..2562c70f7de 100644 --- a/demo/contractHost/vat-alice.js +++ b/demo/contractHost/vat-alice.js @@ -3,251 +3,209 @@ import harden from '@agoric/harden'; -import { insist } from '../../collections/insist'; import { allComparable } from '../../collections/sameStructure'; import { makeCollect } from './contractHost'; function makeAlice(E, host, log) { const collect = makeCollect(E, log); + // TODO BUG: All callers should wait until settled before doing + // anything that would change the balance before show*Balance* reads + // it. function showPaymentBalance(name, paymentP) { return E(paymentP) .getXferBalance() .then(amount => log(name, ' xfer balance ', amount)); } - let initialized = false; - - let escrowExchangeInstallationP; - let coveredCallInstallationP; - let timerP; - let inviteIssuerP; - let inviteIssuerLabel; - - let myMoneyPurseP; - let moneyIssuerP; - - let myStockPurseP; - let stockIssuerP; - - let myOptFinPurseP; - let optFinIssuerP; - - let optFredP; - - function init( - escrowExchangeInst, - coveredCallInst, - timer, - myMoneyPurse, - myStockPurse, - myOptFinPurse = undefined, - optFred = undefined, - ) { - escrowExchangeInstallationP = escrowExchangeInst; - coveredCallInstallationP = coveredCallInst; - timerP = E.resolve(timer); - inviteIssuerP = E(host).getInviteIssuer(); - inviteIssuerLabel = harden({ - issuer: inviteIssuerP, - description: 'contract host', - }); - - myMoneyPurseP = E.resolve(myMoneyPurse); - moneyIssuerP = E(myMoneyPurseP).getIssuer(); - - myStockPurseP = E.resolve(myStockPurse); - stockIssuerP = E(myStockPurseP).getIssuer(); - - if (myOptFinPurse) { - myOptFinPurseP = E.resolve(myOptFinPurse); - optFinIssuerP = E(myOptFinPurseP).getIssuer(); - } - optFredP = optFred; - - initialized = true; - // eslint-disable-next-line no-use-before-define - return alice; // alice and init use each other - } - - const alice = harden({ - init, - payBobWell(bob) { - log('++ alice.payBobWell starting'); - insist(initialized)`\ -ERR: alice.payBobWell called before init()`; - - const paymentP = E(myMoneyPurseP).withdraw(10); - return E(bob).buy('shoe', paymentP); - }, - - acceptInvite(allegedInvitePaymentP) { - log('++ alice.acceptInvite starting'); - insist(initialized)`\ -ERR: alice.acceptInvite called before init()`; - - showPaymentBalance('alice invite', allegedInvitePaymentP); - - const allegedInviteAmountP = E(allegedInvitePaymentP).getXferBalance(); + return harden({ + init( + escrowExchangeInstallationP, + coveredCallInstallationP, + timerP, + myMoneyPurseP, + myStockPurseP, + myOptFinPurseP = undefined, + optFredP = undefined, + ) { + const inviteIssuerP = E(host).getInviteIssuer(); + const inviteIssuerLabel = harden({ + issuer: inviteIssuerP, + description: 'contract host', + }); + const moneyIssuerP = E(myMoneyPurseP).getIssuer(); + const stockIssuerP = E(myStockPurseP).getIssuer(); + const optFinIssuerP = myOptFinPurseP && E(myOptFinPurseP).getIssuer(); + + const alice = harden({ + payBobWell(bob) { + log('++ alice.payBobWell starting'); + const paymentP = E(myMoneyPurseP).withdraw(10); + return E(bob).buy('shoe', paymentP); + }, - const verifiedInviteP = E.resolve(allegedInviteAmountP).then( - allegedInviteAmount => { - const clams10 = harden({ - label: { - issuer: moneyIssuerP, - description: 'clams', + acceptInvite(allegedInvitePaymentP) { + log('++ alice.acceptInvite starting'); + showPaymentBalance('alice invite', allegedInvitePaymentP); + + const allegedInviteAmountP = E( + allegedInvitePaymentP, + ).getXferBalance(); + + const verifiedInviteP = E.resolve(allegedInviteAmountP).then( + allegedInviteAmount => { + const clams10 = harden({ + label: { + issuer: moneyIssuerP, + description: 'clams', + }, + quantity: 10, + }); + const fudco7 = harden({ + label: { + issuer: stockIssuerP, + description: 'fudco', + }, + quantity: 7, + }); + + const inviteAmountP = allComparable( + harden({ + label: inviteIssuerLabel, + quantity: { + installation: escrowExchangeInstallationP, + terms: [clams10, fudco7], + seatIdentity: allegedInviteAmount.quantity.seatIdentity, + seatDesc: 'left', + }, + }), + ); + + return E.resolve(inviteAmountP).then(inviteAmount => { + return E(inviteIssuerP).getExclusive( + inviteAmount, + allegedInvitePaymentP, + 'verified invite', + ); + }); }, - quantity: 10, - }); - const fudco7 = harden({ - label: { - issuer: stockIssuerP, - description: 'fudco', - }, - quantity: 7, - }); - - const inviteAmountP = allComparable( - harden({ - label: inviteIssuerLabel, - quantity: { - installation: escrowExchangeInstallationP, - terms: [clams10, fudco7], - seatIdentity: allegedInviteAmount.quantity.seatIdentity, - seatDesc: 'left', - }, - }), ); - return E.resolve(inviteAmountP).then(inviteAmount => { - return E(inviteIssuerP).getExclusive( - inviteAmount, - allegedInvitePaymentP, - 'verified invite', - ); + return E.resolve( + showPaymentBalance('verified invite', verifiedInviteP), + ).then(_ => { + const seatP = E(host).redeem(verifiedInviteP); + const moneyPaymentP = E(myMoneyPurseP).withdraw(10); + E(seatP).offer(moneyPaymentP); + return collect(seatP, myStockPurseP, myMoneyPurseP, 'alice escrow'); }); }, - ); - - return E.resolve( - showPaymentBalance('verified invite', verifiedInviteP), - ).then(_ => { - const seatP = E(host).redeem(verifiedInviteP); - const moneyPaymentP = E(myMoneyPurseP).withdraw(10); - E(seatP).offer(moneyPaymentP); - return collect(seatP, myStockPurseP, myMoneyPurseP, 'alice escrow'); - }); - }, - - acceptOption(allegedInvitePaymentP) { - if (optFredP) { - return alice.acceptOptionForFred(allegedInvitePaymentP); - } - return alice.acceptOptionDirectly(allegedInvitePaymentP); - }, - acceptOptionDirectly(allegedInvitePaymentP) { - log('++ alice.acceptOptionDirectly starting'); - insist(initialized)`\ -ERR: alice.acceptOptionDirectly called before init()`; - - showPaymentBalance('alice invite', allegedInvitePaymentP); - - const allegedInviteAmountP = E(allegedInvitePaymentP).getXferBalance(); + acceptOption(allegedInvitePaymentP) { + if (optFredP) { + return alice.acceptOptionForFred(allegedInvitePaymentP); + } + return alice.acceptOptionDirectly(allegedInvitePaymentP); + }, - const verifiedInvitePaymentP = E.resolve(allegedInviteAmountP).then( - allegedInviteAmount => { - const smackers10 = harden({ - label: { - issuer: moneyIssuerP, - description: 'smackers', + acceptOptionDirectly(allegedInvitePaymentP) { + log('++ alice.acceptOptionDirectly starting'); + showPaymentBalance('alice invite', allegedInvitePaymentP); + + const allegedInviteAmountP = E( + allegedInvitePaymentP, + ).getXferBalance(); + + const verifiedInvitePaymentP = E.resolve(allegedInviteAmountP).then( + allegedInviteAmount => { + const smackers10 = harden({ + label: { + issuer: moneyIssuerP, + description: 'smackers', + }, + quantity: 10, + }); + const yoyodyne7 = harden({ + label: { + issuer: stockIssuerP, + description: 'yoyodyne', + }, + quantity: 7, + }); + + const inviteAmountP = allComparable( + harden({ + label: inviteIssuerLabel, + quantity: { + installation: coveredCallInstallationP, + terms: [smackers10, yoyodyne7, timerP, 'singularity'], + seatIdentity: allegedInviteAmount.quantity.seatIdentity, + seatDesc: 'holder', + }, + }), + ); + + return E.resolve(inviteAmountP).then(inviteAmount => { + return E(inviteIssuerP).getExclusive( + inviteAmount, + allegedInvitePaymentP, + 'verified invite', + ); + }); }, - quantity: 10, - }); - const yoyodyne7 = harden({ - label: { - issuer: stockIssuerP, - description: 'yoyodyne', - }, - quantity: 7, - }); - - const inviteAmountP = allComparable( - harden({ - label: inviteIssuerLabel, - quantity: { - installation: coveredCallInstallationP, - terms: [smackers10, yoyodyne7, timerP, 'singularity'], - seatIdentity: allegedInviteAmount.quantity.seatIdentity, - seatDesc: 'holder', - }, - }), ); - return E.resolve(inviteAmountP).then(inviteAmount => { - return E(inviteIssuerP).getExclusive( - inviteAmount, - allegedInvitePaymentP, - 'verified invite', - ); + return E.resolve( + showPaymentBalance('verified invite', verifiedInvitePaymentP), + ).then(_ => { + const seatP = E(host).redeem(verifiedInvitePaymentP); + const moneyPaymentP = E(myMoneyPurseP).withdraw(10); + E(seatP).offer(moneyPaymentP); + return collect(seatP, myStockPurseP, myMoneyPurseP, 'alice option'); }); }, - ); - return E.resolve( - showPaymentBalance('verified invite', verifiedInvitePaymentP), - ).then(_ => { - const seatP = E(host).redeem(verifiedInvitePaymentP); - const moneyPaymentP = E(myMoneyPurseP).withdraw(10); - E(seatP).offer(moneyPaymentP); - return collect(seatP, myStockPurseP, myMoneyPurseP, 'alice option'); - }); - }, - - acceptOptionForFred(allegedInvitePaymentP) { - log('++ alice.acceptOptionForFred starting'); - insist(initialized)`\ -ERR: alice.acceptOptionForFred called before init()`; - - const finNeededP = E(E(optFinIssuerP).getAssay()).make(55); - const inviteNeededP = E(allegedInvitePaymentP).getXferBalance(); - - const termsP = harden([finNeededP, inviteNeededP]); - // const invitesP = E(E(host).install(escrowExchangeSrc)).spawn(termsP); - const invitesP = E(escrowExchangeInstallationP).spawn(termsP); - const fredInviteP = invitesP.then(invites => invites[0]); - const aliceForFredInviteP = invitesP.then(invites => invites[1]); - const doneP = Promise.all([ - E(optFredP).acceptOptionOffer(fredInviteP), - E(alice).completeOptionsSale( - aliceForFredInviteP, - allegedInvitePaymentP, - ), - ]); - doneP.then( - _res => log('++ alice.acceptOptionForFred done'), - rej => log('++ alice.acceptOptionForFred reject: ', rej), - ); - return doneP; - }, - - completeOptionsSale(aliceForFredInviteP, allegedInvitePaymentP) { - log('++ alice.completeOptionsSale starting'); - insist(initialized)`\ -ERR: alice.completeOptionsSale called before init()`; + acceptOptionForFred(allegedInvitePaymentP) { + log('++ alice.acceptOptionForFred starting'); + const finNeededP = E(E(optFinIssuerP).getAssay()).make(55); + const inviteNeededP = E(allegedInvitePaymentP).getXferBalance(); + + const termsP = harden([finNeededP, inviteNeededP]); + // const invitesP = E(E(host).install(escrowExchangeSrc)).spawn(termsP); + const invitesP = E(escrowExchangeInstallationP).spawn(termsP); + const fredInviteP = invitesP.then(invites => invites[0]); + const aliceForFredInviteP = invitesP.then(invites => invites[1]); + const doneP = Promise.all([ + E(optFredP).acceptOptionOffer(fredInviteP), + E(alice).completeOptionsSale( + aliceForFredInviteP, + allegedInvitePaymentP, + ), + ]); + doneP.then( + _res => log('++ alice.acceptOptionForFred done'), + rej => log('++ alice.acceptOptionForFred reject: ', rej), + ); + return doneP; + }, - const aliceForFredSeatP = E(host).redeem(aliceForFredInviteP); - E(aliceForFredSeatP).offer(allegedInvitePaymentP); - const myInvitePurseP = E(inviteIssuerP).makeEmptyPurse(); - return collect( - aliceForFredSeatP, - myOptFinPurseP, - myInvitePurseP, - 'alice options sale', - ); + completeOptionsSale(aliceForFredInviteP, allegedInvitePaymentP) { + log('++ alice.completeOptionsSale starting'); + const aliceForFredSeatP = E(host).redeem(aliceForFredInviteP); + + E(aliceForFredSeatP).offer(allegedInvitePaymentP); + const myInvitePurseP = E(inviteIssuerP).makeEmptyPurse(); + return collect( + aliceForFredSeatP, + myOptFinPurseP, + myInvitePurseP, + 'alice options sale', + ); + }, + }); + return alice; }, }); - return alice; } function setup(syscall, state, helpers) { diff --git a/demo/contractHost/vat-bob.js b/demo/contractHost/vat-bob.js index 048eb4dafe5..83bd3289fdb 100644 --- a/demo/contractHost/vat-bob.js +++ b/demo/contractHost/vat-bob.js @@ -3,140 +3,103 @@ import harden from '@agoric/harden'; -import { insist } from '../../collections/insist'; import { makeCollect } from './contractHost'; function makeBob(E, host, log) { const collect = makeCollect(E, log); - let initialized = false; - - let escrowExchangeInstallationP; - let coveredCallInstallationP; - let timerP; - - let myMoneyPurseP; - let moneyIssuerP; - let moneyNeededP; - - let myStockPurseP; - let stockIssuerP; - let stockNeededP; - - function init( - escrowExchangeInst, - coveredCallInst, - timer, - myMoneyPurse, - myStockPurse, - ) { - escrowExchangeInstallationP = escrowExchangeInst; - coveredCallInstallationP = coveredCallInst; - timerP = E.resolve(timer); - - myMoneyPurseP = E.resolve(myMoneyPurse); - moneyIssuerP = E(myMoneyPurseP).getIssuer(); - moneyNeededP = E(E(moneyIssuerP).getAssay()).make(10); - - myStockPurseP = E.resolve(myStockPurse); - stockIssuerP = E(myStockPurseP).getIssuer(); - stockNeededP = E(E(stockIssuerP).getAssay()).make(7); - - initialized = true; - // eslint-disable-next-line no-use-before-define - return bob; // bob and init use each other - } - - const bob = harden({ - init, - - /** - * This is not an imperative to Bob to buy something but rather - * the opposite. It is a request by a client to buy something from - * Bob, and therefore a request that Bob sell something. OO naming - * is a bit confusing here. - */ - buy(desc, paymentP) { - insist(initialized)`\ -ERR: buy called before init()`; - - /* eslint-disable-next-line no-unused-vars */ - let amount; - let good; - desc = `${desc}`; - switch (desc) { - case 'shoe': { - amount = 10; - good = 'If it fits, ware it.'; - break; - } - default: { - throw new Error(`unknown desc: ${desc}`); - } - } - - return E(myMoneyPurseP) - .deposit(amount, paymentP) - .then(_ => good); - }, - - tradeWell(alice) { - log('++ bob.tradeWell starting'); - insist(initialized)`\ -ERR: tradeWell called before init()`; - - const termsP = harden([moneyNeededP, stockNeededP]); - const invitesP = E(escrowExchangeInstallationP).spawn(termsP); - const aliceInviteP = invitesP.then(invites => invites[0]); - const bobInviteP = invitesP.then(invites => invites[1]); - const doneP = Promise.all([ - E(alice).acceptInvite(aliceInviteP), - E(bob).acceptInvite(bobInviteP), - ]); - doneP.then( - _res => log('++ bob.tradeWell done'), - rej => log('++ bob.tradeWell reject: ', rej), - ); - return doneP; - }, - - acceptInvite(inviteP) { - insist(initialized)`\ -ERR: acceptInvite called before init()`; - - const seatP = E(host).redeem(inviteP); - const stockPaymentP = E(myStockPurseP).withdraw(7); - E(seatP).offer(stockPaymentP); - return collect(seatP, myMoneyPurseP, myStockPurseP, 'bob escrow'); - }, - - offerAliceOption(alice) { - log('++ bob.offerAliceOption starting'); - insist(initialized)`\ -ERR: offerAliceOption called before init()`; - - const termsP = harden([ - moneyNeededP, - stockNeededP, - timerP, - 'singularity', - ]); - const bobInviteP = E(coveredCallInstallationP).spawn(termsP); - const bobSeatP = E(host).redeem(bobInviteP); - const stockPaymentP = E(myStockPurseP).withdraw(7); - const aliceInviteP = E(bobSeatP).offer(stockPaymentP); - const doneP = Promise.all([ - E(alice).acceptOption(aliceInviteP), - collect(bobSeatP, myMoneyPurseP, myStockPurseP, 'bob option'), - ]); - doneP.then( - _res => log('++ bob.offerAliceOption done'), - rej => log('++ bob.offerAliceOption reject: ', rej), - ); - return doneP; + return harden({ + init( + escrowExchangeInstallationP, + coveredCallInstallationP, + timerP, + myMoneyPurseP, + myStockPurseP, + ) { + const moneyIssuerP = E(myMoneyPurseP).getIssuer(); + const moneyNeededP = E(E(moneyIssuerP).getAssay()).make(10); + + const stockIssuerP = E(myStockPurseP).getIssuer(); + const stockNeededP = E(E(stockIssuerP).getAssay()).make(7); + + const bob = harden({ + /** + * This is not an imperative to Bob to buy something but rather + * the opposite. It is a request by a client to buy something from + * Bob, and therefore a request that Bob sell something. OO naming + * is a bit confusing here. + */ + buy(desc, paymentP) { + /* eslint-disable-next-line no-unused-vars */ + let amount; + let good; + desc = `${desc}`; + switch (desc) { + case 'shoe': { + amount = 10; + good = 'If it fits, ware it.'; + break; + } + default: { + throw new Error(`unknown desc: ${desc}`); + } + } + + return E(myMoneyPurseP) + .deposit(amount, paymentP) + .then(_ => good); + }, + + tradeWell(alice) { + log('++ bob.tradeWell starting'); + const termsP = harden([moneyNeededP, stockNeededP]); + const invitesP = E(escrowExchangeInstallationP).spawn(termsP); + const aliceInviteP = invitesP.then(invites => invites[0]); + const bobInviteP = invitesP.then(invites => invites[1]); + const doneP = Promise.all([ + E(alice).acceptInvite(aliceInviteP), + E(bob).acceptInvite(bobInviteP), + ]); + doneP.then( + _res => log('++ bob.tradeWell done'), + rej => log('++ bob.tradeWell reject: ', rej), + ); + return doneP; + }, + + acceptInvite(inviteP) { + const seatP = E(host).redeem(inviteP); + const stockPaymentP = E(myStockPurseP).withdraw(7); + E(seatP).offer(stockPaymentP); + return collect(seatP, myMoneyPurseP, myStockPurseP, 'bob escrow'); + }, + + offerAliceOption(alice) { + log('++ bob.offerAliceOption starting'); + const termsP = harden([ + moneyNeededP, + stockNeededP, + timerP, + 'singularity', + ]); + const bobInviteP = E(coveredCallInstallationP).spawn(termsP); + const bobSeatP = E(host).redeem(bobInviteP); + const stockPaymentP = E(myStockPurseP).withdraw(7); + const aliceInviteP = E(bobSeatP).offer(stockPaymentP); + const doneP = Promise.all([ + E(alice).acceptOption(aliceInviteP), + collect(bobSeatP, myMoneyPurseP, myStockPurseP, 'bob option'), + ]); + doneP.then( + _res => log('++ bob.offerAliceOption done'), + rej => log('++ bob.offerAliceOption reject: ', rej), + ); + return doneP; + }, + }); + return bob; }, }); - return bob; } function setup(syscall, state, helpers) { diff --git a/demo/contractHost/vat-fred.js b/demo/contractHost/vat-fred.js index ec3cb5737be..c63c3b5c8ea 100644 --- a/demo/contractHost/vat-fred.js +++ b/demo/contractHost/vat-fred.js @@ -4,156 +4,130 @@ import harden from '@agoric/harden'; import { allComparable } from '../../collections/sameStructure'; -import { insist } from '../../collections/insist'; import { makeCollect } from './contractHost'; function makeFred(E, host, log) { const collect = makeCollect(E, log); - let initialized = false; - - let escrowExchangeInstallationP; - let coveredCallInstallationP; - let timerP; - let inviteIssuerP; - let inviteIssuerLabel; - - let myMoneyPurseP; - let moneyIssuerP; - - let myStockPurseP; - let stockIssuerP; - - let myFinPurseP; - let finIssuerP; - - function init( - escrowExchangeInst, - coveredCallInst, - timer, - myMoneyPurse, - myStockPurse, - myFinPurse, - ) { - escrowExchangeInstallationP = escrowExchangeInst; - coveredCallInstallationP = coveredCallInst; - timerP = E.resolve(timer); - inviteIssuerP = E(host).getInviteIssuer(); - inviteIssuerLabel = harden({ - issuer: inviteIssuerP, - description: 'contract host', - }); - - myMoneyPurseP = E.resolve(myMoneyPurse); - moneyIssuerP = E(myMoneyPurseP).getIssuer(); - - myStockPurseP = E.resolve(myStockPurse); - stockIssuerP = E(myStockPurseP).getIssuer(); - - myFinPurseP = E.resolve(myFinPurse); - finIssuerP = E(myFinPurseP).getIssuer(); - - initialized = true; - // eslint-disable-next-line no-use-before-define - return fred; // fred and init use each other - } - - const fred = harden({ - init, - acceptOptionOffer(allegedSaleInvitePaymentP) { - log('++ fred.acceptOptionOffer starting'); - insist(initialized)`\ -ERR: fred.acceptOptionOffer called before init()`; - - const dough10 = harden({ - label: { - issuer: moneyIssuerP, - description: 'dough', - }, - quantity: 10, - }); - const wonka7 = harden({ - label: { - issuer: stockIssuerP, - description: 'wonka', - }, - quantity: 7, - }); - const fin55 = harden({ - label: { - issuer: finIssuerP, - description: 'fins', - }, - quantity: 55, + return harden({ + init( + escrowExchangeInstallationP, + coveredCallInstallationP, + timerP, + myMoneyPurseP, + myStockPurseP, + myFinPurseP, + ) { + const inviteIssuerP = E(host).getInviteIssuer(); + const inviteIssuerLabel = harden({ + issuer: inviteIssuerP, + description: 'contract host', }); - const allegedSaleAmountP = E(allegedSaleInvitePaymentP).getXferBalance(); + const moneyIssuerP = E(myMoneyPurseP).getIssuer(); + + const stockIssuerP = E(myStockPurseP).getIssuer(); - const verifiedSaleInvitePaymentP = E.resolve(allegedSaleAmountP).then( - allegedSaleInviteAmount => { - const allegedOptionsInviteAmount = - allegedSaleInviteAmount.quantity.terms[1]; + const finIssuerP = E(myFinPurseP).getIssuer(); - const optionsInviteAmount = harden({ - label: inviteIssuerLabel, - quantity: { - installation: coveredCallInstallationP, - terms: [dough10, wonka7, timerP, 'singularity'], - seatIdentity: allegedOptionsInviteAmount.quantity.seatIdentity, - seatDesc: 'holder', + const fred = harden({ + acceptOptionOffer(allegedSaleInvitePaymentP) { + log('++ fred.acceptOptionOffer starting'); + + const dough10 = harden({ + label: { + issuer: moneyIssuerP, + description: 'dough', + }, + quantity: 10, + }); + const wonka7 = harden({ + label: { + issuer: stockIssuerP, + description: 'wonka', + }, + quantity: 7, + }); + const fin55 = harden({ + label: { + issuer: finIssuerP, + description: 'fins', }, + quantity: 55, }); - const saleInviteAmountP = allComparable( - harden({ - label: inviteIssuerLabel, - quantity: { - installation: escrowExchangeInstallationP, - terms: [fin55, optionsInviteAmount], - seatIdentity: allegedSaleInviteAmount.quantity.seatIdentity, - seatDesc: 'left', - }, - }), + const allegedSaleAmountP = E( + allegedSaleInvitePaymentP, + ).getXferBalance(); + + const verifiedSaleInvitePaymentP = E.resolve(allegedSaleAmountP).then( + allegedSaleInviteAmount => { + const allegedOptionsInviteAmount = + allegedSaleInviteAmount.quantity.terms[1]; + + const optionsInviteAmount = harden({ + label: inviteIssuerLabel, + quantity: { + installation: coveredCallInstallationP, + terms: [dough10, wonka7, timerP, 'singularity'], + seatIdentity: + allegedOptionsInviteAmount.quantity.seatIdentity, + seatDesc: 'holder', + }, + }); + + const saleInviteAmountP = allComparable( + harden({ + label: inviteIssuerLabel, + quantity: { + installation: escrowExchangeInstallationP, + terms: [fin55, optionsInviteAmount], + seatIdentity: allegedSaleInviteAmount.quantity.seatIdentity, + seatDesc: 'left', + }, + }), + ); + + return E.resolve(saleInviteAmountP).then(saleInviteAmount => { + return E(inviteIssuerP).getExclusive( + saleInviteAmount, + allegedSaleInvitePaymentP, + 'verified sale invite', + ); + }); + }, ); - return E.resolve(saleInviteAmountP).then(saleInviteAmount => { - return E(inviteIssuerP).getExclusive( - saleInviteAmount, - allegedSaleInvitePaymentP, - 'verified sale invite', - ); + const saleSeatP = E(host).redeem(verifiedSaleInvitePaymentP); + const finPaymentP = E(myFinPurseP).withdraw(55); + E(saleSeatP).offer(finPaymentP); + const optionInvitePurseP = E(inviteIssuerP).makeEmptyPurse(); + const gotOptionP = collect( + saleSeatP, + optionInvitePurseP, + myFinPurseP, + 'fred buys escrowed option', + ); + return E.resolve(gotOptionP).then(_ => { + // Fred bought the option. Now fred tries to exercise the option. + const optionInvitePaymentP = E(optionInvitePurseP).withdrawAll(); + const optionSeatP = E(host).redeem(optionInvitePaymentP); + return E.resolve(allComparable(dough10)).then(d10 => { + const doughPaymentP = E(myMoneyPurseP).withdraw(d10); + E(optionSeatP).offer(doughPaymentP); + return collect( + optionSeatP, + myStockPurseP, + myMoneyPurseP, + 'fred exercises option, buying stock', + ); + }); }); }, - ); - - const saleSeatP = E(host).redeem(verifiedSaleInvitePaymentP); - const finPaymentP = E(myFinPurseP).withdraw(55); - E(saleSeatP).offer(finPaymentP); - const optionInvitePurseP = E(inviteIssuerP).makeEmptyPurse(); - const gotOptionP = collect( - saleSeatP, - optionInvitePurseP, - myFinPurseP, - 'fred buys escrowed option', - ); - return E.resolve(gotOptionP).then(_ => { - // Fred bought the option. Now fred tries to exercise the option. - const optionInvitePaymentP = E(optionInvitePurseP).withdrawAll(); - const optionSeatP = E(host).redeem(optionInvitePaymentP); - return E.resolve(allComparable(dough10)).then(d10 => { - const doughPaymentP = E(myMoneyPurseP).withdraw(d10); - E(optionSeatP).offer(doughPaymentP); - return collect( - optionSeatP, - myStockPurseP, - myMoneyPurseP, - 'fred exercises option, buying stock', - ); - }); }); + return fred; }, }); - return fred; } function setup(syscall, state, helpers) { From af09398e0a153d5886bca62fd83eb627ea8fe364 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Thu, 30 May 2019 19:20:44 -0700 Subject: [PATCH 16/18] minor --- demo/contractHost/vat-alice.js | 1 - demo/contractHost/vat-fred.js | 3 --- 2 files changed, 4 deletions(-) diff --git a/demo/contractHost/vat-alice.js b/demo/contractHost/vat-alice.js index 2562c70f7de..49e4e3f9c48 100644 --- a/demo/contractHost/vat-alice.js +++ b/demo/contractHost/vat-alice.js @@ -171,7 +171,6 @@ function makeAlice(E, host, log) { const inviteNeededP = E(allegedInvitePaymentP).getXferBalance(); const termsP = harden([finNeededP, inviteNeededP]); - // const invitesP = E(E(host).install(escrowExchangeSrc)).spawn(termsP); const invitesP = E(escrowExchangeInstallationP).spawn(termsP); const fredInviteP = invitesP.then(invites => invites[0]); const aliceForFredInviteP = invitesP.then(invites => invites[1]); diff --git a/demo/contractHost/vat-fred.js b/demo/contractHost/vat-fred.js index c63c3b5c8ea..4fa4d276456 100644 --- a/demo/contractHost/vat-fred.js +++ b/demo/contractHost/vat-fred.js @@ -23,11 +23,8 @@ function makeFred(E, host, log) { issuer: inviteIssuerP, description: 'contract host', }); - const moneyIssuerP = E(myMoneyPurseP).getIssuer(); - const stockIssuerP = E(myStockPurseP).getIssuer(); - const finIssuerP = E(myFinPurseP).getIssuer(); const fred = harden({ From f9417b6d33b14089c68c360550f85c996d56d6d8 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Thu, 30 May 2019 19:24:18 -0700 Subject: [PATCH 17/18] minor --- demo/contractHost/vat-alice.js | 4 ++-- demo/contractHost/vat-bob.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/demo/contractHost/vat-alice.js b/demo/contractHost/vat-alice.js index 49e4e3f9c48..39df2cc8067 100644 --- a/demo/contractHost/vat-alice.js +++ b/demo/contractHost/vat-alice.js @@ -170,8 +170,8 @@ function makeAlice(E, host, log) { const finNeededP = E(E(optFinIssuerP).getAssay()).make(55); const inviteNeededP = E(allegedInvitePaymentP).getXferBalance(); - const termsP = harden([finNeededP, inviteNeededP]); - const invitesP = E(escrowExchangeInstallationP).spawn(termsP); + const terms = harden([finNeededP, inviteNeededP]); + const invitesP = E(escrowExchangeInstallationP).spawn(terms); const fredInviteP = invitesP.then(invites => invites[0]); const aliceForFredInviteP = invitesP.then(invites => invites[1]); const doneP = Promise.all([ diff --git a/demo/contractHost/vat-bob.js b/demo/contractHost/vat-bob.js index 83bd3289fdb..5fcea121ef1 100644 --- a/demo/contractHost/vat-bob.js +++ b/demo/contractHost/vat-bob.js @@ -52,8 +52,8 @@ function makeBob(E, host, log) { tradeWell(alice) { log('++ bob.tradeWell starting'); - const termsP = harden([moneyNeededP, stockNeededP]); - const invitesP = E(escrowExchangeInstallationP).spawn(termsP); + const terms = harden([moneyNeededP, stockNeededP]); + const invitesP = E(escrowExchangeInstallationP).spawn(terms); const aliceInviteP = invitesP.then(invites => invites[0]); const bobInviteP = invitesP.then(invites => invites[1]); const doneP = Promise.all([ @@ -76,13 +76,13 @@ function makeBob(E, host, log) { offerAliceOption(alice) { log('++ bob.offerAliceOption starting'); - const termsP = harden([ + const terms = harden([ moneyNeededP, stockNeededP, timerP, 'singularity', ]); - const bobInviteP = E(coveredCallInstallationP).spawn(termsP); + const bobInviteP = E(coveredCallInstallationP).spawn(terms); const bobSeatP = E(host).redeem(bobInviteP); const stockPaymentP = E(myStockPurseP).withdraw(7); const aliceInviteP = E(bobSeatP).offer(stockPaymentP); From 3ae77b482e6a97ec17c14f18b02830aaa51cc13d Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Fri, 31 May 2019 08:59:28 -0700 Subject: [PATCH 18/18] better names post scope cleanup --- demo/contractHost/bootstrap.js | 68 +++++++++++++++++++++------------- demo/contractHost/vat-alice.js | 8 ++-- demo/contractHost/vat-bob.js | 8 ++-- demo/contractHost/vat-fred.js | 8 ++-- 4 files changed, 54 insertions(+), 38 deletions(-) diff --git a/demo/contractHost/bootstrap.js b/demo/contractHost/bootstrap.js index 1858e1a962f..48833633231 100644 --- a/demo/contractHost/bootstrap.js +++ b/demo/contractHost/bootstrap.js @@ -117,7 +117,7 @@ function build(E, log) { }); } - function betterContractTestAliceFirst(host, mint, aliceI, bobI) { + function betterContractTestAliceFirst(host, mint, aliceMaker, bobMaker) { const escrowExchangeInstallationP = E(host).install(escrowExchangeSrc); const coveredCallInstallationP = E(host).install(coveredCallSrc); @@ -129,14 +129,14 @@ function build(E, log) { const aliceStockPurseP = E(stockMintP).mint(2002); const bobStockPurseP = E(stockMintP).mint(2003); - const aliceP = E(aliceI).init( + const aliceP = E(aliceMaker).make( escrowExchangeInstallationP, coveredCallInstallationP, fakeNeverTimer, aliceMoneyPurseP, aliceStockPurseP, ); - const bobP = E(bobI).init( + const bobP = E(bobMaker).make( escrowExchangeInstallationP, coveredCallInstallationP, fakeNeverTimer, @@ -156,7 +156,7 @@ function build(E, log) { }); } - function betterContractTestBobFirst(host, mint, aliceI, bobI) { + function betterContractTestBobFirst(host, mint, aliceMaker, bobMaker) { const escrowExchangeInstallationP = E(host).install(escrowExchangeSrc); const coveredCallInstallationP = E(host).install(coveredCallSrc); @@ -168,14 +168,14 @@ function build(E, log) { const aliceStockPurseP = E(stockMintP).mint(2002, 'aliceMainStock'); const bobStockPurseP = E(stockMintP).mint(2003, 'bobMainStock'); - const aliceP = E(aliceI).init( + const aliceP = E(aliceMaker).make( escrowExchangeInstallationP, coveredCallInstallationP, fakeNeverTimer, aliceMoneyPurseP, aliceStockPurseP, ); - const bobP = E(bobI).init( + const bobP = E(bobMaker).make( escrowExchangeInstallationP, coveredCallInstallationP, fakeNeverTimer, @@ -201,7 +201,7 @@ function build(E, log) { }); } - function coveredCallTest(host, mint, aliceI, bobI) { + function coveredCallTest(host, mint, aliceMaker, bobMaker) { const escrowExchangeInstallationP = E(host).install(escrowExchangeSrc); const coveredCallInstallationP = E(host).install(coveredCallSrc); @@ -213,14 +213,14 @@ function build(E, log) { const aliceStockPurseP = E(stockMintP).mint(2002, 'aliceMainStock'); const bobStockPurseP = E(stockMintP).mint(2003, 'bobMainStock'); - const aliceP = E(aliceI).init( + const aliceP = E(aliceMaker).make( escrowExchangeInstallationP, coveredCallInstallationP, fakeNeverTimer, aliceMoneyPurseP, aliceStockPurseP, ); - const bobP = E(bobI).init( + const bobP = E(bobMaker).make( escrowExchangeInstallationP, coveredCallInstallationP, fakeNeverTimer, @@ -246,7 +246,7 @@ function build(E, log) { }); } - function coveredCallSaleTest(host, mint, aliceI, bobI, fredI) { + function coveredCallSaleTest(host, mint, aliceMaker, bobMaker, fredMaker) { const escrowExchangeInstallationP = E(host).install(escrowExchangeSrc); const coveredCallInstallationP = E(host).install(coveredCallSrc); @@ -264,14 +264,14 @@ function build(E, log) { const aliceFinPurseP = E(finMintP).mint(3000, 'aliceFins'); const fredFinPurseP = E(finMintP).mint(3001, 'fredFins'); - const bobP = E(bobI).init( + const bobP = E(bobMaker).make( escrowExchangeInstallationP, coveredCallInstallationP, fakeNeverTimer, bobDoughPurseP, bobStockPurseP, ); - const fredP = E(fredI).init( + const fredP = E(fredMaker).make( escrowExchangeInstallationP, coveredCallInstallationP, fakeNeverTimer, @@ -279,7 +279,7 @@ function build(E, log) { fredStockPurseP, fredFinPurseP, ); - const aliceP = E(aliceI).init( + const aliceP = E(aliceMaker).make( escrowExchangeInstallationP, coveredCallInstallationP, fakeNeverTimer, @@ -327,28 +327,44 @@ function build(E, log) { } case 'alice-first': { const host = await E(vats.host).makeHost(); - const aliceI = await E(vats.alice).makeAlice(host); - const bobI = await E(vats.bob).makeBob(host); - return betterContractTestAliceFirst(host, vats.mint, aliceI, bobI); + const aliceMaker = await E(vats.alice).makeAliceMaker(host); + const bobMaker = await E(vats.bob).makeBobMaker(host); + return betterContractTestAliceFirst( + host, + vats.mint, + aliceMaker, + bobMaker, + ); } case 'bob-first': { const host = await E(vats.host).makeHost(); - const aliceI = await E(vats.alice).makeAlice(host); - const bobI = await E(vats.bob).makeBob(host); - return betterContractTestBobFirst(host, vats.mint, aliceI, bobI); + const aliceMaker = await E(vats.alice).makeAliceMaker(host); + const bobMaker = await E(vats.bob).makeBobMaker(host); + return betterContractTestBobFirst( + host, + vats.mint, + aliceMaker, + bobMaker, + ); } case 'covered-call': { const host = await E(vats.host).makeHost(); - const aliceI = await E(vats.alice).makeAlice(host); - const bobI = await E(vats.bob).makeBob(host); - return coveredCallTest(host, vats.mint, aliceI, bobI); + const aliceMaker = await E(vats.alice).makeAliceMaker(host); + const bobMaker = await E(vats.bob).makeBobMaker(host); + return coveredCallTest(host, vats.mint, aliceMaker, bobMaker); } case 'covered-call-sale': { const host = await E(vats.host).makeHost(); - const aliceI = await E(vats.alice).makeAlice(host); - const bobI = await E(vats.bob).makeBob(host); - const fredI = await E(vats.fred).makeFred(host); - return coveredCallSaleTest(host, vats.mint, aliceI, bobI, fredI); + const aliceMaker = await E(vats.alice).makeAliceMaker(host); + const bobMaker = await E(vats.bob).makeBobMaker(host); + const fredMaker = await E(vats.fred).makeFredMaker(host); + return coveredCallSaleTest( + host, + vats.mint, + aliceMaker, + bobMaker, + fredMaker, + ); } default: { throw new Error(`unrecognized argument value ${argv[0]}`); diff --git a/demo/contractHost/vat-alice.js b/demo/contractHost/vat-alice.js index 39df2cc8067..79fc1d2e049 100644 --- a/demo/contractHost/vat-alice.js +++ b/demo/contractHost/vat-alice.js @@ -6,7 +6,7 @@ import harden from '@agoric/harden'; import { allComparable } from '../../collections/sameStructure'; import { makeCollect } from './contractHost'; -function makeAlice(E, host, log) { +function makeAliceMaker(E, host, log) { const collect = makeCollect(E, log); // TODO BUG: All callers should wait until settled before doing @@ -19,7 +19,7 @@ function makeAlice(E, host, log) { } return harden({ - init( + make( escrowExchangeInstallationP, coveredCallInstallationP, timerP, @@ -214,8 +214,8 @@ function setup(syscall, state, helpers) { } return helpers.makeLiveSlots(syscall, state, E => harden({ - makeAlice(host) { - return harden(makeAlice(E, host, log)); + makeAliceMaker(host) { + return harden(makeAliceMaker(E, host, log)); }, }), ); diff --git a/demo/contractHost/vat-bob.js b/demo/contractHost/vat-bob.js index 5fcea121ef1..d7e1bbd2a7a 100644 --- a/demo/contractHost/vat-bob.js +++ b/demo/contractHost/vat-bob.js @@ -5,11 +5,11 @@ import harden from '@agoric/harden'; import { makeCollect } from './contractHost'; -function makeBob(E, host, log) { +function makeBobMaker(E, host, log) { const collect = makeCollect(E, log); return harden({ - init( + make( escrowExchangeInstallationP, coveredCallInstallationP, timerP, @@ -109,8 +109,8 @@ function setup(syscall, state, helpers) { } return helpers.makeLiveSlots(syscall, state, E => harden({ - makeBob(host) { - return harden(makeBob(E, host, log)); + makeBobMaker(host) { + return harden(makeBobMaker(E, host, log)); }, }), ); diff --git a/demo/contractHost/vat-fred.js b/demo/contractHost/vat-fred.js index 4fa4d276456..846efe1d3b1 100644 --- a/demo/contractHost/vat-fred.js +++ b/demo/contractHost/vat-fred.js @@ -6,11 +6,11 @@ import harden from '@agoric/harden'; import { allComparable } from '../../collections/sameStructure'; import { makeCollect } from './contractHost'; -function makeFred(E, host, log) { +function makeFredMaker(E, host, log) { const collect = makeCollect(E, log); return harden({ - init( + make( escrowExchangeInstallationP, coveredCallInstallationP, timerP, @@ -134,8 +134,8 @@ function setup(syscall, state, helpers) { } return helpers.makeLiveSlots(syscall, state, E => harden({ - makeFred(host) { - return harden(makeFred(E, host, log)); + makeFredMaker(host) { + return harden(makeFredMaker(E, host, log)); }, }), );