From 61a58962a9fe9a43714543df686425cd4e629521 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Mon, 23 Dec 2024 14:36:27 -0500 Subject: [PATCH] chore: Add post-upgrade core-eval proposal for terminating v26 zcf-b1-9f877-ATOM-USD_price_feed-governor Discovery of magic value "board02963": ``` sqlite> SELECT json_extract(value, '$.name') AS name FROM kvStore WHERE key='v26.options'; zcf-b1-9f877-ATOM-USD_price_feed-governor sqlite> SELECT * FROM kvStore WHERE key >= 'v2.' AND key < 'v2/' AND value LIKE '%v26%'; v2.vs.vom.o+d13/21|{"vatID":{"body":"#\"v26\"","slots":[]}} sqlite> SELECT * FROM kvStore WHERE key='v2.c.o+d13/21'; -- v26 admin node v2.c.o+d13/21|ko326 sqlite> SELECT * FROM kvStore WHERE key='v9.c.ko326'; v9.c.ko326|R o-142 sqlite> SELECT * FROM kvStore WHERE key >= 'v9.' AND key < 'v9/' AND value LIKE '%o-142%'; v9.c.ko326|R o-142 v9.vs.vom.o+d26/14|{"instanceState":{"body":"#\"$0.Alleged: InstanceRecord\"","slots":["o+d27/14"]},"adminNode":{"body":"#\"$0.Alleged: adminNode\"","slots":["o-142"]},"root":{"body":"#\"$0.Alleged: undefined\"","slots":["o-143"]},"functions":{"body":"#\"#undefined\"","slots":[]}} v9.vs.vom.o+d33/14|{"offerFilterStrings":{"body":"#[]","slots":[]},"publicFacet":{"body":"#\"$0.Alleged: ContractGovernorKit public\"","slots":["o-181"]},"handleOfferObj":{"body":"#\"$0.Alleged: handleOfferObj\"","slots":["o-180"]},"zoeInstanceStorageManager":{"body":"#\"$0.Alleged: InstanceStorageManager instanceStorageManager\"","slots":["o+d26/14:1"]},"seatHandleToZoeSeatAdmin":{"body":"#\"$0.Alleged: weakMapStore\"","slots":["o+d7/24"]},"instanceHandle":{"body":"#\"$0.Alleged: InstanceHandle\"","slots":["o+d29/14"]},"acceptingOffers":{"body":"#true","slots":[]},"zoeSeatAdmins":{"body":"#\"$0.Alleged: setStore\"","slots":["o+d8/56"]},"adminNode":{"body":"#\"$0.Alleged: adminNode\"","slots":["o-142"]}} v9.vs.vom.o+d34/14|{"instanceStorage":{"body":"#\"$0.Alleged: InstanceStorageManager instanceStorageManager\"","slots":["o+d26/14:1"]},"instanceAdmin":{"body":"#\"$0.Alleged: instanceAdmin instanceAdmin\"","slots":["o+d33/14:1"]},"seatHandleToSeatAdmin":{"body":"#\"$0.Alleged: weakMapStore\"","slots":["o+d7/24"]},"adminNode":{"body":"#\"$0.Alleged: adminNode\"","slots":["o-142"]}} v9.vs.vom.o+d37/14|{"adminNode":{"body":"#\"$0.Alleged: adminNode\"","slots":["o-142"]},"zcfBundleCap":{"body":"#\"$0.Alleged: device node\"","slots":["d-70"]},"contractBundleCap":{"body":"#\"$0.Alleged: device node\"","slots":["d-75"]}} sqlite> SELECT * FROM kvStore WHERE key='v9.c.o+d37/14'; -- Zoe contract instance admin facet v9.c.o+d37/14|ko396 sqlite> SELECT * FROM kvStore WHERE key >= 'v1' AND key < 'v:' AND substr(key, -length('.c.ko396')) = '.c.ko396' AND key LIKE 'v%.c.ko396'; v1.c.ko396|R o-264 v9.c.ko396|R o+d37/14 sqlite> SELECT * FROM kvStore WHERE key >= 'v1.' AND key < 'v1/' AND value LIKE '%o-264%'; v1.c.ko396|R o-264 v1.vs.vc.8.r0000000001:o-268|{"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\":\"ATOM-USD_price_feed\",\"publicFacet\":\"$6.Alleged: fluxAggregator public\"}","slots":["o-271","o-270","o-266","o-264","o-265","o-268","o-269"]} 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-268 sqlite> SELECT * FROM kvStore WHERE key='v1.c.o-268'; v1.c.o-268|ko352 sqlite> SELECT * FROM kvStore WHERE key >= 'v1' AND key < 'v:' AND substr(key, -length('.c.ko352')) = '.c.ko352' AND key LIKE 'v%.c.ko352'; v1.c.ko352|R o-268 v15.c.ko352|R o-59 v26.c.ko352|R o-71 v29.c.ko352|R o-54 v43.c.ko352|R o-104 v6.c.ko352|R o-121 v7.c.ko352|R o-124 v9.c.ko352|R o+d29/17 sqlite> SELECT * FROM kvStore WHERE key >= 'v7.' AND key < 'v7/' AND value LIKE '%o-124%'; v7.c.ko352|R o-124 v7.vs.vc.5.sboard02963|{"body":"#\"$0.Alleged: InstanceHandle\"","slots":["o-124"]} v7.vs.vom.o+d11/5|{"valueDurability":{"body":"#\"mandatory\"","slots":[]},"publishCount":{"body":"#\"+29\"","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_axl\",\"$16.Alleged: InstanceHandle\"],[\"psm-IST-USDC_grv\",\"$17.Alleged: InstanceHandle\"],[\"psm-IST-USDT_axl\",\"$18.Alleged: InstanceHandle\"],[\"psm-IST-USDT_grv\",\"$19.Alleged: InstanceHandle\"],[\"reserve\",\"$20.Alleged: InstanceHandle\"],[\"reserveGovernor\",\"$21.Alleged: InstanceHandle\"],[\"scaledPriceAuthority-stATOM\",\"$22.Alleged: InstanceHandle\"],[\"stATOM-USD price feed\",\"$23.Alleged: InstanceHandle\"],[\"walletFactory\",\"$24.Alleged: InstanceHandle\"]]","slots":["o-124","o-183","o-178","o-184","o-121","o-134","o-193","o-122","o-105","o-123","o-167","o-163","o-160","o-125","o-128","o-129","o-130","o-131","o-132","o-133","o-119","o-126","o-176","o-175","o-127"]}} sqlite> -- our Bootstrap Powers key (v1:o-268/ko352/v7:o-124) can be retrieved from the board with key "board02963" ``` --- .../proposals/n:upgrade-next/initial.test.js | 2 + .../proposals/n:upgrade-next/test.sh | 2 + .../proposals/p:upgrade-19/.gitignore | 1 + .../proposals/p:upgrade-19/package.json | 3 +- .../p:upgrade-19/terminateGovernor.test.js | 33 +++++ .../proposals/p:upgrade-19/test.sh | 1 + .../proposals/p:upgrade-19/vatDetails.js | 129 ++++++++++++++++++ 7 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 a3p-integration/proposals/p:upgrade-19/terminateGovernor.test.js create mode 100644 a3p-integration/proposals/p:upgrade-19/vatDetails.js diff --git a/a3p-integration/proposals/n:upgrade-next/initial.test.js b/a3p-integration/proposals/n:upgrade-next/initial.test.js index a0b54b3316d..c3e26d68692 100644 --- a/a3p-integration/proposals/n:upgrade-next/initial.test.js +++ b/a3p-integration/proposals/n:upgrade-next/initial.test.js @@ -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 => { diff --git a/a3p-integration/proposals/n:upgrade-next/test.sh b/a3p-integration/proposals/n:upgrade-next/test.sh index 122c9eb047d..c7b42dda2d0 100755 --- a/a3p-integration/proposals/n:upgrade-next/test.sh +++ b/a3p-integration/proposals/n:upgrade-next/test.sh @@ -1,4 +1,6 @@ #!/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. diff --git a/a3p-integration/proposals/p:upgrade-19/.gitignore b/a3p-integration/proposals/p:upgrade-19/.gitignore index 29940cd1eb4..ffa9f22d144 100644 --- a/a3p-integration/proposals/p:upgrade-19/.gitignore +++ b/a3p-integration/proposals/p:upgrade-19/.gitignore @@ -6,3 +6,4 @@ upgradeAgoricNames/ publishTestInfo/ upgrade-mintHolder/ upgradeAssetReserve/ +terminate-governor/ diff --git a/a3p-integration/proposals/p:upgrade-19/package.json b/a3p-integration/proposals/p:upgrade-19/package.json index 97ec3e2679d..638d622ecf4 100644 --- a/a3p-integration/proposals/p:upgrade-19/package.json +++ b/a3p-integration/proposals/p:upgrade-19/package.json @@ -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", diff --git a/a3p-integration/proposals/p:upgrade-19/terminateGovernor.test.js b/a3p-integration/proposals/p:upgrade-19/terminateGovernor.test.js new file mode 100644 index 00000000000..b5f29159b7d --- /dev/null +++ b/a3p-integration/proposals/p:upgrade-19/terminateGovernor.test.js @@ -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(); +}); diff --git a/a3p-integration/proposals/p:upgrade-19/test.sh b/a3p-integration/proposals/p:upgrade-19/test.sh index cc1611e9017..9b175d8e6e0 100644 --- a/a3p-integration/proposals/p:upgrade-19/test.sh +++ b/a3p-integration/proposals/p:upgrade-19/test.sh @@ -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 diff --git a/a3p-integration/proposals/p:upgrade-19/vatDetails.js b/a3p-integration/proposals/p:upgrade-19/vatDetails.js new file mode 100644 index 00000000000..9eeafdf5949 --- /dev/null +++ b/a3p-integration/proposals/p:upgrade-19/vatDetails.js @@ -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 {(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; +};