Skip to content

Commit

Permalink
Manage parameters for a contract (#3208)
Browse files Browse the repository at this point in the history
* feat: manage parameters for a contract

closes #3186

outstanding issues:
 * should this validate types?
 * is there a clean way to assert something is an amount?
 * should managed amounts (and ratios) only set the value and leave
    the brand to the managed contract?
 * are there others types to add or remove?

* chore: convert functions to arrow functions

* chore: add types for handle and any.

* chore: drop an inadvertant import

* feat: add lookup for param names and details

* chore: update version info

* chore: review suggestions

dependency reduction
support instance and installation rather than handle
support NAT type which must be a bigint
rename publicFacet to params

* refactor: paramManager separates each param updater

change 'any' to 'unknown'
drop an unneeded guard clause
a single getParams() describes all parameters

* chore: typescript corrections

Thanks to Michael Fig.

* chore: review cleanups

dependency trimming
better test for AMOUNT.
improve some error messages
narrow some type declarations
add tests for INSTANCE and INSTALLATION

* chore: correct esm import
  • Loading branch information
Chris-Hibbert authored Jun 18, 2021
1 parent e02286c commit 8447993
Show file tree
Hide file tree
Showing 8 changed files with 541 additions and 0 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"packages/bundle-source",
"packages/import-bundle",
"packages/eventual-send",
"packages/governance",
"packages/promise-kit",
"packages/tame-metering",
"packages/transform-metering",
Expand Down
5 changes: 5 additions & 0 deletions packages/governance/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Change Log

All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

Empty file added packages/governance/NEWS.md
Empty file.
18 changes: 18 additions & 0 deletions packages/governance/jsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// This file can contain .js-specific Typescript compiler config.
{
"compilerOptions": {
"target": "esnext",

"noEmit": true,
/*
// The following flags are for creating .d.ts files:
"noEmit": false,
"declaration": true,
"emitDeclarationOnly": true,
*/
"downlevelIteration": true,
"strictNullChecks": true,
"moduleResolution": "node",
},
"include": ["src/**/*.js", "test/**/*.js"],
}
71 changes: 71 additions & 0 deletions packages/governance/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"name": "@agoric/governance",
"version": "0.1.0",
"description": "Core governance support",
"parsers": {
"js": "mjs"
},
"main": "src/paramManager.js",
"engines": {
"node": ">=14.15.0"
},
"scripts": {
"build": "exit 0",
"test": "ava",
"test:xs": "exit 0",
"lint-fix": "yarn lint:eslint --fix && yarn lint:types",
"lint-check": "yarn lint",
"lint": "yarn lint:types && yarn lint:eslint",
"lint:eslint": "eslint '**/*.js'",
"lint:types": "tsc -p jsconfig.json"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Agoric/agoric-sdk.git"
},
"author": "Agoric",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/Agoric/agoric-sdk/issues"
},
"homepage": "https://github.com/Agoric/agoric-sdk#readme",
"dependencies": {
"@agoric/assert": "^0.3.0",
"@agoric/ertp": "^0.11.2",
"@agoric/marshal": "^0.4.13",
"@agoric/nat": "^4.1.0",
"@agoric/notifier": "^0.3.14",
"@agoric/store": "^0.4.15",
"@agoric/zoe": "^0.16.0"
},
"devDependencies": {
"@agoric/install-ses": "^0.5.13",
"ava": "^3.12.1",
"esm": "agoric-labs/esm#Agoric-built"
},
"files": [
"src/",
"NEWS.md"
],
"ava": {
"files": [
"test/**/test-*.js"
],
"require": [
"esm"
],
"timeout": "10m"
},
"eslintConfig": {
"extends": [
"@agoric"
]
},
"prettier": {
"trailingComma": "all",
"singleQuote": true
},
"publishConfig": {
"access": "public"
}
}
127 changes: 127 additions & 0 deletions packages/governance/src/paramManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// @ts-check

import { assert, details as X } from '@agoric/assert';
import { assertIsRatio } from '@agoric/zoe/src/contractSupport';
import { AmountMath, looksLikeBrand } from '@agoric/ertp';
import { Far } from '@agoric/marshal';
import { assertKeywordName } from '@agoric/zoe/src/cleanProposal';

/**
* @type {{
* AMOUNT: 'amount',
* BRAND: 'brand',
* INSTANCE: 'instance',
* INSTALLATION: 'installation',
* NAT: 'nat',
* RATIO: 'ratio',
* STRING: 'string',
* UNKNOWN: 'unknown',
* }}
*/
const ParamType = {
AMOUNT: 'amount',
BRAND: 'brand',
INSTANCE: 'instance',
INSTALLATION: 'installation',
NAT: 'nat',
RATIO: 'ratio',
STRING: 'string',
UNKNOWN: 'unknown',
};
harden(ParamType);

const assertType = (type, value, name) => {
switch (type) {
case ParamType.AMOUNT:
// It would be nice to have a clean way to assert something is an amount.
AmountMath.coerce(value.brand, value);
break;
case ParamType.BRAND:
assert(
looksLikeBrand(value),
X`value for ${name} must be a brand, was ${value}`,
);
break;
case ParamType.INSTALLATION:
// TODO(3344): add a better assertion once Zoe validates installations
assert(
typeof value === 'object' && !Object.getOwnPropertyNames(value).length,
X`value for ${name} must be an Installation, was ${value}`,
);
break;
case ParamType.INSTANCE:
// TODO(3344): add a better assertion once Zoe validates instances
assert(
typeof value === 'object' && !Object.getOwnPropertyNames(value).length,
X`value for ${name} must be an Instance, was ${value}`,
);
break;
case ParamType.NAT:
assert.typeof(value, 'bigint');
break;
case ParamType.RATIO:
assertIsRatio(value);
break;
case ParamType.STRING:
assert.typeof(value, 'string');
break;
// This is an escape hatch for types we haven't added yet. If you need to
// use it, please file an issue and ask us to support the new type.
case ParamType.UNKNOWN:
break;
default:
assert.fail(X`unrecognized type ${type}`);
}
};

const parse = paramDesc => {
const typesAndValues = {};
// manager has an updateFoo() for each Foo param. It will be returned.
const manager = {};

paramDesc.forEach(({ name, value, type }) => {
// we want to create function names like updateFeeRatio(), so we insist that
// the name has Keyword-nature.
assertKeywordName(name);

assert(
!typesAndValues[name],
X`each parameter name must be unique: ${name} duplicated`,
);
assertType(type, value, name);

typesAndValues[name] = { type, value };
manager[`update${name}`] = newValue => {
assertType(type, newValue, name);
typesAndValues[name].value = newValue;
};
});

const getParams = () => {
/** @type {Record<Keyword,ParamDescription>} */
const descriptions = {};
Object.getOwnPropertyNames(typesAndValues).forEach(name => {
descriptions[name] = {
name,
type: typesAndValues[name].type,
value: typesAndValues[name].value,
};
});
return harden(descriptions);
};

return { getParams, manager };
};

/** @type {BuildParamManager} */
const buildParamManager = paramDesc => {
const { getParams, manager } = parse(paramDesc);

return Far('param manager', {
getParams,
...manager,
});
};
harden(buildParamManager);

export { ParamType, buildParamManager };
34 changes: 34 additions & 0 deletions packages/governance/src/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// @ts-check

/**
* @typedef { 'amount' | 'brand' | 'installation' | 'instance' | 'nat' | 'ratio' | 'string' | 'unknown' } ParamType
*/

/**
* @typedef { Amount | Brand | Installation | Instance | bigint | Ratio | string | unknown } ParamValue
*/

/**
* @typedef {Object} ParamDescription
* @property {string} name
* @property {ParamValue} value
* @property {ParamType} type
*/

/**
* @typedef {Object} ParamManagerBase
* @property {() => Record<Keyword,ParamDescription>} getParams
*
* @typedef {{ [updater: string]: (arg: ParamValue) => void }} ParamManagerUpdaters
* @typedef {ParamManagerBase & ParamManagerUpdaters} ParamManagerFull
*/

/**
* @typedef {Array<ParamDescription>} ParamDescriptions
*/

/**
* @callback BuildParamManager
* @param {ParamDescriptions} paramDesc
* @returns {ParamManagerFull}
*/
Loading

0 comments on commit 8447993

Please sign in to comment.