Skip to content

Commit

Permalink
chore: Add core-eval for terminating v110 zcf-b1-9f877-stkATOM-USD_pr…
Browse files Browse the repository at this point in the history
…ice_feed-governor (#10752)

Ref #9483

## Description
Termination of a small vat to verify good behavior.

<details><summary>Discovery of magic value "board052184" for mainnet</summary>

```
sqlite> SELECT json_extract(value, '$.name') AS name FROM kvStore WHERE key='v110.options';
zcf-b1-9f877-stkATOM-USD_price_feed-governor

sqlite> SELECT * FROM kvStore WHERE key >= 'v2.' AND key < 'v2/' AND value LIKE '%v110%';
v2.vs.vom.o+d13/105|{"vatID":{"body":"#\"v110\"","slots":[]}}

sqlite> SELECT * FROM kvStore WHERE key='v2.c.o+d13/105'; -- v110 admin node
v2.c.o+d13/105|ko4295546

sqlite> SELECT * FROM kvStore WHERE key='v9.c.ko4295546';
v9.c.ko4295546|R o-990404

sqlite> SELECT * FROM kvStore WHERE key >= 'v9.' AND key < 'v9/' AND value LIKE '%o-990404%';
v9.c.ko4295546|R o-990404
v9.vs.vc.4.sp+484625|{"body":"#[[\"$0.Alleged: InstanceCompletionWatcher\",\"$1.Alleged: instanceAdmin instanceAdmin\",\"$2.Alleged: adminNode\"]]","slots":["o+d1617/1","o+d33/98:1","o-990404"]}
v9.vs.vom.o+d26/98|{"instanceState":{"body":"#\"$0.Alleged: InstanceRecord\"","slots":["o+d27/98"]},"adminNode":{"body":"#\"$0.Alleged: adminNode\"","slots":["o-990404"]},"root":{"body":"#\"$0.Alleged: undefined\"","slots":["o-990405"]},"functions":{"body":"#\"#undefined\"","slots":[]}}
v9.vs.vom.o+d33/98|{"offerFilterStrings":{"body":"#[]","slots":[]},"publicFacet":{"body":"#\"$0.Alleged: ContractGovernorKit public\"","slots":["o-990417"]},"handleOfferObj":{"body":"#\"$0.Alleged: handleOfferObj\"","slots":["o-990416"]},"zoeInstanceStorageManager":{"body":"#\"$0.Alleged: InstanceStorageManager instanceStorageManager\"","slots":["o+d26/98:1"]},"seatHandleToZoeSeatAdmin":{"body":"#\"$0.Alleged: weakMapStore\"","slots":["o+d7/24"]},"instanceHandle":{"body":"#\"$0.Alleged: InstanceHandle\"","slots":["o+d29/98"]},"acceptingOffers":{"body":"#true","slots":[]},"zoeSeatAdmins":{"body":"#\"$0.Alleged: setStore\"","slots":["o+d8/2681"]},"adminNode":{"body":"#\"$0.Alleged: adminNode\"","slots":["o-990404"]}}
v9.vs.vom.o+d34/98|{"instanceStorage":{"body":"#\"$0.Alleged: InstanceStorageManager instanceStorageManager\"","slots":["o+d26/98:1"]},"instanceAdmin":{"body":"#\"$0.Alleged: instanceAdmin instanceAdmin\"","slots":["o+d33/98:1"]},"seatHandleToSeatAdmin":{"body":"#\"$0.Alleged: weakMapStore\"","slots":["o+d7/24"]},"adminNode":{"body":"#\"$0.Alleged: adminNode\"","slots":["o-990404"]}}
v9.vs.vom.o+d37/100|{"adminNode":{"body":"#\"$0.Alleged: adminNode\"","slots":["o-990404"]},"contractBundleCap":{"body":"#\"$0.Alleged: device node\"","slots":["d-75"]}}

sqlite> SELECT * FROM kvStore WHERE key='v9.c.o+d37/100'; -- Zoe contract instance admin facet
v9.c.o+d37/100|ko4295574

sqlite> SELECT * FROM kvStore WHERE key >= 'v1' AND key < 'v:' AND substr(key, -length('.c.ko4295574')) = '.c.ko4295574' AND key LIKE 'v%.c.ko4295574';
v1.c.ko4295574|R o-2920
v9.c.ko4295574|R o+d37/100

sqlite> SELECT * FROM kvStore WHERE key >= 'v1.' AND key < 'v1/' AND value LIKE '%o-2920%';
v1.c.ko4295574|R o-2920
v1.vs.vc.8.r0000000010:o-2924|{"body":"#{\"adminFacet\":\"$0.Alleged: adminFacet\",\"creatorFacet\":\"$1.Alleged: fluxAggregator creator\",\"governor\":\"$2.Alleged: InstanceHandle\",\"governorAdminFacet\":\"$3.Alleged: adminFacet\",\"governorCreatorFacet\":\"$4.Alleged: ContractGovernorKit creator\",\"instance\":\"$5.Alleged: InstanceHandle\",\"label\":\"stkATOM-USD_price_feed\",\"publicFacet\":\"$6.Alleged: fluxAggregator public\"}","slots":["o-2927","o-2926","o-2922","o-2920","o-2921","o-2924","o-2925"]}

sqlite> SELECT * FROM kvStore WHERE key >= 'v1.' AND key < 'v1/' AND value GLOB '*"o+??/8"*';
v1.vs.vc.1.sGovernedContractKits|{"body":"#\"$0.Alleged: mapStore\"","slots":["o+d6/8"]}
v1.vs.vc.5.sgovernedContractKits|{"body":"#\"$0.Alleged: mapStore\"","slots":["o+d6/8"]}

sqlite> SELECT * FROM kvStore WHERE key >= 'v1.' AND key < 'v1/' AND value GLOB '*"o+??/5"*';
v1.vs.vc.1.sBootstrap Powers|{"body":"#\"$0.Alleged: mapStore\"","slots":["o+d6/5"]}
v1.vs.vc.5.spowerStore|{"body":"#\"$0.Alleged: mapStore\"","slots":["o+d6/5"]}

sqlite> -- v1 bootstrap virtual collection 5 is "Bootstrap Powers" exposed to core eval scripts

sqlite> -- in that collection is "governedContractKits", which contains our contract instance kit by key o-2924

sqlite> SELECT * FROM kvStore WHERE key='v1.c.o-2924';
v1.c.o-2924|ko4295554

sqlite> SELECT * FROM kvStore WHERE key >= 'v1' AND key < 'v:' AND substr(key, -length('.c.ko4295554')) = '.c.ko4295554' AND key LIKE 'v%.c.ko4295554';
v1.c.ko4295554|R o-2924
v110.c.ko4295554|R o-71
v111.c.ko4295554|R o-54
v15.c.ko4295554|R o-243
v43.c.ko4295554|R o-1446627
v6.c.ko4295554|R o-170
v7.c.ko4295554|R o-295917
v9.c.ko4295554|R o+d29/99

sqlite> SELECT * FROM kvStore WHERE key >= 'v7.' AND key < 'v7/' AND value LIKE '%o-295917%';
v7.c.ko4295554|R o-295917
v7.vs.vc.5.sboard052184|{"body":"#\"$0.Alleged: InstanceHandle\"","slots":["o-295917"]}
v7.vs.vom.o+d11/5|{"valueDurability":{"body":"#\"mandatory\"","slots":[]},"publishCount":{"body":"#\"+40\"","slots":[]},"status":{"body":"#\"live\"","slots":[]},"hasValue":{"body":"#true","slots":[]},"value":{"body":"#[[\"ATOM-USD price feed\",\"$0.Alleged: InstanceHandle\"],[\"Crabble\",\"$1.Alleged: InstanceHandle\"],[\"CrabbleCommittee\",\"$2.Alleged: InstanceHandle\"],[\"CrabbleGovernor\",\"$3.Alleged: InstanceHandle\"],[\"VaultFactory\",\"$4.Alleged: InstanceHandle\"],[\"VaultFactoryGovernor\",\"$5.Alleged: InstanceHandle\"],[\"auctioneer\",\"$6.Alleged: InstanceHandle\"],[\"econCommitteeCharter\",\"$7.Alleged: InstanceHandle\"],[\"economicCommittee\",\"$8.Alleged: InstanceHandle\"],[\"feeDistributor\",\"$9.Alleged: InstanceHandle\"],[\"kread\",\"$10.Alleged: InstanceHandle\"],[\"kreadCommittee\",\"$11.Alleged: InstanceHandle\"],[\"kreadCommitteeCharter\",\"$12.Alleged: InstanceHandle\"],[\"provisionPool\",\"$13.Alleged: InstanceHandle\"],[\"psm-IST-DAI_axl\",\"$14.Alleged: InstanceHandle\"],[\"psm-IST-DAI_grv\",\"$15.Alleged: InstanceHandle\"],[\"psm-IST-USDC\",\"$16.Alleged: InstanceHandle\"],[\"psm-IST-USDC_axl\",\"$17.Alleged: InstanceHandle\"],[\"psm-IST-USDC_grv\",\"$18.Alleged: InstanceHandle\"],[\"psm-IST-USDT\",\"$19.Alleged: InstanceHandle\"],[\"psm-IST-USDT_axl\",\"$20.Alleged: InstanceHandle\"],[\"psm-IST-USDT_grv\",\"$21.Alleged: InstanceHandle\"],[\"reserve\",\"$22.Alleged: InstanceHandle\"],[\"reserveGovernor\",\"$23.Alleged: InstanceHandle\"],[\"scaledPriceAuthority-stATOM\",\"$24.Alleged: InstanceHandle\"],[\"scaledPriceAuthority-stOSMO\",\"$25.Alleged: InstanceHandle\"],[\"scaledPriceAuthority-stTIA\",\"$26.Alleged: InstanceHandle\"],[\"scaledPriceAuthority-stkATOM\",\"$27.Alleged: InstanceHandle\"],[\"stATOM-USD price feed\",\"$28.Alleged: InstanceHandle\"],[\"stOSMO-USD price feed\",\"$29.Alleged: InstanceHandle\"],[\"stTIA-USD price feed\",\"$30.Alleged: InstanceHandle\"],[\"stkATOM-USD price feed\",\"$31.Alleged: InstanceHandle\"],[\"walletFactory\",\"$32.Alleged: InstanceHandle\"]]","slots":["o-124","o-116341","o-116336","o-116342","o-121","o-134","o-689773","o-122","o-105","o-123","o-46029","o-46025","o-46022","o-125","o-132","o-133","o-68280","o-128","o-129","o-68305","o-130","o-131","o-119","o-126","o-55377","o-172282","o-175436","o-295918","o-55376","o-172281","o-175435","o-295917","o-127"]}}

sqlite> -- our Bootstrap Powers key (v1:o-2924/ko4295554/v7:o-295917) can be retrieved from the board with key "board052184"
```

</details>

### Security Considerations
We must be **very careful** about targeting the correct vat (in this case, the governor of an already-orphaned price feed from a prior generation).
This requests the minimal authority, which unfortunately includes a `governedContractKits` containing admin facets relating to multiple contract instances.

### Scaling Considerations
If slow vat cleanup goes awry, we'll recover quickly because the target vat is small. And if it doesn't, we'll have confidence in a core-eval terminating a bigger vat.

### Documentation Considerations
n/a

### Testing Considerations
Coverage added in a3p-integration.

### Upgrade Considerations
Verification is best accomplished by looking at the work reported in `vat-cleanup` slog entries and watching that block times do not climb (or at least do not remain elevated).
  • Loading branch information
mergify[bot] authored Dec 24, 2024
2 parents e543d46 + 61a5896 commit 10b789b
Show file tree
Hide file tree
Showing 10 changed files with 339 additions and 3 deletions.
2 changes: 2 additions & 0 deletions a3p-integration/proposals/n:upgrade-next/initial.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const vats = {
transfer: { incarnation: 2 },
walletFactory: { incarnation: 5 },
zoe: { incarnation: 3 },
// Terminated in a future proposal.
'-ATOM-USD_price_feed-governor': { incarnation: 0 },
};

test(`vat details`, async t => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,10 @@ const checkNewAuctionVat = async t => {
};

const countPriceFeedVats = async t => {
// price_feed and governor, old and new for two tokens
// price_feed and governor, old and new for two tokens,
// minus governor v110 (terminated by core-eval)
const priceFeedDetails = await getDetailsMatchingVats('price_feed');
t.is(Object.keys(priceFeedDetails).length, 8);
t.is(Object.keys(priceFeedDetails).length, 7);

// Two old SPAs, and two new ones
const details = await getDetailsMatchingVats('scaledPriceAuthority');
Expand Down
7 changes: 7 additions & 0 deletions a3p-integration/proposals/n:upgrade-next/test.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
#!/bin/bash
set -ueo pipefail
source /usr/src/upgrade-test-scripts/env_setup.sh

# Place here any test that should be executed using the executed proposal.
# The effects of this step are not persisted in further proposal layers.

test_val \
"$(agd q swingset params -o json | jq -Sc .vat_cleanup_budget)" \
'[{"key":"default","value":"5"},{"key":"kv","value":"50"}]' \
'vat cleanup budget'

# suppress file names from glob that run earlier
GLOBIGNORE=initial.test.js

Expand Down
1 change: 1 addition & 0 deletions a3p-integration/proposals/p:upgrade-19/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ upgradeAgoricNames/
publishTestInfo/
upgrade-mintHolder/
upgradeAssetReserve/
terminate-governor/
3 changes: 2 additions & 1 deletion a3p-integration/proposals/p:upgrade-19/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"vats/upgrade-agoricNames.js agoricNamesCoreEvals/upgradeAgoricNames",
"testing/add-USD-OLIVES.js agoricNamesCoreEvals/addUsdOlives",
"testing/publish-test-info.js agoricNamesCoreEvals/publishTestInfo",
"vats/upgrade-mintHolder.js upgrade-mintHolder A3P_INTEGRATION"
"vats/upgrade-mintHolder.js upgrade-mintHolder A3P_INTEGRATION",
"vats/terminate-governor-instance.js terminate-governor board02963:ATOM-USD_price_feed"
]
},
"type": "module",
Expand Down
33 changes: 33 additions & 0 deletions a3p-integration/proposals/p:upgrade-19/terminateGovernor.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* eslint-env node */

import test from 'ava';
import '@endo/init/debug.js';

import { retryUntilCondition } from '@agoric/client-utils';
import { evalBundles } from '@agoric/synthetic-chain';
import { getDetailsMatchingVats } from './vatDetails.js';

test('verify governor termination', async t => {
const getVats = () =>
getDetailsMatchingVats('-ATOM-USD_price_feed-governor', true);
const vatIsAlive = vat => !vat.terminated;

const initialVats = await getVats();
t.log('initial instances', initialVats);

const initialLiveVats = initialVats.filter(vatIsAlive);
t.true(initialLiveVats.length > 0);

await evalBundles('terminate-governor');
const checkForTermination = vats => {
t.log(vats);
return vats.filter(vatIsAlive).length < initialLiveVats.length;
};
await retryUntilCondition(
getVats,
checkForTermination,
'ATOM-USD price feed governor termination',
{ setTimeout, retryIntervalMs: 5000, maxRetries: 15 },
);
t.pass();
});
1 change: 1 addition & 0 deletions a3p-integration/proposals/p:upgrade-19/test.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/bin/bash

yarn ava terminateGovernor.test.js
yarn ava replaceFeeDistributor.test.js
yarn ava mintHolder.test.js
yarn ava provisionPool.test.js
Expand Down
129 changes: 129 additions & 0 deletions a3p-integration/proposals/p:upgrade-19/vatDetails.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Temporary fork of
// https://github.com/Agoric/agoric-3-proposals/blob/main/packages/synthetic-chain/src/lib/vat-status.js
// for deleted vat information.
// See https://github.com/Agoric/agoric-3-proposals/issues/208
/* eslint-env node */

import dbOpenAmbient from 'better-sqlite3';

const HOME = process.env.HOME;

/** @type {<T>(val: T | undefined) => T} */
export const NonNullish = val => {
if (!val) throw Error('required');
return val;
};

/**
* @file look up vat incarnation from kernel DB
* @see {getIncarnation}
*/

const swingstorePath = `${HOME}/.agoric/data/agoric/swingstore.sqlite`;

/**
* SQL short-hand
*
* @param {import('better-sqlite3').Database} db
*/
export const dbTool = db => {
const prepare = (strings, ...params) => {
const dml = strings.join('?');
return { stmt: db.prepare(dml), params };
};
const sql = (strings, ...args) => {
const { stmt, params } = prepare(strings, ...args);
return stmt.all(...params);
};
sql.get = (strings, ...args) => {
const { stmt, params } = prepare(strings, ...args);
return stmt.get(...params);
};
return sql;
};

/**
* @param {import('better-sqlite3').Database} db
*/
const makeSwingstore = db => {
const sql = dbTool(db);

/** @param {string} key */
// @ts-expect-error cast
const kvGet = key => sql.get`select * from kvStore where key = ${key}`.value;
/** @param {string} key */
const kvGetJSON = key => JSON.parse(kvGet(key));

/** @param {string} vatID */
const lookupVat = vatID => {
return Object.freeze({
source: () => kvGetJSON(`${vatID}.source`),
options: () => kvGetJSON(`${vatID}.options`),
currentSpan: () =>
sql.get`select * from transcriptSpans where isCurrent = 1 and vatID = ${vatID}`,
getTerminated: () => kvGetJSON('vats.terminated').includes(vatID),
});
};

/**
* @param {string} vatName
* @param {boolean} [includeTerminated]
* @returns {string[]}
*/
const findDynamicVatIDs = (vatName, includeTerminated = false) => {
/** @type {string[]} */
const terminatedVatIDs = kvGetJSON('vats.terminated');
/** @type {string[]} */
const allDynamicIDs = kvGetJSON('vat.dynamicIDs');
const dynamicIDs = includeTerminated
? allDynamicIDs
: allDynamicIDs.filter(vatID => !terminatedVatIDs.includes(vatID));
const matchingIDs = dynamicIDs.filter(vatID =>
lookupVat(vatID).options().name.includes(vatName),
);
return matchingIDs;
};

return Object.freeze({
/**
* @param {string} vatName
* @param {boolean} [includeTerminated]
* @returns {string}
*/
findVat: (vatName, includeTerminated = false) => {
/** @type {string[]} */
const matchingIDs = findDynamicVatIDs(vatName, includeTerminated);
if (matchingIDs.length === 0) throw Error(`vat not found: ${vatName}`);
return matchingIDs[0];
},
findVats: findDynamicVatIDs,
lookupVat,
});
};

/**
* @param {string} vatName
* @param {boolean} [includeTerminated]
*/
export const getDetailsMatchingVats = async (
vatName,
includeTerminated = false,
) => {
const kStore = makeSwingstore(
dbOpenAmbient(swingstorePath, { readonly: true }),
);

const vatIDs = kStore.findVats(vatName, includeTerminated);
const infos = [];
for (const vatID of vatIDs) {
const vatInfo = kStore.lookupVat(vatID);
const name = vatInfo.options().name;
const source = vatInfo.source();
const terminated = includeTerminated && vatInfo.getTerminated();
// @ts-expect-error cast
const { incarnation } = vatInfo.currentSpan();
infos.push({ vatName: name, vatID, incarnation, terminated, ...source });
}

return infos;
};
27 changes: 27 additions & 0 deletions golang/cosmos/app/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,26 @@ func replacePriceFeedsCoreProposal(upgradeName string) (vm.CoreProposalStep, err
)
}

func terminateGovernorCoreProposal(upgradeName string) (vm.CoreProposalStep, error) {
// targets is a slice of "$boardID:$instanceKitLabel" strings.
var targets []string
switch getVariantFromUpgradeName(upgradeName) {
case "MAINNET":
targets = []string{"board052184:stkATOM-USD_price_feed"}
case "A3P_INTEGRATION":
targets = []string{"board04091:stATOM-USD_price_feed"}
default:
return nil, nil
}

return buildProposalStepWithArgs(
"@agoric/builders/scripts/vats/terminate-governor-instance.js",
// Request `defaultProposalBuilder(powers, targets)`.
"defaultProposalBuilder",
[]any{targets},
)
}

// func upgradeMintHolderCoreProposal(upgradeName string) (vm.CoreProposalStep, error) {
// variant := getVariantFromUpgradeName(upgradeName)

Expand Down Expand Up @@ -285,6 +305,13 @@ func unreleasedUpgradeHandler(app *GaiaApp, targetUpgrade string) func(sdk.Conte
// "@agoric/builders/scripts/vats/upgrade-asset-reserve.js",
// ),
// )

terminateOldGovernor, err := terminateGovernorCoreProposal(targetUpgrade)
if err != nil {
return nil, err
} else if terminateOldGovernor != nil {
CoreProposalSteps = append(CoreProposalSteps, terminateOldGovernor)
}
}

app.upgradeDetails = &upgradeDetails{
Expand Down
134 changes: 134 additions & 0 deletions packages/builders/scripts/vats/terminate-governor-instance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/**
* @file Terminate price-feed governor instances such as mainnet v110.
* Functions as both an off-chain builder and an on-chain core-eval.
*/

/// <reference types="@agoric/vats/src/core/types-ambient"/>

import { E } from '@endo/far';

const SELF = '@agoric/builders/scripts/vats/terminate-governor-instance.js';
const USAGE = `Usage: agoric run /path/to/terminate-governor-instance.js \\
<$governorInstanceHandleBoardID:$instanceKitLabel>...`;

const repr = val =>
typeof val === 'string' || (typeof val === 'object' && val !== null)
? JSON.stringify(val)
: String(val);
const defaultMakeError = (strings, ...subs) =>
Error(
strings.map((s, i) => `${i === 0 ? '' : repr(subs[i - 1])}${s}`).join(''),
);
const makeUsageError = (strings, ...subs) => {
const err = defaultMakeError(strings, ...subs);
console.error(err.message);
console.error(USAGE);
return err;
};

const rtarget = /^(?<boardID>board[0-9]+):(?<instanceKitLabel>.+)$/;
/**
* @param {string[]} args
* @param {(strings: TemplateStringsArray | string[], ...subs: unknown[]) => Error} [makeError]
* @returns {Array<{boardID: string, instanceKitLabel: string}>}
*/
const parseTargets = (args = [], makeError = defaultMakeError) => {
if (!Array.isArray(args)) throw makeError`invalid targets: ${args}`;
/** @type {Array<{boardID: string, instanceKitLabel: string}>} */
const targets = [];
const badTargets = [];
for (const arg of args) {
const m = typeof arg === 'string' && arg.match(rtarget);
if (!m) {
badTargets.push(arg);
} else {
// @ts-expect-error cast
targets.push(m.groups);
}
}
if (badTargets.length !== 0) {
throw makeError`malformed target(s): ${badTargets}`;
} else if (targets.length === 0) {
throw makeError`no target(s)`;
}
return targets;
};

/**
* @param {BootstrapPowers} powers
* @param {{ options: { targetSpecifiers: string[] } }} config
*/
export const terminateGovernors = async (
{ consume: { board, governedContractKits } },
{ options: { targetSpecifiers } },
) => {
const { Fail, quote: q } = assert;
const targets = parseTargets(targetSpecifiers, Fail);
const doneP = Promise.allSettled(
targets.map(async ({ boardID, instanceKitLabel }) => {
const logLabel = [boardID, instanceKitLabel];
const contractInstanceHandle = await E(board).getValue(boardID);
const instanceKit = await E(governedContractKits).get(
// @ts-expect-error TS2345 Property '[tag]' is missing
contractInstanceHandle,
);
console.log(
`${q(logLabel)} alleged governor contract instance kit`,
instanceKit,
);
const { label, governorAdminFacet, adminFacet } = instanceKit;
label === instanceKitLabel ||
Fail`${q(logLabel)} unexpected instanceKit label, got ${label} but wanted ${q(instanceKitLabel)}`;
(adminFacet && adminFacet !== governorAdminFacet) ||
Fail`${q(logLabel)} instanceKit adminFacet should have been present and different from governorAdminFacet but was ${adminFacet}`;
const reason = harden(Error(`core-eval terminating ${label} governor`));
await E(governorAdminFacet).terminateContract(reason);
console.log(`${q(logLabel)} terminated governor`);
}),
);
const results = await doneP;
const problems = targets.flatMap(({ boardID, instanceKitLabel }, i) => {
if (results[i].status === 'fulfilled') return [];
return [[boardID, instanceKitLabel, results[i].reason]];
});
if (problems.length !== 0) {
console.error('governor termination(s) failed', problems);
Fail`governor termination(s) failed: ${problems}`;
}
};
harden(terminateGovernors);

export const getManifest = (_powers, targetSpecifiers) => {
parseTargets(targetSpecifiers);
return {
manifest: {
[terminateGovernors.name]: {
consume: { board: true, governedContractKits: true },
},
},
// Provide `terminateGovernors` a second argument like
// `{ options: { targetSpecifiers } }`.
options: { targetSpecifiers },
};
};

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */
export const defaultProposalBuilder = async (_utils, targetSpecifiers) => {
parseTargets(targetSpecifiers);
return harden({
sourceSpec: SELF,
getManifestCall: ['getManifest', targetSpecifiers],
});
};

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').DeployScriptFunction} */
export default async (homeP, endowments) => {
const { scriptArgs } = endowments;
parseTargets(scriptArgs, makeUsageError);
const dspModule = await import('@agoric/deploy-script-support');
const { makeHelpers } = dspModule;
const { writeCoreEval } = await makeHelpers(homeP, endowments);
await writeCoreEval(terminateGovernors.name, utils =>
defaultProposalBuilder(utils, scriptArgs),
);
};

0 comments on commit 10b789b

Please sign in to comment.