From e5ed0ff6abcb83f52b32d49125e21e6e41923ed0 Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Fri, 17 May 2024 14:37:58 -0700 Subject: [PATCH 1/3] feat: a proposal to upgrade scaledPriceAuthorities --- golang/cosmos/app/app.go | 2 + .../vats/upgradeScaledPriceAuthorities.js | 25 ++++++ .../inter-protocol/src/proposals/README.md | 23 +++++- .../upgrade-scaledPriceAuthorities.js | 78 +++++++++++++++++++ 4 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 packages/builders/scripts/vats/upgradeScaledPriceAuthorities.js create mode 100644 packages/inter-protocol/src/proposals/upgrade-scaledPriceAuthorities.js diff --git a/golang/cosmos/app/app.go b/golang/cosmos/app/app.go index d2794e32717..0af37abdb20 100644 --- a/golang/cosmos/app/app.go +++ b/golang/cosmos/app/app.go @@ -918,6 +918,8 @@ func unreleasedUpgradeHandler(app *GaiaApp, targetUpgrade string) func(sdk.Conte vm.CoreProposalStepForModules( "@agoric/builders/scripts/vats/add-auction.js"), // upgrade vaultFactory. vm.CoreProposalStepForModules( "@agoric/builders/scripts/vats/upgradeVaults.js"), + // upgrade scaledPriceAuthorities. + vm.CoreProposalStepForModules( "@agoric/builders/scripts/vats/upgradeScaledPriceAuthorities.js"), } } diff --git a/packages/builders/scripts/vats/upgradeScaledPriceAuthorities.js b/packages/builders/scripts/vats/upgradeScaledPriceAuthorities.js new file mode 100644 index 00000000000..a3a92b03e33 --- /dev/null +++ b/packages/builders/scripts/vats/upgradeScaledPriceAuthorities.js @@ -0,0 +1,25 @@ +import { makeHelpers } from '@agoric/deploy-script-support'; + +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */ +export const defaultProposalBuilder = async ({ publishRef, install }) => + harden({ + sourceSpec: + '@agoric/inter-protocol/src/proposals/upgrade-scaledPriceAuthorities.js', + getManifestCall: [ + 'getManifestForUpgradeScaledPriceAuthorities', + { + scaledPARef: publishRef( + install('@agoric/zoe/src/contracts/scaledPriceAuthority.js'), + ), + }, + ], + }); + +export default async (homeP, endowments) => { + const { writeCoreProposal } = await makeHelpers(homeP, endowments); + + await writeCoreProposal( + 'upgradeScaledPriceAuthorities', + defaultProposalBuilder, + ); +}; diff --git a/packages/inter-protocol/src/proposals/README.md b/packages/inter-protocol/src/proposals/README.md index 63b2dd38a27..250863bc50b 100644 --- a/packages/inter-protocol/src/proposals/README.md +++ b/packages/inter-protocol/src/proposals/README.md @@ -1,7 +1,24 @@ # Proposals -These are code snippets that go into propoals to the BLDer DAO to start the Inter Protocol. +These scripts are referenced by proposals to the BLDer DAO to run on the chain. +There are other collections of proposals in .../vats/src/proposals, +smart-wallet/src/proposals, orchestration/src/proposals, pegasus/src/proposals. -One of the latest is `startPSM.js` so best to model after that. The style in `econ-behaviors.js` will be refactored to be like startPSM. +The overall format is a proposalBuilder (There are several in +.../builders/scripts/vats/) which has a default export that passes resources to +the proposal. The resources include bundled source code and string-value +parameters. The ProposalBuilder specifies (as an import string) the proposal +it uses, identifies the "manifest", (which associates permissions to access +powers in the bootstrap space with functions to be called), and builds bundles +of source code needed by the proposal. -[syntax of the manifests](../../packages/vats/src/core/manifest.js) +`.../builders/scripts/vats/upgradeVaults.js` is a canonical example. It says the +proposal to run is '@agoric/inter-protocol/src/proposals/upgrade-vaults.js', +lists the manifest there as `'getManifestForUpgradeVaults'`, and directs the +creation of a bundle from +'@agoric/inter-protocol/src/vaultFactory/vaultFactory.js', which will be made +available to the proposal as `vaultsRef` in options. + +`upgrade-vaults.js` defines `getManifestForUpgradeVaults()`, which returns a +`manifest` that says `upgradeVaults()` should be executed, and specifies what +powers it should have access to. diff --git a/packages/inter-protocol/src/proposals/upgrade-scaledPriceAuthorities.js b/packages/inter-protocol/src/proposals/upgrade-scaledPriceAuthorities.js new file mode 100644 index 00000000000..d208987830f --- /dev/null +++ b/packages/inter-protocol/src/proposals/upgrade-scaledPriceAuthorities.js @@ -0,0 +1,78 @@ +import { makeTracer } from '@agoric/internal'; +import { E } from '@endo/far'; + +const trace = makeTracer('upgradeScaledPA', true); + +/** + * @param {ChainBootstrapSpace} powers + * @param {{ options: { scaledPARef: { bundleID: string } } }} options + */ +export const upgradeScaledPriceAuthorities = async ( + { + consume: { + agoricNamesAdmin, + contractKits: contractKitsP, + instancePrivateArgs: instancePrivateArgsP, + zoe, + }, + }, + { options }, +) => { + trace('start'); + const { scaledPARef } = options; + await null; + + const bundleID = scaledPARef.bundleID; + if (scaledPARef && bundleID) { + await E.when( + E(zoe).installBundleID(bundleID), + installation => + E(E(agoricNamesAdmin).lookupAdmin('installation')).update( + 'scaledPriceAuthority', + installation, + ), + err => + console.error( + `🚨 failed to update scaledPriceAuthority installation`, + err, + ), + ); + } + + const [contractKits, instancePrivateArgs] = await Promise.all([ + contractKitsP, + instancePrivateArgsP, + ]); + /** @type {StartedInstanceKit[]} */ + const scaledPAKitEntries = Array.from(contractKits.values()).filter( + kit => kit.label && kit.label.match(/scaledPriceAuthority/), + ); + + for (const kitEntry of scaledPAKitEntries) { + const { instance } = kitEntry; + const privateArgs = instancePrivateArgs.get(instance); + trace('upgrade scaledPriceAuthority', kitEntry.label); + await E(kitEntry.adminFacet).upgradeContract(bundleID, privateArgs); + } +}; + +const t = 'upgradeScaledPriceAuthority'; +export const getManifestForUpgradeScaledPriceAuthorities = async ( + _ign, + upgradeSPAOptions, +) => ({ + manifest: { + [upgradeScaledPriceAuthorities.name]: { + consume: { + agoricNamesAdmin: t, + contractKits: t, + instancePrivateArgs: t, + zoe: t, + }, + instance: { + produce: t, + }, + }, + }, + options: { ...upgradeSPAOptions }, +}); From b14941bb781244730cad402b7a2303ac49d30d0b Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Fri, 17 May 2024 14:38:48 -0700 Subject: [PATCH 2/3] feat(ERTP): allow issuers to upgrade from recoverySets to none Upgraded issuers would keep the recoverySet without removing anything. --- packages/ERTP/src/issuerKit.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/ERTP/src/issuerKit.js b/packages/ERTP/src/issuerKit.js index 0a4ee145b7a..fa9a05c962e 100644 --- a/packages/ERTP/src/issuerKit.js +++ b/packages/ERTP/src/issuerKit.js @@ -122,12 +122,9 @@ export const upgradeIssuerKit = ( ) { Fail`Cannot (yet?) upgrade from 'noRecoverySets' to 'hasRecoverySets'`; } - if ( - oldRecoverySetsState === 'hasRecoverySets' && - recoverySetsOption === 'noRecoverySets' - ) { - Fail`Cannot (yet?) upgrade from 'hasRecoverySets' to 'noRecoverySets'`; - } + // if the existing state was 'hasRecoverySets' and recoverySetsOption is + // 'noRecoverySets', then we'll leave the old recoverySet in place, but not + // add to it. const recoverySetsState = recoverySetsOption || oldRecoverySetsState; return setupIssuerKit( issuerRecord, From 04ba2b5d143a7e832e37d816152cf3fa1c97795e Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Mon, 20 May 2024 10:47:21 -0700 Subject: [PATCH 3/3] docs: updates to comments and READMEs --- packages/ERTP/src/issuerKit.js | 6 ++-- packages/deploy-script-support/README.md | 35 +++++++++++++++---- .../inter-protocol/src/proposals/README.md | 20 +---------- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/packages/ERTP/src/issuerKit.js b/packages/ERTP/src/issuerKit.js index fa9a05c962e..939d65b14df 100644 --- a/packages/ERTP/src/issuerKit.js +++ b/packages/ERTP/src/issuerKit.js @@ -122,9 +122,9 @@ export const upgradeIssuerKit = ( ) { Fail`Cannot (yet?) upgrade from 'noRecoverySets' to 'hasRecoverySets'`; } - // if the existing state was 'hasRecoverySets' and recoverySetsOption is - // 'noRecoverySets', then we'll leave the old recoverySet in place, but not - // add to it. + // Extant sets are not currently deleted. If the new option is + // 'noRecoverySets', they won't be used but extant ones will remain. Future + // upgrades may make it possible to delete elements from them. const recoverySetsState = recoverySetsOption || oldRecoverySetsState; return setupIssuerKit( issuerRecord, diff --git a/packages/deploy-script-support/README.md b/packages/deploy-script-support/README.md index 35fc33991e6..3be02f0d117 100644 --- a/packages/deploy-script-support/README.md +++ b/packages/deploy-script-support/README.md @@ -4,14 +4,35 @@ To install code on chain or in the a3p-integration environment, you'll have to generate a proposal, and write a script to build the core proposal. The proposals have limited access to bootstrap powers, described by their manifests. +There are collections of proposals in .../vats/src/proposals, +smart-wallet/src/proposals, orchestration/src/proposals, pegasus/src/proposals. + +The overall format is a proposalBuilder script (There are several in +.../builders/scripts/vats/) which has a default export that passes resources to +the proposal. The resources include bundled source code and string-value +parameters. The ProposalBuilder specifies (as an import string) the proposal +it uses, identifies the "manifest", (which associates permissions to access +powers in the bootstrap space with functions to be called), and builds bundles +of source code needed by the proposal. + +`.../builders/scripts/vats/upgradeVaults.js` is a canonical example. It says the +proposal to run is '@agoric/inter-protocol/src/proposals/upgrade-vaults.js', +lists the manifest there as `'getManifestForUpgradeVaults'`, and directs the +creation of a bundle from +'@agoric/inter-protocol/src/vaultFactory/vaultFactory.js', which will be made +available to the proposal as `vaultsRef` in options. + +`upgrade-vaults.js` defines `getManifestForUpgradeVaults()`, which returns a +`manifest` that says `upgradeVaults()` should be executed, and specifies what +powers it should have access to. ### Proposal The proposal is called with `(powers, options)` available. The manifest detailing the powers that will be used is usually in the same file, and conventionally provided by a function named `getManifestForFoo`. The manifest -needs to have a unique name, since it will be referenced by name from the script. -The usual format is +needs to have a unique name, since it will be referenced by name from the +script. The usual format is ```js export const foo = async ( { @@ -24,6 +45,7 @@ export const foo = async ( }, options, ) => { + const { fooRef } = options; // do the things using powers and options }; @@ -44,14 +66,13 @@ export const getManifestForFoo = (powers, options) => { `options` allows the proposal to be provided with arbitray other powerful objects. -### Script +### proposalBuilder Script The script describes how to build the core proposal. For `agoric-3-proposals` and uploading to the chain, the script must be named in the `CoreProposalSteps` section in [`app.go`](https://github.com/Agoric/agoric-sdk/blob/b13743a2cccf0cb63a412b54384435596d4e81ea/golang/cosmos/app/app.go#L881), -and its `defaultProposalBuilder` will be invoked -directly. +and its `defaultProposalBuilder` will be invoked directly. Script files should export `defaultProposalBuilder` and a `default` function that invokes `writeCoreProposal` one or more times to generate sets of files @@ -79,8 +100,8 @@ export default async (homeP, endowments) => { The first element of `getManifestCall` is interpreted as the name of a proposal. The second element of `getManifestCall` produces the `options` argument passed -to the proposal. (`foo` in the example above). A common thing to want to pass in -options is a reference to code to be installed on-chain. The `fooRef` example +to the proposal. (`fooRef` in the example above). A common thing to want to pass +in options is a reference to code to be installed on-chain. The `fooRef` example above shows how. `publishRef(install())` is built from sources in the sdk, and passed as a `bundleRef`, which contains a `bundleID` suitable for passing to Zoe (for contracts) or `vatAdminService` (for non-contract vat code). diff --git a/packages/inter-protocol/src/proposals/README.md b/packages/inter-protocol/src/proposals/README.md index 250863bc50b..b6d726d285e 100644 --- a/packages/inter-protocol/src/proposals/README.md +++ b/packages/inter-protocol/src/proposals/README.md @@ -1,24 +1,6 @@ # Proposals These scripts are referenced by proposals to the BLDer DAO to run on the chain. -There are other collections of proposals in .../vats/src/proposals, -smart-wallet/src/proposals, orchestration/src/proposals, pegasus/src/proposals. -The overall format is a proposalBuilder (There are several in -.../builders/scripts/vats/) which has a default export that passes resources to -the proposal. The resources include bundled source code and string-value -parameters. The ProposalBuilder specifies (as an import string) the proposal -it uses, identifies the "manifest", (which associates permissions to access -powers in the bootstrap space with functions to be called), and builds bundles -of source code needed by the proposal. +See the documentation in .../deploy-script-support/README.md -`.../builders/scripts/vats/upgradeVaults.js` is a canonical example. It says the -proposal to run is '@agoric/inter-protocol/src/proposals/upgrade-vaults.js', -lists the manifest there as `'getManifestForUpgradeVaults'`, and directs the -creation of a bundle from -'@agoric/inter-protocol/src/vaultFactory/vaultFactory.js', which will be made -available to the proposal as `vaultsRef` in options. - -`upgrade-vaults.js` defines `getManifestForUpgradeVaults()`, which returns a -`manifest` that says `upgradeVaults()` should be executed, and specifies what -powers it should have access to.