From 969235b18abbd15187e343d5f616f12177d224c4 Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Thu, 14 Mar 2024 10:44:25 -0700 Subject: [PATCH 1/7] feat: start a new auction in a3p-integration --- .../proposals/a:upgrade-next/auction.test.js | 9 + golang/cosmos/app/app.go | 2 + packages/builders/scripts/vats/add-auction.js | 28 +++ .../src/proposals/add-auction.js | 167 ++++++++++++++++++ .../src/proposals/econ-behaviors.js | 1 + 5 files changed, 207 insertions(+) create mode 100644 a3p-integration/proposals/a:upgrade-next/auction.test.js create mode 100644 packages/builders/scripts/vats/add-auction.js create mode 100644 packages/inter-protocol/src/proposals/add-auction.js diff --git a/a3p-integration/proposals/a:upgrade-next/auction.test.js b/a3p-integration/proposals/a:upgrade-next/auction.test.js new file mode 100644 index 00000000000..044090532ce --- /dev/null +++ b/a3p-integration/proposals/a:upgrade-next/auction.test.js @@ -0,0 +1,9 @@ +import test from 'ava'; +import { getVatDetails } from '@agoric/synthetic-chain'; + +test('new auction vat', async t => { + const auctionDetails = await getVatDetails('auction'); + console.log(`AUC `, auctionDetails); + + t.pass('new Auction'); +}); diff --git a/golang/cosmos/app/app.go b/golang/cosmos/app/app.go index 5f0c0cb70dd..1d456668cb2 100644 --- a/golang/cosmos/app/app.go +++ b/golang/cosmos/app/app.go @@ -914,6 +914,8 @@ func unreleasedUpgradeHandler(app *GaiaApp, targetUpgrade string) func(sdk.Conte "@agoric/builders/scripts/vats/updateStOsmoPriceFeed.js", "@agoric/builders/scripts/vats/updateStTiaPriceFeed.js", ), + // Add new auction contract. The old one will be retired shortly. + vm.CoreProposalStepForModules( "@agoric/builders/scripts/vats/add-auction.js"), } } diff --git a/packages/builders/scripts/vats/add-auction.js b/packages/builders/scripts/vats/add-auction.js new file mode 100644 index 00000000000..d7970000fc0 --- /dev/null +++ b/packages/builders/scripts/vats/add-auction.js @@ -0,0 +1,28 @@ +import { makeHelpers } from '@agoric/deploy-script-support'; +import { + SECONDS_PER_HOUR, + SECONDS_PER_MINUTE, +} from '@agoric/inter-protocol/src/proposals/econ-behaviors.js'; + +// Build proposal for sim-chain etc. +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */ +export const defaultProposalBuilder = async () => { + const auctionParams = harden({ + StartFrequency: 1n * SECONDS_PER_HOUR, + ClockStep: 3n * SECONDS_PER_MINUTE, + StartingRate: 10500n, + LowestRate: 6500n, + DiscountStep: 500n, + AuctionStartDelay: 2n, + PriceLockPeriod: SECONDS_PER_HOUR / 2n, + }); + return harden({ + sourceSpec: '@agoric/inter-protocol/src/proposals/add-auction.js', + getManifestCall: ['getManifestForAddAuction', auctionParams], + }); +}; + +export default async (homeP, endowments) => { + const { writeCoreProposal } = await makeHelpers(homeP, endowments); + await writeCoreProposal('add-auction', defaultProposalBuilder); +}; diff --git a/packages/inter-protocol/src/proposals/add-auction.js b/packages/inter-protocol/src/proposals/add-auction.js new file mode 100644 index 00000000000..de8fca81f2a --- /dev/null +++ b/packages/inter-protocol/src/proposals/add-auction.js @@ -0,0 +1,167 @@ +import { deeplyFulfilledObject, makeTracer } from '@agoric/internal'; +import { makeStorageNodeChild } from '@agoric/internal/src/lib-chainStorage.js'; +import { E } from '@endo/far'; +import { Stable } from '@agoric/internal/src/tokens.js'; +import { makeGovernedTerms as makeGovernedATerms } from '../auction/params.js'; +import { SECONDS_PER_HOUR, SECONDS_PER_MINUTE } from './econ-behaviors.js'; + +const trace = makeTracer('NewAuction', true); + +/** + * @param {import('./econ-behaviors.js').EconomyBootstrapPowers} powers + * @param {object} config + * @param {any} [config.auctionParams] + */ +export const addAuction = async ( + { + consume: { + zoe, + board, + chainTimerService, + priceAuthority, + chainStorage, + economicCommitteeCreatorFacet: electorateCreatorFacet, + }, + produce: { newAuctioneerKit }, + instance: { + consume: { reserve: reserveInstance }, + }, + installation: { + consume: { + auctioneer: auctionInstallation, + contractGovernor: contractGovernorInstallation, + }, + }, + issuer: { + consume: { [Stable.symbol]: stableIssuerP }, + }, + }, + { + auctionParams = { + StartFrequency: 1n * SECONDS_PER_HOUR, + ClockStep: 3n * SECONDS_PER_MINUTE, + StartingRate: 10500n, + LowestRate: 6500n, + DiscountStep: 500n, + AuctionStartDelay: 2n, + PriceLockPeriod: SECONDS_PER_HOUR / 2n, + }, + } = {}, +) => { + trace('startAuctioneer'); + const STORAGE_PATH = 'auction'; + + const poserInvitationP = E(electorateCreatorFacet).getPoserInvitation(); + + const [initialPoserInvitation, electorateInvitationAmount, stableIssuer] = + await Promise.all([ + poserInvitationP, + E(E(zoe).getInvitationIssuer()).getAmountOf(poserInvitationP), + stableIssuerP, + ]); + + const timerBrand = await E(chainTimerService).getTimerBrand(); + + const storageNode = await makeStorageNodeChild(chainStorage, STORAGE_PATH); + const marshaller = await E(board).getReadonlyMarshaller(); + + const reservePublicFacet = await E(zoe).getPublicFacet(reserveInstance); + + const auctionTerms = makeGovernedATerms( + { storageNode, marshaller }, + chainTimerService, + priceAuthority, + reservePublicFacet, + { + ...auctionParams, + ElectorateInvitationAmount: electorateInvitationAmount, + TimerBrand: timerBrand, + }, + ); + + const governorTerms = await deeplyFulfilledObject( + harden({ + timer: chainTimerService, + governedContractInstallation: auctionInstallation, + governed: { + terms: auctionTerms, + issuerKeywordRecord: { Bid: stableIssuer }, + storageNode, + marshaller, + label: 'auctioneer', + }, + }), + ); + + /** @type {GovernorStartedInstallationKit} */ + const governorStartResult = await E(zoe).startInstance( + contractGovernorInstallation, + undefined, + governorTerms, + harden({ + electorateCreatorFacet, + governed: { + initialPoserInvitation, + storageNode, + marshaller, + }, + }), + 'auctioneer.governor', + ); + + const [governedInstance, governedCreatorFacet, governedPublicFacet] = + await Promise.all([ + E(governorStartResult.creatorFacet).getInstance(), + E(governorStartResult.creatorFacet).getCreatorFacet(), + E(governorStartResult.creatorFacet).getPublicFacet(), + ]); + + // don't overwrite auctioneerKit yet + newAuctioneerKit.resolve( + harden({ + label: 'auctioneer', + creatorFacet: governedCreatorFacet, + adminFacet: governorStartResult.adminFacet, + publicFacet: governedPublicFacet, + instance: governedInstance, + + governor: governorStartResult.instance, + governorCreatorFacet: governorStartResult.creatorFacet, + governorAdminFacet: governorStartResult.adminFacet, + }), + ); + // don't replace auction instance yet. +}; + +export const ADD_AUCTION_MANIFEST = harden({ + [addAuction.name]: { + consume: { + zoe: true, + board: true, + chainTimerService: true, + priceAuthority: true, + chainStorage: true, + economicCommitteeCreatorFacet: true, + }, + produce: { + newAuctioneerKit: true, + }, + instance: { + consume: { reserve: true }, + }, + installation: { + consume: { + auctioneer: true, + contractGovernor: true, + }, + }, + issuer: { + consume: { [Stable.symbol]: true }, + }, + }, +}); + +/* Add a new auction to a chain that already has one. */ +export const getManifestForAddAuction = async () => { + return { manifest: ADD_AUCTION_MANIFEST }; +}; diff --git a/packages/inter-protocol/src/proposals/econ-behaviors.js b/packages/inter-protocol/src/proposals/econ-behaviors.js index 5b669fd31a4..a63685a9017 100644 --- a/packages/inter-protocol/src/proposals/econ-behaviors.js +++ b/packages/inter-protocol/src/proposals/econ-behaviors.js @@ -64,6 +64,7 @@ export const SECONDS_PER_WEEK = 7n * SECONDS_PER_DAY; * >; * vaultFactoryKit: GovernanceFacetKit; * auctioneerKit: AuctioneerKit; + * newAuctioneerKit: AuctioneerKit; * minInitialDebt: NatValue; * }>} EconomyBootstrapSpace */ From e2caf71363c93f84dee6a0e946a62808f002303d Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Thu, 28 Mar 2024 14:58:21 -0700 Subject: [PATCH 2/7] refactor: get auction params from governance --- ....js => priceFeed-follower-auction.test.js} | 2 +- packages/builders/scripts/vats/add-auction.js | 16 +--- .../src/proposals/add-auction.js | 90 ++++++++++--------- 3 files changed, 50 insertions(+), 58 deletions(-) rename a3p-integration/proposals/a:upgrade-next/{auction.test.js => priceFeed-follower-auction.test.js} (74%) diff --git a/a3p-integration/proposals/a:upgrade-next/auction.test.js b/a3p-integration/proposals/a:upgrade-next/priceFeed-follower-auction.test.js similarity index 74% rename from a3p-integration/proposals/a:upgrade-next/auction.test.js rename to a3p-integration/proposals/a:upgrade-next/priceFeed-follower-auction.test.js index 044090532ce..e5e53f3750d 100644 --- a/a3p-integration/proposals/a:upgrade-next/auction.test.js +++ b/a3p-integration/proposals/a:upgrade-next/priceFeed-follower-auction.test.js @@ -5,5 +5,5 @@ test('new auction vat', async t => { const auctionDetails = await getVatDetails('auction'); console.log(`AUC `, auctionDetails); - t.pass('new Auction'); + t.true(Number(auctionDetails.vatID.substring(1)) > 50, 'auction is new'); }); diff --git a/packages/builders/scripts/vats/add-auction.js b/packages/builders/scripts/vats/add-auction.js index d7970000fc0..8a248f17d17 100644 --- a/packages/builders/scripts/vats/add-auction.js +++ b/packages/builders/scripts/vats/add-auction.js @@ -1,24 +1,10 @@ import { makeHelpers } from '@agoric/deploy-script-support'; -import { - SECONDS_PER_HOUR, - SECONDS_PER_MINUTE, -} from '@agoric/inter-protocol/src/proposals/econ-behaviors.js'; -// Build proposal for sim-chain etc. /** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */ export const defaultProposalBuilder = async () => { - const auctionParams = harden({ - StartFrequency: 1n * SECONDS_PER_HOUR, - ClockStep: 3n * SECONDS_PER_MINUTE, - StartingRate: 10500n, - LowestRate: 6500n, - DiscountStep: 500n, - AuctionStartDelay: 2n, - PriceLockPeriod: SECONDS_PER_HOUR / 2n, - }); return harden({ sourceSpec: '@agoric/inter-protocol/src/proposals/add-auction.js', - getManifestCall: ['getManifestForAddAuction', auctionParams], + getManifestCall: ['getManifestForAddAuction'], }); }; diff --git a/packages/inter-protocol/src/proposals/add-auction.js b/packages/inter-protocol/src/proposals/add-auction.js index de8fca81f2a..49fc809bb99 100644 --- a/packages/inter-protocol/src/proposals/add-auction.js +++ b/packages/inter-protocol/src/proposals/add-auction.js @@ -3,7 +3,6 @@ import { makeStorageNodeChild } from '@agoric/internal/src/lib-chainStorage.js'; import { E } from '@endo/far'; import { Stable } from '@agoric/internal/src/tokens.js'; import { makeGovernedTerms as makeGovernedATerms } from '../auction/params.js'; -import { SECONDS_PER_HOUR, SECONDS_PER_MINUTE } from './econ-behaviors.js'; const trace = makeTracer('NewAuction', true); @@ -12,54 +11,60 @@ const trace = makeTracer('NewAuction', true); * @param {object} config * @param {any} [config.auctionParams] */ -export const addAuction = async ( - { +export const addAuction = async ({ + consume: { + zoe, + board, + chainTimerService, + priceAuthority, + chainStorage, + economicCommitteeCreatorFacet: electorateCreatorFacet, + auctioneerKit: legacyKitP, + }, + produce: { newAuctioneerKit }, + instance: { + consume: { reserve: reserveInstance }, + }, + installation: { consume: { - zoe, - board, - chainTimerService, - priceAuthority, - chainStorage, - economicCommitteeCreatorFacet: electorateCreatorFacet, - }, - produce: { newAuctioneerKit }, - instance: { - consume: { reserve: reserveInstance }, - }, - installation: { - consume: { - auctioneer: auctionInstallation, - contractGovernor: contractGovernorInstallation, - }, - }, - issuer: { - consume: { [Stable.symbol]: stableIssuerP }, + auctioneer: auctionInstallation, + contractGovernor: contractGovernorInstallation, }, }, - { - auctionParams = { - StartFrequency: 1n * SECONDS_PER_HOUR, - ClockStep: 3n * SECONDS_PER_MINUTE, - StartingRate: 10500n, - LowestRate: 6500n, - DiscountStep: 500n, - AuctionStartDelay: 2n, - PriceLockPeriod: SECONDS_PER_HOUR / 2n, - }, - } = {}, -) => { - trace('startAuctioneer'); + issuer: { + consume: { [Stable.symbol]: stableIssuerP }, + }, +}) => { + trace('addAuction start'); const STORAGE_PATH = 'auction'; const poserInvitationP = E(electorateCreatorFacet).getPoserInvitation(); - const [initialPoserInvitation, electorateInvitationAmount, stableIssuer] = - await Promise.all([ - poserInvitationP, - E(E(zoe).getInvitationIssuer()).getAmountOf(poserInvitationP), - stableIssuerP, - ]); + const [ + initialPoserInvitation, + electorateInvitationAmount, + stableIssuer, + legacyKit, + ] = await Promise.all([ + poserInvitationP, + E(E(zoe).getInvitationIssuer()).getAmountOf(poserInvitationP), + stableIssuerP, + legacyKitP, + ]); + // Each field has an extra layer of type + value: + // AuctionStartDelay: { type: 'relativeTime', value: { relValue: 2n, timerBrand: Object [Alleged: timerBrand] {} } } + /** @type {any} */ + const paramValues = await E(legacyKit.publicFacet).getGovernedParams(); + const params = harden({ + StartFrequency: paramValues.StartFrequency.value, + ClockStep: paramValues.ClockStep.value, + StartingRate: paramValues.StartingRate.value, + LowestRate: paramValues.LowestRate.value, + DiscountStep: paramValues.DiscountStep.value, + AuctionStartDelay: paramValues.AuctionStartDelay.value, + PriceLockPeriod: paramValues.PriceLockPeriod.value, + }); const timerBrand = await E(chainTimerService).getTimerBrand(); const storageNode = await makeStorageNodeChild(chainStorage, STORAGE_PATH); @@ -73,7 +78,7 @@ export const addAuction = async ( priceAuthority, reservePublicFacet, { - ...auctionParams, + ...params, ElectorateInvitationAmount: electorateInvitationAmount, TimerBrand: timerBrand, }, @@ -142,6 +147,7 @@ export const ADD_AUCTION_MANIFEST = harden({ priceAuthority: true, chainStorage: true, economicCommitteeCreatorFacet: true, + auctioneerKit: true, }, produce: { newAuctioneerKit: true, From fe5ace5d4f6b59e764e9422606adbb76c2b3ccd2 Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Thu, 28 Mar 2024 15:52:31 -0700 Subject: [PATCH 3/7] test: repair test that new auction is present --- .../a:upgrade-next/priceFeed-follower-auction.test.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/a3p-integration/proposals/a:upgrade-next/priceFeed-follower-auction.test.js b/a3p-integration/proposals/a:upgrade-next/priceFeed-follower-auction.test.js index e5e53f3750d..45f80269495 100644 --- a/a3p-integration/proposals/a:upgrade-next/priceFeed-follower-auction.test.js +++ b/a3p-integration/proposals/a:upgrade-next/priceFeed-follower-auction.test.js @@ -1,9 +1,7 @@ import test from 'ava'; -import { getVatDetails } from '@agoric/synthetic-chain'; +import { executeCommand } from '@agoric/synthetic-chain'; test('new auction vat', async t => { - const auctionDetails = await getVatDetails('auction'); - console.log(`AUC `, auctionDetails); - - t.true(Number(auctionDetails.vatID.substring(1)) > 50, 'auction is new'); + const data = await executeCommand('pgrep', ['-cf', 'auctioneer']); + t.is(data, '2'); }); From 6c78f96c3608d59e14139ad859d2cca9280ec330 Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Thu, 28 Mar 2024 17:34:21 -0700 Subject: [PATCH 4/7] feat: addBrand() for issuers that were already in auctions --- .../inter-protocol/src/proposals/add-auction.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/inter-protocol/src/proposals/add-auction.js b/packages/inter-protocol/src/proposals/add-auction.js index 49fc809bb99..9bc60bcdb5f 100644 --- a/packages/inter-protocol/src/proposals/add-auction.js +++ b/packages/inter-protocol/src/proposals/add-auction.js @@ -6,11 +6,7 @@ import { makeGovernedTerms as makeGovernedATerms } from '../auction/params.js'; const trace = makeTracer('NewAuction', true); -/** - * @param {import('./econ-behaviors.js').EconomyBootstrapPowers} powers - * @param {object} config - * @param {any} [config.auctionParams] - */ +/** @param {import('./econ-behaviors.js').EconomyBootstrapPowers} powers */ export const addAuction = async ({ consume: { zoe, @@ -121,6 +117,14 @@ export const addAuction = async ({ E(governorStartResult.creatorFacet).getPublicFacet(), ]); + const allIssuers = await E(zoe).getIssuers(legacyKit.instance); + const { Bid: _istIssuer, ...auctionIssuers } = allIssuers; + await Promise.all( + Object.keys(auctionIssuers).map(kwd => + E(governedCreatorFacet).addBrand(auctionIssuers[kwd], kwd), + ), + ); + // don't overwrite auctioneerKit yet newAuctioneerKit.resolve( harden({ From 699c5c91af7bd40b57ae0f29712b12e6b42e1013 Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Thu, 28 Mar 2024 17:35:29 -0700 Subject: [PATCH 5/7] test: show that there's a second auction running --- a3p-integration/package.json | 3 ++- .../priceFeed-follower-auction.test.js | 7 ++--- a3p-integration/yarn.lock | 26 +++++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/a3p-integration/package.json b/a3p-integration/package.json index 3882c279d1f..fe183229c7b 100644 --- a/a3p-integration/package.json +++ b/a3p-integration/package.json @@ -12,7 +12,8 @@ "doctor": "yarn synthetic-chain doctor" }, "dependencies": { - "@agoric/synthetic-chain": "^0.0.10" + "@agoric/synthetic-chain": "^0.0.10", + "@types/better-sqlite3": "^7.6.9" }, "packageManager": "yarn@4.1.1", "license": "Apache-2.0" diff --git a/a3p-integration/proposals/a:upgrade-next/priceFeed-follower-auction.test.js b/a3p-integration/proposals/a:upgrade-next/priceFeed-follower-auction.test.js index 45f80269495..7ac4865b2bf 100644 --- a/a3p-integration/proposals/a:upgrade-next/priceFeed-follower-auction.test.js +++ b/a3p-integration/proposals/a:upgrade-next/priceFeed-follower-auction.test.js @@ -1,7 +1,8 @@ import test from 'ava'; -import { executeCommand } from '@agoric/synthetic-chain'; +import { getDetailsMatchingVats } from './vatDetails.js'; test('new auction vat', async t => { - const data = await executeCommand('pgrep', ['-cf', 'auctioneer']); - t.is(data, '2'); + const details = await getDetailsMatchingVats('auctioneer'); + // This query matches both the auction and its governor + t.true(Object.keys(details).length > 2); }); diff --git a/a3p-integration/yarn.lock b/a3p-integration/yarn.lock index 64be7d6a2ce..43335d20a3f 100644 --- a/a3p-integration/yarn.lock +++ b/a3p-integration/yarn.lock @@ -69,6 +69,24 @@ __metadata: languageName: node linkType: hard +"@types/better-sqlite3@npm:^7.6.9": + version: 7.6.9 + resolution: "@types/better-sqlite3@npm:7.6.9" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/7d77add3993968982374cd73586a100fc5b9c29570a167b5798a415744983041d9ae3dcbdfd83fcf807247b777e3b8dc4e045fb7dae4a3d8484c9366ab371680 + languageName: node + linkType: hard + +"@types/node@npm:*": + version: 20.11.30 + resolution: "@types/node@npm:20.11.30" + dependencies: + undici-types: "npm:~5.26.4" + checksum: 10c0/867cfaf969c6d8850d8d7304e7ab739898a50ecb1395b61ff2335644f5f48d7a46fbc4a14cee967aed65ec134b61a746edae70d1f32f11321ccf29165e3bc4e6 + languageName: node + linkType: hard + "abbrev@npm:^2.0.0": version: 2.0.0 resolution: "abbrev@npm:2.0.0" @@ -973,6 +991,7 @@ __metadata: resolution: "root-workspace-0b6124@workspace:." dependencies: "@agoric/synthetic-chain": "npm:^0.0.10" + "@types/better-sqlite3": "npm:^7.6.9" languageName: unknown linkType: soft @@ -1190,6 +1209,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 10c0/bb673d7876c2d411b6eb6c560e0c571eef4a01c1c19925175d16e3a30c4c428181fb8d7ae802a261f283e4166a0ac435e2f505743aa9e45d893f9a3df017b501 + languageName: node + linkType: hard + "unique-filename@npm:^3.0.0": version: 3.0.0 resolution: "unique-filename@npm:3.0.0" From 86da7b72852becba4be4263489ccb5e2d0b5650e Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Thu, 25 Apr 2024 09:51:19 -0700 Subject: [PATCH 6/7] chore: review suggestions --- .../a:upgrade-next/priceFeed-follower-auction.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/a3p-integration/proposals/a:upgrade-next/priceFeed-follower-auction.test.js b/a3p-integration/proposals/a:upgrade-next/priceFeed-follower-auction.test.js index 7ac4865b2bf..30ae903b075 100644 --- a/a3p-integration/proposals/a:upgrade-next/priceFeed-follower-auction.test.js +++ b/a3p-integration/proposals/a:upgrade-next/priceFeed-follower-auction.test.js @@ -3,6 +3,6 @@ import { getDetailsMatchingVats } from './vatDetails.js'; test('new auction vat', async t => { const details = await getDetailsMatchingVats('auctioneer'); - // This query matches both the auction and its governor - t.true(Object.keys(details).length > 2); + // This query matches both the auction and its governor, so 2*2 + t.is(Object.keys(details).length, 4); }); From 2d5a66a285a40c675d6b6f5055274a7aa0f9800d Mon Sep 17 00:00:00 2001 From: Chris Hibbert Date: Mon, 29 Apr 2024 10:37:55 -0700 Subject: [PATCH 7/7] chore: re-add missing file for test --- .../proposals/a:upgrade-next/vatDetails.js | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 a3p-integration/proposals/a:upgrade-next/vatDetails.js diff --git a/a3p-integration/proposals/a:upgrade-next/vatDetails.js b/a3p-integration/proposals/a:upgrade-next/vatDetails.js new file mode 100644 index 00000000000..ccf24608309 --- /dev/null +++ b/a3p-integration/proposals/a:upgrade-next/vatDetails.js @@ -0,0 +1,100 @@ +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 */ + 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}`, + }); + }; + + return Object.freeze({ + /** @param {string} vatName */ + findVat: vatName => { + /** @type {string[]} */ + const dynamicIDs = kvGetJSON('vat.dynamicIDs'); + const targetVat = dynamicIDs.find(vatID => + lookupVat(vatID).options().name.includes(vatName), + ); + if (!targetVat) throw Error(`vat not found: ${vatName}`); + return targetVat; + }, + /** @param {string} vatName */ + findVats: vatName => { + /** @type {string[]} */ + const dynamicIDs = kvGetJSON('vat.dynamicIDs'); + return dynamicIDs.filter(vatID => + lookupVat(vatID).options().name.includes(vatName), + ); + }, + lookupVat, + }); +}; + +/** @param {string} vatName */ +export const getDetailsMatchingVats = async vatName => { + const kStore = makeSwingstore( + dbOpenAmbient(swingstorePath, { readonly: true }), + ); + + const vatIDs = kStore.findVats(vatName); + const infos = []; + for (const vatID of vatIDs) { + const vatInfo = kStore.lookupVat(vatID); + const source = vatInfo.source(); + // @ts-expect-error cast + const { incarnation } = vatInfo.currentSpan(); + infos.push({ vatName, vatID, incarnation, ...source }); + } + + return infos; +};