Skip to content

Commit

Permalink
Start a new auction in a3p-integration (#9158)
Browse files Browse the repository at this point in the history
refs: #8740
closes: #8918
refs: #8400

## Description

Add a new Auction instance in A3P, so #8757 can make use of it. Also
provides upgrade proposals which can be be applied to MainNet and other
chains.

### Security Considerations

N/A

### Scaling Considerations

This is largely in service of #8400, which reports that priceFeed vats
are accumulating garbage. This PR gives a new auction which can rely on
new priceFeeds. The existing auction is not upgradeable and its
pricefeeds can't be updated.

### Documentation Considerations

No user-visible changes to behavior.

### Testing Considerations

Tested in A3P

### Upgrade Considerations

Auctions are not upgradeable, so we have to replace them and update
their clients.
  • Loading branch information
mergify[bot] committed Apr 29, 2024
2 parents de2008a + 2d5a66a commit 38b6b62
Show file tree
Hide file tree
Showing 8 changed files with 330 additions and 1 deletion.
3 changes: 2 additions & 1 deletion a3p-integration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "[email protected]",
"license": "Apache-2.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import test from 'ava';
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, so 2*2
t.is(Object.keys(details).length, 4);
});
100 changes: 100 additions & 0 deletions a3p-integration/proposals/a:upgrade-next/vatDetails.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
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 */
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;
};
26 changes: 26 additions & 0 deletions a3p-integration/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions golang/cosmos/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
}
}

Expand Down
14 changes: 14 additions & 0 deletions packages/builders/scripts/vats/add-auction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { makeHelpers } from '@agoric/deploy-script-support';

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */
export const defaultProposalBuilder = async () => {
return harden({
sourceSpec: '@agoric/inter-protocol/src/proposals/add-auction.js',
getManifestCall: ['getManifestForAddAuction'],
});
};

export default async (homeP, endowments) => {
const { writeCoreProposal } = await makeHelpers(homeP, endowments);
await writeCoreProposal('add-auction', defaultProposalBuilder);
};
177 changes: 177 additions & 0 deletions packages/inter-protocol/src/proposals/add-auction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
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';

const trace = makeTracer('NewAuction', true);

/** @param {import('./econ-behaviors.js').EconomyBootstrapPowers} powers */
export const addAuction = async ({
consume: {
zoe,
board,
chainTimerService,
priceAuthority,
chainStorage,
economicCommitteeCreatorFacet: electorateCreatorFacet,
auctioneerKit: legacyKitP,
},
produce: { newAuctioneerKit },
instance: {
consume: { reserve: reserveInstance },
},
installation: {
consume: {
auctioneer: auctionInstallation,
contractGovernor: contractGovernorInstallation,
},
},
issuer: {
consume: { [Stable.symbol]: stableIssuerP },
},
}) => {
trace('addAuction start');
const STORAGE_PATH = 'auction';

const poserInvitationP = E(electorateCreatorFacet).getPoserInvitation();

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);
const marshaller = await E(board).getReadonlyMarshaller();

const reservePublicFacet = await E(zoe).getPublicFacet(reserveInstance);

const auctionTerms = makeGovernedATerms(
{ storageNode, marshaller },
chainTimerService,
priceAuthority,
reservePublicFacet,
{
...params,
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<typeof auctionInstallation>} */
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(),
]);

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({
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,
auctioneerKit: 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 };
};
1 change: 1 addition & 0 deletions packages/inter-protocol/src/proposals/econ-behaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const SECONDS_PER_WEEK = 7n * SECONDS_PER_DAY;
* >;
* vaultFactoryKit: GovernanceFacetKit<VFStart>;
* auctioneerKit: AuctioneerKit;
* newAuctioneerKit: AuctioneerKit;
* minInitialDebt: NatValue;
* }>} EconomyBootstrapSpace
*/
Expand Down

0 comments on commit 38b6b62

Please sign in to comment.