diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index dc37a8ed124..e0728e65b5d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,11 +2,20 @@ v ✰ Thanks for creating a PR! ✰ ☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > --> - + closes: #XXXX refs: #XXXX + + ## Description diff --git a/.github/actions/restore-node/action.yml b/.github/actions/restore-node/action.yml index 5797145945d..61ab970341a 100644 --- a/.github/actions/restore-node/action.yml +++ b/.github/actions/restore-node/action.yml @@ -41,10 +41,11 @@ runs: persist-credentials: false path: ${{ inputs.path }} - # Select a branch on Endo to test against by adding text to the body of the - # pull request. For example: #endo-branch: some-pr-branch - # The default is 'NOPE' to indicate not to check out Endo, just use - # the published NPM packages. + # Select an Endo target against which to test, defaulting to 'NOPE' for use + # of internally-referenced NPM packages but allowing overrides in the pull + # request description for referencing a branch of the + # [endo repository](https://github.com/endojs/endo) using lines like + # `#endo-branch: rank-strings-by-codepoint` - name: Get the appropriate Endo branch id: endo-branch uses: actions/github-script@v7 @@ -53,8 +54,8 @@ runs: script: |- let branch = 'NOPE'; if (${{ inputs.ignore-endo-branch }}) { - // Skip endo branch - } else if (context.eventName === 'schedule') { + // Skip any override directive. + } else if (context.eventName === 'schedule') { branch = 'master'; } else if (context.payload.pull_request) { const { body } = context.payload.pull_request; @@ -85,7 +86,7 @@ runs: if: steps.endo-branch.outputs.result != 'NOPE' uses: actions/checkout@v4 with: - repository: agoric/endo + repository: endojs/endo path: ./replacement-endo ref: ${{ steps.endo-branch.outputs.result }} clean: 'false' diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index a01d25ebad1..4ae7458e4d3 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -35,8 +35,10 @@ jobs: pre_check: uses: ./.github/workflows/pre-check-integration.yml - # This job is meant to emulate what developers working with the Agoric platform will experience - # It should be kept in sync with https://agoric.com/documentation/getting-started/ + # This job is meant to emulate what developers working with the Agoric + # platform will experience. + # It should be kept in sync with the "getting started" workflow at + # https://docs.agoric.com/guides/getting-started/ getting-started: needs: pre_check if: needs.pre_check.outputs.should_run == 'true' @@ -60,9 +62,11 @@ jobs: node-version: 18.18 keep-endo: 'true' - # Select a branch on dapp to test against by adding text to the body of the - # pull request. For example: #getting-started-branch: zoe-release-0.7.0 - # The default is 'main' + # Select a branch of the + # [default dapp repository](https://github.com/Agoric/dapp-offer-up) (cf. + # packages/agoric-cli/src/main.js) against which to test, defaulting to + # 'main' but allowing overrides in the pull request description using + # lines like `#getting-started-branch: zoe-release-0.7.0` - name: Get the appropriate dapp branch id: get-branch uses: actions/github-script@v7 @@ -138,9 +142,10 @@ jobs: # the chances the content of snapshots may deviate between validators xsnap-random-init: '1' - # Select a branch on loadgen to test against by adding text to the body of the - # pull request. For example: #loadgen-branch: user-123-update-foo - # The default is 'main' + # Select a branch of the + # [load generator dapp repository](https://github.com/Agoric/testnet-load-generator) + # to use, defaulting to 'main' but allowing overrides in the pull request + # description using lines like `#loadgen-branch: ceelab` - name: Get the appropriate loadgen branch id: get-loadgen-branch uses: actions/github-script@v7 diff --git a/.github/workflows/test-documentation.yml b/.github/workflows/test-documentation.yml index bade8a4cd19..596bb60a075 100644 --- a/.github/workflows/test-documentation.yml +++ b/.github/workflows/test-documentation.yml @@ -26,9 +26,11 @@ jobs: node-version: ${{ matrix.node-version }} path: ./agoric-sdk - # Select a branch on dapp to test against by adding text to the body of the - # pull request. For example: #dapp-encouragement-branch: zoe-release-0.7.0 - # The default is 'main' + # Select a branch of the + # [documentation repository](https://github.com/Agoric/documentation) + # against which to test, defaulting to 'main' but allowing overrides in + # the pull request description using lines like + # `#documentation-branch: node-22` - name: Get the appropriate dapp branch id: get-branch uses: actions/github-script@v7 @@ -44,7 +46,7 @@ jobs: branch = result[1]; } } - console.log(branch); + console.log('documentation branch: ' + branch); return branch; - name: Check out dapp diff --git a/a3p-integration/package.json b/a3p-integration/package.json index a3f253cbf7c..ee9bb0a570a 100644 --- a/a3p-integration/package.json +++ b/a3p-integration/package.json @@ -1,7 +1,7 @@ { "private": true, "agoricSyntheticChain": { - "fromTag": "use-vaults-auctions" + "fromTag": "use-upgrade-17" }, "scripts": { "build": "yarn run build:sdk && yarn run build:submissions && yarn run build:synthetic-chain", diff --git a/a3p-integration/proposals/f:replace-price-feeds/.yarnrc.yml b/a3p-integration/proposals/f:replace-price-feeds/.yarnrc.yml deleted file mode 100644 index 3186f3f0795..00000000000 --- a/a3p-integration/proposals/f:replace-price-feeds/.yarnrc.yml +++ /dev/null @@ -1 +0,0 @@ -nodeLinker: node-modules diff --git a/a3p-integration/proposals/f:replace-price-feeds/README.md b/a3p-integration/proposals/f:replace-price-feeds/README.md deleted file mode 100644 index 414bb07fe81..00000000000 --- a/a3p-integration/proposals/f:replace-price-feeds/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# CoreEvalProposal to replace existing price_feed and scaledPriceAuthority vats -# with new contracts. Auctions will need to be replaced, and Vaults will need to -# get at least a null upgrade in order to make use of the new prices. Oracle -# operators will need to accept new invitations, and sync to the roundId (0) of -# the new contracts in order to feed the new pipelines. - -The `submission` for this proposal is automatically generated during `yarn build` -in [a3p-integration](../..) using the code in agoric-sdk through -[build-all-submissions.sh](../../scripts/build-all-submissions.sh) and -[build-submission.sh](../../scripts/build-submission.sh). diff --git a/a3p-integration/proposals/f:replace-price-feeds/agd-tools.js b/a3p-integration/proposals/f:replace-price-feeds/agd-tools.js deleted file mode 100644 index c94718bc1ba..00000000000 --- a/a3p-integration/proposals/f:replace-price-feeds/agd-tools.js +++ /dev/null @@ -1,23 +0,0 @@ -import { agd } from '@agoric/synthetic-chain'; - -export const BID_OFFER_ID = 'bid-vaultUpgrade-test3'; - -/** @param {string} path */ -export const queryVstorage = path => - agd.query('vstorage', 'data', '--output', 'json', path); - -export const getOracleInstance = async price => { - const instanceRec = await queryVstorage(`published.agoricNames.instance`); - - const value = JSON.parse(instanceRec.value); - const body = JSON.parse(value.values.at(-1)); - - const feeds = JSON.parse(body.body.substring(1)); - const feedName = `${price}-USD price feed`; - - const key = Object.keys(feeds).find(k => feeds[k][0] === feedName); - if (key) { - return body.slots[key]; - } - return null; -}; diff --git a/a3p-integration/proposals/f:replace-price-feeds/package.json b/a3p-integration/proposals/f:replace-price-feeds/package.json deleted file mode 100644 index ca4637e9cdb..00000000000 --- a/a3p-integration/proposals/f:replace-price-feeds/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "agoricProposal": { - "source": "subdir", - "sdk-generate": [ - "inter-protocol/updatePriceFeeds.js submission A3P_INTEGRATION", - "vats/add-auction.js submission", - "vats/upgradeVaults.js submission", - "inter-protocol/updatePriceFeeds.js submission/main main", - "vats/add-auction.js submission/main", - "vats/upgradeVaults.js submission/main", - "inter-protocol/updatePriceFeeds.js submission/devnet devnet", - "vats/add-auction.js submission/devnet", - "vats/upgradeVaults.js submission/devnet" - ], - "type": "/agoric.swingset.CoreEvalProposal" - }, - "type": "module", - "license": "Apache-2.0", - "dependencies": { - "@agoric/synthetic-chain": "^0.3.0", - "ava": "^5.3.1" - }, - "ava": { - "concurrency": 1, - "timeout": "2m", - "files": [ - "!submission" - ] - }, - "scripts": { - "agops": "yarn --cwd /usr/src/agoric-sdk/ --silent agops" - }, - "packageManager": "yarn@4.2.2" -} diff --git a/a3p-integration/proposals/f:replace-price-feeds/test.sh b/a3p-integration/proposals/f:replace-price-feeds/test.sh deleted file mode 100755 index 23a194f7f79..00000000000 --- a/a3p-integration/proposals/f:replace-price-feeds/test.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -# Place here any test that should be executed using the proposal. -# The effects of this step are not persisted in further layers. - -yarn ava ./*.test.js diff --git a/a3p-integration/proposals/f:replace-price-feeds/yarn.lock b/a3p-integration/proposals/f:replace-price-feeds/yarn.lock deleted file mode 100644 index ee7a03e8e81..00000000000 --- a/a3p-integration/proposals/f:replace-price-feeds/yarn.lock +++ /dev/null @@ -1,2345 +0,0 @@ -# This file is generated by running "yarn install" inside your project. -# Manual changes might be lost - proceed with caution! - -__metadata: - version: 8 - cacheKey: 10c0 - -"@agoric/synthetic-chain@npm:^0.3.0": - version: 0.3.0 - resolution: "@agoric/synthetic-chain@npm:0.3.0" - dependencies: - "@endo/zip": "npm:^1.0.7" - better-sqlite3: "npm:^9.6.0" - chalk: "npm:^5.3.0" - cosmjs-types: "npm:^0.9.0" - execa: "npm:^9.3.1" - bin: - synthetic-chain: dist/cli/cli.js - checksum: 10c0/17c6241bdc48b8a2a7608c9d4d7c0a0c76fb10d4ee44a31a1150104a792bcd1133f4b1a7e8ab26673a07450b3ceabccd9911999117568221b49221b6ee4306a1 - languageName: node - linkType: hard - -"@endo/zip@npm:^1.0.7": - version: 1.0.7 - resolution: "@endo/zip@npm:1.0.7" - checksum: 10c0/a1c0d155448ce877012b34c8fe8cd3a58de9eb807514c81cddeebb802ee8e552b27d8a9a40fab3f3e4c49e0cb7fea6902fa1dd12a23ff6f30b56161fc3edc1f8 - languageName: node - linkType: hard - -"@isaacs/cliui@npm:^8.0.2": - version: 8.0.2 - resolution: "@isaacs/cliui@npm:8.0.2" - dependencies: - string-width: "npm:^5.1.2" - string-width-cjs: "npm:string-width@^4.2.0" - strip-ansi: "npm:^7.0.1" - strip-ansi-cjs: "npm:strip-ansi@^6.0.1" - wrap-ansi: "npm:^8.1.0" - wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" - checksum: 10c0/b1bf42535d49f11dc137f18d5e4e63a28c5569de438a221c369483731e9dac9fb797af554e8bf02b6192d1e5eba6e6402cf93900c3d0ac86391d00d04876789e - languageName: node - linkType: hard - -"@nodelib/fs.scandir@npm:2.1.5": - version: 2.1.5 - resolution: "@nodelib/fs.scandir@npm:2.1.5" - dependencies: - "@nodelib/fs.stat": "npm:2.0.5" - run-parallel: "npm:^1.1.9" - checksum: 10c0/732c3b6d1b1e967440e65f284bd06e5821fedf10a1bea9ed2bb75956ea1f30e08c44d3def9d6a230666574edbaf136f8cfd319c14fd1f87c66e6a44449afb2eb - languageName: node - linkType: hard - -"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": - version: 2.0.5 - resolution: "@nodelib/fs.stat@npm:2.0.5" - checksum: 10c0/88dafe5e3e29a388b07264680dc996c17f4bda48d163a9d4f5c1112979f0ce8ec72aa7116122c350b4e7976bc5566dc3ddb579be1ceaacc727872eb4ed93926d - languageName: node - linkType: hard - -"@nodelib/fs.walk@npm:^1.2.3": - version: 1.2.8 - resolution: "@nodelib/fs.walk@npm:1.2.8" - dependencies: - "@nodelib/fs.scandir": "npm:2.1.5" - fastq: "npm:^1.6.0" - checksum: 10c0/db9de047c3bb9b51f9335a7bb46f4fcfb6829fb628318c12115fbaf7d369bfce71c15b103d1fc3b464812d936220ee9bc1c8f762d032c9f6be9acc99249095b1 - languageName: node - linkType: hard - -"@npmcli/agent@npm:^2.0.0": - version: 2.2.2 - resolution: "@npmcli/agent@npm:2.2.2" - dependencies: - agent-base: "npm:^7.1.0" - http-proxy-agent: "npm:^7.0.0" - https-proxy-agent: "npm:^7.0.1" - lru-cache: "npm:^10.0.1" - socks-proxy-agent: "npm:^8.0.3" - checksum: 10c0/325e0db7b287d4154ecd164c0815c08007abfb07653cc57bceded17bb7fd240998a3cbdbe87d700e30bef494885eccc725ab73b668020811d56623d145b524ae - languageName: node - linkType: hard - -"@npmcli/fs@npm:^3.1.0": - version: 3.1.1 - resolution: "@npmcli/fs@npm:3.1.1" - dependencies: - semver: "npm:^7.3.5" - checksum: 10c0/c37a5b4842bfdece3d14dfdb054f73fe15ed2d3da61b34ff76629fb5b1731647c49166fd2a8bf8b56fcfa51200382385ea8909a3cbecdad612310c114d3f6c99 - languageName: node - linkType: hard - -"@pkgjs/parseargs@npm:^0.11.0": - version: 0.11.0 - resolution: "@pkgjs/parseargs@npm:0.11.0" - checksum: 10c0/5bd7576bb1b38a47a7fc7b51ac9f38748e772beebc56200450c4a817d712232b8f1d3ef70532c80840243c657d491cf6a6be1e3a214cff907645819fdc34aadd - languageName: node - linkType: hard - -"@sec-ant/readable-stream@npm:^0.4.1": - version: 0.4.1 - resolution: "@sec-ant/readable-stream@npm:0.4.1" - checksum: 10c0/64e9e9cf161e848067a5bf60cdc04d18495dc28bb63a8d9f8993e4dd99b91ad34e4b563c85de17d91ffb177ec17a0664991d2e115f6543e73236a906068987af - languageName: node - linkType: hard - -"@sindresorhus/merge-streams@npm:^4.0.0": - version: 4.0.0 - resolution: "@sindresorhus/merge-streams@npm:4.0.0" - checksum: 10c0/482ee543629aa1933b332f811a1ae805a213681ecdd98c042b1c1b89387df63e7812248bb4df3910b02b3cc5589d3d73e4393f30e197c9dde18046ccd471fc6b - languageName: node - linkType: hard - -"abbrev@npm:^2.0.0": - version: 2.0.0 - resolution: "abbrev@npm:2.0.0" - checksum: 10c0/f742a5a107473946f426c691c08daba61a1d15942616f300b5d32fd735be88fef5cba24201757b6c407fd564555fb48c751cfa33519b2605c8a7aadd22baf372 - languageName: node - linkType: hard - -"acorn-walk@npm:^8.2.0": - version: 8.3.4 - resolution: "acorn-walk@npm:8.3.4" - dependencies: - acorn: "npm:^8.11.0" - checksum: 10c0/76537ac5fb2c37a64560feaf3342023dadc086c46da57da363e64c6148dc21b57d49ace26f949e225063acb6fb441eabffd89f7a3066de5ad37ab3e328927c62 - languageName: node - linkType: hard - -"acorn@npm:^8.11.0, acorn@npm:^8.8.2": - version: 8.12.1 - resolution: "acorn@npm:8.12.1" - bin: - acorn: bin/acorn - checksum: 10c0/51fb26cd678f914e13287e886da2d7021f8c2bc0ccc95e03d3e0447ee278dd3b40b9c57dc222acd5881adcf26f3edc40901a4953403232129e3876793cd17386 - languageName: node - linkType: hard - -"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1": - version: 7.1.1 - resolution: "agent-base@npm:7.1.1" - dependencies: - debug: "npm:^4.3.4" - checksum: 10c0/e59ce7bed9c63bf071a30cc471f2933862044c97fd9958967bfe22521d7a0f601ce4ed5a8c011799d0c726ca70312142ae193bbebb60f576b52be19d4a363b50 - languageName: node - linkType: hard - -"aggregate-error@npm:^3.0.0": - version: 3.1.0 - resolution: "aggregate-error@npm:3.1.0" - dependencies: - clean-stack: "npm:^2.0.0" - indent-string: "npm:^4.0.0" - checksum: 10c0/a42f67faa79e3e6687a4923050e7c9807db3848a037076f791d10e092677d65c1d2d863b7848560699f40fc0502c19f40963fb1cd1fb3d338a7423df8e45e039 - languageName: node - linkType: hard - -"aggregate-error@npm:^4.0.0": - version: 4.0.1 - resolution: "aggregate-error@npm:4.0.1" - dependencies: - clean-stack: "npm:^4.0.0" - indent-string: "npm:^5.0.0" - checksum: 10c0/75fd739f5c4c60a667cce35ccaf0edf135e147ef0be9a029cab75de14ac9421779b15339d562e58d25b233ea0ef2bbd4c916f149fdbcb73c2b9a62209e611343 - languageName: node - linkType: hard - -"ansi-regex@npm:^5.0.1": - version: 5.0.1 - resolution: "ansi-regex@npm:5.0.1" - checksum: 10c0/9a64bb8627b434ba9327b60c027742e5d17ac69277960d041898596271d992d4d52ba7267a63ca10232e29f6107fc8a835f6ce8d719b88c5f8493f8254813737 - languageName: node - linkType: hard - -"ansi-regex@npm:^6.0.1": - version: 6.1.0 - resolution: "ansi-regex@npm:6.1.0" - checksum: 10c0/a91daeddd54746338478eef88af3439a7edf30f8e23196e2d6ed182da9add559c601266dbef01c2efa46a958ad6f1f8b176799657616c702b5b02e799e7fd8dc - languageName: node - linkType: hard - -"ansi-styles@npm:^4.0.0": - version: 4.3.0 - resolution: "ansi-styles@npm:4.3.0" - dependencies: - color-convert: "npm:^2.0.1" - checksum: 10c0/895a23929da416f2bd3de7e9cb4eabd340949328ab85ddd6e484a637d8f6820d485f53933446f5291c3b760cbc488beb8e88573dd0f9c7daf83dccc8fe81b041 - languageName: node - linkType: hard - -"ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": - version: 6.2.1 - resolution: "ansi-styles@npm:6.2.1" - checksum: 10c0/5d1ec38c123984bcedd996eac680d548f31828bd679a66db2bdf11844634dde55fec3efa9c6bb1d89056a5e79c1ac540c4c784d592ea1d25028a92227d2f2d5c - languageName: node - linkType: hard - -"anymatch@npm:~3.1.2": - version: 3.1.3 - resolution: "anymatch@npm:3.1.3" - dependencies: - normalize-path: "npm:^3.0.0" - picomatch: "npm:^2.0.4" - checksum: 10c0/57b06ae984bc32a0d22592c87384cd88fe4511b1dd7581497831c56d41939c8a001b28e7b853e1450f2bf61992dfcaa8ae2d0d161a0a90c4fb631ef07098fbac - languageName: node - linkType: hard - -"argparse@npm:^1.0.7": - version: 1.0.10 - resolution: "argparse@npm:1.0.10" - dependencies: - sprintf-js: "npm:~1.0.2" - checksum: 10c0/b2972c5c23c63df66bca144dbc65d180efa74f25f8fd9b7d9a0a6c88ae839db32df3d54770dcb6460cf840d232b60695d1a6b1053f599d84e73f7437087712de - languageName: node - linkType: hard - -"array-find-index@npm:^1.0.1": - version: 1.0.2 - resolution: "array-find-index@npm:1.0.2" - checksum: 10c0/86b9485c74ddd324feab807e10a6de3f9c1683856267236fac4bb4d4667ada6463e106db3f6c540ae6b720e0442b590ec701d13676df4c6af30ebf4da09b4f57 - languageName: node - linkType: hard - -"arrgv@npm:^1.0.2": - version: 1.0.2 - resolution: "arrgv@npm:1.0.2" - checksum: 10c0/7e6e782e6b749923ac7cbc4048ef6fe0844c4a59bfc8932fcd4c44566ba25eed46501f94dd7cf3c7297da88f3f599ca056bfb77d0c2484aebc92f04239f69124 - languageName: node - linkType: hard - -"arrify@npm:^3.0.0": - version: 3.0.0 - resolution: "arrify@npm:3.0.0" - checksum: 10c0/2e26601b8486f29780f1f70f7ac05a226755814c2a3ab42e196748f650af1dc310cd575a11dd4b9841c70fd7460b2dd2b8fe6fb7a3375878e2660706efafa58e - languageName: node - linkType: hard - -"ava@npm:^5.3.1": - version: 5.3.1 - resolution: "ava@npm:5.3.1" - dependencies: - acorn: "npm:^8.8.2" - acorn-walk: "npm:^8.2.0" - ansi-styles: "npm:^6.2.1" - arrgv: "npm:^1.0.2" - arrify: "npm:^3.0.0" - callsites: "npm:^4.0.0" - cbor: "npm:^8.1.0" - chalk: "npm:^5.2.0" - chokidar: "npm:^3.5.3" - chunkd: "npm:^2.0.1" - ci-info: "npm:^3.8.0" - ci-parallel-vars: "npm:^1.0.1" - clean-yaml-object: "npm:^0.1.0" - cli-truncate: "npm:^3.1.0" - code-excerpt: "npm:^4.0.0" - common-path-prefix: "npm:^3.0.0" - concordance: "npm:^5.0.4" - currently-unhandled: "npm:^0.4.1" - debug: "npm:^4.3.4" - emittery: "npm:^1.0.1" - figures: "npm:^5.0.0" - globby: "npm:^13.1.4" - ignore-by-default: "npm:^2.1.0" - indent-string: "npm:^5.0.0" - is-error: "npm:^2.2.2" - is-plain-object: "npm:^5.0.0" - is-promise: "npm:^4.0.0" - matcher: "npm:^5.0.0" - mem: "npm:^9.0.2" - ms: "npm:^2.1.3" - p-event: "npm:^5.0.1" - p-map: "npm:^5.5.0" - picomatch: "npm:^2.3.1" - pkg-conf: "npm:^4.0.0" - plur: "npm:^5.1.0" - pretty-ms: "npm:^8.0.0" - resolve-cwd: "npm:^3.0.0" - stack-utils: "npm:^2.0.6" - strip-ansi: "npm:^7.0.1" - supertap: "npm:^3.0.1" - temp-dir: "npm:^3.0.0" - write-file-atomic: "npm:^5.0.1" - yargs: "npm:^17.7.2" - peerDependencies: - "@ava/typescript": "*" - peerDependenciesMeta: - "@ava/typescript": - optional: true - bin: - ava: entrypoints/cli.mjs - checksum: 10c0/262cbdb9e8c3ce7177be91b92ba521e9d5aef577dcc8095cc591f86baaa291b91c88925928f5d26832c4d1b381a6ae99f2e8804077c592d0d32322c1212605cc - languageName: node - linkType: hard - -"balanced-match@npm:^1.0.0": - version: 1.0.2 - resolution: "balanced-match@npm:1.0.2" - checksum: 10c0/9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee - languageName: node - linkType: hard - -"base64-js@npm:^1.3.1": - version: 1.5.1 - resolution: "base64-js@npm:1.5.1" - checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf - languageName: node - linkType: hard - -"better-sqlite3@npm:^9.6.0": - version: 9.6.0 - resolution: "better-sqlite3@npm:9.6.0" - dependencies: - bindings: "npm:^1.5.0" - node-gyp: "npm:latest" - prebuild-install: "npm:^7.1.1" - checksum: 10c0/8db9b38f414e26a56d4c40fc16e94a253118491dae0e2c054338a9e470f1a883c7eb4cb330f2f5737db30f704d4f2e697c59071ca04e03364ee9fe04375aa9c8 - languageName: node - linkType: hard - -"binary-extensions@npm:^2.0.0": - version: 2.3.0 - resolution: "binary-extensions@npm:2.3.0" - checksum: 10c0/75a59cafc10fb12a11d510e77110c6c7ae3f4ca22463d52487709ca7f18f69d886aa387557cc9864fbdb10153d0bdb4caacabf11541f55e89ed6e18d12ece2b5 - languageName: node - linkType: hard - -"bindings@npm:^1.5.0": - version: 1.5.0 - resolution: "bindings@npm:1.5.0" - dependencies: - file-uri-to-path: "npm:1.0.0" - checksum: 10c0/3dab2491b4bb24124252a91e656803eac24292473e56554e35bbfe3cc1875332cfa77600c3bac7564049dc95075bf6fcc63a4609920ff2d64d0fe405fcf0d4ba - languageName: node - linkType: hard - -"bl@npm:^4.0.3": - version: 4.1.0 - resolution: "bl@npm:4.1.0" - dependencies: - buffer: "npm:^5.5.0" - inherits: "npm:^2.0.4" - readable-stream: "npm:^3.4.0" - checksum: 10c0/02847e1d2cb089c9dc6958add42e3cdeaf07d13f575973963335ac0fdece563a50ac770ac4c8fa06492d2dd276f6cc3b7f08c7cd9c7a7ad0f8d388b2a28def5f - languageName: node - linkType: hard - -"blueimp-md5@npm:^2.10.0": - version: 2.19.0 - resolution: "blueimp-md5@npm:2.19.0" - checksum: 10c0/85d04343537dd99a288c62450341dcce7380d3454c81f8e5a971ddd80307d6f9ef51b5b92ad7d48aaaa92fd6d3a1f6b2f4fada068faae646887f7bfabc17a346 - languageName: node - linkType: hard - -"brace-expansion@npm:^2.0.1": - version: 2.0.1 - resolution: "brace-expansion@npm:2.0.1" - dependencies: - balanced-match: "npm:^1.0.0" - checksum: 10c0/b358f2fe060e2d7a87aa015979ecea07f3c37d4018f8d6deb5bd4c229ad3a0384fe6029bb76cd8be63c81e516ee52d1a0673edbe2023d53a5191732ae3c3e49f - languageName: node - linkType: hard - -"braces@npm:^3.0.3, braces@npm:~3.0.2": - version: 3.0.3 - resolution: "braces@npm:3.0.3" - dependencies: - fill-range: "npm:^7.1.1" - checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04 - languageName: node - linkType: hard - -"buffer@npm:^5.5.0": - version: 5.7.1 - resolution: "buffer@npm:5.7.1" - dependencies: - base64-js: "npm:^1.3.1" - ieee754: "npm:^1.1.13" - checksum: 10c0/27cac81cff434ed2876058d72e7c4789d11ff1120ef32c9de48f59eab58179b66710c488987d295ae89a228f835fc66d088652dffeb8e3ba8659f80eb091d55e - languageName: node - linkType: hard - -"cacache@npm:^18.0.0": - version: 18.0.4 - resolution: "cacache@npm:18.0.4" - dependencies: - "@npmcli/fs": "npm:^3.1.0" - fs-minipass: "npm:^3.0.0" - glob: "npm:^10.2.2" - lru-cache: "npm:^10.0.1" - minipass: "npm:^7.0.3" - minipass-collect: "npm:^2.0.1" - minipass-flush: "npm:^1.0.5" - minipass-pipeline: "npm:^1.2.4" - p-map: "npm:^4.0.0" - ssri: "npm:^10.0.0" - tar: "npm:^6.1.11" - unique-filename: "npm:^3.0.0" - checksum: 10c0/6c055bafed9de4f3dcc64ac3dc7dd24e863210902b7c470eb9ce55a806309b3efff78033e3d8b4f7dcc5d467f2db43c6a2857aaaf26f0094b8a351d44c42179f - languageName: node - linkType: hard - -"callsites@npm:^4.0.0": - version: 4.2.0 - resolution: "callsites@npm:4.2.0" - checksum: 10c0/8f7e269ec09fc0946bb22d838a8bc7932e1909ab4a833b964749f4d0e8bdeaa1f253287c4f911f61781f09620b6925ccd19a5ea4897489c4e59442c660c312a3 - languageName: node - linkType: hard - -"cbor@npm:^8.1.0": - version: 8.1.0 - resolution: "cbor@npm:8.1.0" - dependencies: - nofilter: "npm:^3.1.0" - checksum: 10c0/a836e2e7ea0efb1b9c4e5a4be906c57113d730cc42293a34072e0164ed110bb8ac035dc7dca2e3ebb641bd4b37e00fdbbf09c951aa864b3d4888a6ed8c6243f7 - languageName: node - linkType: hard - -"chalk@npm:^5.2.0, chalk@npm:^5.3.0": - version: 5.3.0 - resolution: "chalk@npm:5.3.0" - checksum: 10c0/8297d436b2c0f95801103ff2ef67268d362021b8210daf8ddbe349695333eb3610a71122172ff3b0272f1ef2cf7cc2c41fdaa4715f52e49ffe04c56340feed09 - languageName: node - linkType: hard - -"chokidar@npm:^3.5.3": - version: 3.6.0 - resolution: "chokidar@npm:3.6.0" - dependencies: - anymatch: "npm:~3.1.2" - braces: "npm:~3.0.2" - fsevents: "npm:~2.3.2" - glob-parent: "npm:~5.1.2" - is-binary-path: "npm:~2.1.0" - is-glob: "npm:~4.0.1" - normalize-path: "npm:~3.0.0" - readdirp: "npm:~3.6.0" - dependenciesMeta: - fsevents: - optional: true - checksum: 10c0/8361dcd013f2ddbe260eacb1f3cb2f2c6f2b0ad118708a343a5ed8158941a39cb8fb1d272e0f389712e74ee90ce8ba864eece9e0e62b9705cb468a2f6d917462 - languageName: node - linkType: hard - -"chownr@npm:^1.1.1": - version: 1.1.4 - resolution: "chownr@npm:1.1.4" - checksum: 10c0/ed57952a84cc0c802af900cf7136de643d3aba2eecb59d29344bc2f3f9bf703a301b9d84cdc71f82c3ffc9ccde831b0d92f5b45f91727d6c9da62f23aef9d9db - languageName: node - linkType: hard - -"chownr@npm:^2.0.0": - version: 2.0.0 - resolution: "chownr@npm:2.0.0" - checksum: 10c0/594754e1303672171cc04e50f6c398ae16128eb134a88f801bf5354fd96f205320f23536a045d9abd8b51024a149696e51231565891d4efdab8846021ecf88e6 - languageName: node - linkType: hard - -"chunkd@npm:^2.0.1": - version: 2.0.1 - resolution: "chunkd@npm:2.0.1" - checksum: 10c0/4e0c5aac6048ecedfa4cd0a5f6c4f010c70a7b7645aeca7bfeb47cb0733c3463054f0ced3f2667b2e0e67edd75d68a8e05481b01115ba3f8a952a93026254504 - languageName: node - linkType: hard - -"ci-info@npm:^3.8.0": - version: 3.9.0 - resolution: "ci-info@npm:3.9.0" - checksum: 10c0/6f0109e36e111684291d46123d491bc4e7b7a1934c3a20dea28cba89f1d4a03acd892f5f6a81ed3855c38647e285a150e3c9ba062e38943bef57fee6c1554c3a - languageName: node - linkType: hard - -"ci-parallel-vars@npm:^1.0.1": - version: 1.0.1 - resolution: "ci-parallel-vars@npm:1.0.1" - checksum: 10c0/80952f699cbbc146092b077b4f3e28d085620eb4e6be37f069b4dbb3db0ee70e8eec3beef4ebe70ff60631e9fc743b9d0869678489f167442cac08b260e5ac08 - languageName: node - linkType: hard - -"clean-stack@npm:^2.0.0": - version: 2.2.0 - resolution: "clean-stack@npm:2.2.0" - checksum: 10c0/1f90262d5f6230a17e27d0c190b09d47ebe7efdd76a03b5a1127863f7b3c9aec4c3e6c8bb3a7bbf81d553d56a1fd35728f5a8ef4c63f867ac8d690109742a8c1 - languageName: node - linkType: hard - -"clean-stack@npm:^4.0.0": - version: 4.2.0 - resolution: "clean-stack@npm:4.2.0" - dependencies: - escape-string-regexp: "npm:5.0.0" - checksum: 10c0/2bdf981a0fef0a23c14255df693b30eb9ae27eedf212470d8c400a0c0b6fb82fbf1ff8c5216ccd5721e3670b700389c886b1dce5070776dc9fbcc040957758c0 - languageName: node - linkType: hard - -"clean-yaml-object@npm:^0.1.0": - version: 0.1.0 - resolution: "clean-yaml-object@npm:0.1.0" - checksum: 10c0/a6505310590038afb9f0adc7f17a4c66787719c94d23f8491267ea4d9c405cdd378bd576ae1926169b6d997d4c59a8b86516bf4d16ba228280cf615598c58e05 - languageName: node - linkType: hard - -"cli-truncate@npm:^3.1.0": - version: 3.1.0 - resolution: "cli-truncate@npm:3.1.0" - dependencies: - slice-ansi: "npm:^5.0.0" - string-width: "npm:^5.0.0" - checksum: 10c0/a19088878409ec0e5dc2659a5166929629d93cfba6d68afc9cde2282fd4c751af5b555bf197047e31c87c574396348d011b7aa806fec29c4139ea4f7f00b324c - languageName: node - linkType: hard - -"cliui@npm:^8.0.1": - version: 8.0.1 - resolution: "cliui@npm:8.0.1" - dependencies: - string-width: "npm:^4.2.0" - strip-ansi: "npm:^6.0.1" - wrap-ansi: "npm:^7.0.0" - checksum: 10c0/4bda0f09c340cbb6dfdc1ed508b3ca080f12992c18d68c6be4d9cf51756033d5266e61ec57529e610dacbf4da1c634423b0c1b11037709cc6b09045cbd815df5 - languageName: node - linkType: hard - -"code-excerpt@npm:^4.0.0": - version: 4.0.0 - resolution: "code-excerpt@npm:4.0.0" - dependencies: - convert-to-spaces: "npm:^2.0.1" - checksum: 10c0/b6c5a06e039cecd2ab6a0e10ee0831de8362107d1f298ca3558b5f9004cb8e0260b02dd6c07f57b9a0e346c76864d2873311ee1989809fdeb05bd5fbbadde773 - languageName: node - linkType: hard - -"color-convert@npm:^2.0.1": - version: 2.0.1 - resolution: "color-convert@npm:2.0.1" - dependencies: - color-name: "npm:~1.1.4" - checksum: 10c0/37e1150172f2e311fe1b2df62c6293a342ee7380da7b9cfdba67ea539909afbd74da27033208d01d6d5cfc65ee7868a22e18d7e7648e004425441c0f8a15a7d7 - languageName: node - linkType: hard - -"color-name@npm:~1.1.4": - version: 1.1.4 - resolution: "color-name@npm:1.1.4" - checksum: 10c0/a1a3f914156960902f46f7f56bc62effc6c94e84b2cae157a526b1c1f74b677a47ec602bf68a61abfa2b42d15b7c5651c6dbe72a43af720bc588dff885b10f95 - languageName: node - linkType: hard - -"common-path-prefix@npm:^3.0.0": - version: 3.0.0 - resolution: "common-path-prefix@npm:3.0.0" - checksum: 10c0/c4a74294e1b1570f4a8ab435285d185a03976c323caa16359053e749db4fde44e3e6586c29cd051100335e11895767cbbd27ea389108e327d62f38daf4548fdb - languageName: node - linkType: hard - -"concordance@npm:^5.0.4": - version: 5.0.4 - resolution: "concordance@npm:5.0.4" - dependencies: - date-time: "npm:^3.1.0" - esutils: "npm:^2.0.3" - fast-diff: "npm:^1.2.0" - js-string-escape: "npm:^1.0.1" - lodash: "npm:^4.17.15" - md5-hex: "npm:^3.0.1" - semver: "npm:^7.3.2" - well-known-symbols: "npm:^2.0.0" - checksum: 10c0/59b440f330df3a7c9aa148ba588b3e99aed86acab225b4f01ffcea34ace4cf11f817e31153254e8f38ed48508998dad40b9106951a743c334d751f7ab21afb8a - languageName: node - linkType: hard - -"convert-to-spaces@npm:^2.0.1": - version: 2.0.1 - resolution: "convert-to-spaces@npm:2.0.1" - checksum: 10c0/d90aa0e3b6a27f9d5265a8d32def3c5c855b3e823a9db1f26d772f8146d6b91020a2fdfd905ce8048a73fad3aaf836fef8188c67602c374405e2ae8396c4ac46 - languageName: node - linkType: hard - -"cosmjs-types@npm:^0.9.0": - version: 0.9.0 - resolution: "cosmjs-types@npm:0.9.0" - checksum: 10c0/bc20f4293fb34629d7c5f96bafe533987f753df957ff68eb078d0128ae5a418320cb945024441769a07bb9bc5dde9d22b972fd40d485933e5706ea191c43727b - languageName: node - linkType: hard - -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3": - version: 7.0.3 - resolution: "cross-spawn@npm:7.0.3" - dependencies: - path-key: "npm:^3.1.0" - shebang-command: "npm:^2.0.0" - which: "npm:^2.0.1" - checksum: 10c0/5738c312387081c98d69c98e105b6327b069197f864a60593245d64c8089c8a0a744e16349281210d56835bb9274130d825a78b2ad6853ca13cfbeffc0c31750 - languageName: node - linkType: hard - -"currently-unhandled@npm:^0.4.1": - version: 0.4.1 - resolution: "currently-unhandled@npm:0.4.1" - dependencies: - array-find-index: "npm:^1.0.1" - checksum: 10c0/32d197689ec32f035910202c1abb0dc6424dce01d7b51779c685119b380d98535c110ffff67a262fc7e367612a7dfd30d3d3055f9a6634b5a9dd1302de7ef11c - languageName: node - linkType: hard - -"date-time@npm:^3.1.0": - version: 3.1.0 - resolution: "date-time@npm:3.1.0" - dependencies: - time-zone: "npm:^1.0.0" - checksum: 10c0/aa3e2e930d74b0b9e90f69de7a16d3376e30f21f1f4ce9a2311d8fec32d760e776efea752dafad0ce188187265235229013036202be053fc2d7979813bfb6ded - languageName: node - linkType: hard - -"debug@npm:4, debug@npm:^4.3.4": - version: 4.3.7 - resolution: "debug@npm:4.3.7" - dependencies: - ms: "npm:^2.1.3" - peerDependenciesMeta: - supports-color: - optional: true - checksum: 10c0/1471db19c3b06d485a622d62f65947a19a23fbd0dd73f7fd3eafb697eec5360cde447fb075919987899b1a2096e85d35d4eb5a4de09a57600ac9cf7e6c8e768b - languageName: node - linkType: hard - -"decompress-response@npm:^6.0.0": - version: 6.0.0 - resolution: "decompress-response@npm:6.0.0" - dependencies: - mimic-response: "npm:^3.1.0" - checksum: 10c0/bd89d23141b96d80577e70c54fb226b2f40e74a6817652b80a116d7befb8758261ad073a8895648a29cc0a5947021ab66705cb542fa9c143c82022b27c5b175e - languageName: node - linkType: hard - -"deep-extend@npm:^0.6.0": - version: 0.6.0 - resolution: "deep-extend@npm:0.6.0" - checksum: 10c0/1c6b0abcdb901e13a44c7d699116d3d4279fdb261983122a3783e7273844d5f2537dc2e1c454a23fcf645917f93fbf8d07101c1d03c015a87faa662755212566 - languageName: node - linkType: hard - -"detect-libc@npm:^2.0.0": - version: 2.0.3 - resolution: "detect-libc@npm:2.0.3" - checksum: 10c0/88095bda8f90220c95f162bf92cad70bd0e424913e655c20578600e35b91edc261af27531cf160a331e185c0ced93944bc7e09939143225f56312d7fd800fdb7 - languageName: node - linkType: hard - -"dir-glob@npm:^3.0.1": - version: 3.0.1 - resolution: "dir-glob@npm:3.0.1" - dependencies: - path-type: "npm:^4.0.0" - checksum: 10c0/dcac00920a4d503e38bb64001acb19df4efc14536ada475725e12f52c16777afdee4db827f55f13a908ee7efc0cb282e2e3dbaeeb98c0993dd93d1802d3bf00c - languageName: node - linkType: hard - -"eastasianwidth@npm:^0.2.0": - version: 0.2.0 - resolution: "eastasianwidth@npm:0.2.0" - checksum: 10c0/26f364ebcdb6395f95124fda411f63137a4bfb5d3a06453f7f23dfe52502905bd84e0488172e0f9ec295fdc45f05c23d5d91baf16bd26f0fe9acd777a188dc39 - languageName: node - linkType: hard - -"emittery@npm:^1.0.1": - version: 1.0.3 - resolution: "emittery@npm:1.0.3" - checksum: 10c0/91605d044f3891dd1f8ab731aeb94b520488b21e707f7064dcbcf5303bac3b4e7133dfa23c343ede1fc970340bd78a9b1aed522b805bc15104606bba630dd71e - languageName: node - linkType: hard - -"emoji-regex@npm:^8.0.0": - version: 8.0.0 - resolution: "emoji-regex@npm:8.0.0" - checksum: 10c0/b6053ad39951c4cf338f9092d7bfba448cdfd46fe6a2a034700b149ac9ffbc137e361cbd3c442297f86bed2e5f7576c1b54cc0a6bf8ef5106cc62f496af35010 - languageName: node - linkType: hard - -"emoji-regex@npm:^9.2.2": - version: 9.2.2 - resolution: "emoji-regex@npm:9.2.2" - checksum: 10c0/af014e759a72064cf66e6e694a7fc6b0ed3d8db680427b021a89727689671cefe9d04151b2cad51dbaf85d5ba790d061cd167f1cf32eb7b281f6368b3c181639 - languageName: node - linkType: hard - -"encoding@npm:^0.1.13": - version: 0.1.13 - resolution: "encoding@npm:0.1.13" - dependencies: - iconv-lite: "npm:^0.6.2" - checksum: 10c0/36d938712ff00fe1f4bac88b43bcffb5930c1efa57bbcdca9d67e1d9d6c57cfb1200fb01efe0f3109b2ce99b231f90779532814a81370a1bd3274a0f58585039 - languageName: node - linkType: hard - -"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1": - version: 1.4.4 - resolution: "end-of-stream@npm:1.4.4" - dependencies: - once: "npm:^1.4.0" - checksum: 10c0/870b423afb2d54bb8d243c63e07c170409d41e20b47eeef0727547aea5740bd6717aca45597a9f2745525667a6b804c1e7bede41f856818faee5806dd9ff3975 - languageName: node - linkType: hard - -"env-paths@npm:^2.2.0": - version: 2.2.1 - resolution: "env-paths@npm:2.2.1" - checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4 - languageName: node - linkType: hard - -"err-code@npm:^2.0.2": - version: 2.0.3 - resolution: "err-code@npm:2.0.3" - checksum: 10c0/b642f7b4dd4a376e954947550a3065a9ece6733ab8e51ad80db727aaae0817c2e99b02a97a3d6cecc648a97848305e728289cf312d09af395403a90c9d4d8a66 - languageName: node - linkType: hard - -"escalade@npm:^3.1.1": - version: 3.2.0 - resolution: "escalade@npm:3.2.0" - checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65 - languageName: node - linkType: hard - -"escape-string-regexp@npm:5.0.0, escape-string-regexp@npm:^5.0.0": - version: 5.0.0 - resolution: "escape-string-regexp@npm:5.0.0" - checksum: 10c0/6366f474c6f37a802800a435232395e04e9885919873e382b157ab7e8f0feb8fed71497f84a6f6a81a49aab41815522f5839112bd38026d203aea0c91622df95 - languageName: node - linkType: hard - -"escape-string-regexp@npm:^2.0.0": - version: 2.0.0 - resolution: "escape-string-regexp@npm:2.0.0" - checksum: 10c0/2530479fe8db57eace5e8646c9c2a9c80fa279614986d16dcc6bcaceb63ae77f05a851ba6c43756d816c61d7f4534baf56e3c705e3e0d884818a46808811c507 - languageName: node - linkType: hard - -"esprima@npm:^4.0.0": - version: 4.0.1 - resolution: "esprima@npm:4.0.1" - bin: - esparse: ./bin/esparse.js - esvalidate: ./bin/esvalidate.js - checksum: 10c0/ad4bab9ead0808cf56501750fd9d3fb276f6b105f987707d059005d57e182d18a7c9ec7f3a01794ebddcca676773e42ca48a32d67a250c9d35e009ca613caba3 - languageName: node - linkType: hard - -"esutils@npm:^2.0.3": - version: 2.0.3 - resolution: "esutils@npm:2.0.3" - checksum: 10c0/9a2fe69a41bfdade834ba7c42de4723c97ec776e40656919c62cbd13607c45e127a003f05f724a1ea55e5029a4cf2de444b13009f2af71271e42d93a637137c7 - languageName: node - linkType: hard - -"execa@npm:^9.3.1": - version: 9.4.0 - resolution: "execa@npm:9.4.0" - dependencies: - "@sindresorhus/merge-streams": "npm:^4.0.0" - cross-spawn: "npm:^7.0.3" - figures: "npm:^6.1.0" - get-stream: "npm:^9.0.0" - human-signals: "npm:^8.0.0" - is-plain-obj: "npm:^4.1.0" - is-stream: "npm:^4.0.1" - npm-run-path: "npm:^6.0.0" - pretty-ms: "npm:^9.0.0" - signal-exit: "npm:^4.1.0" - strip-final-newline: "npm:^4.0.0" - yoctocolors: "npm:^2.0.0" - checksum: 10c0/6ad06c627b5d7bb007bc7b6cc35d7e32b5a3365375ffc8ddbcc12d2423651fa9928ba0c447cc9e60079e505e9b24fbe0a57f80371511d7d20302c04c2d3ce95e - languageName: node - linkType: hard - -"expand-template@npm:^2.0.3": - version: 2.0.3 - resolution: "expand-template@npm:2.0.3" - checksum: 10c0/1c9e7afe9acadf9d373301d27f6a47b34e89b3391b1ef38b7471d381812537ef2457e620ae7f819d2642ce9c43b189b3583813ec395e2938319abe356a9b2f51 - languageName: node - linkType: hard - -"exponential-backoff@npm:^3.1.1": - version: 3.1.1 - resolution: "exponential-backoff@npm:3.1.1" - checksum: 10c0/160456d2d647e6019640bd07111634d8c353038d9fa40176afb7cd49b0548bdae83b56d05e907c2cce2300b81cae35d800ef92fefb9d0208e190fa3b7d6bb579 - languageName: node - linkType: hard - -"fast-diff@npm:^1.2.0": - version: 1.3.0 - resolution: "fast-diff@npm:1.3.0" - checksum: 10c0/5c19af237edb5d5effda008c891a18a585f74bf12953be57923f17a3a4d0979565fc64dbc73b9e20926b9d895f5b690c618cbb969af0cf022e3222471220ad29 - languageName: node - linkType: hard - -"fast-glob@npm:^3.3.0": - version: 3.3.2 - resolution: "fast-glob@npm:3.3.2" - dependencies: - "@nodelib/fs.stat": "npm:^2.0.2" - "@nodelib/fs.walk": "npm:^1.2.3" - glob-parent: "npm:^5.1.2" - merge2: "npm:^1.3.0" - micromatch: "npm:^4.0.4" - checksum: 10c0/42baad7b9cd40b63e42039132bde27ca2cb3a4950d0a0f9abe4639ea1aa9d3e3b40f98b1fe31cbc0cc17b664c9ea7447d911a152fa34ec5b72977b125a6fc845 - languageName: node - linkType: hard - -"fastq@npm:^1.6.0": - version: 1.17.1 - resolution: "fastq@npm:1.17.1" - dependencies: - reusify: "npm:^1.0.4" - checksum: 10c0/1095f16cea45fb3beff558bb3afa74ca7a9250f5a670b65db7ed585f92b4b48381445cd328b3d87323da81e43232b5d5978a8201bde84e0cd514310f1ea6da34 - languageName: node - linkType: hard - -"figures@npm:^5.0.0": - version: 5.0.0 - resolution: "figures@npm:5.0.0" - dependencies: - escape-string-regexp: "npm:^5.0.0" - is-unicode-supported: "npm:^1.2.0" - checksum: 10c0/ce0f17d4ea8b0fc429c5207c343534a2f5284ecfb22aa08607da7dc84ed9e1cf754f5b97760e8dcb98d3c9d1a1e4d3d578fe3b5b99c426f05d0f06c7ba618e16 - languageName: node - linkType: hard - -"figures@npm:^6.1.0": - version: 6.1.0 - resolution: "figures@npm:6.1.0" - dependencies: - is-unicode-supported: "npm:^2.0.0" - checksum: 10c0/9159df4264d62ef447a3931537de92f5012210cf5135c35c010df50a2169377581378149abfe1eb238bd6acbba1c0d547b1f18e0af6eee49e30363cedaffcfe4 - languageName: node - linkType: hard - -"file-uri-to-path@npm:1.0.0": - version: 1.0.0 - resolution: "file-uri-to-path@npm:1.0.0" - checksum: 10c0/3b545e3a341d322d368e880e1c204ef55f1d45cdea65f7efc6c6ce9e0c4d22d802d5629320eb779d006fe59624ac17b0e848d83cc5af7cd101f206cb704f5519 - languageName: node - linkType: hard - -"fill-range@npm:^7.1.1": - version: 7.1.1 - resolution: "fill-range@npm:7.1.1" - dependencies: - to-regex-range: "npm:^5.0.1" - checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018 - languageName: node - linkType: hard - -"find-up@npm:^6.0.0": - version: 6.3.0 - resolution: "find-up@npm:6.3.0" - dependencies: - locate-path: "npm:^7.1.0" - path-exists: "npm:^5.0.0" - checksum: 10c0/07e0314362d316b2b13f7f11ea4692d5191e718ca3f7264110127520f3347996349bf9e16805abae3e196805814bc66ef4bff2b8904dc4a6476085fc9b0eba07 - languageName: node - linkType: hard - -"foreground-child@npm:^3.1.0": - version: 3.3.0 - resolution: "foreground-child@npm:3.3.0" - dependencies: - cross-spawn: "npm:^7.0.0" - signal-exit: "npm:^4.0.1" - checksum: 10c0/028f1d41000553fcfa6c4bb5c372963bf3d9bf0b1f25a87d1a6253014343fb69dfb1b42d9625d7cf44c8ba429940f3d0ff718b62105d4d4a4f6ef8ca0a53faa2 - languageName: node - linkType: hard - -"fs-constants@npm:^1.0.0": - version: 1.0.0 - resolution: "fs-constants@npm:1.0.0" - checksum: 10c0/a0cde99085f0872f4d244e83e03a46aa387b74f5a5af750896c6b05e9077fac00e9932fdf5aef84f2f16634cd473c63037d7a512576da7d5c2b9163d1909f3a8 - languageName: node - linkType: hard - -"fs-minipass@npm:^2.0.0": - version: 2.1.0 - resolution: "fs-minipass@npm:2.1.0" - dependencies: - minipass: "npm:^3.0.0" - checksum: 10c0/703d16522b8282d7299337539c3ed6edddd1afe82435e4f5b76e34a79cd74e488a8a0e26a636afc2440e1a23b03878e2122e3a2cfe375a5cf63c37d92b86a004 - languageName: node - linkType: hard - -"fs-minipass@npm:^3.0.0": - version: 3.0.3 - resolution: "fs-minipass@npm:3.0.3" - dependencies: - minipass: "npm:^7.0.3" - checksum: 10c0/63e80da2ff9b621e2cb1596abcb9207f1cf82b968b116ccd7b959e3323144cce7fb141462200971c38bbf2ecca51695069db45265705bed09a7cd93ae5b89f94 - languageName: node - linkType: hard - -"fsevents@npm:~2.3.2": - version: 2.3.3 - resolution: "fsevents@npm:2.3.3" - dependencies: - node-gyp: "npm:latest" - checksum: 10c0/a1f0c44595123ed717febbc478aa952e47adfc28e2092be66b8ab1635147254ca6cfe1df792a8997f22716d4cbafc73309899ff7bfac2ac3ad8cf2e4ecc3ec60 - conditions: os=darwin - languageName: node - linkType: hard - -"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": - version: 2.3.3 - resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" - dependencies: - node-gyp: "npm:latest" - conditions: os=darwin - languageName: node - linkType: hard - -"get-caller-file@npm:^2.0.5": - version: 2.0.5 - resolution: "get-caller-file@npm:2.0.5" - checksum: 10c0/c6c7b60271931fa752aeb92f2b47e355eac1af3a2673f47c9589e8f8a41adc74d45551c1bc57b5e66a80609f10ffb72b6f575e4370d61cc3f7f3aaff01757cde - languageName: node - linkType: hard - -"get-stream@npm:^9.0.0": - version: 9.0.1 - resolution: "get-stream@npm:9.0.1" - dependencies: - "@sec-ant/readable-stream": "npm:^0.4.1" - is-stream: "npm:^4.0.1" - checksum: 10c0/d70e73857f2eea1826ac570c3a912757dcfbe8a718a033fa0c23e12ac8e7d633195b01710e0559af574cbb5af101009b42df7b6f6b29ceec8dbdf7291931b948 - languageName: node - linkType: hard - -"github-from-package@npm:0.0.0": - version: 0.0.0 - resolution: "github-from-package@npm:0.0.0" - checksum: 10c0/737ee3f52d0a27e26332cde85b533c21fcdc0b09fb716c3f8e522cfaa9c600d4a631dec9fcde179ec9d47cca89017b7848ed4d6ae6b6b78f936c06825b1fcc12 - languageName: node - linkType: hard - -"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": - version: 5.1.2 - resolution: "glob-parent@npm:5.1.2" - dependencies: - is-glob: "npm:^4.0.1" - checksum: 10c0/cab87638e2112bee3f839ef5f6e0765057163d39c66be8ec1602f3823da4692297ad4e972de876ea17c44d652978638d2fd583c6713d0eb6591706825020c9ee - languageName: node - linkType: hard - -"glob@npm:^10.2.2, glob@npm:^10.3.10": - version: 10.4.5 - resolution: "glob@npm:10.4.5" - dependencies: - foreground-child: "npm:^3.1.0" - jackspeak: "npm:^3.1.2" - minimatch: "npm:^9.0.4" - minipass: "npm:^7.1.2" - package-json-from-dist: "npm:^1.0.0" - path-scurry: "npm:^1.11.1" - bin: - glob: dist/esm/bin.mjs - checksum: 10c0/19a9759ea77b8e3ca0a43c2f07ecddc2ad46216b786bb8f993c445aee80d345925a21e5280c7b7c6c59e860a0154b84e4b2b60321fea92cd3c56b4a7489f160e - languageName: node - linkType: hard - -"globby@npm:^13.1.4": - version: 13.2.2 - resolution: "globby@npm:13.2.2" - dependencies: - dir-glob: "npm:^3.0.1" - fast-glob: "npm:^3.3.0" - ignore: "npm:^5.2.4" - merge2: "npm:^1.4.1" - slash: "npm:^4.0.0" - checksum: 10c0/a8d7cc7cbe5e1b2d0f81d467bbc5bc2eac35f74eaded3a6c85fc26d7acc8e6de22d396159db8a2fc340b8a342e74cac58de8f4aee74146d3d146921a76062664 - languageName: node - linkType: hard - -"graceful-fs@npm:^4.2.6": - version: 4.2.11 - resolution: "graceful-fs@npm:4.2.11" - checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 - languageName: node - linkType: hard - -"http-cache-semantics@npm:^4.1.1": - version: 4.1.1 - resolution: "http-cache-semantics@npm:4.1.1" - checksum: 10c0/ce1319b8a382eb3cbb4a37c19f6bfe14e5bb5be3d09079e885e8c513ab2d3cd9214902f8a31c9dc4e37022633ceabfc2d697405deeaf1b8f3552bb4ed996fdfc - languageName: node - linkType: hard - -"http-proxy-agent@npm:^7.0.0": - version: 7.0.2 - resolution: "http-proxy-agent@npm:7.0.2" - dependencies: - agent-base: "npm:^7.1.0" - debug: "npm:^4.3.4" - checksum: 10c0/4207b06a4580fb85dd6dff521f0abf6db517489e70863dca1a0291daa7f2d3d2d6015a57bd702af068ea5cf9f1f6ff72314f5f5b4228d299c0904135d2aef921 - languageName: node - linkType: hard - -"https-proxy-agent@npm:^7.0.1": - version: 7.0.5 - resolution: "https-proxy-agent@npm:7.0.5" - dependencies: - agent-base: "npm:^7.0.2" - debug: "npm:4" - checksum: 10c0/2490e3acec397abeb88807db52cac59102d5ed758feee6df6112ab3ccd8325e8a1ce8bce6f4b66e5470eca102d31e425ace904242e4fa28dbe0c59c4bafa7b2c - languageName: node - linkType: hard - -"human-signals@npm:^8.0.0": - version: 8.0.0 - resolution: "human-signals@npm:8.0.0" - checksum: 10c0/e4dac4f7d3eb791ed04129fc6a85bd454a9102d3e3b76c911d0db7057ebd60b2956b435b5b5712aec18960488ede3c21ef7c56e42cdd70760c0d84d3c05cd92e - languageName: node - linkType: hard - -"iconv-lite@npm:^0.6.2": - version: 0.6.3 - resolution: "iconv-lite@npm:0.6.3" - dependencies: - safer-buffer: "npm:>= 2.1.2 < 3.0.0" - checksum: 10c0/98102bc66b33fcf5ac044099d1257ba0b7ad5e3ccd3221f34dd508ab4070edff183276221684e1e0555b145fce0850c9f7d2b60a9fcac50fbb4ea0d6e845a3b1 - languageName: node - linkType: hard - -"ieee754@npm:^1.1.13": - version: 1.2.1 - resolution: "ieee754@npm:1.2.1" - checksum: 10c0/b0782ef5e0935b9f12883a2e2aa37baa75da6e66ce6515c168697b42160807d9330de9a32ec1ed73149aea02e0d822e572bca6f1e22bdcbd2149e13b050b17bb - languageName: node - linkType: hard - -"ignore-by-default@npm:^2.1.0": - version: 2.1.0 - resolution: "ignore-by-default@npm:2.1.0" - checksum: 10c0/3a6040dac25ed9da39dee73bf1634fdd1e15b0eb7cf52a6bdec81c310565782d8811c104ce40acb3d690d61c5fc38a91c78e6baee830a8a2232424dbc6b66981 - languageName: node - linkType: hard - -"ignore@npm:^5.2.4": - version: 5.3.2 - resolution: "ignore@npm:5.3.2" - checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337 - languageName: node - linkType: hard - -"imurmurhash@npm:^0.1.4": - version: 0.1.4 - resolution: "imurmurhash@npm:0.1.4" - checksum: 10c0/8b51313850dd33605c6c9d3fd9638b714f4c4c40250cff658209f30d40da60f78992fb2df5dabee4acf589a6a82bbc79ad5486550754bd9ec4e3fc0d4a57d6a6 - languageName: node - linkType: hard - -"indent-string@npm:^4.0.0": - version: 4.0.0 - resolution: "indent-string@npm:4.0.0" - checksum: 10c0/1e1904ddb0cb3d6cce7cd09e27a90184908b7a5d5c21b92e232c93579d314f0b83c246ffb035493d0504b1e9147ba2c9b21df0030f48673fba0496ecd698161f - languageName: node - linkType: hard - -"indent-string@npm:^5.0.0": - version: 5.0.0 - resolution: "indent-string@npm:5.0.0" - checksum: 10c0/8ee77b57d92e71745e133f6f444d6fa3ed503ad0e1bcd7e80c8da08b42375c07117128d670589725ed07b1978065803fa86318c309ba45415b7fe13e7f170220 - languageName: node - linkType: hard - -"inherits@npm:^2.0.3, inherits@npm:^2.0.4": - version: 2.0.4 - resolution: "inherits@npm:2.0.4" - checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 - languageName: node - linkType: hard - -"ini@npm:~1.3.0": - version: 1.3.8 - resolution: "ini@npm:1.3.8" - checksum: 10c0/ec93838d2328b619532e4f1ff05df7909760b6f66d9c9e2ded11e5c1897d6f2f9980c54dd638f88654b00919ce31e827040631eab0a3969e4d1abefa0719516a - languageName: node - linkType: hard - -"ip-address@npm:^9.0.5": - version: 9.0.5 - resolution: "ip-address@npm:9.0.5" - dependencies: - jsbn: "npm:1.1.0" - sprintf-js: "npm:^1.1.3" - checksum: 10c0/331cd07fafcb3b24100613e4b53e1a2b4feab11e671e655d46dc09ee233da5011284d09ca40c4ecbdfe1d0004f462958675c224a804259f2f78d2465a87824bc - languageName: node - linkType: hard - -"irregular-plurals@npm:^3.3.0": - version: 3.5.0 - resolution: "irregular-plurals@npm:3.5.0" - checksum: 10c0/7c033bbe7325e5a6e0a26949cc6863b6ce273403d4cd5b93bd99b33fecb6605b0884097c4259c23ed0c52c2133bf7d1cdcdd7a0630e8c325161fe269b3447918 - languageName: node - linkType: hard - -"is-binary-path@npm:~2.1.0": - version: 2.1.0 - resolution: "is-binary-path@npm:2.1.0" - dependencies: - binary-extensions: "npm:^2.0.0" - checksum: 10c0/a16eaee59ae2b315ba36fad5c5dcaf8e49c3e27318f8ab8fa3cdb8772bf559c8d1ba750a589c2ccb096113bb64497084361a25960899cb6172a6925ab6123d38 - languageName: node - linkType: hard - -"is-error@npm:^2.2.2": - version: 2.2.2 - resolution: "is-error@npm:2.2.2" - checksum: 10c0/475d3463968bf16e94485555d7cb7a879ed68685e08d365a3370972e626054f1846ebbb3934403091e06682445568601fe919e41646096e5007952d0c1f4fd9b - languageName: node - linkType: hard - -"is-extglob@npm:^2.1.1": - version: 2.1.1 - resolution: "is-extglob@npm:2.1.1" - checksum: 10c0/5487da35691fbc339700bbb2730430b07777a3c21b9ebaecb3072512dfd7b4ba78ac2381a87e8d78d20ea08affb3f1971b4af629173a6bf435ff8a4c47747912 - languageName: node - linkType: hard - -"is-fullwidth-code-point@npm:^3.0.0": - version: 3.0.0 - resolution: "is-fullwidth-code-point@npm:3.0.0" - checksum: 10c0/bb11d825e049f38e04c06373a8d72782eee0205bda9d908cc550ccb3c59b99d750ff9537982e01733c1c94a58e35400661f57042158ff5e8f3e90cf936daf0fc - languageName: node - linkType: hard - -"is-fullwidth-code-point@npm:^4.0.0": - version: 4.0.0 - resolution: "is-fullwidth-code-point@npm:4.0.0" - checksum: 10c0/df2a717e813567db0f659c306d61f2f804d480752526886954a2a3e2246c7745fd07a52b5fecf2b68caf0a6c79dcdace6166fdf29cc76ed9975cc334f0a018b8 - languageName: node - linkType: hard - -"is-glob@npm:^4.0.1, is-glob@npm:~4.0.1": - version: 4.0.3 - resolution: "is-glob@npm:4.0.3" - dependencies: - is-extglob: "npm:^2.1.1" - checksum: 10c0/17fb4014e22be3bbecea9b2e3a76e9e34ff645466be702f1693e8f1ee1adac84710d0be0bd9f967d6354036fd51ab7c2741d954d6e91dae6bb69714de92c197a - languageName: node - linkType: hard - -"is-lambda@npm:^1.0.1": - version: 1.0.1 - resolution: "is-lambda@npm:1.0.1" - checksum: 10c0/85fee098ae62ba6f1e24cf22678805473c7afd0fb3978a3aa260e354cb7bcb3a5806cf0a98403188465efedec41ab4348e8e4e79305d409601323855b3839d4d - languageName: node - linkType: hard - -"is-number@npm:^7.0.0": - version: 7.0.0 - resolution: "is-number@npm:7.0.0" - checksum: 10c0/b4686d0d3053146095ccd45346461bc8e53b80aeb7671cc52a4de02dbbf7dc0d1d2a986e2fe4ae206984b4d34ef37e8b795ebc4f4295c978373e6575e295d811 - languageName: node - linkType: hard - -"is-plain-obj@npm:^4.1.0": - version: 4.1.0 - resolution: "is-plain-obj@npm:4.1.0" - checksum: 10c0/32130d651d71d9564dc88ba7e6fda0e91a1010a3694648e9f4f47bb6080438140696d3e3e15c741411d712e47ac9edc1a8a9de1fe76f3487b0d90be06ac9975e - languageName: node - linkType: hard - -"is-plain-object@npm:^5.0.0": - version: 5.0.0 - resolution: "is-plain-object@npm:5.0.0" - checksum: 10c0/893e42bad832aae3511c71fd61c0bf61aa3a6d853061c62a307261842727d0d25f761ce9379f7ba7226d6179db2a3157efa918e7fe26360f3bf0842d9f28942c - languageName: node - linkType: hard - -"is-promise@npm:^4.0.0": - version: 4.0.0 - resolution: "is-promise@npm:4.0.0" - checksum: 10c0/ebd5c672d73db781ab33ccb155fb9969d6028e37414d609b115cc534654c91ccd061821d5b987eefaa97cf4c62f0b909bb2f04db88306de26e91bfe8ddc01503 - languageName: node - linkType: hard - -"is-stream@npm:^4.0.1": - version: 4.0.1 - resolution: "is-stream@npm:4.0.1" - checksum: 10c0/2706c7f19b851327ba374687bc4a3940805e14ca496dc672b9629e744d143b1ad9c6f1b162dece81c7bfbc0f83b32b61ccc19ad2e05aad2dd7af347408f60c7f - languageName: node - linkType: hard - -"is-unicode-supported@npm:^1.2.0": - version: 1.3.0 - resolution: "is-unicode-supported@npm:1.3.0" - checksum: 10c0/b8674ea95d869f6faabddc6a484767207058b91aea0250803cbf1221345cb0c56f466d4ecea375dc77f6633d248d33c47bd296fb8f4cdba0b4edba8917e83d8a - languageName: node - linkType: hard - -"is-unicode-supported@npm:^2.0.0": - version: 2.1.0 - resolution: "is-unicode-supported@npm:2.1.0" - checksum: 10c0/a0f53e9a7c1fdbcf2d2ef6e40d4736fdffff1c9f8944c75e15425118ff3610172c87bf7bc6c34d3903b04be59790bb2212ddbe21ee65b5a97030fc50370545a5 - languageName: node - linkType: hard - -"isexe@npm:^2.0.0": - version: 2.0.0 - resolution: "isexe@npm:2.0.0" - checksum: 10c0/228cfa503fadc2c31596ab06ed6aa82c9976eec2bfd83397e7eaf06d0ccf42cd1dfd6743bf9aeb01aebd4156d009994c5f76ea898d2832c1fe342da923ca457d - languageName: node - linkType: hard - -"isexe@npm:^3.1.1": - version: 3.1.1 - resolution: "isexe@npm:3.1.1" - checksum: 10c0/9ec257654093443eb0a528a9c8cbba9c0ca7616ccb40abd6dde7202734d96bb86e4ac0d764f0f8cd965856aacbff2f4ce23e730dc19dfb41e3b0d865ca6fdcc7 - languageName: node - linkType: hard - -"jackspeak@npm:^3.1.2": - version: 3.4.3 - resolution: "jackspeak@npm:3.4.3" - dependencies: - "@isaacs/cliui": "npm:^8.0.2" - "@pkgjs/parseargs": "npm:^0.11.0" - dependenciesMeta: - "@pkgjs/parseargs": - optional: true - checksum: 10c0/6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9 - languageName: node - linkType: hard - -"js-string-escape@npm:^1.0.1": - version: 1.0.1 - resolution: "js-string-escape@npm:1.0.1" - checksum: 10c0/2c33b9ff1ba6b84681c51ca0997e7d5a1639813c95d5b61cb7ad47e55cc28fa4a0b1935c3d218710d8e6bcee5d0cd8c44755231e3a4e45fc604534d9595a3628 - languageName: node - linkType: hard - -"js-yaml@npm:^3.14.1": - version: 3.14.1 - resolution: "js-yaml@npm:3.14.1" - dependencies: - argparse: "npm:^1.0.7" - esprima: "npm:^4.0.0" - bin: - js-yaml: bin/js-yaml.js - checksum: 10c0/6746baaaeac312c4db8e75fa22331d9a04cccb7792d126ed8ce6a0bbcfef0cedaddd0c5098fade53db067c09fe00aa1c957674b4765610a8b06a5a189e46433b - languageName: node - linkType: hard - -"jsbn@npm:1.1.0": - version: 1.1.0 - resolution: "jsbn@npm:1.1.0" - checksum: 10c0/4f907fb78d7b712e11dea8c165fe0921f81a657d3443dde75359ed52eb2b5d33ce6773d97985a089f09a65edd80b11cb75c767b57ba47391fee4c969f7215c96 - languageName: node - linkType: hard - -"load-json-file@npm:^7.0.0": - version: 7.0.1 - resolution: "load-json-file@npm:7.0.1" - checksum: 10c0/7117459608a0b6329c7f78e6e1f541b3162dd901c29dd5af721fec8b270177d2e3d7999c971f344fff04daac368d052732e2c7146014bc84d15e0b636975e19a - languageName: node - linkType: hard - -"locate-path@npm:^7.1.0": - version: 7.2.0 - resolution: "locate-path@npm:7.2.0" - dependencies: - p-locate: "npm:^6.0.0" - checksum: 10c0/139e8a7fe11cfbd7f20db03923cacfa5db9e14fa14887ea121345597472b4a63c1a42a8a5187defeeff6acf98fd568da7382aa39682d38f0af27433953a97751 - languageName: node - linkType: hard - -"lodash@npm:^4.17.15": - version: 4.17.21 - resolution: "lodash@npm:4.17.21" - checksum: 10c0/d8cbea072bb08655bb4c989da418994b073a608dffa608b09ac04b43a791b12aeae7cd7ad919aa4c925f33b48490b5cfe6c1f71d827956071dae2e7bb3a6b74c - languageName: node - linkType: hard - -"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": - version: 10.4.3 - resolution: "lru-cache@npm:10.4.3" - checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb - languageName: node - linkType: hard - -"make-fetch-happen@npm:^13.0.0": - version: 13.0.1 - resolution: "make-fetch-happen@npm:13.0.1" - dependencies: - "@npmcli/agent": "npm:^2.0.0" - cacache: "npm:^18.0.0" - http-cache-semantics: "npm:^4.1.1" - is-lambda: "npm:^1.0.1" - minipass: "npm:^7.0.2" - minipass-fetch: "npm:^3.0.0" - minipass-flush: "npm:^1.0.5" - minipass-pipeline: "npm:^1.2.4" - negotiator: "npm:^0.6.3" - proc-log: "npm:^4.2.0" - promise-retry: "npm:^2.0.1" - ssri: "npm:^10.0.0" - checksum: 10c0/df5f4dbb6d98153b751bccf4dc4cc500de85a96a9331db9805596c46aa9f99d9555983954e6c1266d9f981ae37a9e4647f42b9a4bb5466f867f4012e582c9e7e - languageName: node - linkType: hard - -"map-age-cleaner@npm:^0.1.3": - version: 0.1.3 - resolution: "map-age-cleaner@npm:0.1.3" - dependencies: - p-defer: "npm:^1.0.0" - checksum: 10c0/7495236c7b0950956c144fd8b4bc6399d4e78072a8840a4232fe1c4faccbb5eb5d842e5c0a56a60afc36d723f315c1c672325ca03c1b328650f7fcc478f385fd - languageName: node - linkType: hard - -"matcher@npm:^5.0.0": - version: 5.0.0 - resolution: "matcher@npm:5.0.0" - dependencies: - escape-string-regexp: "npm:^5.0.0" - checksum: 10c0/eda5471fc9d5b7264d63c81727824adc3585ddb5cfdc5fce5a9b7c86f946ff181610735d330b1c37a84811df872d1290bf4e9401d2be2a414204343701144b18 - languageName: node - linkType: hard - -"md5-hex@npm:^3.0.1": - version: 3.0.1 - resolution: "md5-hex@npm:3.0.1" - dependencies: - blueimp-md5: "npm:^2.10.0" - checksum: 10c0/ee2b4d8da16b527b3a3fe4d7a96720f43afd07b46a82d49421208b5a126235fb75cfb30b80d4029514772c8844273f940bddfbf4155c787f968f3be4060d01e4 - languageName: node - linkType: hard - -"mem@npm:^9.0.2": - version: 9.0.2 - resolution: "mem@npm:9.0.2" - dependencies: - map-age-cleaner: "npm:^0.1.3" - mimic-fn: "npm:^4.0.0" - checksum: 10c0/c2c56141399e520d8f0e50186bb7e4b49300b33984dc919682f3f13e53dec0e6608fbd327d5ae99494f45061a3a05a8ee04ccba6dcf795c3c215b5aa906eb41f - languageName: node - linkType: hard - -"merge2@npm:^1.3.0, merge2@npm:^1.4.1": - version: 1.4.1 - resolution: "merge2@npm:1.4.1" - checksum: 10c0/254a8a4605b58f450308fc474c82ac9a094848081bf4c06778200207820e5193726dc563a0d2c16468810516a5c97d9d3ea0ca6585d23c58ccfff2403e8dbbeb - languageName: node - linkType: hard - -"micromatch@npm:^4.0.4": - version: 4.0.8 - resolution: "micromatch@npm:4.0.8" - dependencies: - braces: "npm:^3.0.3" - picomatch: "npm:^2.3.1" - checksum: 10c0/166fa6eb926b9553f32ef81f5f531d27b4ce7da60e5baf8c021d043b27a388fb95e46a8038d5045877881e673f8134122b59624d5cecbd16eb50a42e7a6b5ca8 - languageName: node - linkType: hard - -"mimic-fn@npm:^4.0.0": - version: 4.0.0 - resolution: "mimic-fn@npm:4.0.0" - checksum: 10c0/de9cc32be9996fd941e512248338e43407f63f6d497abe8441fa33447d922e927de54d4cc3c1a3c6d652857acd770389d5a3823f311a744132760ce2be15ccbf - languageName: node - linkType: hard - -"mimic-response@npm:^3.1.0": - version: 3.1.0 - resolution: "mimic-response@npm:3.1.0" - checksum: 10c0/0d6f07ce6e03e9e4445bee655202153bdb8a98d67ee8dc965ac140900d7a2688343e6b4c9a72cfc9ef2f7944dfd76eef4ab2482eb7b293a68b84916bac735362 - languageName: node - linkType: hard - -"minimatch@npm:^9.0.4": - version: 9.0.5 - resolution: "minimatch@npm:9.0.5" - dependencies: - brace-expansion: "npm:^2.0.1" - checksum: 10c0/de96cf5e35bdf0eab3e2c853522f98ffbe9a36c37797778d2665231ec1f20a9447a7e567cb640901f89e4daaa95ae5d70c65a9e8aa2bb0019b6facbc3c0575ed - languageName: node - linkType: hard - -"minimist@npm:^1.2.0, minimist@npm:^1.2.3": - version: 1.2.8 - resolution: "minimist@npm:1.2.8" - checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6 - languageName: node - linkType: hard - -"minipass-collect@npm:^2.0.1": - version: 2.0.1 - resolution: "minipass-collect@npm:2.0.1" - dependencies: - minipass: "npm:^7.0.3" - checksum: 10c0/5167e73f62bb74cc5019594709c77e6a742051a647fe9499abf03c71dca75515b7959d67a764bdc4f8b361cf897fbf25e2d9869ee039203ed45240f48b9aa06e - languageName: node - linkType: hard - -"minipass-fetch@npm:^3.0.0": - version: 3.0.5 - resolution: "minipass-fetch@npm:3.0.5" - dependencies: - encoding: "npm:^0.1.13" - minipass: "npm:^7.0.3" - minipass-sized: "npm:^1.0.3" - minizlib: "npm:^2.1.2" - dependenciesMeta: - encoding: - optional: true - checksum: 10c0/9d702d57f556274286fdd97e406fc38a2f5c8d15e158b498d7393b1105974b21249289ec571fa2b51e038a4872bfc82710111cf75fae98c662f3d6f95e72152b - languageName: node - linkType: hard - -"minipass-flush@npm:^1.0.5": - version: 1.0.5 - resolution: "minipass-flush@npm:1.0.5" - dependencies: - minipass: "npm:^3.0.0" - checksum: 10c0/2a51b63feb799d2bb34669205eee7c0eaf9dce01883261a5b77410c9408aa447e478efd191b4de6fc1101e796ff5892f8443ef20d9544385819093dbb32d36bd - languageName: node - linkType: hard - -"minipass-pipeline@npm:^1.2.4": - version: 1.2.4 - resolution: "minipass-pipeline@npm:1.2.4" - dependencies: - minipass: "npm:^3.0.0" - checksum: 10c0/cbda57cea20b140b797505dc2cac71581a70b3247b84480c1fed5ca5ba46c25ecc25f68bfc9e6dcb1a6e9017dab5c7ada5eab73ad4f0a49d84e35093e0c643f2 - languageName: node - linkType: hard - -"minipass-sized@npm:^1.0.3": - version: 1.0.3 - resolution: "minipass-sized@npm:1.0.3" - dependencies: - minipass: "npm:^3.0.0" - checksum: 10c0/298f124753efdc745cfe0f2bdfdd81ba25b9f4e753ca4a2066eb17c821f25d48acea607dfc997633ee5bf7b6dfffb4eee4f2051eb168663f0b99fad2fa4829cb - languageName: node - linkType: hard - -"minipass@npm:^3.0.0": - version: 3.3.6 - resolution: "minipass@npm:3.3.6" - dependencies: - yallist: "npm:^4.0.0" - checksum: 10c0/a114746943afa1dbbca8249e706d1d38b85ed1298b530f5808ce51f8e9e941962e2a5ad2e00eae7dd21d8a4aae6586a66d4216d1a259385e9d0358f0c1eba16c - languageName: node - linkType: hard - -"minipass@npm:^5.0.0": - version: 5.0.0 - resolution: "minipass@npm:5.0.0" - checksum: 10c0/a91d8043f691796a8ac88df039da19933ef0f633e3d7f0d35dcd5373af49131cf2399bfc355f41515dc495e3990369c3858cd319e5c2722b4753c90bf3152462 - languageName: node - linkType: hard - -"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.1.2": - version: 7.1.2 - resolution: "minipass@npm:7.1.2" - checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557 - languageName: node - linkType: hard - -"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": - version: 2.1.2 - resolution: "minizlib@npm:2.1.2" - dependencies: - minipass: "npm:^3.0.0" - yallist: "npm:^4.0.0" - checksum: 10c0/64fae024e1a7d0346a1102bb670085b17b7f95bf6cfdf5b128772ec8faf9ea211464ea4add406a3a6384a7d87a0cd1a96263692134323477b4fb43659a6cab78 - languageName: node - linkType: hard - -"mkdirp-classic@npm:^0.5.2, mkdirp-classic@npm:^0.5.3": - version: 0.5.3 - resolution: "mkdirp-classic@npm:0.5.3" - checksum: 10c0/95371d831d196960ddc3833cc6907e6b8f67ac5501a6582f47dfae5eb0f092e9f8ce88e0d83afcae95d6e2b61a01741ba03714eeafb6f7a6e9dcc158ac85b168 - languageName: node - linkType: hard - -"mkdirp@npm:^1.0.3": - version: 1.0.4 - resolution: "mkdirp@npm:1.0.4" - bin: - mkdirp: bin/cmd.js - checksum: 10c0/46ea0f3ffa8bc6a5bc0c7081ffc3907777f0ed6516888d40a518c5111f8366d97d2678911ad1a6882bf592fa9de6c784fea32e1687bb94e1f4944170af48a5cf - languageName: node - linkType: hard - -"ms@npm:^2.1.3": - version: 2.1.3 - resolution: "ms@npm:2.1.3" - checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 - languageName: node - linkType: hard - -"napi-build-utils@npm:^1.0.1": - version: 1.0.2 - resolution: "napi-build-utils@npm:1.0.2" - checksum: 10c0/37fd2cd0ff2ad20073ce78d83fd718a740d568b225924e753ae51cb69d68f330c80544d487e5e5bd18e28702ed2ca469c2424ad948becd1862c1b0209542b2e9 - languageName: node - linkType: hard - -"negotiator@npm:^0.6.3": - version: 0.6.3 - resolution: "negotiator@npm:0.6.3" - checksum: 10c0/3ec9fd413e7bf071c937ae60d572bc67155262068ed522cf4b3be5edbe6ddf67d095ec03a3a14ebf8fc8e95f8e1d61be4869db0dbb0de696f6b837358bd43fc2 - languageName: node - linkType: hard - -"node-abi@npm:^3.3.0": - version: 3.67.0 - resolution: "node-abi@npm:3.67.0" - dependencies: - semver: "npm:^7.3.5" - checksum: 10c0/72ce2edbdfb84745bc201a4e48aa7146fd88a0d2c80046b6b17f28439c9a7683eab846f40f1e819349c31f7d9331ed5c50d1e741208d938dd5f38b29cab2275e - languageName: node - linkType: hard - -"node-gyp@npm:latest": - version: 10.2.0 - resolution: "node-gyp@npm:10.2.0" - dependencies: - env-paths: "npm:^2.2.0" - exponential-backoff: "npm:^3.1.1" - glob: "npm:^10.3.10" - graceful-fs: "npm:^4.2.6" - make-fetch-happen: "npm:^13.0.0" - nopt: "npm:^7.0.0" - proc-log: "npm:^4.1.0" - semver: "npm:^7.3.5" - tar: "npm:^6.2.1" - which: "npm:^4.0.0" - bin: - node-gyp: bin/node-gyp.js - checksum: 10c0/00630d67dbd09a45aee0a5d55c05e3916ca9e6d427ee4f7bc392d2d3dc5fad7449b21fc098dd38260a53d9dcc9c879b36704a1994235d4707e7271af7e9a835b - languageName: node - linkType: hard - -"nofilter@npm:^3.1.0": - version: 3.1.0 - resolution: "nofilter@npm:3.1.0" - checksum: 10c0/92459f3864a067b347032263f0b536223cbfc98153913b5dce350cb39c8470bc1813366e41993f22c33cc6400c0f392aa324a4b51e24c22040635c1cdb046499 - languageName: node - linkType: hard - -"nopt@npm:^7.0.0": - version: 7.2.1 - resolution: "nopt@npm:7.2.1" - dependencies: - abbrev: "npm:^2.0.0" - bin: - nopt: bin/nopt.js - checksum: 10c0/a069c7c736767121242037a22a788863accfa932ab285a1eb569eb8cd534b09d17206f68c37f096ae785647435e0c5a5a0a67b42ec743e481a455e5ae6a6df81 - languageName: node - linkType: hard - -"normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": - version: 3.0.0 - resolution: "normalize-path@npm:3.0.0" - checksum: 10c0/e008c8142bcc335b5e38cf0d63cfd39d6cf2d97480af9abdbe9a439221fd4d749763bab492a8ee708ce7a194bb00c9da6d0a115018672310850489137b3da046 - languageName: node - linkType: hard - -"npm-run-path@npm:^6.0.0": - version: 6.0.0 - resolution: "npm-run-path@npm:6.0.0" - dependencies: - path-key: "npm:^4.0.0" - unicorn-magic: "npm:^0.3.0" - checksum: 10c0/b223c8a0dcd608abf95363ea5c3c0ccc3cd877daf0102eaf1b0f2390d6858d8337fbb7c443af2403b067a7d2c116d10691ecd22ab3c5273c44da1ff8d07753bd - languageName: node - linkType: hard - -"once@npm:^1.3.1, once@npm:^1.4.0": - version: 1.4.0 - resolution: "once@npm:1.4.0" - dependencies: - wrappy: "npm:1" - checksum: 10c0/5d48aca287dfefabd756621c5dfce5c91a549a93e9fdb7b8246bc4c4790aa2ec17b34a260530474635147aeb631a2dcc8b32c613df0675f96041cbb8244517d0 - languageName: node - linkType: hard - -"p-defer@npm:^1.0.0": - version: 1.0.0 - resolution: "p-defer@npm:1.0.0" - checksum: 10c0/ed603c3790e74b061ac2cb07eb6e65802cf58dce0fbee646c113a7b71edb711101329ad38f99e462bd2e343a74f6e9366b496a35f1d766c187084d3109900487 - languageName: node - linkType: hard - -"p-event@npm:^5.0.1": - version: 5.0.1 - resolution: "p-event@npm:5.0.1" - dependencies: - p-timeout: "npm:^5.0.2" - checksum: 10c0/2317171489537f316661fa863f3bb711b2ceb89182937238422cec10223cbb958c432d6c26a238446a622d788187bdd295b1d8ecedbe2e467e045930d60202b0 - languageName: node - linkType: hard - -"p-limit@npm:^4.0.0": - version: 4.0.0 - resolution: "p-limit@npm:4.0.0" - dependencies: - yocto-queue: "npm:^1.0.0" - checksum: 10c0/a56af34a77f8df2ff61ddfb29431044557fcbcb7642d5a3233143ebba805fc7306ac1d448de724352861cb99de934bc9ab74f0d16fe6a5460bdbdf938de875ad - languageName: node - linkType: hard - -"p-locate@npm:^6.0.0": - version: 6.0.0 - resolution: "p-locate@npm:6.0.0" - dependencies: - p-limit: "npm:^4.0.0" - checksum: 10c0/d72fa2f41adce59c198270aa4d3c832536c87a1806e0f69dffb7c1a7ca998fb053915ca833d90f166a8c082d3859eabfed95f01698a3214c20df6bb8de046312 - languageName: node - linkType: hard - -"p-map@npm:^4.0.0": - version: 4.0.0 - resolution: "p-map@npm:4.0.0" - dependencies: - aggregate-error: "npm:^3.0.0" - checksum: 10c0/592c05bd6262c466ce269ff172bb8de7c6975afca9b50c975135b974e9bdaafbfe80e61aaaf5be6d1200ba08b30ead04b88cfa7e25ff1e3b93ab28c9f62a2c75 - languageName: node - linkType: hard - -"p-map@npm:^5.5.0": - version: 5.5.0 - resolution: "p-map@npm:5.5.0" - dependencies: - aggregate-error: "npm:^4.0.0" - checksum: 10c0/410bce846b1e3db6bb2ccab6248372ecf4e635fc2b31331c8f56478e73fec9e146e8b4547585e635703160a3d252a6a65b8f855834aebc2c3408eb5789630cc4 - languageName: node - linkType: hard - -"p-timeout@npm:^5.0.2": - version: 5.1.0 - resolution: "p-timeout@npm:5.1.0" - checksum: 10c0/1b026cf9d5878c64bec4341ca9cda8ec6b8b3aea8a57885ca0fe2b35753a20d767fb6f9d3aa41e1252f42bc95432c05ea33b6b18f271fb10bfb0789591850a41 - languageName: node - linkType: hard - -"package-json-from-dist@npm:^1.0.0": - version: 1.0.0 - resolution: "package-json-from-dist@npm:1.0.0" - checksum: 10c0/e3ffaf6ac1040ab6082a658230c041ad14e72fabe99076a2081bb1d5d41210f11872403fc09082daf4387fc0baa6577f96c9c0e94c90c394fd57794b66aa4033 - languageName: node - linkType: hard - -"parse-ms@npm:^3.0.0": - version: 3.0.0 - resolution: "parse-ms@npm:3.0.0" - checksum: 10c0/056b4a32a9d3749f3f4cfffefb45c45540491deaa8e1d8ad43c2ddde7ba04edd076bd1b298f521238bb5fb084a9b2c4a2ebb78aefa651afbc4c2b0af4232fc54 - languageName: node - linkType: hard - -"parse-ms@npm:^4.0.0": - version: 4.0.0 - resolution: "parse-ms@npm:4.0.0" - checksum: 10c0/a7900f4f1ebac24cbf5e9708c16fb2fd482517fad353aecd7aefb8c2ba2f85ce017913ccb8925d231770404780df46244ea6fec598b3bde6490882358b4d2d16 - languageName: node - linkType: hard - -"path-exists@npm:^5.0.0": - version: 5.0.0 - resolution: "path-exists@npm:5.0.0" - checksum: 10c0/b170f3060b31604cde93eefdb7392b89d832dfbc1bed717c9718cbe0f230c1669b7e75f87e19901da2250b84d092989a0f9e44d2ef41deb09aa3ad28e691a40a - languageName: node - linkType: hard - -"path-key@npm:^3.1.0": - version: 3.1.1 - resolution: "path-key@npm:3.1.1" - checksum: 10c0/748c43efd5a569c039d7a00a03b58eecd1d75f3999f5a28303d75f521288df4823bc057d8784eb72358b2895a05f29a070bc9f1f17d28226cc4e62494cc58c4c - languageName: node - linkType: hard - -"path-key@npm:^4.0.0": - version: 4.0.0 - resolution: "path-key@npm:4.0.0" - checksum: 10c0/794efeef32863a65ac312f3c0b0a99f921f3e827ff63afa5cb09a377e202c262b671f7b3832a4e64731003fa94af0263713962d317b9887bd1e0c48a342efba3 - languageName: node - linkType: hard - -"path-scurry@npm:^1.11.1": - version: 1.11.1 - resolution: "path-scurry@npm:1.11.1" - dependencies: - lru-cache: "npm:^10.2.0" - minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" - checksum: 10c0/32a13711a2a505616ae1cc1b5076801e453e7aae6ac40ab55b388bb91b9d0547a52f5aaceff710ea400205f18691120d4431e520afbe4266b836fadede15872d - languageName: node - linkType: hard - -"path-type@npm:^4.0.0": - version: 4.0.0 - resolution: "path-type@npm:4.0.0" - checksum: 10c0/666f6973f332f27581371efaf303fd6c272cc43c2057b37aa99e3643158c7e4b2626549555d88626e99ea9e046f82f32e41bbde5f1508547e9a11b149b52387c - languageName: node - linkType: hard - -"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1": - version: 2.3.1 - resolution: "picomatch@npm:2.3.1" - checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be - languageName: node - linkType: hard - -"pkg-conf@npm:^4.0.0": - version: 4.0.0 - resolution: "pkg-conf@npm:4.0.0" - dependencies: - find-up: "npm:^6.0.0" - load-json-file: "npm:^7.0.0" - checksum: 10c0/27d027609f27228edcde121f6f707b4ba1f5488e95e98f2e58652ae4e99792081bd1de67d591f4a0f05b02c0b66d745591d49f82041cbc8d41e2238ef5d73eb4 - languageName: node - linkType: hard - -"plur@npm:^5.1.0": - version: 5.1.0 - resolution: "plur@npm:5.1.0" - dependencies: - irregular-plurals: "npm:^3.3.0" - checksum: 10c0/26bb622b8545fcfd47bbf56fbcca66c08693708a232e403fa3589e00003c56c14231ac57c7588ca5db83ef4be1f61383402c4ea954000768f779f8aef6eb6da8 - languageName: node - linkType: hard - -"prebuild-install@npm:^7.1.1": - version: 7.1.2 - resolution: "prebuild-install@npm:7.1.2" - dependencies: - detect-libc: "npm:^2.0.0" - expand-template: "npm:^2.0.3" - github-from-package: "npm:0.0.0" - minimist: "npm:^1.2.3" - mkdirp-classic: "npm:^0.5.3" - napi-build-utils: "npm:^1.0.1" - node-abi: "npm:^3.3.0" - pump: "npm:^3.0.0" - rc: "npm:^1.2.7" - simple-get: "npm:^4.0.0" - tar-fs: "npm:^2.0.0" - tunnel-agent: "npm:^0.6.0" - bin: - prebuild-install: bin.js - checksum: 10c0/e64868ba9ef2068fd7264f5b03e5298a901e02a450acdb1f56258d88c09dea601eefdb3d1dfdff8513fdd230a92961712be0676192626a3b4d01ba154d48bdd3 - languageName: node - linkType: hard - -"pretty-ms@npm:^8.0.0": - version: 8.0.0 - resolution: "pretty-ms@npm:8.0.0" - dependencies: - parse-ms: "npm:^3.0.0" - checksum: 10c0/e960d633ecca45445cf5c6dffc0f5e4bef6744c92449ab0e8c6c704800675ab71e181c5e02ece5265e02137a33e313d3f3e355fbf8ea30b4b5b23de423329f8d - languageName: node - linkType: hard - -"pretty-ms@npm:^9.0.0": - version: 9.1.0 - resolution: "pretty-ms@npm:9.1.0" - dependencies: - parse-ms: "npm:^4.0.0" - checksum: 10c0/fd111aad8800a04dfd654e6016da69bdaa6fc6a4c280f8e727cffd8b5960558e94942f1a94d4aa6e4d179561a0fbb0366a9ebe0ccefbbb0f8ff853b129cdefb9 - languageName: node - linkType: hard - -"proc-log@npm:^4.1.0, proc-log@npm:^4.2.0": - version: 4.2.0 - resolution: "proc-log@npm:4.2.0" - checksum: 10c0/17db4757c2a5c44c1e545170e6c70a26f7de58feb985091fb1763f5081cab3d01b181fb2dd240c9f4a4255a1d9227d163d5771b7e69c9e49a561692db865efb9 - languageName: node - linkType: hard - -"promise-retry@npm:^2.0.1": - version: 2.0.1 - resolution: "promise-retry@npm:2.0.1" - dependencies: - err-code: "npm:^2.0.2" - retry: "npm:^0.12.0" - checksum: 10c0/9c7045a1a2928094b5b9b15336dcd2a7b1c052f674550df63cc3f36cd44028e5080448175b6f6ca32b642de81150f5e7b1a98b728f15cb069f2dd60ac2616b96 - languageName: node - linkType: hard - -"pump@npm:^3.0.0": - version: 3.0.2 - resolution: "pump@npm:3.0.2" - dependencies: - end-of-stream: "npm:^1.1.0" - once: "npm:^1.3.1" - checksum: 10c0/5ad655cb2a7738b4bcf6406b24ad0970d680649d996b55ad20d1be8e0c02394034e4c45ff7cd105d87f1e9b96a0e3d06fd28e11fae8875da26e7f7a8e2c9726f - languageName: node - linkType: hard - -"queue-microtask@npm:^1.2.2": - version: 1.2.3 - resolution: "queue-microtask@npm:1.2.3" - checksum: 10c0/900a93d3cdae3acd7d16f642c29a642aea32c2026446151f0778c62ac089d4b8e6c986811076e1ae180a694cedf077d453a11b58ff0a865629a4f82ab558e102 - languageName: node - linkType: hard - -"rc@npm:^1.2.7": - version: 1.2.8 - resolution: "rc@npm:1.2.8" - dependencies: - deep-extend: "npm:^0.6.0" - ini: "npm:~1.3.0" - minimist: "npm:^1.2.0" - strip-json-comments: "npm:~2.0.1" - bin: - rc: ./cli.js - checksum: 10c0/24a07653150f0d9ac7168e52943cc3cb4b7a22c0e43c7dff3219977c2fdca5a2760a304a029c20811a0e79d351f57d46c9bde216193a0f73978496afc2b85b15 - languageName: node - linkType: hard - -"readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0": - version: 3.6.2 - resolution: "readable-stream@npm:3.6.2" - dependencies: - inherits: "npm:^2.0.3" - string_decoder: "npm:^1.1.1" - util-deprecate: "npm:^1.0.1" - checksum: 10c0/e37be5c79c376fdd088a45fa31ea2e423e5d48854be7a22a58869b4e84d25047b193f6acb54f1012331e1bcd667ffb569c01b99d36b0bd59658fb33f513511b7 - languageName: node - linkType: hard - -"readdirp@npm:~3.6.0": - version: 3.6.0 - resolution: "readdirp@npm:3.6.0" - dependencies: - picomatch: "npm:^2.2.1" - checksum: 10c0/6fa848cf63d1b82ab4e985f4cf72bd55b7dcfd8e0a376905804e48c3634b7e749170940ba77b32804d5fe93b3cc521aa95a8d7e7d725f830da6d93f3669ce66b - languageName: node - linkType: hard - -"require-directory@npm:^2.1.1": - version: 2.1.1 - resolution: "require-directory@npm:2.1.1" - checksum: 10c0/83aa76a7bc1531f68d92c75a2ca2f54f1b01463cb566cf3fbc787d0de8be30c9dbc211d1d46be3497dac5785fe296f2dd11d531945ac29730643357978966e99 - languageName: node - linkType: hard - -"resolve-cwd@npm:^3.0.0": - version: 3.0.0 - resolution: "resolve-cwd@npm:3.0.0" - dependencies: - resolve-from: "npm:^5.0.0" - checksum: 10c0/e608a3ebd15356264653c32d7ecbc8fd702f94c6703ea4ac2fb81d9c359180cba0ae2e6b71faa446631ed6145454d5a56b227efc33a2d40638ac13f8beb20ee4 - languageName: node - linkType: hard - -"resolve-from@npm:^5.0.0": - version: 5.0.0 - resolution: "resolve-from@npm:5.0.0" - checksum: 10c0/b21cb7f1fb746de8107b9febab60095187781137fd803e6a59a76d421444b1531b641bba5857f5dc011974d8a5c635d61cec49e6bd3b7fc20e01f0fafc4efbf2 - languageName: node - linkType: hard - -"retry@npm:^0.12.0": - version: 0.12.0 - resolution: "retry@npm:0.12.0" - checksum: 10c0/59933e8501727ba13ad73ef4a04d5280b3717fd650408460c987392efe9d7be2040778ed8ebe933c5cbd63da3dcc37919c141ef8af0a54a6e4fca5a2af177bfe - languageName: node - linkType: hard - -"reusify@npm:^1.0.4": - version: 1.0.4 - resolution: "reusify@npm:1.0.4" - checksum: 10c0/c19ef26e4e188f408922c46f7ff480d38e8dfc55d448310dfb518736b23ed2c4f547fb64a6ed5bdba92cd7e7ddc889d36ff78f794816d5e71498d645ef476107 - languageName: node - linkType: hard - -"root-workspace-0b6124@workspace:.": - version: 0.0.0-use.local - resolution: "root-workspace-0b6124@workspace:." - dependencies: - "@agoric/synthetic-chain": "npm:^0.3.0" - ava: "npm:^5.3.1" - languageName: unknown - linkType: soft - -"run-parallel@npm:^1.1.9": - version: 1.2.0 - resolution: "run-parallel@npm:1.2.0" - dependencies: - queue-microtask: "npm:^1.2.2" - checksum: 10c0/200b5ab25b5b8b7113f9901bfe3afc347e19bb7475b267d55ad0eb86a62a46d77510cb0f232507c9e5d497ebda569a08a9867d0d14f57a82ad5564d991588b39 - languageName: node - linkType: hard - -"safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0": - version: 5.2.1 - resolution: "safe-buffer@npm:5.2.1" - checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3 - languageName: node - linkType: hard - -"safer-buffer@npm:>= 2.1.2 < 3.0.0": - version: 2.1.2 - resolution: "safer-buffer@npm:2.1.2" - checksum: 10c0/7e3c8b2e88a1841c9671094bbaeebd94448111dd90a81a1f606f3f67708a6ec57763b3b47f06da09fc6054193e0e6709e77325415dc8422b04497a8070fa02d4 - languageName: node - linkType: hard - -"semver@npm:^7.3.2, semver@npm:^7.3.5": - version: 7.6.3 - resolution: "semver@npm:7.6.3" - bin: - semver: bin/semver.js - checksum: 10c0/88f33e148b210c153873cb08cfe1e281d518aaa9a666d4d148add6560db5cd3c582f3a08ccb91f38d5f379ead256da9931234ed122057f40bb5766e65e58adaf - languageName: node - linkType: hard - -"serialize-error@npm:^7.0.1": - version: 7.0.1 - resolution: "serialize-error@npm:7.0.1" - dependencies: - type-fest: "npm:^0.13.1" - checksum: 10c0/7982937d578cd901276c8ab3e2c6ed8a4c174137730f1fb0402d005af209a0e84d04acc874e317c936724c7b5b26c7a96ff7e4b8d11a469f4924a4b0ea814c05 - languageName: node - linkType: hard - -"shebang-command@npm:^2.0.0": - version: 2.0.0 - resolution: "shebang-command@npm:2.0.0" - dependencies: - shebang-regex: "npm:^3.0.0" - checksum: 10c0/a41692e7d89a553ef21d324a5cceb5f686d1f3c040759c50aab69688634688c5c327f26f3ecf7001ebfd78c01f3c7c0a11a7c8bfd0a8bc9f6240d4f40b224e4e - languageName: node - linkType: hard - -"shebang-regex@npm:^3.0.0": - version: 3.0.0 - resolution: "shebang-regex@npm:3.0.0" - checksum: 10c0/1dbed0726dd0e1152a92696c76c7f06084eb32a90f0528d11acd764043aacf76994b2fb30aa1291a21bd019d6699164d048286309a278855ee7bec06cf6fb690 - languageName: node - linkType: hard - -"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": - version: 4.1.0 - resolution: "signal-exit@npm:4.1.0" - checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 - languageName: node - linkType: hard - -"simple-concat@npm:^1.0.0": - version: 1.0.1 - resolution: "simple-concat@npm:1.0.1" - checksum: 10c0/62f7508e674414008910b5397c1811941d457dfa0db4fd5aa7fa0409eb02c3609608dfcd7508cace75b3a0bf67a2a77990711e32cd213d2c76f4fd12ee86d776 - languageName: node - linkType: hard - -"simple-get@npm:^4.0.0": - version: 4.0.1 - resolution: "simple-get@npm:4.0.1" - dependencies: - decompress-response: "npm:^6.0.0" - once: "npm:^1.3.1" - simple-concat: "npm:^1.0.0" - checksum: 10c0/b0649a581dbca741babb960423248899203165769747142033479a7dc5e77d7b0fced0253c731cd57cf21e31e4d77c9157c3069f4448d558ebc96cf9e1eebcf0 - languageName: node - linkType: hard - -"slash@npm:^4.0.0": - version: 4.0.0 - resolution: "slash@npm:4.0.0" - checksum: 10c0/b522ca75d80d107fd30d29df0549a7b2537c83c4c4ecd12cd7d4ea6c8aaca2ab17ada002e7a1d78a9d736a0261509f26ea5b489082ee443a3a810586ef8eff18 - languageName: node - linkType: hard - -"slice-ansi@npm:^5.0.0": - version: 5.0.0 - resolution: "slice-ansi@npm:5.0.0" - dependencies: - ansi-styles: "npm:^6.0.0" - is-fullwidth-code-point: "npm:^4.0.0" - checksum: 10c0/2d4d40b2a9d5cf4e8caae3f698fe24ae31a4d778701724f578e984dcb485ec8c49f0c04dab59c401821e80fcdfe89cace9c66693b0244e40ec485d72e543914f - languageName: node - linkType: hard - -"smart-buffer@npm:^4.2.0": - version: 4.2.0 - resolution: "smart-buffer@npm:4.2.0" - checksum: 10c0/a16775323e1404dd43fabafe7460be13a471e021637bc7889468eb45ce6a6b207261f454e4e530a19500cc962c4cc5348583520843b363f4193cee5c00e1e539 - languageName: node - linkType: hard - -"socks-proxy-agent@npm:^8.0.3": - version: 8.0.4 - resolution: "socks-proxy-agent@npm:8.0.4" - dependencies: - agent-base: "npm:^7.1.1" - debug: "npm:^4.3.4" - socks: "npm:^2.8.3" - checksum: 10c0/345593bb21b95b0508e63e703c84da11549f0a2657d6b4e3ee3612c312cb3a907eac10e53b23ede3557c6601d63252103494caa306b66560f43af7b98f53957a - languageName: node - linkType: hard - -"socks@npm:^2.8.3": - version: 2.8.3 - resolution: "socks@npm:2.8.3" - dependencies: - ip-address: "npm:^9.0.5" - smart-buffer: "npm:^4.2.0" - checksum: 10c0/d54a52bf9325165770b674a67241143a3d8b4e4c8884560c4e0e078aace2a728dffc7f70150660f51b85797c4e1a3b82f9b7aa25e0a0ceae1a243365da5c51a7 - languageName: node - linkType: hard - -"sprintf-js@npm:^1.1.3": - version: 1.1.3 - resolution: "sprintf-js@npm:1.1.3" - checksum: 10c0/09270dc4f30d479e666aee820eacd9e464215cdff53848b443964202bf4051490538e5dd1b42e1a65cf7296916ca17640aebf63dae9812749c7542ee5f288dec - languageName: node - linkType: hard - -"sprintf-js@npm:~1.0.2": - version: 1.0.3 - resolution: "sprintf-js@npm:1.0.3" - checksum: 10c0/ecadcfe4c771890140da5023d43e190b7566d9cf8b2d238600f31bec0fc653f328da4450eb04bd59a431771a8e9cc0e118f0aa3974b683a4981b4e07abc2a5bb - languageName: node - linkType: hard - -"ssri@npm:^10.0.0": - version: 10.0.6 - resolution: "ssri@npm:10.0.6" - dependencies: - minipass: "npm:^7.0.3" - checksum: 10c0/e5a1e23a4057a86a97971465418f22ea89bd439ac36ade88812dd920e4e61873e8abd6a9b72a03a67ef50faa00a2daf1ab745c5a15b46d03e0544a0296354227 - languageName: node - linkType: hard - -"stack-utils@npm:^2.0.6": - version: 2.0.6 - resolution: "stack-utils@npm:2.0.6" - dependencies: - escape-string-regexp: "npm:^2.0.0" - checksum: 10c0/651c9f87667e077584bbe848acaecc6049bc71979f1e9a46c7b920cad4431c388df0f51b8ad7cfd6eed3db97a2878d0fc8b3122979439ea8bac29c61c95eec8a - languageName: node - linkType: hard - -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": - version: 4.2.3 - resolution: "string-width@npm:4.2.3" - dependencies: - emoji-regex: "npm:^8.0.0" - is-fullwidth-code-point: "npm:^3.0.0" - strip-ansi: "npm:^6.0.1" - checksum: 10c0/1e525e92e5eae0afd7454086eed9c818ee84374bb80328fc41217ae72ff5f065ef1c9d7f72da41de40c75fa8bb3dee63d92373fd492c84260a552c636392a47b - languageName: node - linkType: hard - -"string-width@npm:^5.0.0, string-width@npm:^5.0.1, string-width@npm:^5.1.2": - version: 5.1.2 - resolution: "string-width@npm:5.1.2" - dependencies: - eastasianwidth: "npm:^0.2.0" - emoji-regex: "npm:^9.2.2" - strip-ansi: "npm:^7.0.1" - checksum: 10c0/ab9c4264443d35b8b923cbdd513a089a60de339216d3b0ed3be3ba57d6880e1a192b70ae17225f764d7adbf5994e9bb8df253a944736c15a0240eff553c678ca - languageName: node - linkType: hard - -"string_decoder@npm:^1.1.1": - version: 1.3.0 - resolution: "string_decoder@npm:1.3.0" - dependencies: - safe-buffer: "npm:~5.2.0" - checksum: 10c0/810614ddb030e271cd591935dcd5956b2410dd079d64ff92a1844d6b7588bf992b3e1b69b0f4d34a3e06e0bd73046ac646b5264c1987b20d0601f81ef35d731d - languageName: node - linkType: hard - -"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": - version: 6.0.1 - resolution: "strip-ansi@npm:6.0.1" - dependencies: - ansi-regex: "npm:^5.0.1" - checksum: 10c0/1ae5f212a126fe5b167707f716942490e3933085a5ff6c008ab97ab2f272c8025d3aa218b7bd6ab25729ca20cc81cddb252102f8751e13482a5199e873680952 - languageName: node - linkType: hard - -"strip-ansi@npm:^7.0.1": - version: 7.1.0 - resolution: "strip-ansi@npm:7.1.0" - dependencies: - ansi-regex: "npm:^6.0.1" - checksum: 10c0/a198c3762e8832505328cbf9e8c8381de14a4fa50a4f9b2160138158ea88c0f5549fb50cb13c651c3088f47e63a108b34622ec18c0499b6c8c3a5ddf6b305ac4 - languageName: node - linkType: hard - -"strip-final-newline@npm:^4.0.0": - version: 4.0.0 - resolution: "strip-final-newline@npm:4.0.0" - checksum: 10c0/b0cf2b62d597a1b0e3ebc42b88767f0a0d45601f89fd379a928a1812c8779440c81abba708082c946445af1d6b62d5f16e2a7cf4f30d9d6587b89425fae801ff - languageName: node - linkType: hard - -"strip-json-comments@npm:~2.0.1": - version: 2.0.1 - resolution: "strip-json-comments@npm:2.0.1" - checksum: 10c0/b509231cbdee45064ff4f9fd73609e2bcc4e84a4d508e9dd0f31f70356473fde18abfb5838c17d56fb236f5a06b102ef115438de0600b749e818a35fbbc48c43 - languageName: node - linkType: hard - -"supertap@npm:^3.0.1": - version: 3.0.1 - resolution: "supertap@npm:3.0.1" - dependencies: - indent-string: "npm:^5.0.0" - js-yaml: "npm:^3.14.1" - serialize-error: "npm:^7.0.1" - strip-ansi: "npm:^7.0.1" - checksum: 10c0/8164674f2e280cab875f0fef5bb36c15553c13e29697ff92f4e0d6bc62149f0303a89eee47535413ed145ea72e14a24d065bab233059d48a499ec5ebb4566b0f - languageName: node - linkType: hard - -"tar-fs@npm:^2.0.0": - version: 2.1.1 - resolution: "tar-fs@npm:2.1.1" - dependencies: - chownr: "npm:^1.1.1" - mkdirp-classic: "npm:^0.5.2" - pump: "npm:^3.0.0" - tar-stream: "npm:^2.1.4" - checksum: 10c0/871d26a934bfb7beeae4c4d8a09689f530b565f79bd0cf489823ff0efa3705da01278160da10bb006d1a793fa0425cf316cec029b32a9159eacbeaff4965fb6d - languageName: node - linkType: hard - -"tar-stream@npm:^2.1.4": - version: 2.2.0 - resolution: "tar-stream@npm:2.2.0" - dependencies: - bl: "npm:^4.0.3" - end-of-stream: "npm:^1.4.1" - fs-constants: "npm:^1.0.0" - inherits: "npm:^2.0.3" - readable-stream: "npm:^3.1.1" - checksum: 10c0/2f4c910b3ee7196502e1ff015a7ba321ec6ea837667220d7bcb8d0852d51cb04b87f7ae471008a6fb8f5b1a1b5078f62f3a82d30c706f20ada1238ac797e7692 - languageName: node - linkType: hard - -"tar@npm:^6.1.11, tar@npm:^6.2.1": - version: 6.2.1 - resolution: "tar@npm:6.2.1" - dependencies: - chownr: "npm:^2.0.0" - fs-minipass: "npm:^2.0.0" - minipass: "npm:^5.0.0" - minizlib: "npm:^2.1.1" - mkdirp: "npm:^1.0.3" - yallist: "npm:^4.0.0" - checksum: 10c0/a5eca3eb50bc11552d453488344e6507156b9193efd7635e98e867fab275d527af53d8866e2370cd09dfe74378a18111622ace35af6a608e5223a7d27fe99537 - languageName: node - linkType: hard - -"temp-dir@npm:^3.0.0": - version: 3.0.0 - resolution: "temp-dir@npm:3.0.0" - checksum: 10c0/a86978a400984cd5f315b77ebf3fe53bb58c61f192278cafcb1f3fb32d584a21dc8e08b93171d7874b7cc972234d3455c467306cc1bfc4524b622e5ad3bfd671 - languageName: node - linkType: hard - -"time-zone@npm:^1.0.0": - version: 1.0.0 - resolution: "time-zone@npm:1.0.0" - checksum: 10c0/d00ebd885039109011b6e2423ebbf225160927333c2ade6d833e9cc4676db20759f1f3855fafde00d1bd668c243a6aa68938ce71fe58aab0d514e820d59c1d81 - languageName: node - linkType: hard - -"to-regex-range@npm:^5.0.1": - version: 5.0.1 - resolution: "to-regex-range@npm:5.0.1" - dependencies: - is-number: "npm:^7.0.0" - checksum: 10c0/487988b0a19c654ff3e1961b87f471702e708fa8a8dd02a298ef16da7206692e8552a0250e8b3e8759270f62e9d8314616f6da274734d3b558b1fc7b7724e892 - languageName: node - linkType: hard - -"tunnel-agent@npm:^0.6.0": - version: 0.6.0 - resolution: "tunnel-agent@npm:0.6.0" - dependencies: - safe-buffer: "npm:^5.0.1" - checksum: 10c0/4c7a1b813e7beae66fdbf567a65ec6d46313643753d0beefb3c7973d66fcec3a1e7f39759f0a0b4465883499c6dc8b0750ab8b287399af2e583823e40410a17a - languageName: node - linkType: hard - -"type-fest@npm:^0.13.1": - version: 0.13.1 - resolution: "type-fest@npm:0.13.1" - checksum: 10c0/0c0fa07ae53d4e776cf4dac30d25ad799443e9eef9226f9fddbb69242db86b08584084a99885cfa5a9dfe4c063ebdc9aa7b69da348e735baede8d43f1aeae93b - languageName: node - linkType: hard - -"unicorn-magic@npm:^0.3.0": - version: 0.3.0 - resolution: "unicorn-magic@npm:0.3.0" - checksum: 10c0/0a32a997d6c15f1c2a077a15b1c4ca6f268d574cf5b8975e778bb98e6f8db4ef4e86dfcae4e158cd4c7e38fb4dd383b93b13eefddc7f178dea13d3ac8a603271 - languageName: node - linkType: hard - -"unique-filename@npm:^3.0.0": - version: 3.0.0 - resolution: "unique-filename@npm:3.0.0" - dependencies: - unique-slug: "npm:^4.0.0" - checksum: 10c0/6363e40b2fa758eb5ec5e21b3c7fb83e5da8dcfbd866cc0c199d5534c42f03b9ea9ab069769cc388e1d7ab93b4eeef28ef506ab5f18d910ef29617715101884f - languageName: node - linkType: hard - -"unique-slug@npm:^4.0.0": - version: 4.0.0 - resolution: "unique-slug@npm:4.0.0" - dependencies: - imurmurhash: "npm:^0.1.4" - checksum: 10c0/cb811d9d54eb5821b81b18205750be84cb015c20a4a44280794e915f5a0a70223ce39066781a354e872df3572e8155c228f43ff0cce94c7cbf4da2cc7cbdd635 - languageName: node - linkType: hard - -"util-deprecate@npm:^1.0.1": - version: 1.0.2 - resolution: "util-deprecate@npm:1.0.2" - checksum: 10c0/41a5bdd214df2f6c3ecf8622745e4a366c4adced864bc3c833739791aeeeb1838119af7daed4ba36428114b5c67dcda034a79c882e97e43c03e66a4dd7389942 - languageName: node - linkType: hard - -"well-known-symbols@npm:^2.0.0": - version: 2.0.0 - resolution: "well-known-symbols@npm:2.0.0" - checksum: 10c0/cb6c12e98877e8952ec28d13ae6f4fdb54ae1cb49b16a728720276dadd76c930e6cb0e174af3a4620054dd2752546f842540122920c6e31410208abd4958ee6b - languageName: node - linkType: hard - -"which@npm:^2.0.1": - version: 2.0.2 - resolution: "which@npm:2.0.2" - dependencies: - isexe: "npm:^2.0.0" - bin: - node-which: ./bin/node-which - checksum: 10c0/66522872a768b60c2a65a57e8ad184e5372f5b6a9ca6d5f033d4b0dc98aff63995655a7503b9c0a2598936f532120e81dd8cc155e2e92ed662a2b9377cc4374f - languageName: node - linkType: hard - -"which@npm:^4.0.0": - version: 4.0.0 - resolution: "which@npm:4.0.0" - dependencies: - isexe: "npm:^3.1.1" - bin: - node-which: bin/which.js - checksum: 10c0/449fa5c44ed120ccecfe18c433296a4978a7583bf2391c50abce13f76878d2476defde04d0f79db8165bdf432853c1f8389d0485ca6e8ebce3bbcded513d5e6a - languageName: node - linkType: hard - -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": - version: 7.0.0 - resolution: "wrap-ansi@npm:7.0.0" - dependencies: - ansi-styles: "npm:^4.0.0" - string-width: "npm:^4.1.0" - strip-ansi: "npm:^6.0.0" - checksum: 10c0/d15fc12c11e4cbc4044a552129ebc75ee3f57aa9c1958373a4db0292d72282f54373b536103987a4a7594db1ef6a4f10acf92978f79b98c49306a4b58c77d4da - languageName: node - linkType: hard - -"wrap-ansi@npm:^8.1.0": - version: 8.1.0 - resolution: "wrap-ansi@npm:8.1.0" - dependencies: - ansi-styles: "npm:^6.1.0" - string-width: "npm:^5.0.1" - strip-ansi: "npm:^7.0.1" - checksum: 10c0/138ff58a41d2f877eae87e3282c0630fc2789012fc1af4d6bd626eeb9a2f9a65ca92005e6e69a75c7b85a68479fe7443c7dbe1eb8fbaa681a4491364b7c55c60 - languageName: node - linkType: hard - -"wrappy@npm:1": - version: 1.0.2 - resolution: "wrappy@npm:1.0.2" - checksum: 10c0/56fece1a4018c6a6c8e28fbc88c87e0fbf4ea8fd64fc6c63b18f4acc4bd13e0ad2515189786dd2c30d3eec9663d70f4ecf699330002f8ccb547e4a18231fc9f0 - languageName: node - linkType: hard - -"write-file-atomic@npm:^5.0.1": - version: 5.0.1 - resolution: "write-file-atomic@npm:5.0.1" - dependencies: - imurmurhash: "npm:^0.1.4" - signal-exit: "npm:^4.0.1" - checksum: 10c0/e8c850a8e3e74eeadadb8ad23c9d9d63e4e792bd10f4836ed74189ef6e996763959f1249c5650e232f3c77c11169d239cbfc8342fc70f3fe401407d23810505d - languageName: node - linkType: hard - -"y18n@npm:^5.0.5": - version: 5.0.8 - resolution: "y18n@npm:5.0.8" - checksum: 10c0/4df2842c36e468590c3691c894bc9cdbac41f520566e76e24f59401ba7d8b4811eb1e34524d57e54bc6d864bcb66baab7ffd9ca42bf1eda596618f9162b91249 - languageName: node - linkType: hard - -"yallist@npm:^4.0.0": - version: 4.0.0 - resolution: "yallist@npm:4.0.0" - checksum: 10c0/2286b5e8dbfe22204ab66e2ef5cc9bbb1e55dfc873bbe0d568aa943eb255d131890dfd5bf243637273d31119b870f49c18fcde2c6ffbb7a7a092b870dc90625a - languageName: node - linkType: hard - -"yargs-parser@npm:^21.1.1": - version: 21.1.1 - resolution: "yargs-parser@npm:21.1.1" - checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2 - languageName: node - linkType: hard - -"yargs@npm:^17.7.2": - version: 17.7.2 - resolution: "yargs@npm:17.7.2" - dependencies: - cliui: "npm:^8.0.1" - escalade: "npm:^3.1.1" - get-caller-file: "npm:^2.0.5" - require-directory: "npm:^2.1.1" - string-width: "npm:^4.2.3" - y18n: "npm:^5.0.5" - yargs-parser: "npm:^21.1.1" - checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05 - languageName: node - linkType: hard - -"yocto-queue@npm:^1.0.0": - version: 1.1.1 - resolution: "yocto-queue@npm:1.1.1" - checksum: 10c0/cb287fe5e6acfa82690acb43c283de34e945c571a78a939774f6eaba7c285bacdf6c90fbc16ce530060863984c906d2b4c6ceb069c94d1e0a06d5f2b458e2a92 - languageName: node - linkType: hard - -"yoctocolors@npm:^2.0.0": - version: 2.1.1 - resolution: "yoctocolors@npm:2.1.1" - checksum: 10c0/85903f7fa96f1c70badee94789fade709f9d83dab2ec92753d612d84fcea6d34c772337a9f8914c6bed2f5fc03a428ac5d893e76fab636da5f1236ab725486d0 - languageName: node - linkType: hard diff --git a/a3p-integration/proposals/n:upgrade-next/.gitignore b/a3p-integration/proposals/n:upgrade-next/.gitignore index da0da987d7b..2f3ed03b169 100644 --- a/a3p-integration/proposals/n:upgrade-next/.gitignore +++ b/a3p-integration/proposals/n:upgrade-next/.gitignore @@ -4,3 +4,5 @@ add-OLIVES/ upgrade-bank/ upgrade-provisionPool/ upgrade-orch-core/ +replace-electorate/ +price-feeds/ diff --git a/a3p-integration/proposals/n:upgrade-next/acceptInvites.js b/a3p-integration/proposals/n:upgrade-next/acceptInvites.js new file mode 100644 index 00000000000..72245bf6d3e --- /dev/null +++ b/a3p-integration/proposals/n:upgrade-next/acceptInvites.js @@ -0,0 +1,19 @@ +#!/usr/bin/env node +import '@endo/init'; +import { agops, GOV1ADDR, GOV2ADDR } from '@agoric/synthetic-chain'; +import { GOV4ADDR } from './agoric-tools.js'; + +// New GOV4 account to be added +const addresses = [GOV1ADDR, GOV2ADDR, GOV4ADDR]; + +await Promise.all( + addresses.map((addr, idx) => + agops.ec('committee', '--send-from', addr, '--voter', `${idx}`), + ), +); + +await Promise.all( + addresses.map(addr => + agops.ec('charter', '--send-from', addr, '--name', 'econCommitteeCharter'), + ), +); diff --git a/a3p-integration/proposals/n:upgrade-next/addGov4.js b/a3p-integration/proposals/n:upgrade-next/addGov4.js new file mode 100644 index 00000000000..d84ec7f5890 --- /dev/null +++ b/a3p-integration/proposals/n:upgrade-next/addGov4.js @@ -0,0 +1,20 @@ +import '@endo/init'; +import { execFileSync } from 'node:child_process'; +import { makeAgd } from './synthetic-chain-excerpt.js'; +import { GOV4ADDR } from './agoric-tools.js'; + +const agd = makeAgd({ execFileSync }).withOpts({ keyringBackend: 'test' }); + +agd.keys.add( + 'gov4', + 'smile unveil sketch gaze length bulb goddess street case exact table fetch robust chronic power choice endorse toward pledge dish access sad illegal dance', +); + +agd.tx( + ['swingset', 'provision-one', 'faucet_provision', GOV4ADDR, 'SMART_WALLET'], + { + chainId: 'agoriclocal', + from: 'validator', + yes: true, + }, +); diff --git a/a3p-integration/proposals/f:replace-price-feeds/agoric-tools.js b/a3p-integration/proposals/n:upgrade-next/agoric-tools.js similarity index 68% rename from a3p-integration/proposals/f:replace-price-feeds/agoric-tools.js rename to a3p-integration/proposals/n:upgrade-next/agoric-tools.js index 1c6e10ac8f6..9e69836862d 100644 --- a/a3p-integration/proposals/f:replace-price-feeds/agoric-tools.js +++ b/a3p-integration/proposals/n:upgrade-next/agoric-tools.js @@ -1,5 +1,42 @@ import assert from 'node:assert'; -import { agops, agoric, executeOffer } from '@agoric/synthetic-chain'; +import { + queryVstorage, + agops, + agoric, + executeOffer, +} from '@agoric/synthetic-chain'; +import { makeMarshal, Remotable } from '@endo/marshal'; + +export const GOV4ADDR = 'agoric1c9gyu460lu70rtcdp95vummd6032psmpdx7wdy'; + +const slotToRemotable = (_slotId, iface = 'Remotable') => + Remotable(iface, undefined, { + getBoardId: () => _slotId, + }); + +// /** @param {BoardRemote | object} val */ +const boardValToSlot = val => { + if ('getBoardId' in val) { + return val.getBoardId(); + } + throw Error(`unknown obj in boardSlottingMarshaller.valToSlot ${val}`); +}; + +const boardSlottingMarshaller = slotToVal => { + return makeMarshal(boardValToSlot, slotToVal, { + serializeBodyFormat: 'smallcaps', + }); +}; + +export const marshaller = boardSlottingMarshaller(slotToRemotable); + +export const queryVstorageFormatted = async (path, index = -1) => { + const data = await queryVstorage(path); + + const formattedData = JSON.parse(data.value); + const formattedDataAtIndex = JSON.parse(formattedData.values.at(index)); + return marshaller.fromCapData(formattedDataAtIndex); +}; export const generateVaultDirectorParamChange = async ( previousOfferId, diff --git a/a3p-integration/proposals/f:replace-price-feeds/eval.sh b/a3p-integration/proposals/n:upgrade-next/eval.sh similarity index 100% rename from a3p-integration/proposals/f:replace-price-feeds/eval.sh rename to a3p-integration/proposals/n:upgrade-next/eval.sh diff --git a/a3p-integration/proposals/n:upgrade-next/package.json b/a3p-integration/proposals/n:upgrade-next/package.json index f88ce460a7e..e2914aa6253 100644 --- a/a3p-integration/proposals/n:upgrade-next/package.json +++ b/a3p-integration/proposals/n:upgrade-next/package.json @@ -11,7 +11,17 @@ "vats/upgrade-bank.js upgrade-bank", "vats/upgrade-provisionPool.js upgrade-provisionPool", "testing/add-LEMONS.js add-LEMONS", - "testing/add-OLIVES.js add-OLIVES" + "testing/add-OLIVES.js add-OLIVES", + "inter-protocol/replace-electorate-core.js replace-electorate A3P_INTEGRATION", + "inter-protocol/updatePriceFeeds.js price-feeds A3P_INTEGRATION", + "vats/add-auction.js price-feeds", + "vats/upgradeVaults.js price-feeds", + "inter-protocol/updatePriceFeeds.js submission/main main", + "vats/add-auction.js submission/main", + "vats/upgradeVaults.js submission/main", + "inter-protocol/updatePriceFeeds.js submission/devnet devnet", + "vats/add-auction.js submission/devnet", + "vats/upgradeVaults.js submission/devnet" ], "type": "Software Upgrade Proposal" }, @@ -19,6 +29,8 @@ "license": "Apache-2.0", "dependencies": { "@agoric/synthetic-chain": "^0.3.0", + "@endo/init": "^1.1.5", + "@endo/marshal": "^1.5.4", "ava": "^5.3.1", "better-sqlite3": "^9.6.0", "execa": "^9.3.1" diff --git a/a3p-integration/proposals/f:replace-price-feeds/priceFeedUpdate.test.js b/a3p-integration/proposals/n:upgrade-next/priceFeedUpdate.test.js similarity index 83% rename from a3p-integration/proposals/f:replace-price-feeds/priceFeedUpdate.test.js rename to a3p-integration/proposals/n:upgrade-next/priceFeedUpdate.test.js index 5cbacd019ad..d0bdaa1b055 100644 --- a/a3p-integration/proposals/f:replace-price-feeds/priceFeedUpdate.test.js +++ b/a3p-integration/proposals/n:upgrade-next/priceFeedUpdate.test.js @@ -14,10 +14,12 @@ import { getVaultPrices, getVatDetails, openVault, - pushPrices, - registerOraclesForBrand, USER1ADDR, } from '@agoric/synthetic-chain'; +import { + getPriceFeedRoundId, + verifyPushedPrice, +} from './test-lib/price-feed.js'; import { BID_OFFER_ID } from './agd-tools.js'; @@ -37,12 +39,17 @@ const checkPriceFeedVatsUpdated = async t => { await checkForOracle(t, 'stATOM'); }; -console.log('adding oracle for each brand'); -const oraclesByBrand = generateOracleMap('f-priceFeeds', ['ATOM', 'stATOM']); -await registerOraclesForBrand('ATOM', oraclesByBrand); -await registerOraclesForBrand('stATOM', oraclesByBrand); +/* + * The Oracle for ATOM and stATOM brands are being registered in the offer made at file: + * a3p-integration/proposals/n:upgrade-next/verifyPushedPrice.js + * which is being executed during the use phase of upgrade-next proposal + */ +const oraclesByBrand = generateOracleMap('n-upgrade', ['ATOM', 'stATOM']); -let roundId = 1; +const latestAtomRoundId = await getPriceFeedRoundId('ATOM'); +const latestStAtomRoundId = await getPriceFeedRoundId('stATOM'); +let atomRoundId = latestAtomRoundId + 1; +let stAtomRoundId = latestStAtomRoundId + 1; const tryPushPrices = async t => { // There are no old prices for the other currencies. @@ -52,9 +59,10 @@ const tryPushPrices = async t => { // t.is(stAtomOutPre, '+12010000'); t.log('pushing new prices'); - await pushPrices(13.4, 'ATOM', oraclesByBrand, roundId); - await pushPrices(13.7, 'stATOM', oraclesByBrand, roundId); - roundId += 1; + await verifyPushedPrice(13.4, 'ATOM', oraclesByBrand, atomRoundId); + await verifyPushedPrice(13.7, 'stATOM', oraclesByBrand, stAtomRoundId); + atomRoundId += 1; + stAtomRoundId += 1; t.log('awaiting new quotes'); const atomOut = await getPriceQuote('ATOM'); @@ -89,7 +97,7 @@ const openMarginalVault = async t => { }; const triggerAuction = async t => { - await pushPrices(5.2, 'ATOM', oraclesByBrand, roundId); + await verifyPushedPrice(5.2, 'ATOM', oraclesByBrand, atomRoundId); const atomOut = await getPriceQuote('ATOM'); t.is(atomOut, '+5200000'); diff --git a/a3p-integration/proposals/n:upgrade-next/replaceElectorate.test.js b/a3p-integration/proposals/n:upgrade-next/replaceElectorate.test.js new file mode 100644 index 00000000000..422287dc8b3 --- /dev/null +++ b/a3p-integration/proposals/n:upgrade-next/replaceElectorate.test.js @@ -0,0 +1,35 @@ +import test from 'ava'; +import '@endo/init'; +import { GOV1ADDR, GOV2ADDR } from '@agoric/synthetic-chain'; +import { passStyleOf } from '@endo/marshal'; +import { GOV4ADDR, queryVstorageFormatted } from './agoric-tools.js'; + +const governanceAddresses = [GOV4ADDR, GOV2ADDR, GOV1ADDR]; + +test.serial('should be able to view the new accepted invitations', async t => { + const instance = await queryVstorageFormatted( + `published.agoricNames.instance`, + ); + const instances = Object.fromEntries(instance); + + for (const address of governanceAddresses) { + const wallet = await queryVstorageFormatted( + `published.wallet.${address}.current`, + ); + const usedInvitations = wallet.offerToUsedInvitation.map(v => v[1]); + + const charterInvitation = usedInvitations.find( + v => + v.value[0].instance.getBoardId() === + instances.econCommitteeCharter.getBoardId(), + ); + t.is(passStyleOf(charterInvitation), 'copyRecord'); + + const committeeInvitation = usedInvitations.find( + v => + v.value[0].instance.getBoardId() === + instances.economicCommittee.getBoardId(), + ); + t.is(passStyleOf(committeeInvitation), 'copyRecord'); + } +}); diff --git a/a3p-integration/proposals/f:replace-price-feeds/resetChargingPeriod.js b/a3p-integration/proposals/n:upgrade-next/resetChargingPeriod.js similarity index 100% rename from a3p-integration/proposals/f:replace-price-feeds/resetChargingPeriod.js rename to a3p-integration/proposals/n:upgrade-next/resetChargingPeriod.js diff --git a/a3p-integration/proposals/n:upgrade-next/submitBid.js b/a3p-integration/proposals/n:upgrade-next/submitBid.js new file mode 100644 index 00000000000..5ac4c1be879 --- /dev/null +++ b/a3p-integration/proposals/n:upgrade-next/submitBid.js @@ -0,0 +1,47 @@ +#!/usr/bin/env node + +import { + GOV1ADDR, + CHAINID, + agd, + agopsInter, + addUser, + waitForBlock, + provisionSmartWallet, + ATOM_DENOM, + VALIDATORADDR, +} from '@agoric/synthetic-chain'; + +/** + * + * @param {string} addr + * @param {string} wanted + * @param {string | undefined} from + */ +export const bankSend = (addr, wanted, from = VALIDATORADDR) => { + const chain = ['--chain-id', CHAINID]; + const fromArg = ['--from', from]; + const testKeyring = ['--keyring-backend', 'test']; + const noise = [...fromArg, ...chain, ...testKeyring, '--yes']; + + return agd.tx('bank', 'send', from, addr, wanted, ...noise); +}; + +const bidder = await addUser('long-living-bidder'); +console.log('BIDDER', bidder); +await bankSend(bidder, `80000000uist`, GOV1ADDR); +console.log('IST sent'); +await provisionSmartWallet(bidder, `20000000ubld,100000000${ATOM_DENOM}`); +console.log('Provision sent'); +await waitForBlock(3); +console.log('Wait For Block done. Sending bid offer'); +agopsInter( + 'bid', + 'by-price', + `--price 49.0`, + `--give 80IST`, + '--from', + bidder, + '--keyring-backend test', + `--offer-id long-living-bid-for-acceptance`, +); diff --git a/a3p-integration/proposals/n:upgrade-next/test-lib/price-feed.js b/a3p-integration/proposals/n:upgrade-next/test-lib/price-feed.js new file mode 100644 index 00000000000..c0e2acd311d --- /dev/null +++ b/a3p-integration/proposals/n:upgrade-next/test-lib/price-feed.js @@ -0,0 +1,62 @@ +/* eslint-env node */ + +import { + agoric, + getContractInfo, + pushPrices, + getPriceQuote, +} from '@agoric/synthetic-chain'; +import { retryUntilCondition } from './sync-tools.js'; + +export const scale6 = x => BigInt(x * 1_000_000); + +/** + * + * @param {number} price + * @param {string} brand + * @param {Map} oraclesByBrand + * @param {number} roundId + * @returns {Promise} + */ +export const verifyPushedPrice = async ( + price, + brand, + oraclesByBrand, + roundId, +) => { + const pushPriceRetryOpts = { + maxRetries: 5, // arbitrary + retryIntervalMs: 5000, // in ms + }; + + await pushPrices(price, brand, oraclesByBrand, roundId); + console.log(`Pushing price ${price} for ${brand}`); + + await retryUntilCondition( + () => getPriceQuote(brand), + res => res === `+${scale6(price).toString()}`, + 'price not pushed yet', + { + log: console.log, + setTimeout: global.setTimeout, + ...pushPriceRetryOpts, + }, + ); + console.log(`Price ${price} pushed for ${brand}`); +}; + +/** + * + * @param {string} brand + * @returns {Promise} + */ +export const getPriceFeedRoundId = async brand => { + const latestRoundPath = `published.priceFeed.${brand}-USD_price_feed.latestRound`; + const latestRound = await getContractInfo(latestRoundPath, { + agoric, + prefix: '', + }); + + console.log('latestRound: ', latestRound); + return Number(latestRound.roundId); +}; diff --git a/a3p-integration/proposals/n:upgrade-next/test-lib/sync-tools.js b/a3p-integration/proposals/n:upgrade-next/test-lib/sync-tools.js new file mode 100644 index 00000000000..4a0e727c465 --- /dev/null +++ b/a3p-integration/proposals/n:upgrade-next/test-lib/sync-tools.js @@ -0,0 +1,72 @@ +/* eslint-env node */ + +/** + * @file These tools mostly duplicate code that will be added in other PRs + * and eventually migrated to synthetic-chain. Sorry for the duplication. + */ + +/** + * @typedef {object} RetryOptions + * @property {number} [maxRetries] + * @property {number} [retryIntervalMs] + * @property {(...arg0: string[]) => void} log + * @property {(object) => void} [setTimeout] + * @property {string} [errorMessage=Error] + */ + +const ambientSetTimeout = global.setTimeout; + +/** + * From https://github.com/Agoric/agoric-sdk/blob/442f07c8f0af03281b52b90e90c27131eef6f331/multichain-testing/tools/sleep.ts#L10 + * + * @param {number} ms + * @param {*} sleepOptions + */ +const sleep = (ms, { log = () => {}, setTimeout = ambientSetTimeout }) => + new Promise(resolve => { + log(`Sleeping for ${ms}ms...`); + setTimeout(resolve, ms); + }); + +/** + * From https://github.com/Agoric/agoric-sdk/blob/442f07c8f0af03281b52b90e90c27131eef6f331/multichain-testing/tools/sleep.ts#L24 + * + * @param {() => Promise} operation + * @param {(result: any) => boolean} condition + * @param {string} message + * @param {RetryOptions} options + */ +export const retryUntilCondition = async ( + operation, + condition, + message, + { maxRetries = 6, retryIntervalMs = 3500, log, setTimeout }, +) => { + console.log({ maxRetries, retryIntervalMs, message }); + let retries = 0; + + await null; + while (retries < maxRetries) { + try { + const result = await operation(); + log('RESULT', result); + if (condition(result)) { + return result; + } + } catch (error) { + if (error instanceof Error) { + log(`Error: ${error.message}`); + } else { + log(`Unknown error: ${String(error)}`); + } + } + + retries += 1; + console.log( + `Retry ${retries}/${maxRetries} - Waiting for ${retryIntervalMs}ms for ${message}...`, + ); + await sleep(retryIntervalMs, { log, setTimeout }); + } + + throw Error(`${message} condition failed after ${maxRetries} retries.`); +}; diff --git a/a3p-integration/proposals/n:upgrade-next/test.sh b/a3p-integration/proposals/n:upgrade-next/test.sh index 215f30f6aaf..07c0eb0d035 100755 --- a/a3p-integration/proposals/n:upgrade-next/test.sh +++ b/a3p-integration/proposals/n:upgrade-next/test.sh @@ -3,10 +3,13 @@ # Place here any test that should be executed using the executed proposal. # The effects of this step are not persisted in further proposal layers. +# suppress file names from glob that run earlier GLOBIGNORE=initial.test.js +yarn ava ./replaceElectorate.test.js + # test the state right after upgrade yarn ava initial.test.js -# test more, in ways that changes system state +# test more, in ways that change system state yarn ava ./*.test.js diff --git a/a3p-integration/proposals/n:upgrade-next/tsconfig.json b/a3p-integration/proposals/n:upgrade-next/tsconfig.json index 39de5a422e9..835db5a0316 100644 --- a/a3p-integration/proposals/n:upgrade-next/tsconfig.json +++ b/a3p-integration/proposals/n:upgrade-next/tsconfig.json @@ -1,7 +1,8 @@ { "compilerOptions": { "target": "esnext", - "module": "esnext", + "module": "NodeNext", + "moduleResolution": "NodeNext", "allowJs": true, "checkJs": true, "strict": false, diff --git a/a3p-integration/proposals/n:upgrade-next/use.sh b/a3p-integration/proposals/n:upgrade-next/use.sh new file mode 100644 index 00000000000..129c3b92444 --- /dev/null +++ b/a3p-integration/proposals/n:upgrade-next/use.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Exit when any command fails +set -uxeo pipefail + +node ./addGov4 +./acceptInvites.js + +./verifyPushedPrice.js 'ATOM' 12.01 +./verifyPushedPrice.js 'stATOM' 12.01 +./submitBid.js diff --git a/a3p-integration/proposals/n:upgrade-next/verifyPushedPrice.js b/a3p-integration/proposals/n:upgrade-next/verifyPushedPrice.js new file mode 100644 index 00000000000..98449c316a5 --- /dev/null +++ b/a3p-integration/proposals/n:upgrade-next/verifyPushedPrice.js @@ -0,0 +1,21 @@ +#!/usr/bin/env node + +import { + registerOraclesForBrand, + generateOracleMap, +} from '@agoric/synthetic-chain'; +import { argv } from 'node:process'; +import { verifyPushedPrice } from './test-lib/price-feed.js'; + +const brand = argv[2]; +const price = Number(argv[3]); + +const BASE_ID = 'n-upgrade'; +const ROUND_ID = 1; + +const oraclesByBrand = generateOracleMap(BASE_ID, [brand]); +await registerOraclesForBrand(brand, oraclesByBrand); +console.log(`Registering Oracle for ${brand}`); + +await verifyPushedPrice(price, brand, oraclesByBrand, ROUND_ID); +console.log(`Price pushed for ${brand}`); diff --git a/a3p-integration/proposals/n:upgrade-next/yarn.lock b/a3p-integration/proposals/n:upgrade-next/yarn.lock index 5de3eb1fd07..78dc133a0f9 100644 --- a/a3p-integration/proposals/n:upgrade-next/yarn.lock +++ b/a3p-integration/proposals/n:upgrade-next/yarn.lock @@ -20,6 +20,113 @@ __metadata: languageName: node linkType: hard +"@endo/base64@npm:^1.0.8": + version: 1.0.8 + resolution: "@endo/base64@npm:1.0.8" + checksum: 10c0/3501efbf866acc25b9ad0912ec2383e3b976c890a18dc67b5c6eb128433708db69e8ed1cc57190305266bdcbd132659aa87edfc6d02a9886b711e8b86adc21c0 + languageName: node + linkType: hard + +"@endo/common@npm:^1.2.6": + version: 1.2.6 + resolution: "@endo/common@npm:1.2.6" + dependencies: + "@endo/errors": "npm:^1.2.6" + "@endo/eventual-send": "npm:^1.2.6" + "@endo/promise-kit": "npm:^1.1.6" + checksum: 10c0/7cca372677bd1ab535a0f8fc250eca9bfc202ef7dfd24cac6ff1260003aa4512f8db18419dd632b3e57270a589fec54891496904ff2e8a97047f1e187e3f1401 + languageName: node + linkType: hard + +"@endo/env-options@npm:^1.1.7": + version: 1.1.7 + resolution: "@endo/env-options@npm:1.1.7" + checksum: 10c0/5784bd68790041b08d9ead4f6c29cc7871d2e554c23fc44fff38cb20b6b4e55cdba2f78d844ba5ad4b0818185c32475ff318c1b77890d628690d7c7a6ede9475 + languageName: node + linkType: hard + +"@endo/errors@npm:^1.2.6": + version: 1.2.6 + resolution: "@endo/errors@npm:1.2.6" + dependencies: + ses: "npm:^1.9.0" + checksum: 10c0/cbc541c2d26fbfeb1567dd1cdf0e1f110ee9866430c5a1b91d87270d6f00bc8293cc8512301217c4eda35e60677ce62a941d11eb32c7da1f72d450a011ad2bb5 + languageName: node + linkType: hard + +"@endo/eventual-send@npm:^1.2.6": + version: 1.2.6 + resolution: "@endo/eventual-send@npm:1.2.6" + dependencies: + "@endo/env-options": "npm:^1.1.7" + checksum: 10c0/6983d6b88bf4e99f6c469d4ca037793582b06cc0bfa2da085b5bc7ad67333a1fba6e5e7077b7f279be23ccfba1dff9e06c7f85f9980b09fd002227f89a673c11 + languageName: node + linkType: hard + +"@endo/init@npm:^1.1.5": + version: 1.1.5 + resolution: "@endo/init@npm:1.1.5" + dependencies: + "@endo/base64": "npm:^1.0.8" + "@endo/eventual-send": "npm:^1.2.6" + "@endo/lockdown": "npm:^1.0.11" + "@endo/promise-kit": "npm:^1.1.6" + checksum: 10c0/08abda8a0204450cb39c296270d074189f320cdeb03892e87b27a75f6b98c0b5d9c8471e242c53545843211fe713b01b281eec0eabc1c58ca0760a068b90335c + languageName: node + linkType: hard + +"@endo/lockdown@npm:^1.0.11": + version: 1.0.11 + resolution: "@endo/lockdown@npm:1.0.11" + dependencies: + ses: "npm:^1.9.0" + checksum: 10c0/03bb96f370e7ee69d9d8e26b7b124b6381994d3b28a99dc2bcafe77139283701d7543b40e978226cd49443c149e64610e4dd49c32ada931610ba091809478a11 + languageName: node + linkType: hard + +"@endo/marshal@npm:^1.5.4": + version: 1.5.4 + resolution: "@endo/marshal@npm:1.5.4" + dependencies: + "@endo/common": "npm:^1.2.6" + "@endo/errors": "npm:^1.2.6" + "@endo/eventual-send": "npm:^1.2.6" + "@endo/nat": "npm:^5.0.11" + "@endo/pass-style": "npm:^1.4.4" + "@endo/promise-kit": "npm:^1.1.6" + checksum: 10c0/4668a3678567cfbeefc3a47217912ca3f5d8bdb37e0d5d53339953b4ab83950683505f45c0f5c30b415c989bb9df4fa0859849d23b11f5b1064b0da0a13943ab + languageName: node + linkType: hard + +"@endo/nat@npm:^5.0.11": + version: 5.0.11 + resolution: "@endo/nat@npm:5.0.11" + checksum: 10c0/50cd9033657dd35288f0333a966984f788213f1c836e6772c0d731361d5854d0ce24b8a7d6f351d56618eb40f6b369e130c1ad939c83d5833246c09119b2e777 + languageName: node + linkType: hard + +"@endo/pass-style@npm:^1.4.4": + version: 1.4.4 + resolution: "@endo/pass-style@npm:1.4.4" + dependencies: + "@endo/env-options": "npm:^1.1.7" + "@endo/errors": "npm:^1.2.6" + "@endo/eventual-send": "npm:^1.2.6" + "@endo/promise-kit": "npm:^1.1.6" + "@fast-check/ava": "npm:^1.1.5" + checksum: 10c0/d6c6268b269d4c14541087ce6b2975f9e31847893d01360c0ea92392ae93316d94fbf59cd7299853a0fb5214176ad7ffc4616b9b2581fd720bdb55ef96d0beeb + languageName: node + linkType: hard + +"@endo/promise-kit@npm:^1.1.6": + version: 1.1.6 + resolution: "@endo/promise-kit@npm:1.1.6" + dependencies: + ses: "npm:^1.9.0" + checksum: 10c0/d60de663e58f9de32b6705268c62c63c4721f1874813d3c10cae7846e921e4ea8a60c8622ef8c937741a3387fbc60110076b25304afedf270727af7b0c3fecb3 + languageName: node + linkType: hard + "@endo/zip@npm:^1.0.7": version: 1.0.7 resolution: "@endo/zip@npm:1.0.7" @@ -27,6 +134,17 @@ __metadata: languageName: node linkType: hard +"@fast-check/ava@npm:^1.1.5": + version: 1.2.1 + resolution: "@fast-check/ava@npm:1.2.1" + dependencies: + fast-check: "npm:^3.0.0" + peerDependencies: + ava: ^4 || ^5 || ^6 + checksum: 10c0/3800098fd7e8098102544a2f7a595351d063a7ebaeca18ed4901df5ec2679da2330ba8c0db2c820721d4cbb3e23d817ba22fec6d058957930e229f44fa71a684 + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -119,18 +237,20 @@ __metadata: linkType: hard "acorn-walk@npm:^8.2.0": - version: 8.3.1 - resolution: "acorn-walk@npm:8.3.1" - checksum: 10c0/a23d2f7c6b6cad617f4c77f14dfeb062a239208d61753e9ba808d916c550add92b39535467d2e6028280761ac4f5a904cc9df21530b84d3f834e3edef74ddde5 + version: 8.3.4 + resolution: "acorn-walk@npm:8.3.4" + dependencies: + acorn: "npm:^8.11.0" + checksum: 10c0/76537ac5fb2c37a64560feaf3342023dadc086c46da57da363e64c6148dc21b57d49ace26f949e225063acb6fb441eabffd89f7a3066de5ad37ab3e328927c62 languageName: node linkType: hard -"acorn@npm:^8.8.2": - version: 8.11.2 - resolution: "acorn@npm:8.11.2" +"acorn@npm:^8.11.0, acorn@npm:^8.8.2": + version: 8.13.0 + resolution: "acorn@npm:8.13.0" bin: acorn: bin/acorn - checksum: 10c0/a3ed76c761b75ec54b1ec3068fb7f113a182e95aea7f322f65098c2958d232e3d211cb6dac35ff9c647024b63714bc528a26d54a925d1fef2c25585b4c8e4017 + checksum: 10c0/f35dd53d68177c90699f4c37d0bb205b8abe036d955d0eb011ddb7f14a81e6fd0f18893731c457c1b5bd96754683f4c3d80d9a5585ddecaa53cdf84e0b3d68f7 languageName: node linkType: hard @@ -317,9 +437,9 @@ __metadata: linkType: hard "binary-extensions@npm:^2.0.0": - version: 2.2.0 - resolution: "binary-extensions@npm:2.2.0" - checksum: 10c0/d73d8b897238a2d3ffa5f59c0241870043aa7471335e89ea5e1ff48edb7c2d0bb471517a3e4c5c3f4c043615caa2717b5f80a5e61e07503d51dc85cb848e665d + version: 2.3.0 + resolution: "binary-extensions@npm:2.3.0" + checksum: 10c0/75a59cafc10fb12a11d510e77110c6c7ae3f4ca22463d52487709ca7f18f69d886aa387557cc9864fbdb10153d0bdb4caacabf11541f55e89ed6e18d12ece2b5 languageName: node linkType: hard @@ -359,12 +479,12 @@ __metadata: languageName: node linkType: hard -"braces@npm:^3.0.2, braces@npm:~3.0.2": - version: 3.0.2 - resolution: "braces@npm:3.0.2" +"braces@npm:^3.0.3, braces@npm:~3.0.2": + version: 3.0.3 + resolution: "braces@npm:3.0.3" dependencies: - fill-range: "npm:^7.0.1" - checksum: 10c0/321b4d675791479293264019156ca322163f02dc06e3c4cab33bb15cd43d80b51efef69b0930cfde3acd63d126ebca24cd0544fa6f261e093a0fb41ab9dda381 + fill-range: "npm:^7.1.1" + checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04 languageName: node linkType: hard @@ -399,9 +519,9 @@ __metadata: linkType: hard "callsites@npm:^4.0.0": - version: 4.1.0 - resolution: "callsites@npm:4.1.0" - checksum: 10c0/91700844127a6dcd4792d231a12dd8e9ec10525eb9962180a8558417d7e3f443e52a4f14746ad2838eaf14f79431ee1539d13bd188da280f720a06a91bd1157a + version: 4.2.0 + resolution: "callsites@npm:4.2.0" + checksum: 10c0/8f7e269ec09fc0946bb22d838a8bc7932e1909ab4a833b964749f4d0e8bdeaa1f253287c4f911f61781f09620b6925ccd19a5ea4897489c4e59442c660c312a3 languageName: node linkType: hard @@ -422,8 +542,8 @@ __metadata: linkType: hard "chokidar@npm:^3.5.3": - version: 3.5.3 - resolution: "chokidar@npm:3.5.3" + version: 3.6.0 + resolution: "chokidar@npm:3.6.0" dependencies: anymatch: "npm:~3.1.2" braces: "npm:~3.0.2" @@ -436,7 +556,7 @@ __metadata: dependenciesMeta: fsevents: optional: true - checksum: 10c0/1076953093e0707c882a92c66c0f56ba6187831aa51bb4de878c1fec59ae611a3bf02898f190efec8e77a086b8df61c2b2a3ea324642a0558bdf8ee6c5dc9ca1 + checksum: 10c0/8361dcd013f2ddbe260eacb1f3cb2f2c6f2b0ad118708a343a5ed8158941a39cb8fb1d272e0f389712e74ee90ce8ba864eece9e0e62b9705cb468a2f6d917462 languageName: node linkType: hard @@ -662,9 +782,9 @@ __metadata: linkType: hard "emittery@npm:^1.0.1": - version: 1.0.1 - resolution: "emittery@npm:1.0.1" - checksum: 10c0/2587f2f42bb5e004ba1cde61352d2151f4dd4f29eb79ad36f82e200da2faec9742d7bfca1492a024d60396e001e4b07d9b2b9c43be33547ff751ba8ff87c42ce + version: 1.0.3 + resolution: "emittery@npm:1.0.3" + checksum: 10c0/91605d044f3891dd1f8ab731aeb94b520488b21e707f7064dcbcf5303bac3b4e7133dfa23c343ede1fc970340bd78a9b1aed522b805bc15104606bba630dd71e languageName: node linkType: hard @@ -715,9 +835,9 @@ __metadata: linkType: hard "escalade@npm:^3.1.1": - version: 3.1.1 - resolution: "escalade@npm:3.1.1" - checksum: 10c0/afd02e6ca91ffa813e1108b5e7756566173d6bc0d1eb951cb44d6b21702ec17c1cf116cfe75d4a2b02e05acb0b808a7a9387d0d1ca5cf9c04ad03a8445c3e46d + version: 3.2.0 + resolution: "escalade@npm:3.2.0" + checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65 languageName: node linkType: hard @@ -786,6 +906,15 @@ __metadata: languageName: node linkType: hard +"fast-check@npm:^3.0.0": + version: 3.22.0 + resolution: "fast-check@npm:3.22.0" + dependencies: + pure-rand: "npm:^6.1.0" + checksum: 10c0/15c70f83df655f9bdcec4f8ed7e3207a933bf6e22c220a4fafc7ea17a28a009bc5d0a85588689f5726e3514796745cb5b35e7295ce469ac286992548fd55cc1d + languageName: node + linkType: hard + "fast-diff@npm:^1.2.0": version: 1.3.0 resolution: "fast-diff@npm:1.3.0" @@ -807,11 +936,11 @@ __metadata: linkType: hard "fastq@npm:^1.6.0": - version: 1.15.0 - resolution: "fastq@npm:1.15.0" + version: 1.17.1 + resolution: "fastq@npm:1.17.1" dependencies: reusify: "npm:^1.0.4" - checksum: 10c0/5ce4f83afa5f88c9379e67906b4d31bc7694a30826d6cc8d0f0473c966929017fda65c2174b0ec89f064ede6ace6c67f8a4fe04cef42119b6a55b0d465554c24 + checksum: 10c0/1095f16cea45fb3beff558bb3afa74ca7a9250f5a670b65db7ed585f92b4b48381445cd328b3d87323da81e43232b5d5978a8201bde84e0cd514310f1ea6da34 languageName: node linkType: hard @@ -841,12 +970,12 @@ __metadata: languageName: node linkType: hard -"fill-range@npm:^7.0.1": - version: 7.0.1 - resolution: "fill-range@npm:7.0.1" +"fill-range@npm:^7.1.1": + version: 7.1.1 + resolution: "fill-range@npm:7.1.1" dependencies: to-regex-range: "npm:^5.0.1" - checksum: 10c0/7cdad7d426ffbaadf45aeb5d15ec675bbd77f7597ad5399e3d2766987ed20bda24d5fac64b3ee79d93276f5865608bb22344a26b9b1ae6c4d00bd94bf611623f + checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018 languageName: node linkType: hard @@ -1040,9 +1169,9 @@ __metadata: linkType: hard "ignore@npm:^5.2.4": - version: 5.3.0 - resolution: "ignore@npm:5.3.0" - checksum: 10c0/dc06bea5c23aae65d0725a957a0638b57e235ae4568dda51ca142053ed2c352de7e3bc93a69b2b32ac31966a1952e9a93c5ef2e2ab7c6b06aef9808f6b55b571 + version: 5.3.2 + resolution: "ignore@npm:5.3.2" + checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337 languageName: node linkType: hard @@ -1346,12 +1475,12 @@ __metadata: linkType: hard "micromatch@npm:^4.0.4": - version: 4.0.5 - resolution: "micromatch@npm:4.0.5" + version: 4.0.8 + resolution: "micromatch@npm:4.0.8" dependencies: - braces: "npm:^3.0.2" + braces: "npm:^3.0.3" picomatch: "npm:^2.3.1" - checksum: 10c0/3d6505b20f9fa804af5d8c596cb1c5e475b9b0cd05f652c5b56141cf941bd72adaeb7a436fda344235cef93a7f29b7472efc779fcdb83b478eab0867b95cdeff + checksum: 10c0/166fa6eb926b9553f32ef81f5f531d27b4ce7da60e5baf8c021d043b27a388fb95e46a8038d5045877881e673f8134122b59624d5cecbd16eb50a42e7a6b5ca8 languageName: node linkType: hard @@ -1790,6 +1919,13 @@ __metadata: languageName: node linkType: hard +"pure-rand@npm:^6.1.0": + version: 6.1.0 + resolution: "pure-rand@npm:6.1.0" + checksum: 10c0/1abe217897bf74dcb3a0c9aba3555fe975023147b48db540aa2faf507aee91c03bf54f6aef0eb2bf59cc259a16d06b28eca37f0dc426d94f4692aeff02fb0e65 + languageName: node + linkType: hard + "queue-microtask@npm:^1.2.2": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" @@ -1873,6 +2009,8 @@ __metadata: resolution: "root-workspace-0b6124@workspace:." dependencies: "@agoric/synthetic-chain": "npm:^0.3.0" + "@endo/init": "npm:^1.1.5" + "@endo/marshal": "npm:^1.5.4" ava: "npm:^5.3.1" better-sqlite3: "npm:^9.6.0" execa: "npm:^9.3.1" @@ -1902,7 +2040,16 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.2, semver@npm:^7.3.5": +"semver@npm:^7.3.2": + version: 7.6.3 + resolution: "semver@npm:7.6.3" + bin: + semver: bin/semver.js + checksum: 10c0/88f33e148b210c153873cb08cfe1e281d518aaa9a666d4d148add6560db5cd3c582f3a08ccb91f38d5f379ead256da9931234ed122057f40bb5766e65e58adaf + languageName: node + linkType: hard + +"semver@npm:^7.3.5": version: 7.5.4 resolution: "semver@npm:7.5.4" dependencies: @@ -1922,6 +2069,15 @@ __metadata: languageName: node linkType: hard +"ses@npm:^1.9.0": + version: 1.9.0 + resolution: "ses@npm:1.9.0" + dependencies: + "@endo/env-options": "npm:^1.1.7" + checksum: 10c0/356f9601b04a87f33403a15fc627caf0c649d86d8d7ee1f4b3c75b947ab05c31b254c94c0aa26e9904862787c73950d5a60f3435deebe5dba23017e20c40b0cb + languageName: node + linkType: hard + "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -2323,9 +2479,9 @@ __metadata: linkType: hard "yocto-queue@npm:^1.0.0": - version: 1.0.0 - resolution: "yocto-queue@npm:1.0.0" - checksum: 10c0/856117aa15cf5103d2a2fb173f0ab4acb12b4b4d0ed3ab249fdbbf612e55d1cadfd27a6110940e24746fb0a78cf640b522cc8bca76f30a3b00b66e90cf82abe0 + version: 1.1.1 + resolution: "yocto-queue@npm:1.1.1" + checksum: 10c0/cb287fe5e6acfa82690acb43c283de34e945c571a78a939774f6eaba7c285bacdf6c90fbc16ce530060863984c906d2b4c6ceb069c94d1e0a06d5f2b458e2a92 languageName: node linkType: hard diff --git a/a3p-integration/proposals/z:acceptance/auction.test.js b/a3p-integration/proposals/z:acceptance/auction.test.js new file mode 100644 index 00000000000..eda93ca260c --- /dev/null +++ b/a3p-integration/proposals/z:acceptance/auction.test.js @@ -0,0 +1,220 @@ +/* eslint-env node */ +/** + * @file In this file we aim to test auctioneer in an isolated manner. Here's the scenario to test; + * + * - Prerequisites: In one of the earlier proposal(n:upgrade-next), a user called "long-living-bidder" + * has placed a bid where { give: 80IST, price: 49.0 } + * - Push price so that 1 ATOM is 50 ISTs + * - Wait until the auctioneer captures the price we just pushed + * - Fund actors + * - gov1 gets 100 ATOMs + * - user1 gets 90 ISTs + * - gov3 gets 150 ISTs + * - Place bids for user1 and gov3 following the values in "config" + * - Deposit 100 ATOMs into book0, gov1 is the depositor + * - Wait until placed bids get their payouts + * - Wait until proceeds are distributed to the depositor + * - Make sure all actors receive the correct payouts + */ + +/** @typedef {import('./test-lib/sync-tools.js').RetryOptions} RetryOptions */ + +import { + agd, + agoric, + getUser, + GOV1ADDR, + GOV3ADDR, + USER1ADDR, +} from '@agoric/synthetic-chain'; +import '@endo/init'; +import test from 'ava'; +import { boardSlottingMarshaller, makeFromBoard } from './test-lib/rpc.js'; +import { retryUntilCondition } from './test-lib/sync-tools.js'; +import { + calculateRetryUntilNextStartTime, + checkBidsOutcome, + checkDepositOutcome, + checkPriceCaptured, + depositCollateral, + fundAccts, + placeBids, + pushPricesForAuction, + scale6, +} from './test-lib/auction-lib.js'; + +const ambientAuthority = { + query: agd.query, + follow: agoric.follow, + setTimeout, +}; + +const fromBoard = makeFromBoard(); +const marshaller = boardSlottingMarshaller(fromBoard.convertSlotToVal); + +const config = { + depositor: { + name: 'gov1', + addr: GOV1ADDR, + depositValue: '100000000', + offerId: `gov1-deposit-${Date.now()}`, + }, + price: 50.0, + longLivingBidSetup: { + name: 'long-living-bidder', + // This bid is placed in an earlier proposal + give: '80IST', + }, + currentBidsSetup: { + user1: { + bidder: USER1ADDR, + bidderFund: { + value: 90000000, + denom: 'uist', + }, + offerId: `user1-bid-${Date.now()}`, + give: '90IST', + price: 46, + }, + gov3: { + bidder: GOV3ADDR, + bidderFund: { + value: 150000000, + denom: 'uist', + }, + offerId: `gov3-bid-${Date.now()}`, + give: '150IST', + discount: '13', + }, + }, + bidsOutcome: { + longLivingBidder: { + payouts: { + Bid: 0, + Collateral: 1.68421, + }, + }, + user1: { + payouts: { + Bid: 0, + Collateral: 2.0, + }, + }, + gov3: { + payouts: { + Bid: 0, + Collateral: 3.448275, + }, + }, + }, +}; + +test.before(async t => { + /** @type {RetryOptions} */ + const pushPriceRetryOpts = { + maxRetries: 5, // arbitrary + retryIntervalMs: 5000, // in ms + }; + + /** @type {RetryOptions} */ + const bankSendRetryOpts = { + maxRetries: 3, // arbitrary + retryIntervalMs: 3000, // in ms + }; + + // Get current round id + const round = await agoric.follow( + '-lF', + ':published.priceFeed.ATOM-USD_price_feed.latestRound', + ); + t.context = { + roundId: parseInt(round.roundId, 10), + retryOpts: harden({ + bankSendRetryOpts, + pushPriceRetryOpts, + }), + }; +}); + +test('run auction', async t => { + // Push the price to a point where only our bids can settle + await pushPricesForAuction(t, config.price); + + // Wait until next round starts. Retry error message is useful for debugging + const { retryOptions, nextStartTime } = + await calculateRetryUntilNextStartTime(); + await retryUntilCondition( + () => Promise.resolve(Date.now()), + res => res >= nextStartTime * 1000, + 'next auction round not started yet [AUCTION TEST]', + { + log: t.log, + ...ambientAuthority, + ...retryOptions, + }, + ); + + // Make sure depositor and bidders have enough balance + await fundAccts(t, config.depositor, config.currentBidsSetup); + const bidsP = placeBids(t, config.currentBidsSetup); + const proceedsP = depositCollateral(t, config.depositor); + + // Make sure price captured + // We have to check this after bids are placed and collateral deposited + // because of https://github.com/Agoric/BytePitchPartnerEng/issues/31 + await checkPriceCaptured('book0', scale6(config.price).toString()); + + // Resolves when auction finalizes and depositor gets payouts + const [longLivingBidderAddr] = await Promise.all([ + getUser(config.longLivingBidSetup.name), + ...bidsP, + proceedsP, + ]); + + // Query wallets of the actors involved for assertions + const [gov1Results, longLivingBidResults, user1Results, gov3Results, brands] = + await Promise.all([ + agoric + .follow( + '-lF', + `:published.wallet.${config.depositor.addr}`, + '-o', + 'text', + ) + .then(res => marshaller.fromCapData(JSON.parse(res))), + agoric + .follow( + '-lF', + `:published.wallet.${longLivingBidderAddr}`, + '-o', + 'text', + ) + .then(res => marshaller.fromCapData(JSON.parse(res))), + agoric + .follow('-lF', `:published.wallet.${USER1ADDR}`, '-o', 'text') + .then(res => marshaller.fromCapData(JSON.parse(res))), + agoric + .follow('-lF', `:published.wallet.${GOV3ADDR}`, '-o', 'text') + .then(res => marshaller.fromCapData(JSON.parse(res))), + agoric + .follow('-lF', ':published.agoricNames.brand', '-o', 'text') + .then(res => + Object.fromEntries(marshaller.fromCapData(JSON.parse(res))), + ), + ]); + + // Assert depositor paid correctly + checkDepositOutcome(t, gov1Results.status.payouts, config, brands); + + // Assert bidders paid correctly + checkBidsOutcome( + t, + { + 'longLivingBidder.results': longLivingBidResults, + 'user1.results': user1Results, + 'gov3.results': gov3Results, + }, + config.bidsOutcome, + brands, + ); +}); diff --git a/a3p-integration/proposals/z:acceptance/governance.test.js b/a3p-integration/proposals/z:acceptance/governance.test.js new file mode 100644 index 00000000000..7e4b23b5418 --- /dev/null +++ b/a3p-integration/proposals/z:acceptance/governance.test.js @@ -0,0 +1,98 @@ +/* global fetch setTimeout */ + +import test from 'ava'; +import '@endo/init'; +import { execFileSync } from 'node:child_process'; +import { GOV1ADDR, GOV2ADDR } from '@agoric/synthetic-chain'; +import { networkConfig } from './test-lib/index.js'; +import { getLastUpdate, makeWalletUtils } from './test-lib/wallet.js'; +import { makeGovernanceDriver } from './test-lib/governance.js'; +import { makeTimerUtils } from './test-lib/utils.js'; + +const GOV4ADDR = 'agoric1c9gyu460lu70rtcdp95vummd6032psmpdx7wdy'; +const governanceAddresses = [GOV4ADDR, GOV2ADDR, GOV1ADDR]; + +test.serial( + 'economic committee can make governance proposal and vote on it', + async t => { + const { waitUntil } = makeTimerUtils({ setTimeout }); + const { readLatestHead } = await makeWalletUtils( + { setTimeout, execFileSync, fetch }, + networkConfig, + ); + const governanceDriver = await makeGovernanceDriver(fetch, networkConfig); + + /** @type {any} */ + const instance = await readLatestHead(`published.agoricNames.instance`); + const instances = Object.fromEntries(instance); + + /** @type {any} */ + const wallet = await readLatestHead( + `published.wallet.${governanceAddresses[0]}.current`, + ); + const usedInvitations = wallet.offerToUsedInvitation; + + const charterInvitation = usedInvitations.find( + v => + v[1].value[0].instance.getBoardId() === + instances.econCommitteeCharter.getBoardId(), + ); + + t.log('proposing on param change'); + const params = { + ChargingPeriod: 400n, + }; + const path = { paramPath: { key: 'governedParams' } }; + + await governanceDriver.proposeVaultDirectorParamChange( + governanceAddresses[0], + params, + path, + charterInvitation[0], + ); + + const questionUpdate = await getLastUpdate(governanceAddresses[0], { + readLatestHead, + }); + t.like(questionUpdate, { + status: { numWantsSatisfied: 1 }, + }); + + t.log('Voting on param change'); + for (const address of governanceAddresses) { + /** @type {any} */ + const voteWallet = await readLatestHead( + `published.wallet.${address}.current`, + ); + + const usedInvitationsForVoter = voteWallet.offerToUsedInvitation; + + const committeeInvitationForVoter = usedInvitationsForVoter.find( + v => + v[1].value[0].instance.getBoardId() === + instances.economicCommittee.getBoardId(), + ); + await governanceDriver.voteOnProposedChanges( + address, + committeeInvitationForVoter[0], + ); + + const voteUpdate = await getLastUpdate(address, { readLatestHead }); + t.like(voteUpdate, { + status: { numWantsSatisfied: 1 }, + }); + } + + /** @type {any} */ + const latestQuestion = await readLatestHead( + 'published.committees.Economic_Committee.latestQuestion', + ); + await waitUntil(latestQuestion.closingRule.deadline); + + t.log('check if latest outcome is correct'); + const latestOutcome = await readLatestHead( + 'published.committees.Economic_Committee.latestOutcome', + ); + t.like(latestOutcome, { outcome: 'win' }); + }, +); diff --git a/a3p-integration/proposals/z:acceptance/package.json b/a3p-integration/proposals/z:acceptance/package.json index a5910e5ded6..a0fcaa82e22 100644 --- a/a3p-integration/proposals/z:acceptance/package.json +++ b/a3p-integration/proposals/z:acceptance/package.json @@ -9,11 +9,13 @@ "type": "module", "license": "Apache-2.0", "dependencies": { - "@agoric/internal": "0.3.3-dev-5676146.0", + "@agoric/ertp": "dev", + "@agoric/internal": "dev", "@agoric/synthetic-chain": "^0.3.0", "@endo/errors": "^1.2.2", "@endo/far": "^1.1.5", "@endo/init": "^1.1.4", + "@endo/marshal": "^1.5.3", "ava": "^6.1.2", "execa": "^9.3.1", "tsx": "^4.17.0" diff --git a/a3p-integration/proposals/z:acceptance/psm.test.js b/a3p-integration/proposals/z:acceptance/psm.test.js new file mode 100644 index 00000000000..bd64b3c5229 --- /dev/null +++ b/a3p-integration/proposals/z:acceptance/psm.test.js @@ -0,0 +1,293 @@ +/* eslint-env node */ +/** + * @file The goal of this file is to implement a set of tests to make sure PSM works properly + * + * Here are the steps we want to take; + * 1 - Change swap fees and mint limit according to "psmTestSpecs" below + * 2 - Create a new user using agd.keys + * 3 - Fund new user with a stable coin from the VALIDATOR + * - Do not provision manually + * 4 - Make sure new user is able to mint IST from PSM (fees are applied) + * 5 - Make sure new user can pay their debt and get their anchor (fees are applied) + * 6 - Make sure mint limit is adhered + */ + +import test from 'ava'; +import { + agd, + agoric, + getUser, + GOV1ADDR, + GOV2ADDR, +} from '@agoric/synthetic-chain'; +import { + adjustBalancesIfNotProvisioned, + psmSwap, + bankSend, + checkGovParams, + checkSwapExceedMintLimit, + checkSwapSucceeded, + getPsmMetrics, + implementPsmGovParamChange, + initializeNewUser, + maxMintBelowLimit, +} from './test-lib/psm-lib.js'; +import { getBalances } from './test-lib/utils.js'; +import { NonNullish } from './test-lib/errors.js'; +import { waitUntilAccountFunded } from './test-lib/sync-tools.js'; + +// Export these from synthetic-chain? +const USDC_DENOM = NonNullish(process.env.USDC_DENOM); +const PSM_PAIR = NonNullish(process.env.PSM_PAIR); +const PSM_INSTANCE = `psm-${PSM_PAIR.replace('.', '-')}`; + +const psmSwapIo = { + now: Date.now, + follow: agoric.follow, + setTimeout, +}; + +const psmTestSpecs = { + govParams: { + giveMintedFeeVal: 10n, // in % + wantMintedFeeVal: 10n, // in % + mintLimit: 500n * 1_000_000n, // in IST + votingDuration: 1, // in minutes + }, + psmInstance: PSM_INSTANCE, + anchor: PSM_INSTANCE.split('-')[2], + newUser: { + name: 'new-psm-trader', + fund: { + denom: USDC_DENOM, + value: '300000000', // 300 USDC_axl + }, + }, + otherUser: { + name: 'gov1', + fund: { + denom: USDC_DENOM, + value: '1000000000', // 1000 USDC_axl + }, + toIst: { + value: 500, // in IST + }, + }, + toIst: { + value: 50, // in IST + }, + fromIst: { + value: 50, // in USDC_axl + }, +}; + +test.serial('change gov params', async t => { + await implementPsmGovParamChange( + { + address: GOV1ADDR, + instanceName: psmTestSpecs.psmInstance, + newParams: psmTestSpecs.govParams, + votingDuration: psmTestSpecs.govParams.votingDuration, + }, + { committeeAddrs: [GOV1ADDR, GOV2ADDR], position: 0 }, + { now: Date.now, follow: agoric.follow }, + ); + + await checkGovParams( + t, + { + GiveMintedFee: { + type: 'ratio', + value: { + numerator: { value: psmTestSpecs.govParams.giveMintedFeeVal * 100n }, // convert to bps + }, + }, + WantMintedFee: { + type: 'ratio', + value: { + numerator: { value: psmTestSpecs.govParams.wantMintedFeeVal * 100n }, // convert to bps + }, + }, + MintLimit: { + type: 'amount', + value: { + value: psmTestSpecs.govParams.mintLimit, + }, + }, + }, + psmTestSpecs.psmInstance.split('-')[2], + ); +}); + +test.serial('initialize new user', async t => { + const { + newUser: { name, fund }, + } = psmTestSpecs; + + await initializeNewUser(name, fund, { query: agd.query, setTimeout }); + t.pass(); +}); + +test.serial('swap into IST', async t => { + const { + newUser: { name }, + anchor, + toIst, + govParams: { wantMintedFeeVal }, + } = psmTestSpecs; + + const psmTrader = await getUser(name); + + const [metricsBefore, balances] = await Promise.all([ + getPsmMetrics(anchor), + getBalances([psmTrader]), + ]); + + const balancesBefore = await adjustBalancesIfNotProvisioned( + balances, + psmTrader, + ); + t.log('METRICS', metricsBefore); + t.log('BALANCES', balancesBefore); + + await psmSwap( + psmTrader, + [ + 'swap', + '--pair', + PSM_PAIR, + '--wantMinted', + toIst.value, + '--feePct', + wantMintedFeeVal, + ], + psmSwapIo, + ); + + await checkSwapSucceeded(t, metricsBefore, balancesBefore, { + wantMinted: toIst.value, + trader: psmTrader, + fee: Number(wantMintedFeeVal) / 100, // fee has to be between 0 and 1 + anchor, + }); +}); + +test.serial('swap out of IST', async t => { + const { + newUser: { name }, + anchor, + fromIst, + govParams: { giveMintedFeeVal }, + } = psmTestSpecs; + + const psmTrader = await getUser(name); + + const [metricsBefore, balancesBefore] = await Promise.all([ + getPsmMetrics(anchor), + getBalances([psmTrader]), + ]); + + t.log('METRICS', metricsBefore); + t.log('BALANCES', balancesBefore); + + await psmSwap( + psmTrader, + [ + 'swap', + '--pair', + PSM_PAIR, + '--giveMinted', + fromIst.value, + '--feePct', + giveMintedFeeVal, + ], + psmSwapIo, + ); + + await checkSwapSucceeded(t, metricsBefore, balancesBefore, { + giveMinted: fromIst.value, + trader: psmTrader, + fee: Number(giveMintedFeeVal) / 100, // fee has to be between 0 and 1 + anchor, + }); +}); + +test.serial('mint limit is adhered', async t => { + const { + otherUser: { + fund: { denom, value }, + name, + }, + govParams, + anchor, + } = psmTestSpecs; + + // Fund other user + const otherAddr = await getUser(name); + await bankSend(otherAddr, `${value}${denom}`); + await waitUntilAccountFunded( + otherAddr, + { query: agd.query, setTimeout }, + { denom, value: parseInt(value, 10) }, + { errorMessage: `${otherAddr} could not be funded with ${value}${denom}` }, + ); + + const [metricsBefore, balancesBefore] = await Promise.all([ + getPsmMetrics(anchor), + getBalances([otherAddr]), + ]); + + t.log('METRICS', metricsBefore); + t.log('BALANCES', balancesBefore); + + const { maxMintableValue, wantFeeValue } = await maxMintBelowLimit(anchor); + const maxMintFeesAccounted = Math.floor( + maxMintableValue * (1 - wantFeeValue), + ); + t.log({ maxMintableValue, wantFeeValue, maxMintFeesAccounted }); + + // Send a swap, should fail because mint limit is exceeded + await t.throwsAsync( + () => + psmSwap( + otherAddr, + [ + 'swap', + '--pair', + PSM_PAIR, + '--wantMinted', + maxMintFeesAccounted / 1000000 + 2, // Make sure we exceed the limit + '--feePct', + govParams.wantMintedFeeVal, + ], + psmSwapIo, + ), + { message: /not succeeded/ }, + ); + + // Now check if failed with correct error message + await checkSwapExceedMintLimit(t, otherAddr, metricsBefore); + + // Send another swap offer, this time should succeed + await psmSwap( + otherAddr, + [ + 'swap', + '--pair', + PSM_PAIR, + '--wantMinted', + maxMintFeesAccounted / 1000000, + '--feePct', + govParams.wantMintedFeeVal, + ], + psmSwapIo, + ); + + // Make sure swap succeeded + await checkSwapSucceeded(t, metricsBefore, balancesBefore, { + wantMinted: maxMintFeesAccounted / 1000000, + trader: otherAddr, + fee: Number(govParams.wantMintedFeeVal) / 100, // fee has to be between 0 and 1 + anchor, + }); +}); diff --git a/a3p-integration/proposals/z:acceptance/scripts/test-vaults.mts b/a3p-integration/proposals/z:acceptance/scripts/test-vaults.mts index 7fdbcef510a..6aa5cf15577 100755 --- a/a3p-integration/proposals/z:acceptance/scripts/test-vaults.mts +++ b/a3p-integration/proposals/z:acceptance/scripts/test-vaults.mts @@ -13,7 +13,7 @@ import { ISTunit, provisionWallet, setDebtLimit, -} from '../lib/vaults.mjs'; +} from '../test-lib/vaults.mjs'; const START_FREQUENCY = 600; // StartFrequency: 600s (auction runs every 10m) const CLOCK_STEP = 20; // ClockStep: 20s (ensures auction completes in time) diff --git a/a3p-integration/proposals/z:acceptance/test-lib/auction-lib.js b/a3p-integration/proposals/z:acceptance/test-lib/auction-lib.js new file mode 100644 index 00000000000..9ce1e3ddee1 --- /dev/null +++ b/a3p-integration/proposals/z:acceptance/test-lib/auction-lib.js @@ -0,0 +1,311 @@ +/* eslint-env node */ +import { + agd, + agopsInter, + agoric, + ATOM_DENOM, + CHAINID, + executeOffer, + generateOracleMap, + getPriceQuote, + GOV1ADDR, + pushPrices, + VALIDATORADDR, +} from '@agoric/synthetic-chain'; +import { AmountMath } from '@agoric/ertp'; +import { + retryUntilCondition, + waitUntilAccountFunded, + waitUntilOfferResult, +} from './sync-tools.js'; +import { boardSlottingMarshaller, makeFromBoard } from './rpc.js'; + +/** + * Typo will be fixed with https://github.com/Agoric/agoric-sdk/pull/10171 + * @typedef {import('./sync-tools.js').RetryOptions} RetryOptions + */ + +const ambientAuthority = { + query: agd.query, + follow: agoric.follow, + setTimeout, +}; + +export const scale6 = x => BigInt(x * 1_000_000); + +const fromBoard = makeFromBoard(); +const marshaller = boardSlottingMarshaller(fromBoard.convertSlotToVal); + +// From n-upgrade/verifyPushedPrice.js +const BASE_ID = 'n-upgrade'; + +// Import from synthetic-chain once it is updated +export const bankSend = (from, addr, wanted) => { + const chain = ['--chain-id', CHAINID]; + const fromArg = ['--from', from]; + const testKeyring = ['--keyring-backend', 'test']; + const noise = [...fromArg, ...chain, ...testKeyring, '--yes']; + + return agd.tx('bank', 'send', from, addr, wanted, ...noise); +}; + +export const pushPricesForAuction = async (t, price) => { + const oraclesByBrand = generateOracleMap(BASE_ID, ['ATOM']); + + await pushPrices(price, 'ATOM', oraclesByBrand, t.context.roundId + 1); + + await retryUntilCondition( + () => getPriceQuote('ATOM'), + res => res === `+${scale6(price).toString()}`, + 'price not pushed yet', + { + log: t.log, + setTimeout, + ...t.context.pushPriceRetryOpts, + }, + ); +}; + +/** + * @param {any} t + * @param {{ + * name: string + * offerId: string, + * depositValue: string, + * }} depositor + * @param {Record} bidders + */ +export const fundAccts = async (t, depositor, bidders) => { + const retryOpts = t.context.retryOpts.bankSendRetryOpts; + + await bankSend( + VALIDATORADDR, + GOV1ADDR, + `${depositor.depositValue}${ATOM_DENOM}`, + ); + await waitUntilAccountFunded( + GOV1ADDR, + ambientAuthority, + { denom: ATOM_DENOM, value: Number(depositor.depositValue) }, + { errorMessage: `${depositor.name} not funded yet`, ...retryOpts }, + ); + + for await (const [key, value] of [...Object.entries(bidders)]) { + const fund = value.bidderFund; + await bankSend(GOV1ADDR, value.bidder, `${fund.value}${fund.denom}`); + await waitUntilAccountFunded(value.bidder, ambientAuthority, fund, { + errorMessage: `${key} not funded yet`, + ...retryOpts, + }); + } +}; + +export const bidByPrice = (price, give, offerId, bidder) => { + return agopsInter( + 'bid', + 'by-price', + `--price ${price}`, + `--give ${give}`, + '--from', + bidder, + '--keyring-backend test', + `--offer-id ${offerId}`, + ); +}; + +export const bidByDiscount = (discount, give, offerId, bidder) => { + return agopsInter( + 'bid', + 'by-discount', + `--discount ${discount}`, + `--give ${give}`, + '--from', + bidder, + '--keyring-backend test', + `--offer-id ${offerId}`, + ); +}; + +export const placeBids = (t, bidsSetup) => { + return [...Object.values(bidsSetup)].map( + ({ bidder, offerId, price, give, discount }) => { + if (price) return bidByPrice(price, give, offerId, bidder); + return bidByDiscount(discount, give, offerId, bidder); + }, + ); +}; + +/** + * Calculates retry options based on "nextStartTime" + */ +export const calculateRetryUntilNextStartTime = async () => { + const schedule = await agoric.follow('-lF', ':published.auction.schedule'); + const nextStartTime = parseInt(schedule.nextStartTime.absValue, 10); + + /** @type {RetryOptions} */ + const capturePriceRetryOpts = { + maxRetries: Math.ceil((nextStartTime * 1000 - Date.now()) / 10000) + 2, // wait until next schedule + retryIntervalMs: 10000, // 10 seconds in ms + }; + + return { retryOptions: capturePriceRetryOpts, nextStartTime }; +}; + +/** + * + * @param {any} t + * @param {{ + * offerId: string, + * depositValue: string, + * addr: string + * }} depositor + */ +export const depositCollateral = async (t, depositor) => { + const [brandsRaw, { retryOptions }] = await Promise.all([ + agoric.follow('-lF', ':published.agoricNames.brand', '-o', 'text'), + calculateRetryUntilNextStartTime(), + ]); + const brands = Object.fromEntries( + marshaller.fromCapData(JSON.parse(brandsRaw)), + ); + + const offerSpec = { + id: depositor.offerId, + invitationSpec: { + source: 'agoricContract', + instancePath: ['auctioneer'], + callPipe: [['makeDepositInvitation']], + }, + proposal: { + give: { + Collateral: { + brand: brands.ATOM, + value: BigInt(depositor.depositValue), + }, + }, + }, + }; + + const spendAction = { + method: 'executeOffer', + offer: offerSpec, + }; + + const offer = JSON.stringify(marshaller.toCapData(harden(spendAction))); + t.log('OFFER', offer); + + executeOffer(depositor.addr, offer); + return waitUntilOfferResult( + depositor.addr, + depositor.offerId, + true, + ambientAuthority, + { + errorMessage: 'proceeds not distributed yet', + ...retryOptions, + }, + ); +}; + +export const checkBidsOutcome = (t, settledBids, bidsOutcome, brands) => { + [...Object.entries(settledBids)] + .map(([key, bidResult]) => [key.split('.')[0], bidResult.status.payouts]) + .forEach(([key, { Bid, Collateral }]) => { + t.log({ bidsOutcome }); + const { + payouts: { Bid: outcomeBidVal, Collateral: outcomeColVal }, + } = bidsOutcome[key]; + t.log({ outcomeBidVal, outcomeColVal }); + t.is( + AmountMath.isEqual( + Bid, + AmountMath.make(brands.IST, scale6(outcomeBidVal)), + ), + true, + ); + t.is( + AmountMath.isGTE( + Collateral, + AmountMath.make(brands.ATOM, scale6(outcomeColVal)), + ), + true, + ); + }); +}; + +export const checkDepositOutcome = (t, depositorPayouts, config, brands) => { + // Assert depositor paid correctly + const { Bid: depositorBid, Collateral: depositorCol } = depositorPayouts; + const { + depositor, + longLivingBidSetup: { give: longLivingBidGive }, + currentBidsSetup, + bidsOutcome, + } = config; + + const getNumberFromGive = give => + parseInt(give.substring(0, give.length - 3), 10); + + const calculateGiveTotal = () => { + let currentBidSum = getNumberFromGive(longLivingBidGive); + [...Object.values(currentBidsSetup)].forEach(({ give }) => { + currentBidSum += getNumberFromGive(give); + }); + + return scale6(currentBidSum); + }; + + const calculateOutcomeTotal = () => { + let total = 0n; + [...Object.values(bidsOutcome)] + .map(outcome => outcome.payouts.Collateral) + .forEach(element => { + t.log(element); + total += scale6(element); + }); + + return total; + }; + + t.is( + AmountMath.isEqual( + depositorBid, + AmountMath.make(brands.IST, calculateGiveTotal()), + ), + true, + ); + t.is( + AmountMath.isGTE( + AmountMath.make( + brands.ATOM, + BigInt(depositor.depositValue) - calculateOutcomeTotal(), + ), + depositorCol, + ), + true, + ); +}; + +/** + * + * @param {string} bookId + * @param {string} expected + */ +export const checkPriceCaptured = async (bookId, expected) => { + const { + startPrice: { + numerator: { value }, + }, + } = await agoric.follow('-lF', `:published.auction.${bookId}`); + assert(value, expected); +}; diff --git a/a3p-integration/proposals/z:acceptance/test-lib/auction-lib.test.js b/a3p-integration/proposals/z:acceptance/test-lib/auction-lib.test.js new file mode 100644 index 00000000000..1fa71951158 --- /dev/null +++ b/a3p-integration/proposals/z:acceptance/test-lib/auction-lib.test.js @@ -0,0 +1,122 @@ +import '@endo/init'; +import { AmountMath } from '@agoric/ertp'; +import { Far } from '@endo/far'; +import test from 'ava'; +import { GOV3ADDR, USER1ADDR } from '@agoric/synthetic-chain'; +import { checkBidsOutcome, checkDepositOutcome } from './auction-lib.js'; + +// From auction.test.js +const config = { + depositor: { + name: 'gov1', + depositValue: '100000000', + offerId: `gov1-deposit-${Date.now()}`, + }, + longLivingBidSetup: { + name: 'long-living-bidder', + // This bid is placed in an earlier proposal + give: '80IST', + }, + currentBidsSetup: { + user1: { + bidder: USER1ADDR, + bidderFund: { + value: 90000000, + denom: 'uist', + }, + offerId: `user1-bid-${Date.now()}`, + give: '90IST', + price: 46, + }, + gov3: { + bidder: GOV3ADDR, + bidderFund: { + value: 150000000, + denom: 'uist', + }, + offerId: `gov3-bid-${Date.now()}`, + give: '150IST', + discount: '13', + }, + }, + bidsOutcome: { + longLivingBidder: { + payouts: { + Bid: 0, + Collateral: 1.68421, + }, + }, + user1: { + payouts: { + Bid: 0, + Collateral: 2.0, + }, + }, + gov3: { + payouts: { + Bid: 0, + Collateral: 3.448275, + }, + }, + }, +}; + +test.before(t => { + const mockIST = Far('IST', {}); + const mockATOM = Far('ATOM', {}); + + t.context = { + brands: { + IST: mockIST, + ATOM: mockATOM, + }, + }; +}); + +test('make sure check* functions work properly', t => { + // @ts-expect-error + const { brands } = t.context; + const result = { + status: { + payouts: { + Bid: AmountMath.make(brands.IST, 0n), + Collateral: AmountMath.make(brands.ATOM, 1684210n), + }, + }, + }; + + checkBidsOutcome( + t, + { + 'longLivingBidder.results': result, + 'user1.results': { + status: { + payouts: { + Bid: AmountMath.make(brands.IST, 0n), + Collateral: AmountMath.make(brands.ATOM, 2000000n), + }, + }, + }, + 'gov3.results': { + status: { + payouts: { + Bid: AmountMath.make(brands.IST, 0n), + Collateral: AmountMath.make(brands.ATOM, 3448275n), + }, + }, + }, + }, + config.bidsOutcome, + brands, + ); + + checkDepositOutcome( + t, + harden({ + Bid: AmountMath.make(brands.IST, 320000000n), + Collateral: AmountMath.make(brands.ATOM, 100_000_000n - 7_132_485n), + }), + config, + brands, + ); +}); diff --git a/a3p-integration/proposals/z:acceptance/test-lib/errors.js b/a3p-integration/proposals/z:acceptance/test-lib/errors.js new file mode 100644 index 00000000000..57dc771e6a5 --- /dev/null +++ b/a3p-integration/proposals/z:acceptance/test-lib/errors.js @@ -0,0 +1,20 @@ +/** + * @file Copied from "@agoric/internal" + */ + +import { q } from '@endo/errors'; + +/** + * @template T + * @param {T | null | undefined} val + * @param {string} [optDetails] + * @returns {T} + */ +export const NonNullish = (val, optDetails = `unexpected ${q(val)}`) => { + if (val != null) { + // This `!= null` idiom checks that `val` is neither `null` nor `undefined`. + return val; + } + assert.fail(optDetails); +}; +harden(NonNullish); diff --git a/a3p-integration/proposals/z:acceptance/test-lib/governance.js b/a3p-integration/proposals/z:acceptance/test-lib/governance.js new file mode 100644 index 00000000000..910764d16c2 --- /dev/null +++ b/a3p-integration/proposals/z:acceptance/test-lib/governance.js @@ -0,0 +1,128 @@ +import { agops, agoric, executeOffer } from '@agoric/synthetic-chain'; +import { makeRpcUtils } from './rpc.js'; + +export const makeGovernanceDriver = async (fetch, networkConfig) => { + const { readLatestHead, marshaller } = await makeRpcUtils( + { fetch }, + networkConfig, + ); + + const generateVoteOffer = async previousOfferId => { + const id = `propose-${Date.now()}`; + + /** + * @type {object} + */ + const latestQuestionRecord = await readLatestHead( + 'published.committees.Economic_Committee.latestQuestion', + ); + + const chosenPositions = [latestQuestionRecord.positions[0]]; + const body = { + method: 'executeOffer', + offer: { + id, + invitationSpec: { + invitationMakerName: 'makeVoteInvitation', + previousOffer: previousOfferId, + source: 'continuing', + invitationArgs: harden([ + chosenPositions, + latestQuestionRecord.questionHandle, + ]), + }, + proposal: {}, + }, + }; + + const capData = marshaller.toCapData(harden(body)); + return JSON.stringify(capData); + }; + + const voteOnProposedChanges = async (address, committeeAcceptOfferId) => { + await null; + const offerId = + committeeAcceptOfferId || + (await agops.ec( + 'find-continuing-id', + '--for', + 'Voter0', + '--from', + address, + )); + + return executeOffer(address, generateVoteOffer(offerId)); + }; + + const generateVaultDirectorParamChange = async ( + previousOfferId, + voteDur, + params, + paramsPath, + ) => { + const voteDurSec = BigInt(voteDur); + const toSec = ms => BigInt(Math.round(ms / 1000)); + + const id = `propose-${Date.now()}`; + const deadline = toSec(Date.now()) + voteDurSec; + + const a = await agoric.follow( + '-lF', + ':published.agoricNames.instance', + '-o', + 'text', + ); + const instance = Object.fromEntries(marshaller.fromCapData(JSON.parse(a))); + assert(instance.VaultFactory); + + const body = { + method: 'executeOffer', + offer: { + id, + invitationSpec: { + invitationMakerName: 'VoteOnParamChange', + previousOffer: previousOfferId, + source: 'continuing', + }, + offerArgs: { + deadline, + instance: instance.VaultFactory, + params, + path: paramsPath, + }, + proposal: {}, + }, + }; + + const capData = marshaller.toCapData(harden(body)); + return JSON.stringify(capData); + }; + + const proposeVaultDirectorParamChange = async ( + address, + params, + path, + charterAcceptOfferId, + ) => { + await null; + const offerId = + charterAcceptOfferId || + (await agops.ec( + 'find-continuing-id', + '--for', + `${'charter\\ member\\ invitation'}`, + '--from', + address, + )); + + return executeOffer( + address, + generateVaultDirectorParamChange(offerId, 10, params, path), + ); + }; + + return { + voteOnProposedChanges, + proposeVaultDirectorParamChange, + }; +}; diff --git a/a3p-integration/proposals/z:acceptance/test-lib/index.js b/a3p-integration/proposals/z:acceptance/test-lib/index.js index 2f2e7c02d3c..55bbfe8ff20 100644 --- a/a3p-integration/proposals/z:acceptance/test-lib/index.js +++ b/a3p-integration/proposals/z:acceptance/test-lib/index.js @@ -1,21 +1,4 @@ -/* global fetch setTimeout */ -import { execFileSync } from 'child_process'; -import { makeWalletUtils } from './wallet.js'; - export const networkConfig = { rpcAddrs: ['http://0.0.0.0:26657'], chainName: 'agoriclocal', }; - -/** - * Resolve after a delay in milliseconds. - * - * @param {number} ms - * @returns {Promise} - */ -const delay = ms => new Promise(resolve => setTimeout(() => resolve(), ms)); - -export const walletUtils = await makeWalletUtils( - { delay, execFileSync, fetch }, - networkConfig, -); diff --git a/a3p-integration/proposals/z:acceptance/test-lib/psm-lib.js b/a3p-integration/proposals/z:acceptance/test-lib/psm-lib.js new file mode 100644 index 00000000000..442a947500b --- /dev/null +++ b/a3p-integration/proposals/z:acceptance/test-lib/psm-lib.js @@ -0,0 +1,623 @@ +/* eslint-env node */ + +import '@endo/init'; +import { + addUser, + agd, + agops, + agoric, + executeOffer, + getUser, + agopsLocation, + executeCommand, + CHAINID, + VALIDATORADDR, + GOV1ADDR, + mkTemp, +} from '@agoric/synthetic-chain'; +import { AmountMath } from '@agoric/ertp'; +import fsp from 'node:fs/promises'; +import { boardSlottingMarshaller, makeFromBoard } from './rpc.js'; +import { getBalances } from './utils.js'; +import { + retryUntilCondition, + waitUntilAccountFunded, + waitUntilOfferResult, +} from './sync-tools.js'; +import { NonNullish } from './errors.js'; + +// Export these from synthetic-chain? +const USDC_DENOM = NonNullish(process.env.USDC_DENOM); +const PSM_PAIR = NonNullish(process.env.PSM_PAIR).replace('.', '-'); + +/** + * @typedef {object} PsmMetrics + * @property {import('@agoric/ertp').Amount<'nat'>} anchorPoolBalance + * @property {import('@agoric/ertp').Amount<'nat'>} feePoolBalance + * @property {import('@agoric/ertp').Amount<'nat'>} mintedPoolBalance + * @property {import('@agoric/ertp').Amount<'nat'>} totalAnchorProvided + * @property {import('@agoric/ertp').Amount<'nat'>} totalMintedProvided + * + * @typedef {Array<{ denom: string; amount: string; }>} CosmosBalances + */ + +const fromBoard = makeFromBoard(); +const marshaller = boardSlottingMarshaller(fromBoard.convertSlotToVal); + +/** + * Import from synthetic-chain once it is updated + * + * @param {string} addr + * @param {string} wanted + * @param {string} [from] + */ +export const bankSend = (addr, wanted, from = VALIDATORADDR) => { + const chain = ['--chain-id', CHAINID]; + const fromArg = ['--from', from]; + const testKeyring = ['--keyring-backend', 'test']; + const noise = [...fromArg, ...chain, ...testKeyring, '--yes']; + + return agd.tx('bank', 'send', from, addr, wanted, ...noise); +}; + +/** + * + * @param {{ + * address: string + * instanceName: string + * newParams: Params + * deadline: bigint + * offerId: string + * }} QuestionDetails + */ +export const buildProposePSMParamChangeOffer = async ({ + address, + instanceName, + newParams, + deadline, + offerId, +}) => { + const charterAcceptOfferId = await agops.ec( + 'find-continuing-id', + '--for', + `${'charter\\ member\\ invitation'}`, + '--from', + address, + ); + console.log('charterAcceptOfferId', charterAcceptOfferId); + const [brands, instances] = await Promise.all([ + agoric + .follow('-lF', ':published.agoricNames.brand', '-o', 'text') + .then(brandsRaw => + Object.fromEntries(marshaller.fromCapData(JSON.parse(brandsRaw))), + ), + agoric + .follow('-lF', ':published.agoricNames.instance', '-o', 'text') + .then(instancesRaw => + Object.fromEntries(marshaller.fromCapData(JSON.parse(instancesRaw))), + ), + ]); + + console.log('charterAcceptOfferId', charterAcceptOfferId); + console.log('BRANDS', brands); + console.log('INSTANCE', instances); + + /** + * @param {bigint} numValInPercent + */ + const toRatio = numValInPercent => { + const commonDenominator = AmountMath.make(brands.IST, 10_000n); + const numerator = AmountMath.make(brands.IST, numValInPercent * 100n); // Convert to bps + + return { + numerator, + denominator: commonDenominator, + }; + }; + + const params = {}; + if (newParams.giveMintedFeeVal) { + params.GiveMintedFee = toRatio(newParams.giveMintedFeeVal); + } + + if (newParams.wantMintedFeeVal) { + params.WantMintedFee = toRatio(newParams.wantMintedFeeVal); + } + + if (newParams.mintLimit) { + params.MintLimit = AmountMath.make(brands.IST, newParams.mintLimit); + } + + const offerSpec = { + id: offerId, + invitationSpec: { + source: 'continuing', + previousOffer: charterAcceptOfferId, + invitationMakerName: 'VoteOnParamChange', + }, + proposal: {}, + offerArgs: { + instance: instances[instanceName], + params, + deadline, + }, + }; + + /** @type {string | object} */ + const spendAction = { + method: 'executeOffer', + offer: offerSpec, + }; + + const offer = JSON.stringify(marshaller.toCapData(harden(spendAction))); + console.log(offerSpec); + console.log(offer); + + return executeOffer(address, offer); +}; + +/** + * + * @param {{ + * committeeAddrs: Array + * position: number | string + * }} VotingDetails + */ +export const voteForNewParams = ({ committeeAddrs, position }) => { + console.log('ACTIONS voting for position', position, 'using', committeeAddrs); + return Promise.all( + committeeAddrs.map(account => + // @ts-expect-error Casting + agops.ec('vote', '--forPosition', position, '--send-from', account), + ), + ); +}; + +/** + * @param {{follow: (...params: string[]) => Promise }} io + */ +export const fetchLatestEcQuestion = async io => { + const { follow } = io; + const pathOutcome = ':published.committees.Economic_Committee.latestOutcome'; + const pathQuestion = + ':published.committees.Economic_Committee.latestQuestion'; + + const [latestOutcome, latestQuestion] = await Promise.all([ + follow('-lF', pathOutcome, '-o', 'text').then(outcomeRaw => + marshaller.fromCapData(JSON.parse(outcomeRaw)), + ), + follow('-lF', pathQuestion, '-o', 'text').then(questionRaw => + marshaller.fromCapData(JSON.parse(questionRaw)), + ), + ]); + + return { latestOutcome, latestQuestion }; +}; + +const checkCommitteeElectionResult = (electionResult, expectedResult) => { + const { + latestOutcome: { outcome, question }, + latestQuestion: { + closingRule: { deadline }, + questionHandle, + }, + } = electionResult; + const { outcome: expectedOutcome, deadline: expectedDeadline } = + expectedResult; + + return ( + expectedOutcome === outcome && + deadline === expectedDeadline && + question === questionHandle + ); +}; + +/** + * @typedef {{ + * giveMintedFeeVal: bigint; + * wantMintedFeeVal: bigint; + * mintLimit: bigint; + * }} Params + * + * + * @param {{ + * address: string + * instanceName: string + * newParams: Params + * votingDuration: number + * offerId?: string + * }} question + * + * @param {{ + * committeeAddrs: Array + * position: number + * }} voting + * + * @param {{ now: () => number; follow: (...params: string[]) => Promise}} io + */ +export const implementPsmGovParamChange = async (question, voting, io) => { + const { now: getNow, follow } = io; + const now = getNow(); + const deadline = BigInt( + question.votingDuration * 60 + Math.round(now / 1000), + ); + + await buildProposePSMParamChangeOffer({ + ...question, + deadline, + offerId: now.toString(), + }); + await voteForNewParams(voting); + + console.log('ACTIONS wait for the vote deadline to pass'); + await retryUntilCondition( + () => fetchLatestEcQuestion({ follow }), + electionResult => + checkCommitteeElectionResult(electionResult, { + outcome: 'win', + deadline, + }), + 'PSM param change election failed', + { setTimeout, retryIntervalMs: 5000, maxRetries: 15 }, + ); +}; + +/** + * @param {string} anchor + */ +export const getPsmGovernance = async anchor => { + const governanceRaw = await agoric.follow( + '-lF', + `:published.psm.IST.${anchor}.governance`, + '-o', + 'text', + ); + const { current } = marshaller.fromCapData(JSON.parse(governanceRaw)); + return current; +}; + +/** + * @param {string} anchor + */ +export const getPsmMetrics = async anchor => { + const metricsRaw = await agoric.follow( + '-lF', + `:published.psm.IST.${anchor}.metrics`, + '-o', + 'text', + ); + + return marshaller.fromCapData(JSON.parse(metricsRaw)); +}; + +export const checkGovParams = async (t, expected, psmName) => { + const current = await getPsmGovernance(psmName); + + t.log({ + give: current.WantMintedFee.value, + want: current.GiveMintedFee.value, + mintLimit: current.MintLimit, + }); + + t.like(current, expected); +}; + +/** + * + * @param {string} userName + * @param {{ + * denom: string, + * value: string + * }} expectedAnchorFunds + */ +export const checkUserInitializedSuccessfully = async ( + userName, + expectedAnchorFunds, +) => { + const userAddress = await getUser(userName); + + const balance = await getBalances([userAddress], expectedAnchorFunds.denom); + assert(balance >= BigInt(expectedAnchorFunds.value)); +}; + +/** + * + * @param {string} name + * @param {{ + * denom: string, + * value: string + * }} fund + * @param io + */ +export const initializeNewUser = async (name, fund, io) => { + const psmTrader = await addUser(name); + await Promise.all([ + bankSend(psmTrader, `20000000ubld,${fund.value}${fund.denom}`), + bankSend(psmTrader, `1000000uist`, GOV1ADDR), + ]); + + await waitUntilAccountFunded( + psmTrader, + io, + { denom: fund.denom, value: parseInt(fund.value, 10) }, + { errorMessage: `${psmTrader} not funded` }, + ); + + await checkUserInitializedSuccessfully(name, fund); +}; + +/** + * Similar to https://github.com/Agoric/agoric-3-proposals/blob/422b163fecfcf025d53431caebf6d476778b5db3/packages/synthetic-chain/src/lib/commonUpgradeHelpers.ts#L123-L139 + * However, in the case where "address" is not provisioned "agoric wallet send" is needed because + * "agops perf satisfaction" tries to follow ":published.wallet.${address}" which blocks the execution because no such path exists in + * vstorage. In situations like this "agoric wallet send" seems a better choice as it doesn't depend on following user's vstorage wallet path + * + * @param {string} address + * @param {Promise} offerPromise + */ +export const sendOfferAgoric = async (address, offerPromise) => { + const offerPath = await mkTemp('agops.XXX'); + const offer = await offerPromise; + await fsp.writeFile(offerPath, offer); + + await agoric.wallet( + '--keyring-backend=test', + 'send', + '--offer', + offerPath, + '--from', + address, + ); +}; + +/** + * @param {string} address + * @param {Array} params + * @param {{ + * follow: (...params: string[]) => Promise; + * setTimeout: (callback: Function, delay: number) => void; + * now: () => number + * }} io + */ +export const psmSwap = async (address, params, io) => { + const now = io.now(); + const offerId = `${address}-psm-swap-${now}`; + const newParams = ['psm', ...params, '--offerId', offerId]; + const offerPromise = executeCommand(agopsLocation, newParams); + await sendOfferAgoric(address, offerPromise); + + await waitUntilOfferResult(address, offerId, true, io, { + errorMessage: `${offerId} not succeeded`, + }); +}; + +/** + * + * @param {number} base + * @param {number} fee + */ +const giveAnchor = (base, fee) => Math.ceil(base / (1 - fee)); + +/** + * + * @param {number} base + * @param {number} fee + */ +const receiveAnchor = (base, fee) => Math.ceil(base * (1 - fee)); + +/** + * + * @param {CosmosBalances} balances + * @param {string} targetDenom + */ +const extractBalance = (balances, targetDenom) => { + const balance = balances.find(({ denom }) => denom === targetDenom); + if (!balance) return 0; + return Number(balance.amount); +}; + +/** + * Checking IST balances can be tricky because of the execution fee mentioned in + * https://github.com/Agoric/agoric-sdk/issues/6525. Here we first check with + * whatever is passed in. If the first attempt fails we try again to see if + * there was an execution fee charged. If still fails, we throw. + * + * @param {import('ava').ExecutionContext} t + * @param {number} actualBalance + * @param {number} expectedBalance + */ +const tryISTBalances = async (t, actualBalance, expectedBalance) => { + const firstTry = await t.try( + (tt, actual, expected) => { + tt.deepEqual(actual, expected); + }, + actualBalance, + expectedBalance, + ); + + if (!firstTry.passed) { + firstTry.discard(); + t.deepEqual(actualBalance + 200000, expectedBalance); + } else firstTry.commit(); +}; + +/** + * + * @param {import('ava').ExecutionContext} t + * @param {PsmMetrics} metricsBefore + * @param {CosmosBalances} balancesBefore + * @param {{trader: string; fee: number; anchor: string;} & ( + * | {wantMinted: number} + * | {giveMinted: number} + * )} tradeInfo + */ +export const checkSwapSucceeded = async ( + t, + metricsBefore, + balancesBefore, + tradeInfo, +) => { + const [metricsAfter, balancesAfter] = await Promise.all([ + getPsmMetrics(tradeInfo.anchor), + getBalances([tradeInfo.trader]), + ]); + + t.log('METRICS_AFTER', metricsAfter); + t.log('BALANCES_AFTER', balancesAfter); + + if ('wantMinted' in tradeInfo) { + const anchorPaid = giveAnchor( + tradeInfo.wantMinted * 1000000, + tradeInfo.fee, + ); + const mintedReceived = tradeInfo.wantMinted * 1000000; + const feePaid = anchorPaid - mintedReceived; + + t.deepEqual( + extractBalance(balancesAfter, USDC_DENOM), + extractBalance(balancesBefore, USDC_DENOM) - anchorPaid, + ); + + await tryISTBalances( + t, + extractBalance(balancesAfter, 'uist'), + extractBalance(balancesBefore, 'uist') + mintedReceived, + ); + + t.like(metricsAfter, { + anchorPoolBalance: { + value: metricsBefore.anchorPoolBalance.value + BigInt(anchorPaid), + }, + feePoolBalance: { + value: metricsBefore.feePoolBalance.value + BigInt(feePaid), + }, + mintedPoolBalance: { + value: metricsBefore.mintedPoolBalance.value + BigInt(anchorPaid), + }, + totalAnchorProvided: { + value: metricsBefore.totalAnchorProvided.value, + }, + totalMintedProvided: { + value: metricsBefore.totalMintedProvided.value + BigInt(anchorPaid), + }, + }); + } else if ('giveMinted' in tradeInfo) { + const mintedPaid = tradeInfo.giveMinted * 1000000; + const anchorReceived = receiveAnchor( + tradeInfo.giveMinted * 1000000, + tradeInfo.fee, + ); + const feePaid = mintedPaid - anchorReceived; + + t.deepEqual( + extractBalance(balancesAfter, USDC_DENOM), + extractBalance(balancesBefore, USDC_DENOM) + anchorReceived, + ); + + await tryISTBalances( + t, + extractBalance(balancesAfter, 'uist'), + extractBalance(balancesBefore, 'uist') - mintedPaid, + ); + + t.like(metricsAfter, { + anchorPoolBalance: { + value: metricsBefore.anchorPoolBalance.value - BigInt(anchorReceived), + }, + feePoolBalance: { + value: metricsBefore.feePoolBalance.value + BigInt(feePaid), + }, + mintedPoolBalance: { + value: metricsBefore.mintedPoolBalance.value - BigInt(anchorReceived), + }, + totalAnchorProvided: { + value: metricsBefore.totalAnchorProvided.value + BigInt(anchorReceived), + }, + totalMintedProvided: { + value: metricsBefore.totalMintedProvided.value, + }, + }); + } +}; + +/** + * + * @param {Array<{ denom: string; amount: string }>} balances + * @param {string} address + */ +export const adjustBalancesIfNotProvisioned = async (balances, address) => { + const { children } = await agd.query( + 'vstorage', + 'children', + 'published.wallet', + '-o', + 'json', + ); + const addressProvisioned = children.includes(address); + + if (addressProvisioned === true) return balances; + + const balancesAdjusted = []; + + for (const { denom, amount } of balances) { + if (denom === 'uist') { + const startingAmount = ( + parseInt(amount, 10) + + 250000 - + 1_000_000 + ).toString(); // provision sends 250000uist to new accounts and 1 IST is charged + balancesAdjusted.push({ denom, amount: startingAmount }); + } else { + balancesAdjusted.push({ denom, amount }); + } + } + + return balancesAdjusted; +}; + +/** + * + * @param {any} t + * @param {string} address + * @param {Record} metricsBefore + */ +export const checkSwapExceedMintLimit = async (t, address, metricsBefore) => { + const [offerResult, metricsAfter] = await Promise.all([ + agoric.follow('-lF', `:published.wallet.${address}`), + getPsmMetrics(PSM_PAIR.split('-')[1]), + ]); + const { status, updated } = offerResult; + + t.is(updated, 'offerStatus'); + t.is(status.error, 'Error: Request would exceed mint limit'); + t.like(metricsBefore, { + mintedPoolBalance: { value: metricsAfter.mintedPoolBalance.value }, + }); +}; + +/** + * @param {string} anchor + * @returns {Promise<{ maxMintableValue: number; wantFeeValue: number; giveFeeValue: number; }>} + */ +export const maxMintBelowLimit = async anchor => { + const [governance, metrics] = await Promise.all([ + getPsmGovernance(anchor), + getPsmMetrics(anchor), + ]); + + const mintLimitVal = Number(governance.MintLimit.value.value); + const mintedPoolBalanceVal = Number(metrics.mintedPoolBalance.value); + const maxMintableValue = mintLimitVal - mintedPoolBalanceVal - 1; + + const wantFeeRatio = governance.WantMintedFee.value; + const giveFeeRatio = governance.GiveMintedFee.value; + + const wantFeeValue = + Number(wantFeeRatio.numerator.value) / + Number(wantFeeRatio.denominator.value); + const giveFeeValue = + Number(giveFeeRatio.numerator.value) / + Number(giveFeeRatio.denominator.value); + + return { maxMintableValue, wantFeeValue, giveFeeValue }; +}; diff --git a/a3p-integration/proposals/z:acceptance/test-lib/sync-tools.js b/a3p-integration/proposals/z:acceptance/test-lib/sync-tools.js index f0daeddabde..dac2ba7e04f 100644 --- a/a3p-integration/proposals/z:acceptance/test-lib/sync-tools.js +++ b/a3p-integration/proposals/z:acceptance/test-lib/sync-tools.js @@ -16,12 +16,12 @@ /** * @typedef {object} RetryOptions - * @property {number} maxRetries - * @property {number} retryIntervalMs - * @property {(...arg0: string[]) => void} log - * @property {(object) => void} [setTimeout] - * @property {string} [errorMessage=Error] + * @property {number} [maxRetries] + * @property {number} [retryIntervalMs] + * @property {(...arg0: string[]) => void} [log] + * @property {(callback: Function, delay: number) => void} [setTimeout] * + * @typedef {RetryOptions & {errorMessage: string}} WaitUntilOptions * * @typedef {object} CosmosBalanceThreshold * @property {string} denom @@ -36,7 +36,7 @@ const ambientSetTimeout = global.setTimeout; * @param {number} ms * @param {*} sleepOptions */ -const sleep = (ms, { log = () => {}, setTimeout = ambientSetTimeout }) => +export const sleep = (ms, { log = () => {}, setTimeout = ambientSetTimeout }) => new Promise(resolve => { log(`Sleeping for ${ms}ms...`); setTimeout(resolve, ms); @@ -54,7 +54,7 @@ export const retryUntilCondition = async ( operation, condition, message, - { maxRetries = 6, retryIntervalMs = 3500, log, setTimeout }, + { maxRetries = 6, retryIntervalMs = 3500, log = console.log, setTimeout }, ) => { console.log({ maxRetries, retryIntervalMs, message }); let retries = 0; @@ -85,7 +85,7 @@ export const retryUntilCondition = async ( }; /** - * @param {RetryOptions} options + * @param {WaitUntilOptions} options */ const overrideDefaultOptions = options => { const defaultValues = { @@ -113,7 +113,7 @@ const makeGetInstances = follow => async () => { * * @param {string} contractName * @param {{follow: () => object, setTimeout: (object) => void}} ambientAuthority - * @param {RetryOptions} options + * @param {WaitUntilOptions} options */ export const waitUntilContractDeployed = ( contractName, @@ -155,7 +155,7 @@ const checkCosmosBalance = (balances, threshold) => { * @param {string} destAcct * @param {{query: () => Promise, setTimeout: (object) => void}} ambientAuthority * @param {{denom: string, value: number}} threshold - * @param {RetryOptions} options + * @param {WaitUntilOptions} options */ export const waitUntilAccountFunded = ( destAcct, @@ -197,7 +197,7 @@ const checkOfferState = (offerStatus, waitForPayouts, offerId) => { if (!status) return false; if (status.id !== offerId) return false; if (!status.numWantsSatisfied || status.numWantsSatisfied !== 1) return false; - if (waitForPayouts && status.result && status.payouts) return true; + if (waitForPayouts && status.payouts) return true; if (!waitForPayouts && status.result) return true; return false; @@ -208,8 +208,8 @@ const checkOfferState = (offerStatus, waitForPayouts, offerId) => { * @param {string} addr * @param {string} offerId * @param {boolean} waitForPayouts - * @param {{follow: () => object, setTimeout: (object) => void}} ambientAuthority - * @param {RetryOptions} options + * @param {{follow: () => object, setTimeout: (callback: Function, delay: number) => void}} ambientAuthority + * @param {WaitUntilOptions} options */ export const waitUntilOfferResult = ( addr, @@ -251,7 +251,7 @@ const checkForInvitation = update => { * * @param {string} addr * @param {{follow: () => object, setTimeout: (object) => void}} ambientAuthority - * @param {RetryOptions} options + * @param {WaitUntilOptions} options */ export const waitUntilInvitationReceived = ( addr, diff --git a/a3p-integration/proposals/z:acceptance/test-lib/utils.js b/a3p-integration/proposals/z:acceptance/test-lib/utils.js index 4989d4b71e4..2ea35ba4bae 100644 --- a/a3p-integration/proposals/z:acceptance/test-lib/utils.js +++ b/a3p-integration/proposals/z:acceptance/test-lib/utils.js @@ -46,3 +46,23 @@ export const getBalances = async (addresses, targetDenom = undefined) => { }; export const agopsVaults = addr => agops.vaults('list', '--from', addr); + +export const makeTimerUtils = ({ setTimeout }) => { + /** + * Resolve after a delay in milliseconds. + * + * @param {number} ms + * @returns {Promise} + */ + const delay = ms => new Promise(resolve => setTimeout(() => resolve(), ms)); + + const waitUntil = async timestamp => { + const timeDelta = Math.floor(Date.now() / 1000) - Number(timestamp); + await delay(timeDelta); + }; + + return { + delay, + waitUntil, + }; +}; diff --git a/a3p-integration/proposals/z:acceptance/lib/vaults.mts b/a3p-integration/proposals/z:acceptance/test-lib/vaults.mts similarity index 97% rename from a3p-integration/proposals/z:acceptance/lib/vaults.mts rename to a3p-integration/proposals/z:acceptance/test-lib/vaults.mts index 4ce6697f1ee..cec0cfcf062 100644 --- a/a3p-integration/proposals/z:acceptance/lib/vaults.mts +++ b/a3p-integration/proposals/z:acceptance/test-lib/vaults.mts @@ -11,12 +11,12 @@ import { executeOffer, GOV1ADDR, GOV2ADDR, - GOV3ADDR, provisionSmartWallet, waitForBlock, } from '@agoric/synthetic-chain'; -const govAccounts = [GOV1ADDR, GOV2ADDR, GOV3ADDR]; +const GOV4ADDR = 'agoric1c9gyu460lu70rtcdp95vummd6032psmpdx7wdy'; +const govAccounts = [GOV1ADDR, GOV2ADDR, GOV4ADDR]; export const ISTunit = 1_000_000n; // aka displayInfo: { decimalPlaces: 6 } diff --git a/a3p-integration/proposals/z:acceptance/test-lib/wallet.js b/a3p-integration/proposals/z:acceptance/test-lib/wallet.js index 5fbe9b70027..366a988fc8f 100644 --- a/a3p-integration/proposals/z:acceptance/test-lib/wallet.js +++ b/a3p-integration/proposals/z:acceptance/test-lib/wallet.js @@ -10,6 +10,7 @@ import { E, Far } from '@endo/far'; import { inspect } from 'util'; import { execSwingsetTransaction, pollTx } from './chain.js'; import { makeRpcUtils } from './rpc.js'; +import { makeTimerUtils } from './utils.js'; /** @import {CurrentWalletRecord} from '@agoric/smart-wallet/src/smartWallet.js' */ /** @import {AgoricNamesRemotes} from '@agoric/vats/tools/board-utils.js' */ @@ -132,12 +133,13 @@ export const sendAction = async (bridgeAction, opts) => { }; export const makeWalletUtils = async ( - { delay, execFileSync, fetch }, + { setTimeout, execFileSync, fetch }, networkConfig, ) => { const { agoricNames, fromBoard, marshaller, readLatestHead, vstorage } = await makeRpcUtils({ fetch }, networkConfig); + const { delay } = await makeTimerUtils({ setTimeout }); /** * * @param {string} from diff --git a/a3p-integration/proposals/z:acceptance/test.sh b/a3p-integration/proposals/z:acceptance/test.sh index f9982b31916..7f33803cfbd 100755 --- a/a3p-integration/proposals/z:acceptance/test.sh +++ b/a3p-integration/proposals/z:acceptance/test.sh @@ -5,7 +5,7 @@ set -ueo pipefail # The effects of this step are not persisted in further proposal layers. # test the state right after the previous proposals -yarn ava initial.test.js +# yarn ava initial.test.js # XXX some of these tests have path dependencies so no globs yarn ava core-eval.test.js @@ -28,3 +28,12 @@ yarn ava wallet.test.js echo ACCEPTANCE TESTING vaults yarn ava vaults.test.js + +echo ACCEPTANCE TESTING auction +yarn ava auction.test.js + +echo ACCEPTANCE TESTING psm +yarn ava psm.test.js + +echo ACCEPTANCE TESTING governance +yarn ava governance.test.js diff --git a/a3p-integration/proposals/z:acceptance/valueVow.test.js b/a3p-integration/proposals/z:acceptance/valueVow.test.js index faec82e710a..1193740b541 100644 --- a/a3p-integration/proposals/z:acceptance/valueVow.test.js +++ b/a3p-integration/proposals/z:acceptance/valueVow.test.js @@ -1,6 +1,9 @@ +/* global fetch setTimeout */ + // @ts-check import test from 'ava'; import { inspect } from 'node:util'; +import { execFileSync } from 'node:child_process'; import '@endo/init/debug.js'; import { @@ -10,12 +13,18 @@ import { GOV1ADDR as GETTER, // not particular to governance, just a handy wallet GOV2ADDR as SETTER, // not particular to governance, just a handy wallet } from '@agoric/synthetic-chain'; -import { walletUtils } from './test-lib/index.js'; +import { makeWalletUtils } from './test-lib/wallet.js'; +import { networkConfig } from './test-lib/index.js'; const START_VALUEVOW_DIR = 'start-valueVow'; const RESTART_VALUEVOW_DIR = 'restart-valueVow'; test('vow survives restart', async t => { + const walletUtils = await makeWalletUtils( + { setTimeout, execFileSync, fetch }, + networkConfig, + ); + t.log('start valueVow'); await evalBundles(START_VALUEVOW_DIR); t.is(await getIncarnation('valueVow'), 0); diff --git a/a3p-integration/proposals/z:acceptance/vaults.test.js b/a3p-integration/proposals/z:acceptance/vaults.test.js index 5545a9381be..73f6c9433a2 100644 --- a/a3p-integration/proposals/z:acceptance/vaults.test.js +++ b/a3p-integration/proposals/z:acceptance/vaults.test.js @@ -8,53 +8,15 @@ import { adjustVault, closeVault, getISTBalance, - getPriceQuote, - pushPrices, getContractInfo, ATOM_DENOM, USER1ADDR, waitForBlock, - registerOraclesForBrand, - generateOracleMap, } from '@agoric/synthetic-chain'; import { getBalances, agopsVaults } from './test-lib/utils.js'; -import { retryUntilCondition } from './test-lib/sync-tools.js'; export const scale6 = x => BigInt(x * 1_000_000); -// There may be a new vaultFactory that doesn't have prices yet, so we publish -// prices now -test.before(async t => { - const pushPriceRetryOpts = { - maxRetries: 5, // arbitrary - retryIntervalMs: 5000, // in ms - }; - t.context = { - roundId: 1, - retryOpts: { - pushPriceRetryOpts, - }, - }; - const oraclesByBrand = generateOracleMap('z-acc', ['ATOM']); - await registerOraclesForBrand('ATOM', oraclesByBrand); - - const price = 15.2; - // @ts-expect-error t.context is fine - await pushPrices(price, 'ATOM', oraclesByBrand, t.context.roundId); - - await retryUntilCondition( - () => getPriceQuote('ATOM'), - res => res === `+${scale6(price).toString()}`, - 'price not pushed yet', - { - log: t.log, - setTimeout: global.setTimeout, - // @ts-expect-error t.context is fine - ...t.context.pushPriceRetryOpts, - }, - ); -}); - test.serial('attempt to open vaults under the minimum amount', async t => { const activeVaultsBefore = await agopsVaults(USER1ADDR); await bankSend(USER1ADDR, `20000000${ATOM_DENOM}`); diff --git a/a3p-integration/proposals/z:acceptance/yarn.lock b/a3p-integration/proposals/z:acceptance/yarn.lock index 72977f2a884..9ce83592e4c 100644 --- a/a3p-integration/proposals/z:acceptance/yarn.lock +++ b/a3p-integration/proposals/z:acceptance/yarn.lock @@ -5,55 +5,106 @@ __metadata: version: 8 cacheKey: 10c0 -"@agoric/assert@npm:0.6.1-dev-5676146.0+5676146": - version: 0.6.1-dev-5676146.0 - resolution: "@agoric/assert@npm:0.6.1-dev-5676146.0" - checksum: 10c0/3391d53d64f4ca74ae5a87b623dc7489a20d91f45716723c33e393cfe6536fb1553344b72d24ac966f49b83d56906140c263778968ff513dfcdbb30e1be68091 +"@agoric/base-zone@npm:0.1.1-dev-9c73ae4.0+9c73ae4": + version: 0.1.1-dev-9c73ae4.0 + resolution: "@agoric/base-zone@npm:0.1.1-dev-9c73ae4.0" + dependencies: + "@agoric/store": "npm:0.9.3-dev-9c73ae4.0+9c73ae4" + "@endo/common": "npm:^1.2.7" + "@endo/errors": "npm:^1.2.7" + "@endo/exo": "npm:^1.5.6" + "@endo/far": "npm:^1.1.8" + "@endo/pass-style": "npm:^1.4.6" + "@endo/patterns": "npm:^1.4.6" + checksum: 10c0/a924c8572c17d1e94a711e46a83dff409d37b8a00cd7e0899f091a64401ca981f31ce51cfa60ed00e5bee94d7b96dd5523fd88a1419f542c73d95e418a10c3b8 + languageName: node + linkType: hard + +"@agoric/ertp@npm:dev": + version: 0.16.3-dev-9c73ae4.0 + resolution: "@agoric/ertp@npm:0.16.3-dev-9c73ae4.0" + dependencies: + "@agoric/notifier": "npm:0.6.3-dev-9c73ae4.0+9c73ae4" + "@agoric/store": "npm:0.9.3-dev-9c73ae4.0+9c73ae4" + "@agoric/vat-data": "npm:0.5.3-dev-9c73ae4.0+9c73ae4" + "@agoric/zone": "npm:0.2.3-dev-9c73ae4.0+9c73ae4" + "@endo/errors": "npm:^1.2.7" + "@endo/eventual-send": "npm:^1.2.7" + "@endo/far": "npm:^1.1.8" + "@endo/marshal": "npm:^1.6.1" + "@endo/nat": "npm:^5.0.12" + "@endo/patterns": "npm:^1.4.6" + "@endo/promise-kit": "npm:^1.1.7" + checksum: 10c0/0012ed9cc17d6338196cfdef0198fa16bba784e535a7845758db8f0d0ecb766bbdbb22efc5cedc3971a910d65956305f3d08c77a47c79268037cc6e53a6656b8 + languageName: node + linkType: hard + +"@agoric/internal@npm:0.3.3-dev-9c73ae4.0+9c73ae4, @agoric/internal@npm:dev": + version: 0.3.3-dev-9c73ae4.0 + resolution: "@agoric/internal@npm:0.3.3-dev-9c73ae4.0" + dependencies: + "@agoric/base-zone": "npm:0.1.1-dev-9c73ae4.0+9c73ae4" + "@endo/common": "npm:^1.2.7" + "@endo/errors": "npm:^1.2.7" + "@endo/far": "npm:^1.1.8" + "@endo/init": "npm:^1.1.6" + "@endo/marshal": "npm:^1.6.1" + "@endo/pass-style": "npm:^1.4.6" + "@endo/patterns": "npm:^1.4.6" + "@endo/promise-kit": "npm:^1.1.7" + "@endo/stream": "npm:^1.2.7" + anylogger: "npm:^0.21.0" + jessie.js: "npm:^0.3.4" + checksum: 10c0/cebcf81f503e72cdb431e56d13af438275eaab407ea69512f94ca6ee8117ece71b291c2604e05264f61c31eac59fbd35bbced9227b14a4a37c5ee92f862044f6 languageName: node linkType: hard -"@agoric/base-zone@npm:0.1.1-dev-5676146.0+5676146": - version: 0.1.1-dev-5676146.0 - resolution: "@agoric/base-zone@npm:0.1.1-dev-5676146.0" +"@agoric/notifier@npm:0.6.3-dev-9c73ae4.0+9c73ae4": + version: 0.6.3-dev-9c73ae4.0 + resolution: "@agoric/notifier@npm:0.6.3-dev-9c73ae4.0" dependencies: - "@agoric/store": "npm:0.9.3-dev-5676146.0+5676146" - "@endo/common": "npm:^1.1.0" - "@endo/exo": "npm:^1.2.1" - "@endo/far": "npm:^1.0.4" - "@endo/pass-style": "npm:^1.2.0" - "@endo/patterns": "npm:^1.2.0" - checksum: 10c0/b3dfa47af7cf4a686244e2a31243394b44756f127a7612f5d50c77b1a91f777514386c305866705b46219d2d1e0df2b8d5bff9e08f82275043bb0c198c0601e4 + "@agoric/internal": "npm:0.3.3-dev-9c73ae4.0+9c73ae4" + "@agoric/vat-data": "npm:0.5.3-dev-9c73ae4.0+9c73ae4" + "@endo/errors": "npm:^1.2.7" + "@endo/far": "npm:^1.1.8" + "@endo/marshal": "npm:^1.6.1" + "@endo/patterns": "npm:^1.4.6" + "@endo/promise-kit": "npm:^1.1.7" + checksum: 10c0/5c98617465aac5a32e69f18579e5650c8290cb3bb8643acb80a2c84d5d0cf4012df98de54ecc8c8c6cc09e4cae825818955fa61cd64d4ae2bbf6772f86c3c6d9 languageName: node linkType: hard -"@agoric/internal@npm:0.3.3-dev-5676146.0": - version: 0.3.3-dev-5676146.0 - resolution: "@agoric/internal@npm:0.3.3-dev-5676146.0" +"@agoric/store@npm:0.9.3-dev-9c73ae4.0+9c73ae4": + version: 0.9.3-dev-9c73ae4.0 + resolution: "@agoric/store@npm:0.9.3-dev-9c73ae4.0" dependencies: - "@agoric/assert": "npm:0.6.1-dev-5676146.0+5676146" - "@agoric/base-zone": "npm:0.1.1-dev-5676146.0+5676146" - "@endo/common": "npm:^1.1.0" - "@endo/far": "npm:^1.0.4" - "@endo/init": "npm:^1.0.4" - "@endo/marshal": "npm:^1.3.0" - "@endo/patterns": "npm:^1.2.0" - "@endo/promise-kit": "npm:^1.0.4" - "@endo/stream": "npm:^1.1.0" - anylogger: "npm:^0.21.0" - jessie.js: "npm:^0.3.2" - checksum: 10c0/cd2a81ff39790a1b333621b3815f0791b70d0822f201d491175e46602697c80814f1fb87a610167e541a9ad431a771cd7348afe24517a15c45d1591d3d494bc2 + "@endo/errors": "npm:^1.2.7" + "@endo/exo": "npm:^1.5.6" + "@endo/marshal": "npm:^1.6.1" + "@endo/pass-style": "npm:^1.4.6" + "@endo/patterns": "npm:^1.4.6" + checksum: 10c0/7575dba675c9912968771aa92764fa2e7cb9118f9d8278e40096cdd266f6a035b37e5781f1ea80c41961fc820cc5f3c2174683bba5c0b56c35c98d1890192110 languageName: node linkType: hard -"@agoric/store@npm:0.9.3-dev-5676146.0+5676146": - version: 0.9.3-dev-5676146.0 - resolution: "@agoric/store@npm:0.9.3-dev-5676146.0" +"@agoric/swingset-liveslots@npm:0.10.3-dev-9c73ae4.0+9c73ae4": + version: 0.10.3-dev-9c73ae4.0 + resolution: "@agoric/swingset-liveslots@npm:0.10.3-dev-9c73ae4.0" dependencies: - "@endo/exo": "npm:^1.2.1" - "@endo/marshal": "npm:^1.3.0" - "@endo/pass-style": "npm:^1.2.0" - "@endo/patterns": "npm:^1.2.0" - checksum: 10c0/675e73bbcac024c456b658583ec3fd14a50f69fea5fc07aadf30e593978e5cadbc82d365b13976967b5509614a7adf0adad4e84712f7e0b6c13f2a2a93c9ea63 + "@agoric/internal": "npm:0.3.3-dev-9c73ae4.0+9c73ae4" + "@agoric/store": "npm:0.9.3-dev-9c73ae4.0+9c73ae4" + "@endo/env-options": "npm:^1.1.7" + "@endo/errors": "npm:^1.2.7" + "@endo/eventual-send": "npm:^1.2.7" + "@endo/exo": "npm:^1.5.6" + "@endo/far": "npm:^1.1.8" + "@endo/init": "npm:^1.1.6" + "@endo/marshal": "npm:^1.6.1" + "@endo/nat": "npm:^5.0.12" + "@endo/pass-style": "npm:^1.4.6" + "@endo/patterns": "npm:^1.4.6" + "@endo/promise-kit": "npm:^1.1.7" + checksum: 10c0/93ed5fb3373a05007bc372c84344d3c714561a027ca5a835364e3755ae8bb7a4caa639ebabda78a0062bf209ece1b3d582cc403d6d42235d75d13ab9ac59e656 languageName: node linkType: hard @@ -72,167 +123,194 @@ __metadata: languageName: node linkType: hard -"@endo/base64@npm:^1.0.7": - version: 1.0.7 - resolution: "@endo/base64@npm:1.0.7" - checksum: 10c0/aab10f433f8ea588ebd1786188b6660c0be3a743c119ef2df52ee23afd4ce3844b1d954028952569a747f6657287aeced875afe8e136ea8bff4ba146a60578bd +"@agoric/vat-data@npm:0.5.3-dev-9c73ae4.0+9c73ae4": + version: 0.5.3-dev-9c73ae4.0 + resolution: "@agoric/vat-data@npm:0.5.3-dev-9c73ae4.0" + dependencies: + "@agoric/base-zone": "npm:0.1.1-dev-9c73ae4.0+9c73ae4" + "@agoric/store": "npm:0.9.3-dev-9c73ae4.0+9c73ae4" + "@agoric/swingset-liveslots": "npm:0.10.3-dev-9c73ae4.0+9c73ae4" + "@endo/errors": "npm:^1.2.7" + "@endo/exo": "npm:^1.5.6" + "@endo/patterns": "npm:^1.4.6" + checksum: 10c0/903940756aa8d38ca7f9aa5c35ac1e4df922f0a2efca4b7557ef96f560ac348117da16886894ef6a73ce3b9051067f6f80299c14f36c69c6c914ca0760c31305 languageName: node linkType: hard -"@endo/common@npm:^1.1.0, @endo/common@npm:^1.2.5": - version: 1.2.5 - resolution: "@endo/common@npm:1.2.5" +"@agoric/zone@npm:0.2.3-dev-9c73ae4.0+9c73ae4": + version: 0.2.3-dev-9c73ae4.0 + resolution: "@agoric/zone@npm:0.2.3-dev-9c73ae4.0" dependencies: - "@endo/errors": "npm:^1.2.5" - "@endo/eventual-send": "npm:^1.2.5" - "@endo/promise-kit": "npm:^1.1.5" - checksum: 10c0/104ca2febd87d05b97a77037cb0f281157082b722a39f3fbfca94e36984ad8bc8622e900aeba861d7ed6e6b5d103971599ec2b804eb236537576d498f9ab1fe5 + "@agoric/base-zone": "npm:0.1.1-dev-9c73ae4.0+9c73ae4" + "@agoric/vat-data": "npm:0.5.3-dev-9c73ae4.0+9c73ae4" + "@endo/errors": "npm:^1.2.7" + "@endo/far": "npm:^1.1.8" + "@endo/pass-style": "npm:^1.4.6" + checksum: 10c0/0c4da29f8422c4da27b2ee7abbc21c52f73f3f305c99ee39dcb2dfb9d62199d49aa306d60335b0f9dc3b0e8cf8b3470d95fddfa5e8152bee2ef47ffe2932a5ca languageName: node linkType: hard -"@endo/env-options@npm:^1.1.6": - version: 1.1.6 - resolution: "@endo/env-options@npm:1.1.6" - checksum: 10c0/0001b1cba6954cccfa40104f819378f2f5c8babc89103213a8a5da4f8f94248c8389bfa06ec37cecae81edabe570428558399313d649c64ad7c90743f563dea2 +"@endo/base64@npm:^1.0.8": + version: 1.0.8 + resolution: "@endo/base64@npm:1.0.8" + checksum: 10c0/3501efbf866acc25b9ad0912ec2383e3b976c890a18dc67b5c6eb128433708db69e8ed1cc57190305266bdcbd132659aa87edfc6d02a9886b711e8b86adc21c0 languageName: node linkType: hard -"@endo/errors@npm:^1.2.2, @endo/errors@npm:^1.2.5": - version: 1.2.5 - resolution: "@endo/errors@npm:1.2.5" +"@endo/common@npm:^1.2.7": + version: 1.2.7 + resolution: "@endo/common@npm:1.2.7" dependencies: - ses: "npm:^1.8.0" - checksum: 10c0/32eac3b332139ddec8a85a0013645482541e4f3cc0c484073dde430087f27bb683cde8b0a6e399c5b7f07af007c3b6aa589cf31935a8b8d69e5f869bf71a1662 + "@endo/errors": "npm:^1.2.7" + "@endo/eventual-send": "npm:^1.2.7" + "@endo/promise-kit": "npm:^1.1.7" + checksum: 10c0/2bd25ffc528afd6308b6168650583977139c64e56547b3ea2fee313e01650fb1a041d7b7ce54c6bb70f26c9e78345db2a50fbe3ebccabe9a5c5696eda2cca706 + languageName: node + linkType: hard + +"@endo/env-options@npm:^1.1.7": + version: 1.1.7 + resolution: "@endo/env-options@npm:1.1.7" + checksum: 10c0/5784bd68790041b08d9ead4f6c29cc7871d2e554c23fc44fff38cb20b6b4e55cdba2f78d844ba5ad4b0818185c32475ff318c1b77890d628690d7c7a6ede9475 languageName: node linkType: hard -"@endo/eventual-send@npm:^1.2.5": - version: 1.2.5 - resolution: "@endo/eventual-send@npm:1.2.5" +"@endo/errors@npm:^1.2.2, @endo/errors@npm:^1.2.7": + version: 1.2.7 + resolution: "@endo/errors@npm:1.2.7" dependencies: - "@endo/env-options": "npm:^1.1.6" - checksum: 10c0/7eaa30628582f768920659e4894b871c1056da4252b82b8ad70ed49a24c4559efb8d1655a6845984a0eae83d328179e4272b0917007a2f147dc8b15ecb0ecc52 + ses: "npm:^1.9.1" + checksum: 10c0/17eed5d01dd968d8e266db37ac44e76859d14894a2b70457d120f2f05021819671aaf1bdf7dc7c2506b84f6df616402e029695d62309fbdbdd13ed4ba34890dd languageName: node linkType: hard -"@endo/exo@npm:^1.2.1": - version: 1.5.3 - resolution: "@endo/exo@npm:1.5.3" +"@endo/eventual-send@npm:^1.2.7": + version: 1.2.7 + resolution: "@endo/eventual-send@npm:1.2.7" dependencies: - "@endo/common": "npm:^1.2.5" - "@endo/env-options": "npm:^1.1.6" - "@endo/errors": "npm:^1.2.5" - "@endo/eventual-send": "npm:^1.2.5" - "@endo/far": "npm:^1.1.5" - "@endo/pass-style": "npm:^1.4.3" - "@endo/patterns": "npm:^1.4.3" - checksum: 10c0/5510bc442730910ce2470c6ebdb6c04208c033e452cd60c8684e1769039453ad5f47de31b00629be3c6bf4183bee4a421bace142144e92740b606c591a839c72 + "@endo/env-options": "npm:^1.1.7" + checksum: 10c0/4a483169bcd9ead47a7a07d3f69bdebdc0d1e2a3198f35ad422b82c1646b64528234bdddc9e0544ac38c2ede84a50af1e126eb4086aa8b4ae4b0002895b55e86 languageName: node linkType: hard -"@endo/far@npm:^1.0.0, @endo/far@npm:^1.0.4, @endo/far@npm:^1.1.5": - version: 1.1.5 - resolution: "@endo/far@npm:1.1.5" +"@endo/exo@npm:^1.5.6": + version: 1.5.6 + resolution: "@endo/exo@npm:1.5.6" dependencies: - "@endo/errors": "npm:^1.2.5" - "@endo/eventual-send": "npm:^1.2.5" - "@endo/pass-style": "npm:^1.4.3" - checksum: 10c0/8c50a28323ab1078d0cb6fce1d7fc6da4884247d76585f37f960a2a7134fc7f293075effaae34b41801b7508a1f75d32304c19db0597709727853c4a87eb4999 + "@endo/common": "npm:^1.2.7" + "@endo/env-options": "npm:^1.1.7" + "@endo/errors": "npm:^1.2.7" + "@endo/eventual-send": "npm:^1.2.7" + "@endo/far": "npm:^1.1.8" + "@endo/pass-style": "npm:^1.4.6" + "@endo/patterns": "npm:^1.4.6" + checksum: 10c0/280fe019ec6006f5649b40093453c4ecaa71dfdcbb9767efc9f0ed4fc23081b120818c4a12896e8ec2221444889031736b569229117834048d596ecb09a44057 languageName: node linkType: hard -"@endo/init@npm:^1.0.4, @endo/init@npm:^1.1.4": - version: 1.1.4 - resolution: "@endo/init@npm:1.1.4" +"@endo/far@npm:^1.0.0, @endo/far@npm:^1.1.5, @endo/far@npm:^1.1.8": + version: 1.1.8 + resolution: "@endo/far@npm:1.1.8" dependencies: - "@endo/base64": "npm:^1.0.7" - "@endo/eventual-send": "npm:^1.2.5" - "@endo/lockdown": "npm:^1.0.10" - "@endo/promise-kit": "npm:^1.1.5" - checksum: 10c0/9e915b3b888b7a9f1d563d532ad180dea987253d71e79eda1fcda8d287391611bcca369f2d9b89c59b9f24b3adc548816954e8eaefa4f7402c68585245a686a5 + "@endo/errors": "npm:^1.2.7" + "@endo/eventual-send": "npm:^1.2.7" + "@endo/pass-style": "npm:^1.4.6" + checksum: 10c0/efb0f063e7a19fd67fe150e0a018a9e4b2abd5238b3c3d136830732aa3294bdb829c1a920607360c285eb6e3a3ae5337b6a1e9847cfcf5618247431af02c5a1e languageName: node linkType: hard -"@endo/lockdown@npm:^1.0.10": - version: 1.0.10 - resolution: "@endo/lockdown@npm:1.0.10" +"@endo/init@npm:^1.1.4, @endo/init@npm:^1.1.6": + version: 1.1.6 + resolution: "@endo/init@npm:1.1.6" dependencies: - ses: "npm:^1.8.0" - checksum: 10c0/94be0c1b14cacb2d8088dcc17998e901159a028c51170d78a8cc6a820ae76cabc7d2694f1a1956cb4eab70a8c9a0c8254d88ea4c3f3d9725b739aacf6db83d5b + "@endo/base64": "npm:^1.0.8" + "@endo/eventual-send": "npm:^1.2.7" + "@endo/lockdown": "npm:^1.0.12" + "@endo/promise-kit": "npm:^1.1.7" + checksum: 10c0/1885429e475c780bb5b7ff0fd4fcd5d76bc3a5cb1c3c2f9f2dfc06152ff1c8c3be512b6760483c8c18598e1977129cb3dd766a32c9f6efb19d70b54a1844c683 languageName: node linkType: hard -"@endo/marshal@npm:^1.3.0, @endo/marshal@npm:^1.5.3": - version: 1.5.3 - resolution: "@endo/marshal@npm:1.5.3" +"@endo/lockdown@npm:^1.0.12": + version: 1.0.12 + resolution: "@endo/lockdown@npm:1.0.12" dependencies: - "@endo/common": "npm:^1.2.5" - "@endo/errors": "npm:^1.2.5" - "@endo/eventual-send": "npm:^1.2.5" - "@endo/nat": "npm:^5.0.10" - "@endo/pass-style": "npm:^1.4.3" - "@endo/promise-kit": "npm:^1.1.5" - checksum: 10c0/05f4fceef7727971d3d2a1b8d87f4d9c6651772f9d231e2daa36c3ed0b0e13c3b8d26cb4828ecaadf4329bf77792a293507eadcff7a61df292d4e390936993d1 + ses: "npm:^1.9.1" + checksum: 10c0/0b9d36f359ffe8eadd1e799aa0340ccb0680d48c9b6249c380c27724824c4d875dada9fbec096fb4e2ac76b32c7536955524d3eb6579451a618707602fb958f4 languageName: node linkType: hard -"@endo/nat@npm:^5.0.10": - version: 5.0.10 - resolution: "@endo/nat@npm:5.0.10" - checksum: 10c0/7ad2aa2d216d517409c771aebb465aceb6ea8b88ec808c2dc030d7ffc7fe7d601d8401572f3866384a63ff2aa74209a22f29e1561e773d91d7ad2d81fa13fc7e +"@endo/marshal@npm:^1.5.3, @endo/marshal@npm:^1.6.1": + version: 1.6.1 + resolution: "@endo/marshal@npm:1.6.1" + dependencies: + "@endo/common": "npm:^1.2.7" + "@endo/errors": "npm:^1.2.7" + "@endo/eventual-send": "npm:^1.2.7" + "@endo/nat": "npm:^5.0.12" + "@endo/pass-style": "npm:^1.4.6" + "@endo/promise-kit": "npm:^1.1.7" + checksum: 10c0/e64983abccd833b2a7eb63547e8c5a629f073d3e422229475d470ace95c2640a89e9a9879c46e8389cca8c9e75823ea1c27e27cbeeb8c1c4005146b2c530c530 languageName: node linkType: hard -"@endo/pass-style@npm:^1.2.0, @endo/pass-style@npm:^1.4.3": - version: 1.4.3 - resolution: "@endo/pass-style@npm:1.4.3" +"@endo/nat@npm:^5.0.12": + version: 5.0.12 + resolution: "@endo/nat@npm:5.0.12" + checksum: 10c0/deb792b6a0c9fe9c0e7cf74cc725d8bc36934571f4f06ac3b6def2a0622ac79b0278753c574f9b55a88b063d1186fd6971bbe63326077a7d37982c4c37a1a24c + languageName: node + linkType: hard + +"@endo/pass-style@npm:^1.4.6": + version: 1.4.6 + resolution: "@endo/pass-style@npm:1.4.6" dependencies: - "@endo/env-options": "npm:^1.1.6" - "@endo/errors": "npm:^1.2.5" - "@endo/eventual-send": "npm:^1.2.5" - "@endo/promise-kit": "npm:^1.1.5" + "@endo/env-options": "npm:^1.1.7" + "@endo/errors": "npm:^1.2.7" + "@endo/eventual-send": "npm:^1.2.7" + "@endo/promise-kit": "npm:^1.1.7" "@fast-check/ava": "npm:^1.1.5" - checksum: 10c0/f24c528b1219f5aa122f9a04e80459dec3e9664e7849019b172ad8354870b849b643c8dfc79104857827457d66b2bb09bade9b2c6ea717a97e613ecf6d53c1f9 + checksum: 10c0/fec9d21bb4c70314e92c72f7ae41ec147ac839a23d54f613d689b84f81206e49e657f3fb414db454cbd6ab67dd2a319b1ae25c42b3a1c881edd5de120496b8b4 languageName: node linkType: hard -"@endo/patterns@npm:^1.2.0, @endo/patterns@npm:^1.4.3": - version: 1.4.3 - resolution: "@endo/patterns@npm:1.4.3" +"@endo/patterns@npm:^1.4.6": + version: 1.4.6 + resolution: "@endo/patterns@npm:1.4.6" dependencies: - "@endo/common": "npm:^1.2.5" - "@endo/errors": "npm:^1.2.5" - "@endo/eventual-send": "npm:^1.2.5" - "@endo/marshal": "npm:^1.5.3" - "@endo/promise-kit": "npm:^1.1.5" - checksum: 10c0/10aabc6459d1b5d26e8946ab1b88db23eda80231aa6a0b6c9835568eee1daf745d23c29fa7f202bf11859a7ae77d5f51071c3d863d34e259a62c382ec797bb03 + "@endo/common": "npm:^1.2.7" + "@endo/errors": "npm:^1.2.7" + "@endo/eventual-send": "npm:^1.2.7" + "@endo/marshal": "npm:^1.6.1" + "@endo/promise-kit": "npm:^1.1.7" + checksum: 10c0/518cf4f88ff6aaf6c9df01fbe9f63570aaace763f2a169f986145b039cbd872802154b21736f751fc4cce497d3380aa6be41d2d51e169c8e63a1edb1751d1808 languageName: node linkType: hard -"@endo/promise-kit@npm:^1.0.4, @endo/promise-kit@npm:^1.1.5": - version: 1.1.5 - resolution: "@endo/promise-kit@npm:1.1.5" +"@endo/promise-kit@npm:^1.1.7": + version: 1.1.7 + resolution: "@endo/promise-kit@npm:1.1.7" dependencies: - ses: "npm:^1.8.0" - checksum: 10c0/3a9fb59546507dbbb8c83ada4de664ca4f6085ffcb56c9e3e07789e002e717454b1ee5ae1273549935a7e77ac42be7ae8ddca94ff6d4f16914210d31159ce1a4 + ses: "npm:^1.9.1" + checksum: 10c0/98a8d743c437f106f266871874acd811c0e028fc89553738bbd46a0fea5871b9ba7ef0449ec38e7e3768fc21684993ecdbbd06f5f3429cd69fbe4b867d4c2bd5 languageName: node linkType: hard -"@endo/stream@npm:^1.1.0": - version: 1.2.5 - resolution: "@endo/stream@npm:1.2.5" +"@endo/stream@npm:^1.2.7": + version: 1.2.7 + resolution: "@endo/stream@npm:1.2.7" dependencies: - "@endo/eventual-send": "npm:^1.2.5" - "@endo/promise-kit": "npm:^1.1.5" - ses: "npm:^1.8.0" - checksum: 10c0/625fd9b8b485149c269a01673b76b33fadd0702d76eb37f136c9f7558252e3d51cc9602b2880f1b43971a00f41e5d3e3d2b3a6ebef6f0253bb314d9a144a2cf5 + "@endo/eventual-send": "npm:^1.2.7" + "@endo/promise-kit": "npm:^1.1.7" + ses: "npm:^1.9.1" + checksum: 10c0/d96a2350200cc76ede5eed49e5d780d6d21f63007b42a83658bf5174e7b61e96bfe6b8f11ed2d33ad7f9eb6c3ec2db2a79079bc1679260ac7dac04a34a4a515e languageName: node linkType: hard "@endo/zip@npm:^1.0.7": - version: 1.0.7 - resolution: "@endo/zip@npm:1.0.7" - checksum: 10c0/a1c0d155448ce877012b34c8fe8cd3a58de9eb807514c81cddeebb802ee8e552b27d8a9a40fab3f3e4c49e0cb7fea6902fa1dd12a23ff6f30b56161fc3edc1f8 + version: 1.0.8 + resolution: "@endo/zip@npm:1.0.8" + checksum: 10c0/bb74862121932cd27eef59326325c4b61671ce0962033a2ad18e6d6978a9e94bfe604dbfa8c0b039a38fa7eefc495e6a69c583c0e75929cd267979b2c65b775b languageName: node linkType: hard @@ -476,24 +554,24 @@ __metadata: linkType: hard "@npmcli/agent@npm:^2.0.0": - version: 2.2.0 - resolution: "@npmcli/agent@npm:2.2.0" + version: 2.2.2 + resolution: "@npmcli/agent@npm:2.2.2" dependencies: agent-base: "npm:^7.1.0" http-proxy-agent: "npm:^7.0.0" https-proxy-agent: "npm:^7.0.1" lru-cache: "npm:^10.0.1" - socks-proxy-agent: "npm:^8.0.1" - checksum: 10c0/7b89590598476dda88e79c473766b67c682aae6e0ab0213491daa6083dcc0c171f86b3868f5506f22c09aa5ea69ad7dfb78f4bf39a8dca375d89a42f408645b3 + socks-proxy-agent: "npm:^8.0.3" + checksum: 10c0/325e0db7b287d4154ecd164c0815c08007abfb07653cc57bceded17bb7fd240998a3cbdbe87d700e30bef494885eccc725ab73b668020811d56623d145b524ae languageName: node linkType: hard "@npmcli/fs@npm:^3.1.0": - version: 3.1.0 - resolution: "@npmcli/fs@npm:3.1.0" + version: 3.1.1 + resolution: "@npmcli/fs@npm:3.1.1" dependencies: semver: "npm:^7.3.5" - checksum: 10c0/162b4a0b8705cd6f5c2470b851d1dc6cd228c86d2170e1769d738c1fbb69a87160901411c3c035331e9e99db72f1f1099a8b734bf1637cc32b9a5be1660e4e1e + checksum: 10c0/c37a5b4842bfdece3d14dfdb054f73fe15ed2d3da61b34ff76629fb5b1731647c49166fd2a8bf8b56fcfa51200382385ea8909a3cbecdad612310c114d3f6c99 languageName: node linkType: hard @@ -535,25 +613,25 @@ __metadata: languageName: node linkType: hard -"@vercel/nft@npm:^0.26.2": - version: 0.26.4 - resolution: "@vercel/nft@npm:0.26.4" +"@vercel/nft@npm:^0.27.5": + version: 0.27.5 + resolution: "@vercel/nft@npm:0.27.5" dependencies: "@mapbox/node-pre-gyp": "npm:^1.0.5" "@rollup/pluginutils": "npm:^4.0.0" acorn: "npm:^8.6.0" - acorn-import-attributes: "npm:^1.9.2" + acorn-import-attributes: "npm:^1.9.5" async-sema: "npm:^3.1.1" bindings: "npm:^1.4.0" estree-walker: "npm:2.0.2" glob: "npm:^7.1.3" graceful-fs: "npm:^4.2.9" - micromatch: "npm:^4.0.2" + micromatch: "npm:^4.0.8" node-gyp-build: "npm:^4.2.2" resolve-from: "npm:^5.0.0" bin: nft: out/cli.js - checksum: 10c0/d347fcd7f5371a83362732d0b1b80b9471a2ed3917d6324cc6037392099d6bdc8eae69f0db61bafc87ba2d62af03ef21efe62a7eb52c8eb20341ebcb58903f0d + checksum: 10c0/24455056df4330c9709400e0f9bd5c25870a7694256ef2f1e0812d3435386db3b95a7c7a57f4d74774b081eaf225d4afbf2bd891ce82f9b3e14da10b413538fe languageName: node linkType: hard @@ -571,7 +649,7 @@ __metadata: languageName: node linkType: hard -"acorn-import-attributes@npm:^1.9.2": +"acorn-import-attributes@npm:^1.9.5": version: 1.9.5 resolution: "acorn-import-attributes@npm:1.9.5" peerDependencies: @@ -580,19 +658,21 @@ __metadata: languageName: node linkType: hard -"acorn-walk@npm:^8.3.2": - version: 8.3.2 - resolution: "acorn-walk@npm:8.3.2" - checksum: 10c0/7e2a8dad5480df7f872569b9dccff2f3da7e65f5353686b1d6032ab9f4ddf6e3a2cb83a9b52cf50b1497fd522154dda92f0abf7153290cc79cd14721ff121e52 +"acorn-walk@npm:^8.3.4": + version: 8.3.4 + resolution: "acorn-walk@npm:8.3.4" + dependencies: + acorn: "npm:^8.11.0" + checksum: 10c0/76537ac5fb2c37a64560feaf3342023dadc086c46da57da363e64c6148dc21b57d49ace26f949e225063acb6fb441eabffd89f7a3066de5ad37ab3e328927c62 languageName: node linkType: hard -"acorn@npm:^8.11.3, acorn@npm:^8.6.0": - version: 8.11.3 - resolution: "acorn@npm:8.11.3" +"acorn@npm:^8.11.0, acorn@npm:^8.13.0, acorn@npm:^8.6.0": + version: 8.14.0 + resolution: "acorn@npm:8.14.0" bin: acorn: bin/acorn - checksum: 10c0/3ff155f8812e4a746fee8ecff1f227d527c4c45655bb1fad6347c3cb58e46190598217551b1500f18542d2bbe5c87120cb6927f5a074a59166fbdd9468f0a299 + checksum: 10c0/6d4ee461a7734b2f48836ee0fbb752903606e576cc100eb49340295129ca0b452f3ba91ddd4424a1d4406a98adfb2ebb6bd0ff4c49d7a0930c10e462719bbfd7 languageName: node linkType: hard @@ -605,12 +685,12 @@ __metadata: languageName: node linkType: hard -"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0": - version: 7.1.0 - resolution: "agent-base@npm:7.1.0" +"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1": + version: 7.1.1 + resolution: "agent-base@npm:7.1.1" dependencies: debug: "npm:^4.3.4" - checksum: 10c0/fc974ab57ffdd8421a2bc339644d312a9cca320c20c3393c9d8b1fd91731b9bbabdb985df5fc860f5b79d81c3e350daa3fcb31c5c07c0bb385aafc817df004ce + checksum: 10c0/e59ce7bed9c63bf071a30cc471f2933862044c97fd9958967bfe22521d7a0f601ce4ed5a8c011799d0c726ca70312142ae193bbebb60f576b52be19d4a363b50 languageName: node linkType: hard @@ -632,9 +712,9 @@ __metadata: linkType: hard "ansi-regex@npm:^6.0.1": - version: 6.0.1 - resolution: "ansi-regex@npm:6.0.1" - checksum: 10c0/cbe16dbd2c6b2735d1df7976a7070dd277326434f0212f43abf6d87674095d247968209babdaad31bb00882fa68807256ba9be340eec2f1004de14ca75f52a08 + version: 6.1.0 + resolution: "ansi-regex@npm:6.1.0" + checksum: 10c0/a91daeddd54746338478eef88af3439a7edf30f8e23196e2d6ed182da9add559c601266dbef01c2efa46a958ad6f1f8b176799657616c702b5b02e799e7fd8dc languageName: node linkType: hard @@ -716,17 +796,17 @@ __metadata: linkType: hard "ava@npm:^6.1.2": - version: 6.1.2 - resolution: "ava@npm:6.1.2" + version: 6.2.0 + resolution: "ava@npm:6.2.0" dependencies: - "@vercel/nft": "npm:^0.26.2" - acorn: "npm:^8.11.3" - acorn-walk: "npm:^8.3.2" + "@vercel/nft": "npm:^0.27.5" + acorn: "npm:^8.13.0" + acorn-walk: "npm:^8.3.4" ansi-styles: "npm:^6.2.1" arrgv: "npm:^1.0.2" arrify: "npm:^3.0.0" - callsites: "npm:^4.1.0" - cbor: "npm:^9.0.1" + callsites: "npm:^4.2.0" + cbor: "npm:^9.0.2" chalk: "npm:^5.3.0" chunkd: "npm:^2.0.1" ci-info: "npm:^4.0.0" @@ -736,10 +816,10 @@ __metadata: common-path-prefix: "npm:^3.0.0" concordance: "npm:^5.0.4" currently-unhandled: "npm:^0.4.1" - debug: "npm:^4.3.4" - emittery: "npm:^1.0.1" - figures: "npm:^6.0.1" - globby: "npm:^14.0.0" + debug: "npm:^4.3.7" + emittery: "npm:^1.0.3" + figures: "npm:^6.1.0" + globby: "npm:^14.0.2" ignore-by-default: "npm:^2.1.0" indent-string: "npm:^5.0.0" is-plain-object: "npm:^5.0.0" @@ -747,17 +827,17 @@ __metadata: matcher: "npm:^5.0.0" memoize: "npm:^10.0.0" ms: "npm:^2.1.3" - p-map: "npm:^7.0.1" + p-map: "npm:^7.0.2" package-config: "npm:^5.0.0" - picomatch: "npm:^3.0.1" + picomatch: "npm:^4.0.2" plur: "npm:^5.1.0" - pretty-ms: "npm:^9.0.0" + pretty-ms: "npm:^9.1.0" resolve-cwd: "npm:^3.0.0" stack-utils: "npm:^2.0.6" strip-ansi: "npm:^7.1.0" supertap: "npm:^3.0.1" temp-dir: "npm:^3.0.0" - write-file-atomic: "npm:^5.0.1" + write-file-atomic: "npm:^6.0.0" yargs: "npm:^17.7.2" peerDependencies: "@ava/typescript": "*" @@ -766,7 +846,7 @@ __metadata: optional: true bin: ava: entrypoints/cli.mjs - checksum: 10c0/f35cb1f9bc716714e7c78a601985745774096e4ecd34f9310b858d5779307afa2245ad24274e2d55dd1022c226f4fbdb41947476300977f0b653be0e627adaa4 + checksum: 10c0/25a37413c9ee1b5322dc5a266f546236ea4b52e5c04ae4b52a7b26db9263eebe2dbcda687bf4d464867e558e9148e4567aa09a7ec91d46e3218ab93204e3c653 languageName: node linkType: hard @@ -841,12 +921,12 @@ __metadata: languageName: node linkType: hard -"braces@npm:^3.0.2": - version: 3.0.2 - resolution: "braces@npm:3.0.2" +"braces@npm:^3.0.3": + version: 3.0.3 + resolution: "braces@npm:3.0.3" dependencies: - fill-range: "npm:^7.0.1" - checksum: 10c0/321b4d675791479293264019156ca322163f02dc06e3c4cab33bb15cd43d80b51efef69b0930cfde3acd63d126ebca24cd0544fa6f261e093a0fb41ab9dda381 + fill-range: "npm:^7.1.1" + checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04 languageName: node linkType: hard @@ -861,8 +941,8 @@ __metadata: linkType: hard "cacache@npm:^18.0.0": - version: 18.0.1 - resolution: "cacache@npm:18.0.1" + version: 18.0.4 + resolution: "cacache@npm:18.0.4" dependencies: "@npmcli/fs": "npm:^3.1.0" fs-minipass: "npm:^3.0.0" @@ -876,18 +956,18 @@ __metadata: ssri: "npm:^10.0.0" tar: "npm:^6.1.11" unique-filename: "npm:^3.0.0" - checksum: 10c0/a31666805a80a8b16ad3f85faf66750275a9175a3480896f4f6d31b5d53ef190484fabd71bdb6d2ea5603c717fbef09f4af03d6a65b525c8ef0afaa44c361866 + checksum: 10c0/6c055bafed9de4f3dcc64ac3dc7dd24e863210902b7c470eb9ce55a806309b3efff78033e3d8b4f7dcc5d467f2db43c6a2857aaaf26f0094b8a351d44c42179f languageName: node linkType: hard -"callsites@npm:^4.1.0": - version: 4.1.0 - resolution: "callsites@npm:4.1.0" - checksum: 10c0/91700844127a6dcd4792d231a12dd8e9ec10525eb9962180a8558417d7e3f443e52a4f14746ad2838eaf14f79431ee1539d13bd188da280f720a06a91bd1157a +"callsites@npm:^4.2.0": + version: 4.2.0 + resolution: "callsites@npm:4.2.0" + checksum: 10c0/8f7e269ec09fc0946bb22d838a8bc7932e1909ab4a833b964749f4d0e8bdeaa1f253287c4f911f61781f09620b6925ccd19a5ea4897489c4e59442c660c312a3 languageName: node linkType: hard -"cbor@npm:^9.0.1": +"cbor@npm:^9.0.2": version: 9.0.2 resolution: "cbor@npm:9.0.2" dependencies: @@ -1080,15 +1160,15 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.3.4": - version: 4.3.4 - resolution: "debug@npm:4.3.4" +"debug@npm:4, debug@npm:^4.3.4, debug@npm:^4.3.7": + version: 4.3.7 + resolution: "debug@npm:4.3.7" dependencies: - ms: "npm:2.1.2" + ms: "npm:^2.1.3" peerDependenciesMeta: supports-color: optional: true - checksum: 10c0/cedbec45298dd5c501d01b92b119cd3faebe5438c3917ff11ae1bff86a6c722930ac9c8659792824013168ba6db7c4668225d845c633fbdafbbf902a6389f736 + checksum: 10c0/1471db19c3b06d485a622d62f65947a19a23fbd0dd73f7fd3eafb697eec5360cde447fb075919987899b1a2096e85d35d4eb5a4de09a57600ac9cf7e6c8e768b languageName: node linkType: hard @@ -1116,9 +1196,9 @@ __metadata: linkType: hard "detect-libc@npm:^2.0.0": - version: 2.0.2 - resolution: "detect-libc@npm:2.0.2" - checksum: 10c0/a9f4ffcd2701525c589617d98afe5a5d0676c8ea82bcc4ed6f3747241b79f781d36437c59a5e855254c864d36a3e9f8276568b6b531c28d6e53b093a15703f11 + version: 2.0.3 + resolution: "detect-libc@npm:2.0.3" + checksum: 10c0/88095bda8f90220c95f162bf92cad70bd0e424913e655c20578600e35b91edc261af27531cf160a331e185c0ced93944bc7e09939143225f56312d7fd800fdb7 languageName: node linkType: hard @@ -1129,17 +1209,17 @@ __metadata: languageName: node linkType: hard -"emittery@npm:^1.0.1": - version: 1.0.1 - resolution: "emittery@npm:1.0.1" - checksum: 10c0/2587f2f42bb5e004ba1cde61352d2151f4dd4f29eb79ad36f82e200da2faec9742d7bfca1492a024d60396e001e4b07d9b2b9c43be33547ff751ba8ff87c42ce +"emittery@npm:^1.0.3": + version: 1.0.3 + resolution: "emittery@npm:1.0.3" + checksum: 10c0/91605d044f3891dd1f8ab731aeb94b520488b21e707f7064dcbcf5303bac3b4e7133dfa23c343ede1fc970340bd78a9b1aed522b805bc15104606bba630dd71e languageName: node linkType: hard "emoji-regex@npm:^10.3.0": - version: 10.3.0 - resolution: "emoji-regex@npm:10.3.0" - checksum: 10c0/b4838e8dcdceb44cf47f59abe352c25ff4fe7857acaf5fb51097c427f6f75b44d052eb907a7a3b86f86bc4eae3a93f5c2b7460abe79c407307e6212d65c91163 + version: 10.4.0 + resolution: "emoji-regex@npm:10.4.0" + checksum: 10c0/a3fcedfc58bfcce21a05a5f36a529d81e88d602100145fcca3dc6f795e3c8acc4fc18fe773fbf9b6d6e9371205edb3afa2668ec3473fa2aa7fd47d2a9d46482d languageName: node linkType: hard @@ -1273,9 +1353,9 @@ __metadata: linkType: hard "escalade@npm:^3.1.1": - version: 3.1.1 - resolution: "escalade@npm:3.1.1" - checksum: 10c0/afd02e6ca91ffa813e1108b5e7756566173d6bc0d1eb951cb44d6b21702ec17c1cf116cfe75d4a2b02e05acb0b808a7a9387d0d1ca5cf9c04ad03a8445c3e46d + version: 3.2.0 + resolution: "escalade@npm:3.2.0" + checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65 languageName: node linkType: hard @@ -1318,8 +1398,8 @@ __metadata: linkType: hard "execa@npm:^9.3.1": - version: 9.3.1 - resolution: "execa@npm:9.3.1" + version: 9.5.0 + resolution: "execa@npm:9.5.0" dependencies: "@sindresorhus/merge-streams": "npm:^4.0.0" cross-spawn: "npm:^7.0.3" @@ -1328,12 +1408,12 @@ __metadata: human-signals: "npm:^8.0.0" is-plain-obj: "npm:^4.1.0" is-stream: "npm:^4.0.1" - npm-run-path: "npm:^5.2.0" + npm-run-path: "npm:^6.0.0" pretty-ms: "npm:^9.0.0" signal-exit: "npm:^4.1.0" strip-final-newline: "npm:^4.0.0" yoctocolors: "npm:^2.0.0" - checksum: 10c0/113979ff56575f6cb69fd021eb3894a674fb59b264f5e8c2b9b30e301629abc4f44cee881e680f9fb3b7d4956645df76a2d8c0006869dea985f96ec65f07b226 + checksum: 10c0/93bc077249a778accc019bce141e0ebdf85e1a19ea02eaa1ed00cb49436b0751a7983fee0c0d95434e81727808639c512eea8d1308104598859cac2f01336bb1 languageName: node linkType: hard @@ -1381,15 +1461,15 @@ __metadata: linkType: hard "fastq@npm:^1.6.0": - version: 1.15.0 - resolution: "fastq@npm:1.15.0" + version: 1.17.1 + resolution: "fastq@npm:1.17.1" dependencies: reusify: "npm:^1.0.4" - checksum: 10c0/5ce4f83afa5f88c9379e67906b4d31bc7694a30826d6cc8d0f0473c966929017fda65c2174b0ec89f064ede6ace6c67f8a4fe04cef42119b6a55b0d465554c24 + checksum: 10c0/1095f16cea45fb3beff558bb3afa74ca7a9250f5a670b65db7ed585f92b4b48381445cd328b3d87323da81e43232b5d5978a8201bde84e0cd514310f1ea6da34 languageName: node linkType: hard -"figures@npm:^6.0.1, figures@npm:^6.1.0": +"figures@npm:^6.1.0": version: 6.1.0 resolution: "figures@npm:6.1.0" dependencies: @@ -1405,12 +1485,12 @@ __metadata: languageName: node linkType: hard -"fill-range@npm:^7.0.1": - version: 7.0.1 - resolution: "fill-range@npm:7.0.1" +"fill-range@npm:^7.1.1": + version: 7.1.1 + resolution: "fill-range@npm:7.1.1" dependencies: to-regex-range: "npm:^5.0.1" - checksum: 10c0/7cdad7d426ffbaadf45aeb5d15ec675bbd77f7597ad5399e3d2766987ed20bda24d5fac64b3ee79d93276f5865608bb22344a26b9b1ae6c4d00bd94bf611623f + checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018 languageName: node linkType: hard @@ -1422,12 +1502,12 @@ __metadata: linkType: hard "foreground-child@npm:^3.1.0": - version: 3.1.1 - resolution: "foreground-child@npm:3.1.1" + version: 3.3.0 + resolution: "foreground-child@npm:3.3.0" dependencies: cross-spawn: "npm:^7.0.0" signal-exit: "npm:^4.0.1" - checksum: 10c0/9700a0285628abaeb37007c9a4d92bd49f67210f09067638774338e146c8e9c825c5c877f072b2f75f41dc6a2d0be8664f79ffc03f6576649f54a84fb9b47de0 + checksum: 10c0/028f1d41000553fcfa6c4bb5c372963bf3d9bf0b1f25a87d1a6253014343fb69dfb1b42d9625d7cf44c8ba429940f3d0ff718b62105d4d4a4f6ef8ca0a53faa2 languageName: node linkType: hard @@ -1507,9 +1587,9 @@ __metadata: linkType: hard "get-east-asian-width@npm:^1.0.0": - version: 1.2.0 - resolution: "get-east-asian-width@npm:1.2.0" - checksum: 10c0/914b1e217cf38436c24b4c60b4c45289e39a45bf9e65ef9fd343c2815a1a02b8a0215aeec8bf9c07c516089004b6e3826332481f40a09529fcadbf6e579f286b + version: 1.3.0 + resolution: "get-east-asian-width@npm:1.3.0" + checksum: 10c0/1a049ba697e0f9a4d5514c4623781c5246982bdb61082da6b5ae6c33d838e52ce6726407df285cdbb27ec1908b333cf2820989bd3e986e37bb20979437fdf34b languageName: node linkType: hard @@ -1524,11 +1604,11 @@ __metadata: linkType: hard "get-tsconfig@npm:^4.7.5": - version: 4.7.6 - resolution: "get-tsconfig@npm:4.7.6" + version: 4.8.1 + resolution: "get-tsconfig@npm:4.8.1" dependencies: resolve-pkg-maps: "npm:^1.0.0" - checksum: 10c0/2240e1b13e996dfbb947d177f422f83d09d1f93c9ce16959ebb3c2bdf8bdf4f04f98eba043859172da1685f9c7071091f0acfa964ebbe4780394d83b7dc3f58a + checksum: 10c0/536ee85d202f604f4b5fb6be81bcd6e6d9a96846811e83e9acc6de4a04fb49506edea0e1b8cf1d5ee7af33e469916ec2809d4c5445ab8ae015a7a51fbd1572f9 languageName: node linkType: hard @@ -1549,17 +1629,18 @@ __metadata: linkType: hard "glob@npm:^10.2.2, glob@npm:^10.3.10": - version: 10.3.10 - resolution: "glob@npm:10.3.10" + version: 10.4.5 + resolution: "glob@npm:10.4.5" dependencies: foreground-child: "npm:^3.1.0" - jackspeak: "npm:^2.3.5" - minimatch: "npm:^9.0.1" - minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" - path-scurry: "npm:^1.10.1" + jackspeak: "npm:^3.1.2" + minimatch: "npm:^9.0.4" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^1.11.1" bin: glob: dist/esm/bin.mjs - checksum: 10c0/13d8a1feb7eac7945f8c8480e11cd4a44b24d26503d99a8d8ac8d5aefbf3e9802a2b6087318a829fad04cb4e829f25c5f4f1110c68966c498720dd261c7e344d + checksum: 10c0/19a9759ea77b8e3ca0a43c2f07ecddc2ad46216b786bb8f993c445aee80d345925a21e5280c7b7c6c59e860a0154b84e4b2b60321fea92cd3c56b4a7489f160e languageName: node linkType: hard @@ -1577,9 +1658,9 @@ __metadata: languageName: node linkType: hard -"globby@npm:^14.0.0": - version: 14.0.1 - resolution: "globby@npm:14.0.1" +"globby@npm:^14.0.2": + version: 14.0.2 + resolution: "globby@npm:14.0.2" dependencies: "@sindresorhus/merge-streams": "npm:^2.1.0" fast-glob: "npm:^3.3.2" @@ -1587,7 +1668,7 @@ __metadata: path-type: "npm:^5.0.0" slash: "npm:^5.1.0" unicorn-magic: "npm:^0.1.0" - checksum: 10c0/749a6be91cf455c161ebb5c9130df3991cb9fd7568425db850a8279a6cf45acd031c5069395beb7aeb4dd606b64f0d6ff8116c93726178d8e6182fee58c2736d + checksum: 10c0/3f771cd683b8794db1e7ebc8b6b888d43496d93a82aad4e9d974620f578581210b6c5a6e75ea29573ed16a1345222fab6e9b877a8d1ed56eeb147e09f69c6f78 languageName: node linkType: hard @@ -1613,12 +1694,12 @@ __metadata: linkType: hard "http-proxy-agent@npm:^7.0.0": - version: 7.0.0 - resolution: "http-proxy-agent@npm:7.0.0" + version: 7.0.2 + resolution: "http-proxy-agent@npm:7.0.2" dependencies: agent-base: "npm:^7.1.0" debug: "npm:^4.3.4" - checksum: 10c0/a11574ff39436cee3c7bc67f259444097b09474605846ddd8edf0bf4ad8644be8533db1aa463426e376865047d05dc22755e638632819317c0c2f1b2196657c8 + checksum: 10c0/4207b06a4580fb85dd6dff521f0abf6db517489e70863dca1a0291daa7f2d3d2d6015a57bd702af068ea5cf9f1f6ff72314f5f5b4228d299c0904135d2aef921 languageName: node linkType: hard @@ -1633,12 +1714,12 @@ __metadata: linkType: hard "https-proxy-agent@npm:^7.0.1": - version: 7.0.2 - resolution: "https-proxy-agent@npm:7.0.2" + version: 7.0.5 + resolution: "https-proxy-agent@npm:7.0.5" dependencies: agent-base: "npm:^7.0.2" debug: "npm:4" - checksum: 10c0/7735eb90073db087e7e79312e3d97c8c04baf7ea7ca7b013382b6a45abbaa61b281041a98f4e13c8c80d88f843785bcc84ba189165b4b4087b1e3496ba656d77 + checksum: 10c0/2490e3acec397abeb88807db52cac59102d5ed758feee6df6112ab3ccd8325e8a1ce8bce6f4b66e5470eca102d31e425ace904242e4fa28dbe0c59c4bafa7b2c languageName: node linkType: hard @@ -1673,9 +1754,9 @@ __metadata: linkType: hard "ignore@npm:^5.2.4": - version: 5.3.0 - resolution: "ignore@npm:5.3.0" - checksum: 10c0/dc06bea5c23aae65d0725a957a0638b57e235ae4568dda51ca142053ed2c352de7e3bc93a69b2b32ac31966a1952e9a93c5ef2e2ab7c6b06aef9808f6b55b571 + version: 5.3.2 + resolution: "ignore@npm:5.3.2" + checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337 languageName: node linkType: hard @@ -1724,10 +1805,13 @@ __metadata: languageName: node linkType: hard -"ip@npm:^2.0.0": - version: 2.0.0 - resolution: "ip@npm:2.0.0" - checksum: 10c0/8d186cc5585f57372847ae29b6eba258c68862055e18a75cc4933327232cb5c107f89800ce29715d542eef2c254fbb68b382e780a7414f9ee7caf60b7a473958 +"ip-address@npm:^9.0.5": + version: 9.0.5 + resolution: "ip-address@npm:9.0.5" + dependencies: + jsbn: "npm:1.1.0" + sprintf-js: "npm:^1.1.3" + checksum: 10c0/331cd07fafcb3b24100613e4b53e1a2b4feab11e671e655d46dc09ee233da5011284d09ca40c4ecbdfe1d0004f462958675c224a804259f2f78d2465a87824bc languageName: node linkType: hard @@ -1811,9 +1895,9 @@ __metadata: linkType: hard "is-unicode-supported@npm:^2.0.0": - version: 2.0.0 - resolution: "is-unicode-supported@npm:2.0.0" - checksum: 10c0/3013dfb8265fe9f9a0d1e9433fc4e766595631a8d85d60876c457b4bedc066768dab1477c553d02e2f626d88a4e019162706e04263c94d74994ef636a33b5f94 + version: 2.1.0 + resolution: "is-unicode-supported@npm:2.1.0" + checksum: 10c0/a0f53e9a7c1fdbcf2d2ef6e40d4736fdffff1c9f8944c75e15425118ff3610172c87bf7bc6c34d3903b04be59790bb2212ddbe21ee65b5a97030fc50370545a5 languageName: node linkType: hard @@ -1831,20 +1915,20 @@ __metadata: languageName: node linkType: hard -"jackspeak@npm:^2.3.5": - version: 2.3.6 - resolution: "jackspeak@npm:2.3.6" +"jackspeak@npm:^3.1.2": + version: 3.4.3 + resolution: "jackspeak@npm:3.4.3" dependencies: "@isaacs/cliui": "npm:^8.0.2" "@pkgjs/parseargs": "npm:^0.11.0" dependenciesMeta: "@pkgjs/parseargs": optional: true - checksum: 10c0/f01d8f972d894cd7638bc338e9ef5ddb86f7b208ce177a36d718eac96ec86638a6efa17d0221b10073e64b45edc2ce15340db9380b1f5d5c5d000cbc517dc111 + checksum: 10c0/6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9 languageName: node linkType: hard -"jessie.js@npm:^0.3.2": +"jessie.js@npm:^0.3.4": version: 0.3.4 resolution: "jessie.js@npm:0.3.4" dependencies: @@ -1872,6 +1956,13 @@ __metadata: languageName: node linkType: hard +"jsbn@npm:1.1.0": + version: 1.1.0 + resolution: "jsbn@npm:1.1.0" + checksum: 10c0/4f907fb78d7b712e11dea8c165fe0921f81a657d3443dde75359ed52eb2b5d33ce6773d97985a089f09a65edd80b11cb75c767b57ba47391fee4c969f7215c96 + languageName: node + linkType: hard + "load-json-file@npm:^7.0.1": version: 7.0.1 resolution: "load-json-file@npm:7.0.1" @@ -1886,19 +1977,10 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^10.0.1, lru-cache@npm:^9.1.1 || ^10.0.0": - version: 10.1.0 - resolution: "lru-cache@npm:10.1.0" - checksum: 10c0/778bc8b2626daccd75f24c4b4d10632496e21ba064b126f526c626fbdbc5b28c472013fccd45d7646b9e1ef052444824854aed617b59cd570d01a8b7d651fc1e - languageName: node - linkType: hard - -"lru-cache@npm:^6.0.0": - version: 6.0.0 - resolution: "lru-cache@npm:6.0.0" - dependencies: - yallist: "npm:^4.0.0" - checksum: 10c0/cb53e582785c48187d7a188d3379c181b5ca2a9c78d2bce3e7dee36f32761d1c42983da3fe12b55cb74e1779fa94cdc2e5367c028a9b35317184ede0c07a30a9 +"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": + version: 10.4.3 + resolution: "lru-cache@npm:10.4.3" + checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb languageName: node linkType: hard @@ -1912,8 +1994,8 @@ __metadata: linkType: hard "make-fetch-happen@npm:^13.0.0": - version: 13.0.0 - resolution: "make-fetch-happen@npm:13.0.0" + version: 13.0.1 + resolution: "make-fetch-happen@npm:13.0.1" dependencies: "@npmcli/agent": "npm:^2.0.0" cacache: "npm:^18.0.0" @@ -1924,9 +2006,10 @@ __metadata: minipass-flush: "npm:^1.0.5" minipass-pipeline: "npm:^1.2.4" negotiator: "npm:^0.6.3" + proc-log: "npm:^4.2.0" promise-retry: "npm:^2.0.1" ssri: "npm:^10.0.0" - checksum: 10c0/43b9f6dcbc6fe8b8604cb6396957c3698857a15ba4dbc38284f7f0e61f248300585ef1eb8cc62df54e9c724af977e45b5cdfd88320ef7f53e45070ed3488da55 + checksum: 10c0/df5f4dbb6d98153b751bccf4dc4cc500de85a96a9331db9805596c46aa9f99d9555983954e6c1266d9f981ae37a9e4647f42b9a4bb5466f867f4012e582c9e7e languageName: node linkType: hard @@ -1964,13 +2047,13 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4": - version: 4.0.5 - resolution: "micromatch@npm:4.0.5" +"micromatch@npm:^4.0.4, micromatch@npm:^4.0.8": + version: 4.0.8 + resolution: "micromatch@npm:4.0.8" dependencies: - braces: "npm:^3.0.2" + braces: "npm:^3.0.3" picomatch: "npm:^2.3.1" - checksum: 10c0/3d6505b20f9fa804af5d8c596cb1c5e475b9b0cd05f652c5b56141cf941bd72adaeb7a436fda344235cef93a7f29b7472efc779fcdb83b478eab0867b95cdeff + checksum: 10c0/166fa6eb926b9553f32ef81f5f531d27b4ce7da60e5baf8c021d043b27a388fb95e46a8038d5045877881e673f8134122b59624d5cecbd16eb50a42e7a6b5ca8 languageName: node linkType: hard @@ -1997,12 +2080,12 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.1": - version: 9.0.3 - resolution: "minimatch@npm:9.0.3" +"minimatch@npm:^9.0.4": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" dependencies: brace-expansion: "npm:^2.0.1" - checksum: 10c0/85f407dcd38ac3e180f425e86553911d101455ca3ad5544d6a7cec16286657e4f8a9aa6695803025c55e31e35a91a2252b5dc8e7d527211278b8b65b4dbd5eac + checksum: 10c0/de96cf5e35bdf0eab3e2c853522f98ffbe9a36c37797778d2665231ec1f20a9447a7e567cb640901f89e4daaa95ae5d70c65a9e8aa2bb0019b6facbc3c0575ed languageName: node linkType: hard @@ -2023,8 +2106,8 @@ __metadata: linkType: hard "minipass-fetch@npm:^3.0.0": - version: 3.0.4 - resolution: "minipass-fetch@npm:3.0.4" + version: 3.0.5 + resolution: "minipass-fetch@npm:3.0.5" dependencies: encoding: "npm:^0.1.13" minipass: "npm:^7.0.3" @@ -2033,7 +2116,7 @@ __metadata: dependenciesMeta: encoding: optional: true - checksum: 10c0/1b63c1f3313e88eeac4689f1b71c9f086598db9a189400e3ee960c32ed89e06737fa23976c9305c2d57464fb3fcdc12749d3378805c9d6176f5569b0d0ee8a75 + checksum: 10c0/9d702d57f556274286fdd97e406fc38a2f5c8d15e158b498d7393b1105974b21249289ec571fa2b51e038a4872bfc82710111cf75fae98c662f3d6f95e72152b languageName: node linkType: hard @@ -2080,10 +2163,10 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3": - version: 7.0.4 - resolution: "minipass@npm:7.0.4" - checksum: 10c0/6c7370a6dfd257bf18222da581ba89a5eaedca10e158781232a8b5542a90547540b4b9b7e7f490e4cda43acfbd12e086f0453728ecf8c19e0ef6921bc5958ac5 +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.1.2": + version: 7.1.2 + resolution: "minipass@npm:7.1.2" + checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557 languageName: node linkType: hard @@ -2113,13 +2196,6 @@ __metadata: languageName: node linkType: hard -"ms@npm:2.1.2": - version: 2.1.2 - resolution: "ms@npm:2.1.2" - checksum: 10c0/a437714e2f90dbf881b5191d35a6db792efbca5badf112f87b9e1c712aace4b4b9b742dd6537f3edf90fd6f684de897cec230abde57e87883766712ddda297cc - languageName: node - linkType: hard - "ms@npm:^2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3" @@ -2135,18 +2211,18 @@ __metadata: linkType: hard "negotiator@npm:^0.6.3": - version: 0.6.3 - resolution: "negotiator@npm:0.6.3" - checksum: 10c0/3ec9fd413e7bf071c937ae60d572bc67155262068ed522cf4b3be5edbe6ddf67d095ec03a3a14ebf8fc8e95f8e1d61be4869db0dbb0de696f6b837358bd43fc2 + version: 0.6.4 + resolution: "negotiator@npm:0.6.4" + checksum: 10c0/3e677139c7fb7628a6f36335bf11a885a62c21d5390204590a1a214a5631fcbe5ea74ef6a610b60afe84b4d975cbe0566a23f20ee17c77c73e74b80032108dea languageName: node linkType: hard "node-abi@npm:^3.3.0": - version: 3.54.0 - resolution: "node-abi@npm:3.54.0" + version: 3.71.0 + resolution: "node-abi@npm:3.71.0" dependencies: semver: "npm:^7.3.5" - checksum: 10c0/9ebbb21e6951aa51e831549ed62b68dc56bcc10f6b21ffd04195a16a6abf5ddfc48b6ae5e3334720fe4459cafde5ec8103025902efff5599d0539f8656fc694e + checksum: 10c0/dbd0792ea729329cd9d099f28a5681ff9e8a6db48cf64e1437bf6a7fd669009d1e758a784619a1c4cc8bfd1ed17162f042c787654edf19a1f64b5018457c9c1f languageName: node linkType: hard @@ -2165,19 +2241,19 @@ __metadata: linkType: hard "node-gyp-build@npm:^4.2.2": - version: 4.8.0 - resolution: "node-gyp-build@npm:4.8.0" + version: 4.8.2 + resolution: "node-gyp-build@npm:4.8.2" bin: node-gyp-build: bin.js node-gyp-build-optional: optional.js node-gyp-build-test: build-test.js - checksum: 10c0/85324be16f81f0235cbbc42e3eceaeb1b5ab94c8d8f5236755e1435b4908338c65a4e75f66ee343cbcb44ddf9b52a428755bec16dcd983295be4458d95c8e1ad + checksum: 10c0/d816b43974d31d6257b6e87d843f2626c72389a285208394bc57a7766b210454d2642860a5e5b5c333d8ecabaeabad3b31b94f58cf8ca1aabdef0c320d02baaa languageName: node linkType: hard "node-gyp@npm:latest": - version: 10.0.1 - resolution: "node-gyp@npm:10.0.1" + version: 10.2.0 + resolution: "node-gyp@npm:10.2.0" dependencies: env-paths: "npm:^2.2.0" exponential-backoff: "npm:^3.1.1" @@ -2185,13 +2261,13 @@ __metadata: graceful-fs: "npm:^4.2.6" make-fetch-happen: "npm:^13.0.0" nopt: "npm:^7.0.0" - proc-log: "npm:^3.0.0" + proc-log: "npm:^4.1.0" semver: "npm:^7.3.5" - tar: "npm:^6.1.2" + tar: "npm:^6.2.1" which: "npm:^4.0.0" bin: node-gyp: bin/node-gyp.js - checksum: 10c0/abddfff7d873312e4ed4a5fb75ce893a5c4fb69e7fcb1dfa71c28a6b92a7f1ef6b62790dffb39181b5a82728ba8f2f32d229cf8cbe66769fe02cea7db4a555aa + checksum: 10c0/00630d67dbd09a45aee0a5d55c05e3916ca9e6d427ee4f7bc392d2d3dc5fad7449b21fc098dd38260a53d9dcc9c879b36704a1994235d4707e7271af7e9a835b languageName: node linkType: hard @@ -2214,22 +2290,23 @@ __metadata: linkType: hard "nopt@npm:^7.0.0": - version: 7.2.0 - resolution: "nopt@npm:7.2.0" + version: 7.2.1 + resolution: "nopt@npm:7.2.1" dependencies: abbrev: "npm:^2.0.0" bin: nopt: bin/nopt.js - checksum: 10c0/9bd7198df6f16eb29ff16892c77bcf7f0cc41f9fb5c26280ac0def2cf8cf319f3b821b3af83eba0e74c85807cc430a16efe0db58fe6ae1f41e69519f585b6aff + checksum: 10c0/a069c7c736767121242037a22a788863accfa932ab285a1eb569eb8cd534b09d17206f68c37f096ae785647435e0c5a5a0a67b42ec743e481a455e5ae6a6df81 languageName: node linkType: hard -"npm-run-path@npm:^5.2.0": - version: 5.3.0 - resolution: "npm-run-path@npm:5.3.0" +"npm-run-path@npm:^6.0.0": + version: 6.0.0 + resolution: "npm-run-path@npm:6.0.0" dependencies: path-key: "npm:^4.0.0" - checksum: 10c0/124df74820c40c2eb9a8612a254ea1d557ddfab1581c3e751f825e3e366d9f00b0d76a3c94ecd8398e7f3eee193018622677e95816e8491f0797b21e30b2deba + unicorn-magic: "npm:^0.3.0" + checksum: 10c0/b223c8a0dcd608abf95363ea5c3c0ccc3cd877daf0102eaf1b0f2390d6858d8337fbb7c443af2403b067a7d2c116d10691ecd22ab3c5273c44da1ff8d07753bd languageName: node linkType: hard @@ -2270,7 +2347,7 @@ __metadata: languageName: node linkType: hard -"p-map@npm:^7.0.1": +"p-map@npm:^7.0.2": version: 7.0.2 resolution: "p-map@npm:7.0.2" checksum: 10c0/e10548036648d1c043153f9997112fe5a7de54a319210238628f8ea22ee36587fd6ee740811f88b60bbf29d932e23ae35df7fced40df477116c84c18e797047e @@ -2287,6 +2364,13 @@ __metadata: languageName: node linkType: hard +"package-json-from-dist@npm:^1.0.0": + version: 1.0.1 + resolution: "package-json-from-dist@npm:1.0.1" + checksum: 10c0/62ba2785eb655fec084a257af34dbe24292ab74516d6aecef97ef72d4897310bc6898f6c85b5cd22770eaa1ce60d55a0230e150fb6a966e3ecd6c511e23d164b + languageName: node + linkType: hard + "parse-ms@npm:^4.0.0": version: 4.0.0 resolution: "parse-ms@npm:4.0.0" @@ -2315,13 +2399,13 @@ __metadata: languageName: node linkType: hard -"path-scurry@npm:^1.10.1": - version: 1.10.1 - resolution: "path-scurry@npm:1.10.1" +"path-scurry@npm:^1.11.1": + version: 1.11.1 + resolution: "path-scurry@npm:1.11.1" dependencies: - lru-cache: "npm:^9.1.1 || ^10.0.0" + lru-cache: "npm:^10.2.0" minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" - checksum: 10c0/e5dc78a7348d25eec61ab166317e9e9c7b46818aa2c2b9006c507a6ff48c672d011292d9662527213e558f5652ce0afcc788663a061d8b59ab495681840c0c1e + checksum: 10c0/32a13711a2a505616ae1cc1b5076801e453e7aae6ac40ab55b388bb91b9d0547a52f5aaceff710ea400205f18691120d4431e520afbe4266b836fadede15872d languageName: node linkType: hard @@ -2339,10 +2423,10 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^3.0.1": - version: 3.0.1 - resolution: "picomatch@npm:3.0.1" - checksum: 10c0/70ec738569f1864658378b7abdab8939d15dae0718c1df994eae3346fd33daf6a3c1ff4e0c1a0cd1e2c0319130985b63a2cff34d192f2f2acbb78aca76111736 +"picomatch@npm:^4.0.2": + version: 4.0.2 + resolution: "picomatch@npm:4.0.2" + checksum: 10c0/7c51f3ad2bb42c776f49ebf964c644958158be30d0a510efd5a395e8d49cb5acfed5b82c0c5b365523ce18e6ab85013c9ebe574f60305892ec3fa8eee8304ccc languageName: node linkType: hard @@ -2356,8 +2440,8 @@ __metadata: linkType: hard "prebuild-install@npm:^7.1.1": - version: 7.1.1 - resolution: "prebuild-install@npm:7.1.1" + version: 7.1.2 + resolution: "prebuild-install@npm:7.1.2" dependencies: detect-libc: "npm:^2.0.0" expand-template: "npm:^2.0.3" @@ -2373,23 +2457,23 @@ __metadata: tunnel-agent: "npm:^0.6.0" bin: prebuild-install: bin.js - checksum: 10c0/6dc70f36b0f4adcb2fe0ed38d874ab28b571fb1a9725d769e8ba3f64a15831e58462de09f3e6e64569bcc4a3e03b9328b56faa0d45fe10ae1574478814536c76 + checksum: 10c0/e64868ba9ef2068fd7264f5b03e5298a901e02a450acdb1f56258d88c09dea601eefdb3d1dfdff8513fdd230a92961712be0676192626a3b4d01ba154d48bdd3 languageName: node linkType: hard -"pretty-ms@npm:^9.0.0": - version: 9.0.0 - resolution: "pretty-ms@npm:9.0.0" +"pretty-ms@npm:^9.0.0, pretty-ms@npm:^9.1.0": + version: 9.1.0 + resolution: "pretty-ms@npm:9.1.0" dependencies: parse-ms: "npm:^4.0.0" - checksum: 10c0/ba4a2acd1fe92a1c629e5cdeb555d7fa344ae9920e20fa00e8ac1db61b8d3dff8638ffc70c7569f681e375df68c9f31291c2c1912cefd02ef1b1bdd0861a4aed + checksum: 10c0/fd111aad8800a04dfd654e6016da69bdaa6fc6a4c280f8e727cffd8b5960558e94942f1a94d4aa6e4d179561a0fbb0366a9ebe0ccefbbb0f8ff853b129cdefb9 languageName: node linkType: hard -"proc-log@npm:^3.0.0": - version: 3.0.0 - resolution: "proc-log@npm:3.0.0" - checksum: 10c0/f66430e4ff947dbb996058f6fd22de2c66612ae1a89b097744e17fb18a4e8e7a86db99eda52ccf15e53f00b63f4ec0b0911581ff2aac0355b625c8eac509b0dc +"proc-log@npm:^4.1.0, proc-log@npm:^4.2.0": + version: 4.2.0 + resolution: "proc-log@npm:4.2.0" + checksum: 10c0/17db4757c2a5c44c1e545170e6c70a26f7de58feb985091fb1763f5081cab3d01b181fb2dd240c9f4a4255a1d9227d163d5771b7e69c9e49a561692db865efb9 languageName: node linkType: hard @@ -2404,12 +2488,12 @@ __metadata: linkType: hard "pump@npm:^3.0.0": - version: 3.0.0 - resolution: "pump@npm:3.0.0" + version: 3.0.2 + resolution: "pump@npm:3.0.2" dependencies: end-of-stream: "npm:^1.1.0" once: "npm:^1.3.1" - checksum: 10c0/bbdeda4f747cdf47db97428f3a135728669e56a0ae5f354a9ac5b74556556f5446a46f720a8f14ca2ece5be9b4d5d23c346db02b555f46739934cc6c093a5478 + checksum: 10c0/5ad655cb2a7738b4bcf6406b24ad0970d680649d996b55ad20d1be8e0c02394034e4c45ff7cd105d87f1e9b96a0e3d06fd28e11fae8875da26e7f7a8e2c9726f languageName: node linkType: hard @@ -2511,11 +2595,13 @@ __metadata: version: 0.0.0-use.local resolution: "root-workspace-0b6124@workspace:." dependencies: - "@agoric/internal": "npm:0.3.3-dev-5676146.0" + "@agoric/ertp": "npm:dev" + "@agoric/internal": "npm:dev" "@agoric/synthetic-chain": "npm:^0.3.0" "@endo/errors": "npm:^1.2.2" "@endo/far": "npm:^1.1.5" "@endo/init": "npm:^1.1.4" + "@endo/marshal": "npm:^1.5.3" ava: "npm:^6.1.2" execa: "npm:^9.3.1" tsx: "npm:^4.17.0" @@ -2556,13 +2642,11 @@ __metadata: linkType: hard "semver@npm:^7.3.2, semver@npm:^7.3.5": - version: 7.5.4 - resolution: "semver@npm:7.5.4" - dependencies: - lru-cache: "npm:^6.0.0" + version: 7.6.3 + resolution: "semver@npm:7.6.3" bin: semver: bin/semver.js - checksum: 10c0/5160b06975a38b11c1ab55950cb5b8a23db78df88275d3d8a42ccf1f29e55112ac995b3a26a522c36e3b5f76b0445f1eef70d696b8c7862a2b4303d7b0e7609e + checksum: 10c0/88f33e148b210c153873cb08cfe1e281d518aaa9a666d4d148add6560db5cd3c582f3a08ccb91f38d5f379ead256da9931234ed122057f40bb5766e65e58adaf languageName: node linkType: hard @@ -2575,12 +2659,12 @@ __metadata: languageName: node linkType: hard -"ses@npm:^1.8.0": - version: 1.8.0 - resolution: "ses@npm:1.8.0" +"ses@npm:^1.9.1": + version: 1.9.1 + resolution: "ses@npm:1.9.1" dependencies: - "@endo/env-options": "npm:^1.1.6" - checksum: 10c0/4b2114e586a547dd2a71477e0a42e8ea5d0ea9c3ff135d0dbfb63569eeda19c7152db76b82bcad12a2969d3f5fb09e5fa52e921b5a2831560e6876ca1f9ba207 + "@endo/env-options": "npm:^1.1.7" + checksum: 10c0/1e795542954f635aaee2749a1d548460f2978257cb29daaea76b814ef99ffa64ab5cca05fbc3d51a814a57cf9fc4563988ee93312cc53bae4eb63dfff0f0682a languageName: node linkType: hard @@ -2663,24 +2747,31 @@ __metadata: languageName: node linkType: hard -"socks-proxy-agent@npm:^8.0.1": - version: 8.0.2 - resolution: "socks-proxy-agent@npm:8.0.2" +"socks-proxy-agent@npm:^8.0.3": + version: 8.0.4 + resolution: "socks-proxy-agent@npm:8.0.4" dependencies: - agent-base: "npm:^7.0.2" + agent-base: "npm:^7.1.1" debug: "npm:^4.3.4" - socks: "npm:^2.7.1" - checksum: 10c0/a842402fc9b8848a31367f2811ca3cd14c4106588b39a0901cd7a69029998adfc6456b0203617c18ed090542ad0c24ee4e9d4c75a0c4b75071e214227c177eb7 + socks: "npm:^2.8.3" + checksum: 10c0/345593bb21b95b0508e63e703c84da11549f0a2657d6b4e3ee3612c312cb3a907eac10e53b23ede3557c6601d63252103494caa306b66560f43af7b98f53957a languageName: node linkType: hard -"socks@npm:^2.7.1": - version: 2.7.1 - resolution: "socks@npm:2.7.1" +"socks@npm:^2.8.3": + version: 2.8.3 + resolution: "socks@npm:2.8.3" dependencies: - ip: "npm:^2.0.0" + ip-address: "npm:^9.0.5" smart-buffer: "npm:^4.2.0" - checksum: 10c0/43f69dbc9f34fc8220bc51c6eea1c39715ab3cfdb115d6e3285f6c7d1a603c5c75655668a5bbc11e3c7e2c99d60321fb8d7ab6f38cda6a215fadd0d6d0b52130 + checksum: 10c0/d54a52bf9325165770b674a67241143a3d8b4e4c8884560c4e0e078aace2a728dffc7f70150660f51b85797c4e1a3b82f9b7aa25e0a0ceae1a243365da5c51a7 + languageName: node + linkType: hard + +"sprintf-js@npm:^1.1.3": + version: 1.1.3 + resolution: "sprintf-js@npm:1.1.3" + checksum: 10c0/09270dc4f30d479e666aee820eacd9e464215cdff53848b443964202bf4051490538e5dd1b42e1a65cf7296916ca17640aebf63dae9812749c7542ee5f288dec languageName: node linkType: hard @@ -2692,11 +2783,11 @@ __metadata: linkType: hard "ssri@npm:^10.0.0": - version: 10.0.5 - resolution: "ssri@npm:10.0.5" + version: 10.0.6 + resolution: "ssri@npm:10.0.6" dependencies: minipass: "npm:^7.0.3" - checksum: 10c0/b091f2ae92474183c7ac5ed3f9811457e1df23df7a7e70c9476eaa9a0c4a0c8fc190fb45acefbf023ca9ee864dd6754237a697dc52a0fb182afe65d8e77443d8 + checksum: 10c0/e5a1e23a4057a86a97971465418f22ea89bd439ac36ade88812dd920e4e61873e8abd6a9b72a03a67ef50faa00a2daf1ab745c5a15b46d03e0544a0296354227 languageName: node linkType: hard @@ -2732,13 +2823,13 @@ __metadata: linkType: hard "string-width@npm:^7.0.0": - version: 7.1.0 - resolution: "string-width@npm:7.1.0" + version: 7.2.0 + resolution: "string-width@npm:7.2.0" dependencies: emoji-regex: "npm:^10.3.0" get-east-asian-width: "npm:^1.0.0" strip-ansi: "npm:^7.1.0" - checksum: 10c0/68a99fbc3bd3d8eb42886ff38dce819767dee55f606f74dfa4687a07dfd21262745d9683df0aa53bf81a5dd47c13da921a501925b974bec66a7ddd634fef0634 + checksum: 10c0/eb0430dd43f3199c7a46dcbf7a0b34539c76fe3aa62763d0b0655acdcbdf360b3f66f3d58ca25ba0205f42ea3491fa00f09426d3b7d3040e506878fc7664c9b9 languageName: node linkType: hard @@ -2820,9 +2911,9 @@ __metadata: languageName: node linkType: hard -"tar@npm:^6.1.11, tar@npm:^6.1.2": - version: 6.2.0 - resolution: "tar@npm:6.2.0" +"tar@npm:^6.1.11, tar@npm:^6.2.1": + version: 6.2.1 + resolution: "tar@npm:6.2.1" dependencies: chownr: "npm:^2.0.0" fs-minipass: "npm:^2.0.0" @@ -2830,7 +2921,7 @@ __metadata: minizlib: "npm:^2.1.1" mkdirp: "npm:^1.0.3" yallist: "npm:^4.0.0" - checksum: 10c0/02ca064a1a6b4521fef88c07d389ac0936730091f8c02d30ea60d472e0378768e870769ab9e986d87807bfee5654359cf29ff4372746cc65e30cbddc352660d8 + checksum: 10c0/a5eca3eb50bc11552d453488344e6507156b9193efd7635e98e867fab275d527af53d8866e2370cd09dfe74378a18111622ace35af6a608e5223a7d27fe99537 languageName: node linkType: hard @@ -2865,8 +2956,8 @@ __metadata: linkType: hard "tsx@npm:^4.17.0": - version: 4.17.0 - resolution: "tsx@npm:4.17.0" + version: 4.19.2 + resolution: "tsx@npm:4.19.2" dependencies: esbuild: "npm:~0.23.0" fsevents: "npm:~2.3.3" @@ -2876,7 +2967,7 @@ __metadata: optional: true bin: tsx: dist/cli.mjs - checksum: 10c0/ad720b81d6447c7695d24c27947fa1a2b6db9d2ef03216389edd6fa0006aa479bc0d8348a1ac9975a08edef4ce791ff5629a24d8dccbb0987f42e5407785cfa4 + checksum: 10c0/63164b889b1d170403e4d8753a6755dec371f220f5ce29a8e88f1f4d6085a784a12d8dc2ee669116611f2c72757ac9beaa3eea5c452796f541bdd2dc11753721 languageName: node linkType: hard @@ -2897,22 +2988,22 @@ __metadata: linkType: hard "typescript@npm:^5.5.4": - version: 5.5.4 - resolution: "typescript@npm:5.5.4" + version: 5.6.3 + resolution: "typescript@npm:5.6.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/422be60f89e661eab29ac488c974b6cc0a660fb2228003b297c3d10c32c90f3bcffc1009b43876a082515a3c376b1eefcce823d6e78982e6878408b9a923199c + checksum: 10c0/44f61d3fb15c35359bc60399cb8127c30bae554cd555b8e2b46d68fa79d680354b83320ad419ff1b81a0bdf324197b29affe6cc28988cd6a74d4ac60c94f9799 languageName: node linkType: hard "typescript@patch:typescript@npm%3A^5.5.4#optional!builtin": - version: 5.5.4 - resolution: "typescript@patch:typescript@npm%3A5.5.4#optional!builtin::version=5.5.4&hash=b45daf" + version: 5.6.3 + resolution: "typescript@patch:typescript@npm%3A5.6.3#optional!builtin::version=5.6.3&hash=b45daf" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/10dd9881baba22763de859e8050d6cb6e2db854197495c6f1929b08d1eb2b2b00d0b5d9b0bcee8472f1c3f4a7ef6a5d7ebe0cfd703f853aa5ae465b8404bc1ba + checksum: 10c0/ac8307bb06bbfd08ae7137da740769b7d8c3ee5943188743bb622c621f8ad61d244767480f90fbd840277fbf152d8932aa20c33f867dea1bb5e79b187ca1a92f languageName: node linkType: hard @@ -2923,6 +3014,13 @@ __metadata: languageName: node linkType: hard +"unicorn-magic@npm:^0.3.0": + version: 0.3.0 + resolution: "unicorn-magic@npm:0.3.0" + checksum: 10c0/0a32a997d6c15f1c2a077a15b1c4ca6f268d574cf5b8975e778bb98e6f8db4ef4e86dfcae4e158cd4c7e38fb4dd383b93b13eefddc7f178dea13d3ac8a603271 + languageName: node + linkType: hard + "unique-filename@npm:^3.0.0": version: 3.0.0 resolution: "unique-filename@npm:3.0.0" @@ -3032,13 +3130,13 @@ __metadata: languageName: node linkType: hard -"write-file-atomic@npm:^5.0.1": - version: 5.0.1 - resolution: "write-file-atomic@npm:5.0.1" +"write-file-atomic@npm:^6.0.0": + version: 6.0.0 + resolution: "write-file-atomic@npm:6.0.0" dependencies: imurmurhash: "npm:^0.1.4" signal-exit: "npm:^4.0.1" - checksum: 10c0/e8c850a8e3e74eeadadb8ad23c9d9d63e4e792bd10f4836ed74189ef6e996763959f1249c5650e232f3c77c11169d239cbfc8342fc70f3fe401407d23810505d + checksum: 10c0/ae2f1c27474758a9aca92037df6c1dd9cb94c4e4983451210bd686bfe341f142662f6aa5913095e572ab037df66b1bfe661ed4ce4c0369ed0e8219e28e141786 languageName: node linkType: hard diff --git a/golang/cosmos/app/upgrade.go b/golang/cosmos/app/upgrade.go index 4a37e2d2fce..4b5565045c0 100644 --- a/golang/cosmos/app/upgrade.go +++ b/golang/cosmos/app/upgrade.go @@ -1,7 +1,10 @@ package gaia import ( + "encoding/json" "fmt" + "strings" + "text/template" "github.com/Agoric/agoric-sdk/golang/cosmos/vm" swingsetkeeper "github.com/Agoric/agoric-sdk/golang/cosmos/x/swingset/keeper" @@ -72,6 +75,76 @@ func isFirstTimeUpgradeOfThisVersion(app *GaiaApp, ctx sdk.Context) bool { return true } +func buildProposalStepWithArgs(moduleName string, entrypoint string, opts map[string]any) (vm.CoreProposalStep, error) { + t := template.Must(template.New("").Parse(`{ + "module": "{{.moduleName}}", + "entrypoint": "{{.entrypoint}}", + "args": [ {{.args}} ] + }`)) + + args, err := json.Marshal(opts) + if err != nil { + return nil, err + } + + var result strings.Builder + err = t.Execute(&result, map[string]any{ + "moduleName": moduleName, + "entrypoint": entrypoint, + "args": string(args), + }) + if err != nil { + return nil, err + } + jsonStr := result.String() + jsonBz := []byte(jsonStr) + if !json.Valid(jsonBz) { + return nil, fmt.Errorf("invalid JSON: %s", jsonStr) + } + proposal := vm.ArbitraryCoreProposal{Json: jsonBz} + return vm.CoreProposalStepForModules(proposal), nil +} + +func getVariantFromUpgradeName(upgradeName string) string { + switch upgradeName { + case "UNRELEASED_A3P_INTEGRATION": + return "A3P_INTEGRATION" + case "UNRELEASED_main": + return "MAINNET" + case "UNRELEASED_devnet": + return "DEVNET" + // Noupgrade for this version. + case "UNRELEASED_BASIC": + return "" + default: + return "" + } +} + +func replaceElectorateCoreProposalStep(upgradeName string) (vm.CoreProposalStep, error) { + variant := getVariantFromUpgradeName(upgradeName) + + return buildProposalStepWithArgs( + "@agoric/builders/scripts/inter-protocol/replace-electorate-core.js", + "defaultProposalBuilder", + map[string]any{ + "variant": variant, + }, + ) +} + +func replacePriceFeedsCoreProposal(upgradeName string) (vm.CoreProposalStep, error) { + variant := getVariantFromUpgradeName(upgradeName) + + return buildProposalStepWithArgs( + "@agoric/builders/scripts/inter-protocol/updatePriceFeeds.js", + "defaultProposalBuilder", + map[string]any{ + "variant": variant, + }, + ) +} + // unreleasedUpgradeHandler performs standard upgrade actions plus custom actions for the unreleased upgrade. func unreleasedUpgradeHandler(app *GaiaApp, targetUpgrade string) func(sdk.Context, upgradetypes.Plan, module.VersionMap) (module.VersionMap, error) { return func(ctx sdk.Context, plan upgradetypes.Plan, fromVm module.VersionMap) (module.VersionMap, error) { @@ -89,25 +162,35 @@ func unreleasedUpgradeHandler(app *GaiaApp, targetUpgrade string) func(sdk.Conte return module.VersionMap{}, fmt.Errorf("cannot run %s as first upgrade", plan.Name) } + replaceElectorateStep, err := replaceElectorateCoreProposalStep(targetUpgrade) + if err != nil { + return nil, err + } + + priceFeedUpdate, err := replacePriceFeedsCoreProposal(targetUpgrade) + if err != nil { + return nil, err + } + // Each CoreProposalStep runs sequentially, and can be constructed from // one or more modules executing in parallel within the step. CoreProposalSteps = []vm.CoreProposalStep{ + replaceElectorateStep, + priceFeedUpdate, vm.CoreProposalStepForModules( - // Upgrade Zoe (no new ZCF needed). - "@agoric/builders/scripts/vats/upgrade-zoe.js", - ), - vm.CoreProposalStepForModules( - // Upgrade to new liveslots for repaired vow usage. - "@agoric/builders/scripts/vats/upgrade-orch-core.js", + "@agoric/builders/scripts/vats/add-auction.js", ), vm.CoreProposalStepForModules( - // Upgrade to new liveslots and support vows. - "@agoric/builders/scripts/smart-wallet/build-wallet-factory2-upgrade.js", + "@agoric/builders/scripts/vats/upgradeVaults.js", ), vm.CoreProposalStepForModules( - // Create vat-orchestration. - "@agoric/builders/scripts/vats/init-orchestration.js", + // Upgrade Zoe (no new ZCF needed). + "@agoric/builders/scripts/vats/upgrade-zoe.js", ), + // Revive KREAd characters + vm.CoreProposalStepForModules( + "@agoric/builders/scripts/vats/revive-kread.js", + ), } } diff --git a/packages/SwingSet/docs/host-app.md b/packages/SwingSet/docs/host-app.md new file mode 100644 index 00000000000..581406c3907 --- /dev/null +++ b/packages/SwingSet/docs/host-app.md @@ -0,0 +1,61 @@ +# The Host Application + +SwingSet is a library that helps you write a "Host Application" around a kernel, which in turn manages some number of vats and devices. The Host Application does not come with SwingSet: you must write one specific to your particular application. + +## Host Application Responsibilities + +### State Management + +First, the host app is responsible for managing the kernel's state in a "SwingStore", using the `@agoric/swing-store` package, which is a SQLite database in some user-selected base directory, wrapped in useful APIs. A SwingStore is created by the `@agoric/swing-store` package, and consists of both a `hostStorage` facet and a `kernelStorage` facet. The `kernelStorage` facet must be given to `makeSwingsetController()`, the primary API for creating a kernel. All kernel state is kept inside the SwingStore. + +The host app must use `hostStorage.commit()` to commit the SwingStore changes after each group of device inputs and cranks (usually triggered with one or more calls to `controller.run()`). The host must not commit while the run is execution: it must wait for the `controller.run()` return Promise to settle first. + +### Device IO + +The host app is also responsible for all device input and output. The kernel itself cannot talk to the outside world, except through devices. These devices are configured with the kernel config record (along with static vats), but the device *endowments* are provided by the host app via the `deviceEndowments` argument to `makeSwingsetController()`. + +SwingSet provides robust and deterministic computation, even in the face of unexpected reboot, and avoids a failure mode called "hangover inconsistency" by following the lead of the Waterken and E systems. Output messages (in fact all communication with the outside world) must be embargoed until all consequences of an inbound delivery have been durably committed. To maintain this, device endowments must refrain from transmitting their outputs or modifying state outside of the DB until after the host app calls `hostStorage.commit()`, and they must be prepared to re-transmit their outputs or re-apply their effects if they awaken into a world where the durable state says that a message must be transmitted but no record of an acknowledgment is also recorded. See the comms subsystem, especially the "mailbox" device, for more details. + +### Kernel Upgrade + +The life cycle of a SwingSet kernel begins with the one and only call to `initializeSwingset()`, which populates the SwingStore DB for the first time. After that, the kernel is presumed to be immortal, but its execution is broken up into a series of reboots. Each reboot (e.g. each time the host application is started), the app must build a new controller with `makeSwingsetController()`, to have something to run. + +From time to time, the host app will be upgraded to use a newer version of the SwingSet kernel code (e.g. a new version of this `@agoric/swingset-vat` package). The newer version might require an upgrade to the kernel's persisted/durable state. For example, the way it represents some vat metadata might be made more efficient, and the upgrade process needs to examine and rewrite vat state to use the new representation. Or, a bug might be fixed, and the upgrade process needs to locate and remediate any consequences of the bug having been present during earlier execution. + +To make the resulting state changes occur deterministically, upgrades are not automatic. Instead, each time the host app reboots with a new version of the kernel code, it must call `upgradeSwingset(kernelStorage)`. It must do this *before* calling `makeSwingsetController()`, as that function will throw an error if given a SwingStore that has not been upgraded. + +It is safe to call `upgradeSwingset` on reboots that do not change the version of the kernel code: the function is idempotent, and will do nothing if the SwingStore is already up-to-date. + +Some upgrades (in particular bug remediations) need to add events to the kernel's run-queue. To avoid having these events be intermingled with work that might already be on the run-queue at reboot time (e.g. work leftover from previous runs), these events are not automatically injected at that time. Instead, the kernel remembers what needs to be done, and waits for the host to invoke `controller.injectQueuedUpgradeEvents()` at a time of their choosing. This should be done before the next `commit()`, to avoid the risk of them being lost by a reboot. + +So most host applications will start each reboot with a sequence like this: + +```js +const { hostStorage, kernelStorage } = openSwingStore(baseDirectory); +upgradeSwingset(kernelStorage); +const controller = makeSwingsetController(kernelStorage, deviceEndowments); +controller.injectQueuedUpgradeEvents(); +``` + +followed by later code to execute runs. Inputs can be fed all-at-once, or each processed in their own run, but at the end of the block, the host app must `commit()` the DB: + +```js +async function doBlock(deviceInputs) { + for (const input of deviceInputs) { + injectDeviceInput(input); + await controller.run(); + } + hostStorage.commit(); + emitDeviceOutputs(); +} +``` + +The actual signature of `upgradeSwingset` is `const { modified } = upgradeSwingset(kernelStorage)`, and the `modified` flag indicates whether anything actually got upgraded. Host applications which have other means to keep track of software upgrades may wish to assert that `modified === false` in reboots that are not associated with a change to the kernel package version. They can also safely skip the `injectQueuedUpgradeEvents` call if nothing was modified. + +### Crank Execution + +For convenience in discussion, we split execution into "blocks". During a block, the host may call one or more device inputs, such as inbound messages, or timer wakeup events. The end of the block is marked by one or more calls to `controller.run()`, followed by a `hostStorage.commit()`, followed by the host-provided device endowments doing whatever kind of outbound IO they need to do. + +In a replicated/blockchain host environment, these are the same blocks that make up the chain. Inbound messages come from the signed transactions that are included in each block. And each block can inform the timer device that time has advanced to whatever consensus time is computed as part of the blockchain voting process. "Outbound IO" is really just recording data in the chain state, where external parties can retrieve it and verify it against the block header and its hash. + +In a singular/solo environment, "block boundaries" are simply points in time when the host app decides it would be useful to perform computation, commit state, and release outbound messages. These "blocks" are triggered by inbound IO requests, or timer wakeup events. The host might choose to trigger a "block" immediately after each such event (to minimize latency), or it might defer execution for a little while to batch them together (for efficiency). diff --git a/packages/SwingSet/docs/run-policy.md b/packages/SwingSet/docs/run-policy.md index 7624e9be0ff..33ba7376a2c 100644 --- a/packages/SwingSet/docs/run-policy.md +++ b/packages/SwingSet/docs/run-policy.md @@ -68,16 +68,19 @@ Some vats may grow very large (i.e. large c-lists with lots of imported/exported To protect the system against these bursts, the run policy can be configured to terminate vats slowly. Instead of doing all the cleanup work immediately, the policy allows the kernel to do a little bit of work each time `controller.run()` is called (e.g. once per block, for kernels hosted inside a blockchain). Internally, before servicing the run-queue, the kernel checks to see if any vats are in the "terminated but not fully deleted" state, and executes a "vat-cleanup crank", to delete some state. Depending upon what the run-policy allows, it may do multiple vat-cleanup cranks in a single `controller.run()`, or just one, or none at all. And depending upon the budget provided to each one, it may only take one vat-cleanup crank to finish the job, or millions. If the policy limits the number of cranks in a single block, and limits the budget of the crank, then the cleanup process will be spread over multiple blocks. -For each terminated vat, cleanup proceeds through five phases: +For each terminated vat, cleanup proceeds through six phases: -* `exports`: delete c-list entries for objects/promises *exported* by the vat -* `imports`: same but for objects/promises *imported* by the vat +* `exports`: delete c-list entries for objects *exported* by the vat +* `imports`: same but for objects *imported* by the vat +* `promises`: same but promises referenced by the vat * `kv`: delete all other kv entries for this vat, mostly vatstore records * `snapshots`: delete xsnap heap snapshots, typically one per 200 deliveries (`snapshotInterval`) * `transcripts`: delete transcript spans, and their associated transcript items The first two phases, `exports` and `imports`, cause the most activity in other vats. Deleting `exports` can cause objects to be retired, which will deliver `dispatch.retireImports` GC messages to other vats which have imported those objects and used them as keys in a WeakMapStore. Deleting `imports` can cause refcounts to drop to zero, delivering `dispatch.dropImports` into vats which were exporting those objects. Both of these will add `gcKref` "dirt" to the other vat, eventually triggering a BringOutYourDead, which will cause more DB activity. These are generally the phases we must rate-limit to avoid overwhelming the system. +The `promises` phase may cause kernel promise table entries to be deleted. This is unlikely to trigger activity in other vats, because only settled promises can have references to other objects, and the notification of settlement is enqueued to all subscribers as soon as the promise is fulfilled or rejected. However, if some of these notifications were still in the run-queue when the vat got deleted, the GC work might be accelerated slightly. + The other phases cause DB activity (deleting rows), but do not interact with other vats, so it is easier to accomodate more cleanup steps. The budget can be tuned to allow more kv/snapshots/transcripts than exports/imports in a single cleanup run. There are two RunPolicy methods which control this. The first is `runPolicy.allowCleanup()`. This will be invoked many times during `controller.run()`, each time the kernel tries to decide what to do next (once per step). The return value will enable (or not) a fixed amount of cleanup work. The second is `runPolicy.didCleanup({ cleanups })`, which is called later, to inform the policy of how much cleanup work was actually done. The policy can count the cleanups and switch `allowCleanup()` to return `false` when it reaches a threshold. (We need the pre-check `allowCleanup` method because the simple act of looking for cleanup work is itself a cost that we might not be willing to pay). @@ -92,7 +95,7 @@ The limit can be set to `Infinity` to allow unlimited deletion of that particula Each budget record must include a `{ default }` property, which is used as the default for any phase that is not explicitly mentioned in the budget. This also provides forwards-compatibility for any phases that might be added in the future. So `budget = { default: 5 }` would provide a conservative budget for cleanup, `budget = { default: 5, kv: 50 }` would enable faster deletion of the non-c-list kvstore entries, and `budget = { default: Infinity }` allows unlimited cleanup for all phases. -Note that the cleanup crank ends when no more work is left to be done (which finally allows the vat to be forgotten entirely), or when an individual phase's budget is exceeded. That means multiple phases might see deletion work in a single crank, if the earlier phase finishes its work without exhausting its budget. For example, if the budget is `{ default: 5 }`, but the vat had 4 exports, 4 imports, 4 other kv entries, 4 snapshots, and 4 transcript spans, then all that work would be done in a single crank, because no individual phase would exhaust its budget. The only case that is even worth mentioning is when the end of the `exports` phase overlaps with the start of the `imports` phase, where we might do four more cleanups than usual. +Note that the cleanup crank ends when no more work is left to be done (which finally allows the vat to be forgotten entirely), or when an individual phase's budget is exceeded. That means multiple phases might see deletion work in a single crank, if the earlier phase finishes its work without exhausting its budget. For example, if the budget is `{ default: 5 }`, but the vat had 4 exports, 4 imports, 4 promise entries, 4 other kv entries, 4 snapshots, and 4 transcript spans, then all that work would be done in a single crank, because no individual phase would exhaust its budget. The only case that is even worth mentioning is when the end of the `exports` phase overlaps with the start of the `imports` phase, where we might do four more cleanups than usual. A `true` return value from `allowCleanup()` is equivalent to `{ default: Infinity }`, which allows unlimited cleanup work. This also happens if `allowCleanup()` is missing entirely, which maintains the old behavior for host applications that haven't been updated to make new policy objects. Note that cleanup is higher priority than any delivery, and is second only to acceptance queue routing. diff --git a/packages/SwingSet/misc-tools/scan-9039-promises.js b/packages/SwingSet/misc-tools/scan-9039-promises.js new file mode 100644 index 00000000000..c847c2b55d3 --- /dev/null +++ b/packages/SwingSet/misc-tools/scan-9039-promises.js @@ -0,0 +1,158 @@ +#!/usr/bin/env node +// @ts-nocheck + +// Given a swingstore database, produce a list of vpid/vatID pairs for +// all promises that are resolved but still in the c-list of their +// formerly-deciding vat. This happens when a vat is upgraded and the +// kernel disconnects (rejects) the previous incarnation's outstanding +// promises (https://github.com/Agoric/agoric-sdk/issues/9039). + +import process from 'process'; +import fs from 'fs'; +import sqlite3 from 'better-sqlite3'; +import yargsParser from 'yargs-parser'; +import '@endo/init/debug.js'; + +const main = rawArgv => { + const { _: args, ...options } = yargsParser(rawArgv.slice(2)); + // console.log(args, options); + if (Reflect.ownKeys(options).length > 0 || args.length !== 1) { + console.error( + [ + `Usage: ${rawArgv[1]} /path/to/swingstore.sqlite`, + 'Find leftover promises for bug #9039.', + ].join('\n'), + ); + process.exitCode = 1; + return; + } + + const [ssDBPath] = args; + if (!fs.existsSync(ssDBPath)) { + throw Error(`swingstore DB path (${ssDBPath}) must exist`); + } + const ssDB = sqlite3(/** @type {string} */ (ssDBPath)); + let queries = 0; + + const sqlGet = ssDB.prepare('SELECT value FROM kvStore WHERE key=?').pluck(); + const realRawGet = key => { + queries += 1; + return sqlGet.get(key); + }; + const realGet = key => JSON.parse(realRawGet(key)); + + // fake database for testing + const fake = { + 'vat.names': ['bootstrap'], + 'vat.name.bootstrap': 'v1', + 'vat.dynamicIDs': ['v9'], + runQueue: [10, 10], + 'runQueue.10': { type: 'notify', vatID: 'v9', kpid: 'kp1005304' }, + 'kp1005304.data.body': + '#{"incarnationNumber":0,"name":"vatUpgraded","upgradeMessage":"vat upgraded"}', + 'kp1005304.data.slots': [], + 'kp1005304.refCount': 1, + 'kp1005304.state': 'rejected', + }; + const fakeRawGet = key => { + queries += 1; + return fake[key]; + }; + // eslint-disable-next-line no-unused-vars + const fakeGet = key => fakeRawGet(key); + + // const [get, rawGet] = [fakeGet, fakeRawGet]; + const [get, rawGet] = [realGet, realRawGet]; + + const vatNames = get('vat.names'); + const staticIDs = vatNames.map(name => rawGet(`vat.name.${name}`)); + const dynamicIDs = get('vat.dynamicIDs'); + const allVatIDs = [...staticIDs, ...dynamicIDs]; + // console.log(allVatIDs); + + const sqlRange = ssDB.prepare( + `SELECT * FROM kvStore WHERE key >= ? AND key < ?`, + ); + + // old way took 547472 queries + // const rejectedKPIDs = new Set(); + + const [head, tail] = get('runQueue'); + const notifies = new Map(); // .get(kpid) = [vatIDs..]; + for (let p = head; p < tail; p += 1) { + const rq = get(`runQueue.${p}`); + if (rq.type === 'notify') { + const { vatID, kpid } = rq; + if (!notifies.has(kpid)) { + notifies.set(kpid, []); + } + notifies.get(kpid).push(vatID); + } + } + console.log(`pending notifies:`, notifies); + + const rejectedKPIDs = new Set(); + const nonRejectedKPIDs = new Set(); + + const isRejected = kpid => { + if (nonRejectedKPIDs.has(kpid)) { + return false; + } + if (rejectedKPIDs.has(kpid)) { + return true; + } + const state = rawGet(`${kpid}.state`); + // missing state means the kpid is deleted somehow, shouldn't happen + assert(state, `${kpid}.state is missing`); + if (state === 'rejected') { + rejectedKPIDs.add(kpid); + return true; + } + nonRejectedKPIDs.add(kpid); + return false; + }; + + // Bug 9039 causes the kernel to reject/disconnect a promise on + // behalf of the upgraded vat, but not remove it from the vat's + // c-list (which would normally happen when the vat emitted a + // syscall.resolve). The rejection process erases the `.decider` + // record, so we no longer know which vat to look at. We're looking + // for vpids which are 1: rejected, 2: present in a vat c-list, 3: + // do *not* have a notify scheduled. + + const buggyKPIDs = []; // tuples of [kpid, vatID] + const involvedVats = {}; + + for (const vatID of allVatIDs) { + // TODO: skip vats in vats.terminated, belt-and-suspenders + const prefix = `${vatID}.c.`; + const len = prefix.length; + const k1 = `${prefix}kp`; + const k2 = `${prefix}kq`; + for (const row of sqlRange.iterate(k1, k2)) { + // the kvStore's actual API (getNextKey) requires one query per result + queries += 1; + const kpid = row.key.slice(len); + if (!isRejected(kpid)) { + continue; + } + const n = notifies.get(kpid); + if (!n || !n.includes(vatID)) { + // there is no pending notify + buggyKPIDs.push([kpid, vatID]); + involvedVats[vatID] = 1 + (involvedVats[vatID] || 0); + } + } + } + + // console.log(buggyKPIDs); + console.log(`scan 9039: ${buggyKPIDs.length} kpid/vatID pairs to clean up`); + console.log(`first is:`, buggyKPIDs[0]); + console.log( + `${Reflect.ownKeys(involvedVats).length} vats involved:`, + involvedVats, + ); + console.log(`${queries} DB queries`); +}; + +main(process.argv); diff --git a/packages/SwingSet/src/controller/controller.js b/packages/SwingSet/src/controller/controller.js index 3321d1f92a6..c3091989814 100644 --- a/packages/SwingSet/src/controller/controller.js +++ b/packages/SwingSet/src/controller/controller.js @@ -366,6 +366,10 @@ export async function makeSwingsetController( return kref; }, + kpRegisterInterest(kpid) { + return kernel.kpRegisterInterest(kpid); + }, + kpStatus(kpid) { return kernel.kpStatus(kpid); }, @@ -384,6 +388,8 @@ export async function makeSwingsetController( return kernel.deviceNameToID(deviceName); }, + injectQueuedUpgradeEvents: () => kernel.injectQueuedUpgradeEvents(), + /** * Queue a method call into the named vat * diff --git a/packages/SwingSet/src/controller/upgradeSwingset.js b/packages/SwingSet/src/controller/upgradeSwingset.js index 87195b8cfae..3c12c97f0b6 100644 --- a/packages/SwingSet/src/controller/upgradeSwingset.js +++ b/packages/SwingSet/src/controller/upgradeSwingset.js @@ -1,9 +1,17 @@ +import { Fail } from '@endo/errors'; import { DEFAULT_REAP_DIRT_THRESHOLD_KEY, DEFAULT_GC_KREFS_PER_BOYD, getAllDynamicVats, getAllStaticVats, + incrementReferenceCount, + readQueue, } from '../kernel/state/kernelKeeper.js'; +import { enumeratePrefixedKeys } from '../kernel/state/storageHelper.js'; + +/** + * @import {RunQueueEvent} from '../types-internal.js'; + */ const upgradeVatV0toV1 = (kvStore, defaultReapDirtThreshold, vatID) => { // This is called, once per vat, when upgradeSwingset migrates from @@ -92,11 +100,13 @@ const upgradeVatV0toV1 = (kvStore, defaultReapDirtThreshold, vatID) => { * `hostStorage.commit()` afterwards. * * @param {SwingStoreKernelStorage} kernelStorage - * @returns {boolean} true if any changes were made + * @returns {{ modified: boolean }} */ export const upgradeSwingset = kernelStorage => { const { kvStore } = kernelStorage; let modified = false; + /** @type {RunQueueEvent[]} */ + const upgradeEvents = []; let vstring = kvStore.get('version'); if (vstring === undefined) { vstring = '0'; @@ -204,9 +214,129 @@ export const upgradeSwingset = kernelStorage => { version = 2; } + if (version < 3) { + // v3 means that we've completed remediation for bug #9039 + console.log(`Starting remediation of bug #9039`); + + // find all terminated vats + const terminated = new Set(JSON.parse(getRequired('vats.terminated'))); + + // find all live vats + const allVatIDs = []; + for (const [_name, vatID] of getAllStaticVats(kvStore)) { + if (!terminated.has(vatID)) { + allVatIDs.push(vatID); + } + } + for (const vatID of getAllDynamicVats(getRequired)) { + if (!terminated.has(vatID)) { + allVatIDs.push(vatID); + } + } + + // find all pending notifies + const notifies = new Map(); // .get(kpid) = [vatIDs..]; + for (const name of ['runQueue', 'acceptanceQueue']) { + for (const rq of readQueue(name, getRequired)) { + if (rq.type === 'notify') { + const { vatID, kpid } = rq; + assert(vatID); + assert(kpid); + let vats = notifies.get(kpid); + if (!vats) { + vats = []; + notifies.set(kpid, vats); + } + vats.push(vatID); + } + } + } + console.log(` - pending notifies:`, notifies); + + // cache of known-settled kpids: will grow to num(kpids) + const KPIDStatus = new Map(); + const isSettled = kpid => { + if (KPIDStatus.has(kpid)) { + return KPIDStatus.get(kpid); + } + const state = kvStore.get(`${kpid}.state`); + // missing state means the kpid is deleted somehow, shouldn't happen + state || Fail`${kpid}.state is missing`; + const settled = state !== 'unresolved'; + KPIDStatus.set(kpid, settled); + return settled; + }; + + // walk vNN.c.kpNN for all vats, for each one check the + // kpNN.state, for the settled ones check for a pending notify, + // record the ones without a pending notify + + const buggyKPIDs = []; // [kpid, vatID] + for (const vatID of allVatIDs) { + const prefix = `${vatID}.c.`; + const len = prefix.length; + const ckpPrefix = `${vatID}.c.kp`; + for (const key of enumeratePrefixedKeys(kvStore, ckpPrefix)) { + const kpid = key.slice(len); + if (isSettled(kpid)) { + const n = notifies.get(kpid); + if (!n || !n.includes(vatID)) { + // there is no pending notify + buggyKPIDs.push([kpid, vatID]); + } + } + } + } + console.log(` - found ${buggyKPIDs.length} buggy kpids, enqueueing fixes`); + + // now fix it. The bug means we failed to delete the c-list entry + // and decref it back when the promise was rejected. That decref + // would have pushed the kpid onto maybeFreeKrefs, which would + // have triggered a refcount check at end-of-crank, which might + // have deleted the promise records (if nothing else was + // referencing the promise, like arguments in messages enqueued to + // unresolved promises, or something transient on the + // run-queue). Deleting those promise records might have decreffed + // krefs in the rejection data (although in general 9039 rejects + // those promises with non-slot-bearing DisconnectionObjects). + // + // To avoid duplicating a lot of kernel code inside this upgrade + // handler, we do the simplest possible thing: enqueue a notify to + // the upgraded vat for all these leftover promises. The new vat + // incarnation will ignore it (they don't recognize the vpid), but + // the dispatch.notify() delivery will clear the c-list and decref + // the kpid, and will trigger all the usual GC work. Note that + // these notifies will be delivered before any activity the host + // app might trigger for e.g. a chain upgrade, but they should not + // cause userspace-visible behavior (non-slot-bearing rejection + // data means no other vat will even get a gc-action delivery: + // only the upgraded vat will see anything, and those deliveries + // won't make it past liveslots). + + let count = 0; + for (const [kpid, vatID] of buggyKPIDs) { + // account for the reference to this kpid in upgradeEvents + incrementReferenceCount(getRequired, kvStore, kpid, `enq|notify`); + upgradeEvents.push({ type: 'notify', vatID, kpid }); + count += 1; + } + + console.log(` - #9039 remediation complete, ${count} notifies to inject`); + modified = true; + version = 3; + } + + if (upgradeEvents.length) { + assert(modified); + // stash until host calls controller.injectQueuedUpgradeEvents() + const oldEvents = JSON.parse(kvStore.get('upgradeEvents') || '[]'); + const events = [...oldEvents, ...upgradeEvents]; + kvStore.set('upgradeEvents', JSON.stringify(events)); + } + if (modified) { kvStore.set('version', `${version}`); } - return modified; + return harden({ modified }); }; harden(upgradeSwingset); diff --git a/packages/SwingSet/src/kernel/kernel.js b/packages/SwingSet/src/kernel/kernel.js index b6e11dbd714..b68d6836b95 100644 --- a/packages/SwingSet/src/kernel/kernel.js +++ b/packages/SwingSet/src/kernel/kernel.js @@ -215,6 +215,10 @@ export default function buildKernel( return deviceID; } + function injectQueuedUpgradeEvents() { + kernelKeeper.injectQueuedUpgradeEvents(); + } + function addImport(forVatID, what) { if (!started) { throw Error('must do kernel.start() before addImport()'); @@ -287,15 +291,16 @@ export default function buildKernel( const vatKeeper = kernelKeeper.provideVatKeeper(vatID); critical = vatKeeper.getOptions().critical; - // Reject all promises decided by the vat, making sure to capture the list - // of kpids before that data is deleted. - const deadPromises = [...kernelKeeper.enumeratePromisesByDecider(vatID)]; - // remove vatID from the list of live vats, and mark for deletion + // remove vatID from the list of live vats, and mark for + // deletion (which will happen later, in vat-cleanup events) kernelKeeper.deleteVatID(vatID); kernelKeeper.markVatAsTerminated(vatID); deferred.push(kernelKeeper.removeVatFromSwingStoreExports(vatID)); - for (const kpid of deadPromises) { - resolveToError(kpid, makeError('vat terminated'), vatID); + + // Reject all promises decided by the vat + const errdata = makeError('vat terminated'); + for (const [kpid, _p] of kernelKeeper.enumeratePromisesByDecider(vatID)) { + resolveToError(kpid, errdata, vatID); } } if (critical) { @@ -597,7 +602,9 @@ export default function buildKernel( const p = kernelKeeper.getKernelPromise(kpid); kernelKeeper.incStat('dispatchNotify'); const vatKeeper = kernelKeeper.provideVatKeeper(vatID); - p.state !== 'unresolved' || Fail`spurious notification ${kpid}`; + if (p.state === 'unresolved') { + throw Fail`spurious notification ${kpid}`; + } /** @type { KernelDeliveryOneNotify[] } */ const resolutions = []; if (!vatKeeper.hasCListEntry(kpid)) { @@ -612,7 +619,9 @@ export default function buildKernel( return NO_DELIVERY_CRANK_RESULTS; } for (const toResolve of targets) { - const { state, data } = kernelKeeper.getKernelPromise(toResolve); + const tp = kernelKeeper.getKernelPromise(toResolve); + assert(tp.state !== 'unresolved'); + const { state, data } = tp; resolutions.push([toResolve, { state, data }]); } /** @type { KernelDeliveryNotify } */ @@ -701,6 +710,7 @@ export default function buildKernel( total: work.exports + work.imports + + work.promises + work.kv + work.snapshots + work.transcripts, @@ -742,6 +752,8 @@ export default function buildKernel( kdebug(`vat ${vatID} terminated before startVat delivered`); return NO_DELIVERY_CRANK_RESULTS; } + const vatKeeper = kernelKeeper.provideVatKeeper(vatID); + vatKeeper.setVatParameters(vatParameters); const { meterID } = vatInfo; /** @type { KernelDeliveryStartVat } */ const kd = harden(['startVat', vatParameters]); @@ -995,9 +1007,30 @@ export default function buildKernel( return results; } - // reject all promises for which the vat was decider - for (const kpid of kernelKeeper.enumeratePromisesByDecider(vatID)) { - resolveToError(kpid, disconnectionCapData, vatID); + // We are homesick for a future in which most promises are + // durable, and vats do not need to subscribe to their own + // promises to make promise-watchers work. In that world, vats + // somehow mark the non-durable promises, which we must + // reject/disconnect on their behalf during upgrade. + // + // To handle the present reality, without durable promises, we + // pretend that all promises are so marked. + + // reject all ephemeral promises for which the vat was decider + for (const [kpid, p] of kernelKeeper.enumeratePromisesByDecider(vatID)) { + const isEphemeral = true; // future vats will mark these explicitly + const selfSubscribed = + p.state === 'unresolved' && p.subscribers.includes(vatID); + if (isEphemeral) { + resolveToError(kpid, disconnectionCapData, vatID); + if (!selfSubscribed) { + // If the vat was subscribed to its own promise, the + // resolveToError will enqueue a dispatch.notify, whose delivery + // will delete the c-list entry. If it is *not* subscribed, we + // should delete the c-list entry now, because nobody else will. + vatKeeper.deleteCListEntriesForKernelSlots([kpid]); + } + } } // simulate an abandonExports syscall from the vat, @@ -1021,6 +1054,7 @@ export default function buildKernel( }); const vatOptions = harden({ ...origOptions, workerOptions }); vatKeeper.setSourceAndOptions(source, vatOptions); + vatKeeper.setVatParameters(vatParameters); // TODO: decref the bundleID once setSourceAndOptions increfs it // pause, take a deep breath, appreciate this moment of silence @@ -1186,6 +1220,7 @@ export default function buildKernel( } } default: + // @ts-expect-error throw Fail`unknown promise resolution '${kp.state}'`; } } @@ -1820,6 +1855,7 @@ export default function buildKernel( { exports: M.number(), imports: M.number(), + promises: M.number(), kv: M.number(), snapshots: M.number(), transcripts: M.number(), @@ -2081,6 +2117,7 @@ export default function buildKernel( return p.data; } default: { + // @ts-expect-error throw Fail`invalid state for ${kpid}: ${p.state}`; } } @@ -2179,6 +2216,7 @@ export default function buildKernel( pinObject, vatNameToID, deviceNameToID, + injectQueuedUpgradeEvents, queueToKref, kpRegisterInterest, kpStatus, diff --git a/packages/SwingSet/src/kernel/state/kernelKeeper.js b/packages/SwingSet/src/kernel/state/kernelKeeper.js index 1d456160bf8..1d64b225f1c 100644 --- a/packages/SwingSet/src/kernel/state/kernelKeeper.js +++ b/packages/SwingSet/src/kernel/state/kernelKeeper.js @@ -45,14 +45,16 @@ const enableKernelGC = true; * @typedef { import('../../types-external.js').VatKeeper } VatKeeper * @typedef { import('../../types-internal.js').InternalKernelOptions } InternalKernelOptions * @typedef { import('../../types-internal.js').ReapDirtThreshold } ReapDirtThreshold + * @import {PromiseRecord} from '../../types-internal.js'; * @import {CleanupBudget, CleanupWork, PolicyOutputCleanupBudget} from '../../types-external.js'; * @import {RunQueueEventCleanupTerminatedVat} from '../../types-internal.js'; + * @import {SwingStoreKernelStorage} from '@agoric/swing-store'; */ export { DEFAULT_REAP_DIRT_THRESHOLD_KEY }; // most recent DB schema version -export const CURRENT_SCHEMA_VERSION = 2; +export const CURRENT_SCHEMA_VERSION = 3; // Kernel state lives in a key-value store supporting key retrieval by // lexicographic range. All keys and values are strings. @@ -71,9 +73,9 @@ export const CURRENT_SCHEMA_VERSION = 2; // only modified by a call to upgradeSwingset(). See below for // deltas/upgrades from one version to the next. // -// The current ("v2") schema keys/values are: +// The current ("v3") schema keys/values are: // -// version = '2' +// version = '3' // vat.names = JSON([names..]) // vat.dynamicIDs = JSON([vatIDs..]) // vat.name.$NAME = $vatID = v$NN @@ -117,6 +119,8 @@ export const CURRENT_SCHEMA_VERSION = 2; // old (v0): v$NN.reapCountdown = $NN or 'never' // v$NN.reapDirt = JSON({ deliveries, gcKrefs, computrons }) // missing keys treated as zero // (leave room for v$NN.snapshotDirt and options.snapshotDirtThreshold for #6786) +// v$NN.vatParameters = JSON(capdata) // missing for vats created/upgraded before #8947 +// // exclude from consensus // local.* @@ -141,6 +145,7 @@ export const CURRENT_SCHEMA_VERSION = 2; // gcActions = JSON(gcActions) // reapQueue = JSON([vatIDs...]) // pinnedObjects = ko$NN[,ko$NN..] +// upgradeEvents = JSON([events..]) // ko.nextID = $NN // ko$NN.owner = $vatID @@ -177,6 +182,11 @@ export const CURRENT_SCHEMA_VERSION = 2; // v2: // * change `version` to `'2'` // * add `vats.terminated` with `[]` as initial value +// v3: +// * change `version` to `'3'` +// * perform remediation for bug #9039 +// (after v3, does not get its own version) +// * `upgradeEvents` recognized, but omitted if empty /** @type {(s: string) => string[]} s */ export function commaSplit(s) { @@ -212,6 +222,77 @@ export const getAllDynamicVats = getRequired => { return JSON.parse(getRequired('vat.dynamicIDs')); }; +const getObjectReferenceCount = (kvStore, kref) => { + const data = kvStore.get(`${kref}.refCount`); + if (!data) { + return { reachable: 0, recognizable: 0 }; + } + const [reachable, recognizable] = commaSplit(data).map(Number); + reachable <= recognizable || + Fail`refmismatch(get) ${kref} ${reachable},${recognizable}`; + return { reachable, recognizable }; +}; + +const setObjectReferenceCount = (kvStore, kref, counts) => { + const { reachable, recognizable } = counts; + assert.typeof(reachable, 'number'); + assert.typeof(recognizable, 'number'); + (reachable >= 0 && recognizable >= 0) || + Fail`${kref} underflow ${reachable},${recognizable}`; + reachable <= recognizable || + Fail`refmismatch(set) ${kref} ${reachable},${recognizable}`; + kvStore.set(`${kref}.refCount`, `${reachable},${recognizable}`); +}; + +/** + * Increment the reference count associated with some kernel object. + * + * We track references to promises and objects, but not devices. Promises + * have only a "reachable" count, whereas objects track both "reachable" + * and "recognizable" counts. + * + * @param { (key: string) => string} getRequired + * @param { import('@agoric/swing-store').KVStore } kvStore + * @param {string} kref The kernel slot whose refcount is to be incremented. + * @param {string?} tag Debugging note with rough source of the reference. + * @param {{ isExport?: boolean, onlyRecognizable?: boolean }} options + * 'isExport' means the reference comes from a clist export, which counts + * for promises but not objects. 'onlyRecognizable' means the reference + * provides only recognition, not reachability + */ +export const incrementReferenceCount = ( + getRequired, + kvStore, + kref, + tag, + options = {}, +) => { + const { isExport = false, onlyRecognizable = false } = options; + kref || Fail`incrementRefCount called with empty kref, tag=${tag}`; + const { type } = parseKernelSlot(kref); + if (type === 'promise') { + const refCount = Number(getRequired(`${kref}.refCount`)) + 1; + // kdebug(`++ ${kref} ${tag} ${refCount}`); + kvStore.set(`${kref}.refCount`, `${refCount}`); + } + if (type === 'object' && !isExport) { + let { reachable, recognizable } = getObjectReferenceCount(kvStore, kref); + if (!onlyRecognizable) { + reachable += 1; + } + recognizable += 1; + // kdebug(`++ ${kref} ${tag} ${reachable},${recognizable}`); + setObjectReferenceCount(kvStore, kref, { reachable, recognizable }); + } +}; + +export function* readQueue(queue, getRequired) { + const [head, tail] = JSON.parse(getRequired(`${queue}`)); + for (let i = head; i < tail; i += 1) { + yield JSON.parse(getRequired(`${queue}.${i}`)); + } +} + // we use different starting index values for the various vNN/koNN/kdNN/kpNN // slots, to reduce confusing overlap when looking at debug messages (e.g. // seeing both kp1 and ko1, which are completely unrelated despite having the @@ -389,14 +470,7 @@ export default function makeKernelKeeper( return tail - head; } - function dumpQueue(queue) { - const [head, tail] = JSON.parse(getRequired(`${queue}`)); - const result = []; - for (let i = head; i < tail; i += 1) { - result.push(JSON.parse(getRequired(`${queue}.${i}`))); - } - return result; - } + const dumpQueue = queue => [...readQueue(queue, getRequired)]; /** * @param {InternalKernelOptions} kernelOptions @@ -620,26 +694,9 @@ export default function makeKernelKeeper( return parseReachableAndVatSlot(kvStore.get(kernelKey)); } - function getObjectRefCount(kernelSlot) { - const data = kvStore.get(`${kernelSlot}.refCount`); - if (!data) { - return { reachable: 0, recognizable: 0 }; - } - const [reachable, recognizable] = commaSplit(data).map(Number); - reachable <= recognizable || - Fail`refmismatch(get) ${kernelSlot} ${reachable},${recognizable}`; - return { reachable, recognizable }; - } - - function setObjectRefCount(kernelSlot, { reachable, recognizable }) { - assert.typeof(reachable, 'number'); - assert.typeof(recognizable, 'number'); - (reachable >= 0 && recognizable >= 0) || - Fail`${kernelSlot} underflow ${reachable},${recognizable}`; - reachable <= recognizable || - Fail`refmismatch(set) ${kernelSlot} ${reachable},${recognizable}`; - kvStore.set(`${kernelSlot}.refCount`, `${reachable},${recognizable}`); - } + const getObjectRefCount = kref => getObjectReferenceCount(kvStore, kref); + const setObjectRefCount = (kref, counts) => + setObjectReferenceCount(kvStore, kref, counts); /** * Iterate over non-durable objects exported by a vat. @@ -791,45 +848,41 @@ export default function makeKernelKeeper( return kpid; } + /** + * @param {string} kernelSlot + * @returns {PromiseRecord} + */ function getKernelPromise(kernelSlot) { insistKernelType('promise', kernelSlot); - const p = { state: getRequired(`${kernelSlot}.state`) }; - switch (p.state) { - case undefined: { - throw Fail`unknown kernelPromise '${kernelSlot}'`; - } + const state = getRequired(`${kernelSlot}.state`); + const refCount = Number(kvStore.get(`${kernelSlot}.refCount`)); + switch (state) { case 'unresolved': { - p.refCount = Number(kvStore.get(`${kernelSlot}.refCount`)); - p.decider = kvStore.get(`${kernelSlot}.decider`); - if (p.decider === '') { - p.decider = undefined; - } - p.policy = kvStore.get(`${kernelSlot}.policy`) || 'ignore'; - // @ts-expect-error get() may fail - p.subscribers = commaSplit(kvStore.get(`${kernelSlot}.subscribers`)); - p.queue = Array.from( + const decider = kvStore.get(`${kernelSlot}.decider`) || undefined; + const policy = kvStore.get(`${kernelSlot}.policy`) || 'ignore'; + const subscribers = commaSplit( + kvStore.get(`${kernelSlot}.subscribers`) || '', + ); + const queue = Array.from( getPrefixedValues(kvStore, `${kernelSlot}.queue.`), ).map(s => JSON.parse(s)); - break; + return harden({ state, refCount, decider, policy, subscribers, queue }); } case 'fulfilled': case 'rejected': { - p.refCount = Number(kvStore.get(`${kernelSlot}.refCount`)); - p.data = { - body: kvStore.get(`${kernelSlot}.data.body`), - // @ts-expect-error get() may fail - slots: commaSplit(kvStore.get(`${kernelSlot}.data.slots`)), + const data = { + body: getRequired(`${kernelSlot}.data.body`), + slots: commaSplit(getRequired(`${kernelSlot}.data.slots`)), }; - for (const s of p.data.slots) { + for (const s of data.slots) { parseKernelSlot(s); } - break; + return harden({ state, refCount, data }); } default: { - throw Fail`unknown state for ${kernelSlot}: ${p.state}`; + throw Fail`unknown state for ${kernelSlot}: ${state}`; } } - return harden(p); } function getResolveablePromise(kpid, expectedDecider) { @@ -838,7 +891,9 @@ export default function makeKernelKeeper( insistVatID(expectedDecider); } const p = getKernelPromise(kpid); - p.state === 'unresolved' || Fail`${kpid} was already resolved`; + if (p.state !== 'unresolved') { + throw Fail`${kpid} was already resolved`; + } if (expectedDecider) { p.decider === expectedDecider || Fail`${kpid} is decided by ${p.decider}, not ${expectedDecider}`; @@ -897,6 +952,7 @@ export default function makeKernelKeeper( // up the resolution *now* and set the correct target early. Doing that // might make it easier to remove the Promise Table entry earlier. const p = getKernelPromise(kernelSlot); + assert.equal(p.state, 'unresolved'); for (const msg of p.queue) { const entry = harden({ type: 'send', target: kernelSlot, msg }); enqueue('acceptanceQueue', entry); @@ -967,6 +1023,7 @@ export default function makeKernelKeeper( const work = { exports: 0, imports: 0, + promises: 0, kv: 0, snapshots: 0, transcripts: 0, @@ -991,6 +1048,7 @@ export default function makeKernelKeeper( const clistPrefix = `${vatID}.c.`; const exportPrefix = `${clistPrefix}o+`; const importPrefix = `${clistPrefix}o-`; + const promisePrefix = `${clistPrefix}p`; // Note: ASCII order is "+,-./", and we rely upon this to split the // keyspace into the various o+NN/o-NN/etc spaces. If we were using a @@ -1043,8 +1101,22 @@ export default function makeKernelKeeper( } } - // the caller used enumeratePromisesByDecider() before calling us, - // so they already know the orphaned promises to reject + // The caller used enumeratePromisesByDecider() before calling us, + // so they have already rejected the orphan promises, but those + // kpids are still present in the dead vat's c-list. Clean those + // up now. + remaining = budget.promises ?? budget.default; + for (const k of enumeratePrefixedKeys(kvStore, promisePrefix)) { + const kref = kvStore.get(k) || Fail`getNextKey ensures get`; + const vref = stripPrefix(clistPrefix, k); + vatKeeper.deleteCListEntry(kref, vref); + // that will also delete both db keys + work.promises += 1; + remaining -= 1; + if (remaining <= 0) { + return { done: false, work }; + } + } // now loop back through everything and delete it all remaining = budget.kv ?? budget.default; @@ -1142,18 +1214,22 @@ export default function makeKernelKeeper( function setDecider(kpid, decider) { insistVatID(decider); const p = getKernelPromise(kpid); - p.state === 'unresolved' || Fail`${kpid} was already resolved`; - !p.decider || Fail`${kpid} has decider ${p.decider}, not empty`; + assert.equal(p.state, 'unresolved', `${kpid} was already resolved`); + assert(!p.decider, `${kpid} has decider ${p.decider}, not empty`); kvStore.set(`${kpid}.decider`, decider); } function clearDecider(kpid) { const p = getKernelPromise(kpid); - p.state === 'unresolved' || Fail`${kpid} was already resolved`; - p.decider || Fail`${kpid} does not have a decider`; + assert.equal(p.state, 'unresolved', `${kpid} was already resolved`); + assert(p.decider, `${kpid} does not have a decider`); kvStore.set(`${kpid}.decider`, ''); } + /** + * @param {string} vatID + * @returns {IterableIterator<[kpid: string, p: PromiseRecord]>} + */ function* enumeratePromisesByDecider(vatID) { insistVatID(vatID); const promisePrefix = `${vatID}.c.p`; @@ -1167,10 +1243,10 @@ export default function makeKernelKeeper( // whether the vat is the decider or not. If it is, we add the promise // to the list of promises that must be rejected because the dead vat // will never be able to act upon them. - const kpid = kvStore.get(k); + const kpid = getRequired(k); const p = getKernelPromise(kpid); if (p.state === 'unresolved' && p.decider === vatID) { - yield kpid; + yield [kpid, p]; } } } @@ -1180,6 +1256,7 @@ export default function makeKernelKeeper( insistKernelType('promise', kernelSlot); insistVatID(vatID); const p = getKernelPromise(kernelSlot); + assert.equal(p.state, 'unresolved'); const s = new Set(p.subscribers); s.add(vatID); const v = Array.from(s).sort().join(','); @@ -1210,6 +1287,27 @@ export default function makeKernelKeeper( return dequeue('acceptanceQueue'); } + function injectQueuedUpgradeEvents() { + // refcounts: Any krefs in `upgradeEvents` must have a refcount to + // represent the list's hold on those objects. When + // upgradeSwingset() creates these events, it must also + // incref(kref), otherwise we run the risk of dropping the kref by + // the time injectQueuedUpgradeEvents() is called. We're nominally + // removing each event from upgradeEvents (decref), then pushing + // it onto the run-queue (incref), but since those two cancel each + // other out, we don't actually need to modify any reference + // counts from within this function. Note that + // addToAcceptanceQueue does not increment refcounts, just kernel + // queue-length stats. + + const events = JSON.parse(kvStore.get('upgradeEvents') || '[]'); + kvStore.delete('upgradeEvents'); + for (const e of events) { + assert(e.type, `not an event`); + addToAcceptanceQueue(e); + } + } + function allocateMeter(remaining, threshold) { if (remaining !== 'unlimited') { assert.typeof(remaining, 'bigint'); @@ -1417,40 +1515,8 @@ export default function makeKernelKeeper( maybeFreeKrefs.add(kref); } - /** - * Increment the reference count associated with some kernel object. - * - * We track references to promises and objects, but not devices. Promises - * have only a "reachable" count, whereas objects track both "reachable" - * and "recognizable" counts. - * - * @param {unknown} kernelSlot The kernel slot whose refcount is to be incremented. - * @param {string?} tag Debugging note with rough source of the reference. - * @param {{ isExport?: boolean, onlyRecognizable?: boolean }} options - * 'isExport' means the reference comes from a clist export, which counts - * for promises but not objects. 'onlyRecognizable' means the reference - * provides only recognition, not reachability - */ - function incrementRefCount(kernelSlot, tag, options = {}) { - const { isExport = false, onlyRecognizable = false } = options; - kernelSlot || - Fail`incrementRefCount called with empty kernelSlot, tag=${tag}`; - const { type } = parseKernelSlot(kernelSlot); - if (type === 'promise') { - const refCount = Nat(BigInt(getRequired(`${kernelSlot}.refCount`))) + 1n; - // kdebug(`++ ${kernelSlot} ${tag} ${refCount}`); - kvStore.set(`${kernelSlot}.refCount`, `${refCount}`); - } - if (type === 'object' && !isExport) { - let { reachable, recognizable } = getObjectRefCount(kernelSlot); - if (!onlyRecognizable) { - reachable += 1; - } - recognizable += 1; - // kdebug(`++ ${kernelSlot} ${tag} ${reachable},${recognizable}`); - setObjectRefCount(kernelSlot, { reachable, recognizable }); - } - } + const incrementRefCount = (kref, tag, options = {}) => + incrementReferenceCount(getRequired, kvStore, kref, tag, options); /** * Decrement the reference count associated with some kernel object. @@ -1542,13 +1608,15 @@ export default function makeKernelKeeper( const kp = getKernelPromise(kpid); if (kp.refCount === 0) { let idx = 0; - // TODO (#9889) don't assume promise is settled - for (const slot of kp.data.slots) { - // Note: the following decrement can result in an addition to the - // maybeFreeKrefs set, which we are in the midst of iterating. - // TC39 went to a lot of trouble to ensure that this is kosher. - decrementRefCount(slot, `gc|${kpid}|s${idx}`); - idx += 1; + if (kp.state === 'fulfilled' || kp.state === 'rejected') { + // #9889 don't assume promise is settled + for (const slot of kp.data.slots) { + // Note: the following decrement can result in an addition to the + // maybeFreeKrefs set, which we are in the midst of iterating. + // TC39 went to a lot of trouble to ensure that this is kosher. + decrementRefCount(slot, `gc|${kpid}|s${idx}`); + idx += 1; + } } deleteKernelPromise(kpid); } @@ -1939,6 +2007,8 @@ export default function makeKernelKeeper( getAcceptanceQueueLength, getNextAcceptanceQueueMsg, + injectQueuedUpgradeEvents, + allocateMeter, addMeterRemaining, setMeterThreshold, diff --git a/packages/SwingSet/src/kernel/state/vatKeeper.js b/packages/SwingSet/src/kernel/state/vatKeeper.js index 7322f34ee37..1d4d6b1f12f 100644 --- a/packages/SwingSet/src/kernel/state/vatKeeper.js +++ b/packages/SwingSet/src/kernel/state/vatKeeper.js @@ -7,6 +7,7 @@ import { isObject } from '@endo/marshal'; import { parseKernelSlot } from '../parseKernelSlots.js'; import { makeVatSlot, parseVatSlot } from '../../lib/parseVatSlots.js'; import { insistVatID } from '../../lib/id.js'; +import { insistCapData } from '../../lib/capdata.js'; import { kdebug } from '../../lib/kdebug.js'; import { parseReachableAndVatSlot, @@ -173,6 +174,35 @@ export function makeVatKeeper( return harden(options); } + /** + * @param {SwingSetCapData} newVPCD + */ + function setVatParameters(newVPCD) { + insistCapData(newVPCD); + const key = `${vatID}.vatParameters`; + // increment-before-decrement to minimize spurious rc=0 checks + for (const kref of newVPCD.slots) { + incrementRefCount(kref, `${vatID}.vatParameters`); + } + const old = kvStore.get(key) || '{"slots":[]}'; + for (const kref of JSON.parse(old).slots) { + decrementRefCount(kref, `${vatID}.vatParameters`); + } + kvStore.set(key, JSON.stringify(newVPCD)); + } + + /** + * @returns {SwingSetCapData | undefined} vpcd + */ + function getVatParameters() { + const key = `${vatID}.vatParameters`; + const old = kvStore.get(key); + if (old) { + return JSON.parse(old); + } + return undefined; + } + // This is named "addDirt" because it should increment all dirt // counters (both for reap/BOYD and for heap snapshotting). We don't // have `heapSnapshotDirt` yet, but when we do, it should get @@ -768,6 +798,8 @@ export function makeVatKeeper( setSourceAndOptions, getSourceAndOptions, getOptions, + setVatParameters, + getVatParameters, addDirt, getReapDirt, clearReapDirt, diff --git a/packages/SwingSet/src/kernel/vatTranslator.js b/packages/SwingSet/src/kernel/vatTranslator.js index d781a41cf38..10a7f9559ae 100644 --- a/packages/SwingSet/src/kernel/vatTranslator.js +++ b/packages/SwingSet/src/kernel/vatTranslator.js @@ -50,6 +50,7 @@ function makeTranslateKernelDeliveryToVatDelivery(vatID, kernelKeeper) { parseVatSlot(targetSlot).allocatedByVat || Fail`deliver() to wrong vat`; } else if (type === 'promise') { const p = kernelKeeper.getKernelPromise(target); + assert(p.state === 'unresolved'); p.decider === vatID || Fail`wrong decider`; } const inputSlots = msg.methargs.slots.map(slot => @@ -59,7 +60,9 @@ function makeTranslateKernelDeliveryToVatDelivery(vatID, kernelKeeper) { if (msg.result) { insistKernelType('promise', msg.result); const p = kernelKeeper.getKernelPromise(msg.result); - p.state === 'unresolved' || Fail`result ${msg.result} already resolved`; + if (p.state !== 'unresolved') { + throw Fail`result ${msg.result} already resolved`; + } !p.decider || Fail`result ${msg.result} already has decider ${p.decider}`; resultSlot = vatKeeper.mapKernelSlotToVatSlot(msg.result); insistVatType('promise', resultSlot); @@ -318,8 +321,9 @@ function makeTranslateVatSyscallToKernelSyscall(vatID, kernelKeeper) { // In the case of non-pipelining vats these checks are redundant since // we're guaranteed to have a promise newly allocated by the vat. const p = kernelKeeper.getKernelPromise(result); - p.state === 'unresolved' || - Fail`send() result ${result} is already resolved`; + if (p.state !== 'unresolved') { + throw Fail`send() result ${result} is already resolved`; + } p.decider === vatID || Fail`send() result ${result} is decided by ${p.decider} not ${vatID}`; kernelKeeper.clearDecider(result); diff --git a/packages/SwingSet/src/types-external.js b/packages/SwingSet/src/types-external.js index 9550363c5fd..392324c1300 100644 --- a/packages/SwingSet/src/types-external.js +++ b/packages/SwingSet/src/types-external.js @@ -227,6 +227,7 @@ export {}; * * @typedef { { exports: number, * imports: number, + * promises: number, * kv: number, * snapshots: number, * transcripts: number, diff --git a/packages/SwingSet/src/types-internal.js b/packages/SwingSet/src/types-internal.js index 1ef68a5735f..7a4b2ef1604 100644 --- a/packages/SwingSet/src/types-internal.js +++ b/packages/SwingSet/src/types-internal.js @@ -92,6 +92,17 @@ export {}; * @property {number} [computrons] */ +/** + * @typedef {{ state: 'unresolved', refCount: number, + * decider: string | undefined, policy: string, + * subscribers: string[], queue: string[], + * }} UnresolvedPromiseRecord + * @typedef {{ state: 'fulfilled' | 'rejected', refCount: number, + * data: SwingSetCapData, + * }} SettledPromiseRecord + * @typedef {UnresolvedPromiseRecord | SettledPromiseRecord} PromiseRecord + */ + /** * @typedef {{ * enablePipelining: boolean, diff --git a/packages/SwingSet/test/kernel.test.js b/packages/SwingSet/test/kernel.test.js index f18a0720ff3..ef17e333a34 100644 --- a/packages/SwingSet/test/kernel.test.js +++ b/packages/SwingSet/test/kernel.test.js @@ -1817,3 +1817,46 @@ test('reap gc-krefs 12', async t => { test('reap gc-krefs overrideNever', async t => { await reapGCKrefsTest(t, 12, true); }); + +test('injectQueuedUpgradeEvents', async t => { + const endowments = makeKernelEndowments(); + const { kernelStorage } = endowments; + const { kvStore } = kernelStorage; + await initializeKernel({}, kernelStorage); + const kernel = buildKernel(endowments, {}, {}); + await kernel.start(); + // TODO: extract `queueLength`/`dumpQueue` from kernelKeeper.js? + { + const [start, end] = JSON.parse(kvStore.get('acceptanceQueue')); + t.deepEqual([start, end], [1, 1]); + } + + // These events would fail if they actually got processed: the vatID + // and all the krefs are fake, the methargs are not valid capdata, + // and we haven't updated refcounts. But all we care about is that + // they make it onto the acceptance queue. + const vatID = 'v1'; + const e1 = harden({ type: 'notify', vatID, kpid: 'kp25' }); + // kernelKeeper.incrementRefCount(kpid, `enq|notify`); + const target = 'ko11'; + const methargs = ''; + const msg = harden({ methargs, result: 'kp33' }); + const e2 = harden({ type: 'send', target, msg }); + kvStore.set('upgradeEvents', JSON.stringify([e1, e2])); + + kernel.injectQueuedUpgradeEvents([e1, e2]); + + { + const [start, end] = JSON.parse(kvStore.get('acceptanceQueue')); + t.deepEqual([start, end], [1, 3]); + t.deepEqual(JSON.parse(kvStore.get('acceptanceQueue.1')), e1); + t.deepEqual(JSON.parse(kvStore.get('acceptanceQueue.2')), e2); + } + + // It'd be nice to also check that the stats were incremented: + // stats.acceptanceQueueLength shoule be 2, acceptanceQueueLengthUp + // should be 2, and acceptanceQueueLengthMax should be 2. But the + // stats are held in RAM until a crank is executed, and we're not + // doing that here. And the kernel hides the kernelKeeper so we + // can't call getStats() to check. +}); diff --git a/packages/SwingSet/test/snapshots/state.test.js.md b/packages/SwingSet/test/snapshots/state.test.js.md index 4c02a66077d..674e2a3c811 100644 --- a/packages/SwingSet/test/snapshots/state.test.js.md +++ b/packages/SwingSet/test/snapshots/state.test.js.md @@ -8,8 +8,8 @@ Generated by [AVA](https://avajs.dev). > initial state - '7b16bffd29f6a2d11bae7b536ef4c230af8cadc29284928b6cc2f7338507a987' + 'af35907384e9d63dd9fc4d4df0440005c0ee81ef88f86089a0e0a280fe3793af' > expected activityhash - '7dbf5a49d4e2b999c431730fcd4927c01c713eaa54fe273626e4201853e38d3b' + '040e27413c25f3ce668d9778add3b3d39547358ded553c0b9fba898004968d1b' diff --git a/packages/SwingSet/test/snapshots/state.test.js.snap b/packages/SwingSet/test/snapshots/state.test.js.snap index 0efcf0fac96..a4a79f56520 100644 Binary files a/packages/SwingSet/test/snapshots/state.test.js.snap and b/packages/SwingSet/test/snapshots/state.test.js.snap differ diff --git a/packages/SwingSet/test/state.test.js b/packages/SwingSet/test/state.test.js index c60a01aa79c..b16c3d17d61 100644 --- a/packages/SwingSet/test/state.test.js +++ b/packages/SwingSet/test/state.test.js @@ -183,7 +183,7 @@ test('kernel state', async t => { k.emitCrankHashes(); checkState(t, store.dump, [ - ['version', '2'], + ['version', '3'], ['crankNumber', '0'], ['gcActions', '[]'], ['runQueue', '[1,1]'], @@ -223,7 +223,7 @@ test('kernelKeeper vat names', async t => { k.emitCrankHashes(); checkState(t, store.dump, [ - ['version', '2'], + ['version', '3'], ['crankNumber', '0'], ['gcActions', '[]'], ['runQueue', '[1,1]'], @@ -279,7 +279,7 @@ test('kernelKeeper device names', async t => { k.emitCrankHashes(); checkState(t, store.dump, [ - ['version', '2'], + ['version', '3'], ['crankNumber', '0'], ['gcActions', '[]'], ['runQueue', '[1,1]'], @@ -462,7 +462,7 @@ test('kernelKeeper promises', async t => { k.emitCrankHashes(); checkState(t, store.dump, [ - ['version', '2'], + ['version', '3'], ['crankNumber', '0'], ['device.nextID', '7'], ['vat.nextID', '1'], @@ -592,6 +592,44 @@ test('vatKeeper.getOptions', async t => { t.is(name, 'fred'); }); +test('vatKeeper.setVatParameters', async t => { + const store = buildKeeperStorageInMemory(); + const k = makeKernelKeeper(store, 'uninitialized'); + k.createStartingKernelState({ defaultManagerType: 'local' }); + k.setInitialized(); + const v1 = k.allocateVatIDForNameIfNeeded('name1'); + const bundleID = + 'b1-00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; + const source = { bundleID }; + const workerOptions = { type: 'local' }; + const options = { workerOptions, name: 'fred', reapDirtThreshold: {} }; + k.createVatState(v1, source, options); + + const vk = k.provideVatKeeper(v1); + const ko1 = kslot('ko1'); + const ko2 = kslot('ko2'); + const vp1 = kser({ foo: 1, bar: ko1, baz: ko1 }); + vk.setVatParameters(vp1); + t.deepEqual(JSON.parse(store.kvStore.get('v1.vatParameters')), vp1); + t.is(store.kvStore.get('ko1.refCount'), '1,1'); + t.deepEqual(vk.getVatParameters(), vp1); + + const vp2 = kser({ foo: 1, bar: ko1, baz: ko2 }); + vk.setVatParameters(vp2); + t.deepEqual(JSON.parse(store.kvStore.get('v1.vatParameters')), vp2); + t.is(store.kvStore.get('ko1.refCount'), '1,1'); + t.is(store.kvStore.get('ko2.refCount'), '1,1'); + t.deepEqual(vk.getVatParameters(), vp2); + + const vp3 = kser({ foo: 1, baz: ko2 }); + vk.setVatParameters(vp3); + t.deepEqual(JSON.parse(store.kvStore.get('v1.vatParameters')), vp3); + // this test doesn't do processRefcounts, which would remove the 0,0 + t.is(store.kvStore.get('ko1.refCount'), '0,0'); + t.is(store.kvStore.get('ko2.refCount'), '1,1'); + t.deepEqual(vk.getVatParameters(), vp3); +}); + test('XS vatKeeper defaultManagerType', async t => { const store = buildKeeperStorageInMemory(); const k = makeKernelKeeper(store, 'uninitialized'); @@ -1078,7 +1116,7 @@ test('dirt upgrade', async t => { // * v3.reapCountdown: 'never' // * v3.reapInterval: 'never' - t.is(k.kvStore.get('version'), '2'); + t.is(k.kvStore.get('version'), '3'); k.kvStore.delete(`kernel.defaultReapDirtThreshold`); k.kvStore.set(`kernel.defaultReapInterval`, '1000'); @@ -1109,12 +1147,15 @@ test('dirt upgrade', async t => { // it requires a manual upgrade let k2; + let ks2; { const serialized = store.serialize(); const { kernelStorage } = initSwingStore(null, { serialized }); - upgradeSwingset(kernelStorage); + const { modified } = upgradeSwingset(kernelStorage); + t.true(modified); k2 = makeKernelKeeper(kernelStorage, CURRENT_SCHEMA_VERSION); // works this time k2.loadStats(); + ks2 = kernelStorage; } t.true(k2.kvStore.has(`kernel.defaultReapDirtThreshold`)); @@ -1157,6 +1198,12 @@ test('dirt upgrade', async t => { t.deepEqual(JSON.parse(k2.kvStore.get(`${v3}.options`)).reapDirtThreshold, { never: true, }); + + { + // upgrade is idempotent + const { modified } = upgradeSwingset(ks2); + t.false(modified); + } }); test('v2 upgrade', async t => { @@ -1168,7 +1215,7 @@ test('v2 upgrade', async t => { k.saveStats(); // roll back to v1 - t.is(k.kvStore.get('version'), '2'); + t.is(k.kvStore.get('version'), '3'); k.kvStore.delete(`vats.terminated`); k.kvStore.set('version', '1'); @@ -1177,15 +1224,24 @@ test('v2 upgrade', async t => { // it requires a manual upgrade let k2; + let ks2; { const serialized = store.serialize(); const { kernelStorage } = initSwingStore(null, { serialized }); - upgradeSwingset(kernelStorage); + const { modified } = upgradeSwingset(kernelStorage); + t.true(modified); k2 = makeKernelKeeper(kernelStorage, CURRENT_SCHEMA_VERSION); // works this time k2.loadStats(); + ks2 = kernelStorage; } t.true(k2.kvStore.has(`vats.terminated`)); t.deepEqual(JSON.parse(k2.kvStore.get(`vats.terminated`)), []); - t.is(k2.kvStore.get(`version`), '2'); + t.is(k2.kvStore.get(`version`), '3'); + + { + // upgrade is idempotent + const { modified } = upgradeSwingset(ks2); + t.false(modified); + } }); diff --git a/packages/SwingSet/test/transcript-light.test.js b/packages/SwingSet/test/transcript-light.test.js index 9049eb57845..e911a4e3451 100644 --- a/packages/SwingSet/test/transcript-light.test.js +++ b/packages/SwingSet/test/transcript-light.test.js @@ -17,7 +17,7 @@ test('transcript-light load', async t => { t.teardown(c.shutdown); const serialized0 = debug.serialize(); const kvstate0 = debug.dump().kvEntries; - t.is(kvstate0.version, '2'); + t.is(kvstate0.version, '3'); t.is(kvstate0.runQueue, '[1,1]'); t.not(kvstate0.acceptanceQueue, '[]'); diff --git a/packages/SwingSet/test/upgrade-swingset.test.js b/packages/SwingSet/test/upgrade-swingset.test.js index a18b2699dd8..ca88649d3bb 100644 --- a/packages/SwingSet/test/upgrade-swingset.test.js +++ b/packages/SwingSet/test/upgrade-swingset.test.js @@ -1,9 +1,11 @@ -/* eslint-disable no-underscore-dangle */ // @ts-nocheck -import { initSwingStore } from '@agoric/swing-store'; +// eslint-disable-next-line import/order import { test } from '../tools/prepare-test-env-ava.js'; +import { initSwingStore } from '@agoric/swing-store'; +import { kser } from '@agoric/kmarshal'; + import { initializeSwingset, makeSwingsetController, @@ -28,7 +30,7 @@ test('kernel refuses to run with out-of-date DB - v0', async t => { // kernelkeeper v0 schema, just deleting the version key and adding // 'initialized' - t.is(kvStore.get('version'), '2'); + t.is(kvStore.get('version'), '3'); kvStore.delete(`version`); kvStore.set('initialized', 'true'); await commit(); @@ -51,7 +53,7 @@ test('kernel refuses to run with out-of-date DB - v1', async t => { // kernelkeeper v1 schema, by reducing the version key and removing // vats.terminated - t.is(kvStore.get('version'), '2'); + t.is(kvStore.get('version'), '3'); kvStore.set(`version`, '1'); kvStore.delete('vats.terminated'); await commit(); @@ -62,6 +64,28 @@ test('kernel refuses to run with out-of-date DB - v1', async t => { }); }); +test('kernel refuses to run with out-of-date DB - v2', async t => { + const { hostStorage, kernelStorage } = initSwingStore(); + const { commit } = hostStorage; + const { kvStore } = kernelStorage; + const config = {}; + await initializeSwingset(config, [], kernelStorage, t.context.data); + await commit(); + + // now doctor the initial state to make it look like the + // kernelkeeper v2 schema, by reducing the version key and removing + // vats.terminated + + t.is(kvStore.get('version'), '3'); + kvStore.set(`version`, '2'); + await commit(); + + // Now build a controller around this modified state, which should fail. + await t.throwsAsync(() => makeSwingsetController(kernelStorage), { + message: /kernel DB is too old/, + }); +}); + test('upgrade kernel state', async t => { const { hostStorage, kernelStorage } = initSwingStore(); const { commit } = hostStorage; @@ -96,7 +120,7 @@ test('upgrade kernel state', async t => { t.true(kvStore.has('kernel.defaultReapDirtThreshold')); - t.is(kvStore.get('version'), '2'); + t.is(kvStore.get('version'), '3'); kvStore.delete('version'); // i.e. revert to v0 kvStore.set('initialized', 'true'); kvStore.delete('vats.terminated'); @@ -137,7 +161,8 @@ test('upgrade kernel state', async t => { upgradeSwingset(kernelStorage); // now we should be good to go - const _controller = await makeSwingsetController(kernelStorage); + const controller = await makeSwingsetController(kernelStorage); + controller.injectQueuedUpgradeEvents(); t.true(kvStore.has('kernel.defaultReapDirtThreshold')); // the kernel-wide threshold gets a .gcKrefs (to meet our upcoming @@ -186,7 +211,7 @@ test('upgrade non-reaping kernel state', async t => { t.true(kvStore.has('kernel.defaultReapDirtThreshold')); - t.is(kvStore.get('version'), '2'); + t.is(kvStore.get('version'), '3'); kvStore.delete('version'); // i.e. revert to v0 kvStore.set('initialized', 'true'); kvStore.delete('vats.terminated'); @@ -220,7 +245,8 @@ test('upgrade non-reaping kernel state', async t => { upgradeSwingset(kernelStorage); // now we should be good to go - const _controller = await makeSwingsetController(kernelStorage); + const controller = await makeSwingsetController(kernelStorage); + controller.injectQueuedUpgradeEvents(); t.true(kvStore.has('kernel.defaultReapDirtThreshold')); t.deepEqual(JSON.parse(kvStore.get('kernel.defaultReapDirtThreshold')), { @@ -229,3 +255,211 @@ test('upgrade non-reaping kernel state', async t => { gcKrefs: 'never', }); }); + +test('v3 upgrade', async t => { + // exercise the remediation code for bug #9039 + const { hostStorage, kernelStorage, debug } = initSwingStore(); + const { commit } = hostStorage; + const { kvStore } = kernelStorage; + const config = {}; + await initializeSwingset(config, [], kernelStorage, t.context.data); + await commit(); + + // doctor the initial state to inject #9039 problems, then check + // that upgrade applies the expected fixes. We pretend that + // v1-vatAdmin was upgraded and left some promises lying around. + + const vatID = kvStore.get('vat.name.vatAdmin'); + t.truthy(vatID); + + const disconnectionObject = { + name: 'vatUpgraded', + upgradeMessage: 'test upgrade', + incarnationNumber: 0, + }; + const dccd = kser(disconnectionObject); + + t.is(kvStore.get('version'), '3'); + kvStore.set('version', '2'); // revert to v2 + const runQueue = []; + const acceptanceQueue = []; + const nextID = Number(kvStore.get('kp.nextID')); + const p1 = `kp${nextID}`; + const p2 = `kp${nextID + 1}`; + const p3 = `kp${nextID + 2}`; + const p4 = `kp${nextID + 3}`; + const p5 = `kp${nextID + 4}`; + const p6 = `kp${nextID + 5}`; + kvStore.set('kp.nextID', `${nextID + 6}`); + + // first promise "was" known only to the upgraded vat, but not + // self-subscribed, so no notify was sent: remediated + kvStore.set(`${p1}.state`, 'rejected'); + kvStore.set(`${p1}.data.body`, dccd.body); + kvStore.set(`${p1}.data.slots`, ''); + kvStore.set(`${p1}.refCount`, '1'); + kvStore.set(`${vatID}.c.${p1}`, 'R p+90'); + kvStore.set(`${vatID}.c.p+90`, p1); + + // second promise was also only known to upgraded vat, but we + // pretend it was self-subscribed, and the notify is still sitting + // in the run-queue: ignored + kvStore.set(`${p2}.state`, 'rejected'); + kvStore.set(`${p2}.data.body`, dccd.body); + kvStore.set(`${p2}.data.slots`, ''); + kvStore.set(`${p2}.refCount`, '2'); // c-list, runQueue + kvStore.set(`${vatID}.c.${p2}`, 'R p+91'); + kvStore.set(`${vatID}.c.p+91`, p2); + runQueue.push({ type: 'notify', vatID, kpid: p2 }); + + // third promise is only known to upgraded vat, but self-subscribed, + // and the notify is still sitting in the acceptance queue: ignored + kvStore.set(`${p3}.state`, 'rejected'); + kvStore.set(`${p3}.data.body`, dccd.body); + kvStore.set(`${p3}.data.slots`, ''); + kvStore.set(`${p3}.refCount`, '2'); // c-list, acceptanceQueue + kvStore.set(`${vatID}.c.${p3}`, 'R p+92'); + kvStore.set(`${vatID}.c.p+92`, p3); + acceptanceQueue.push({ type: 'notify', vatID, kpid: p3 }); + + // fourth promise has additional references, still remediated + kvStore.set(`${p4}.state`, 'rejected'); + kvStore.set(`${p4}.data.body`, dccd.body); + kvStore.set(`${p4}.data.slots`, ''); + // note: we aren't being specific about *where* the other reference + // is coming from. A plausible source is an argument of a message + // queued to some other unresolved promise. A non-plausible one is + // in the c-list of some other vat (as a settled promise that one + // should have gotten a notify too, assuming they were subscribed, + // and they shouldn't be not subscribed). If the refcounts were + // stored in a DB with more runtime checking, we'd be creating an + // illegal situation here, but it's not. + kvStore.set(`${p4}.refCount`, '2'); // c-list, other + kvStore.set(`${vatID}.c.${p4}`, 'R p+93'); + kvStore.set(`${vatID}.c.p+93`, p4); + + // fifth promise is fulfilled, not rejected, without a notify: + // remediated (even though strictly speaking 9039 is about rejected + // promises) + kvStore.set(`${p5}.state`, 'fulfilled'); + kvStore.set(`${p5}.data.body`, '#{}'); + kvStore.set(`${p5}.data.slots`, ''); + kvStore.set(`${p5}.refCount`, '1'); + kvStore.set(`${vatID}.c.${p5}`, 'R p+95'); + kvStore.set(`${vatID}.c.p+95`, p5); + + // sixth promise is unresolved: ignored + kvStore.set(`${p6}.state`, 'unresolved'); + kvStore.set(`${p6}.subscribers`, ''); + kvStore.set(`${p6}.queue.nextID`, `0`); + kvStore.set(`${p6}.refCount`, `1`); + kvStore.set(`${p6}.decider`, vatID); + kvStore.set(`${vatID}.c.${p6}`, 'R p+96'); + kvStore.set(`${vatID}.c.p+96`, p6); + + // now update queues + + // eslint-disable-next-line prefer-const + let [runHead, runTail] = JSON.parse(kvStore.get('runQueue')); + for (const m of runQueue) { + kvStore.set(`runQueue.${runTail}`, JSON.stringify(m)); + runTail += 1; + } + kvStore.set('runQueue', JSON.stringify([runHead, runTail])); + + // eslint-disable-next-line prefer-const + let [accHead, accTail] = JSON.parse(kvStore.get('acceptanceQueue')); + for (const m of acceptanceQueue) { + kvStore.set(`acceptanceQueue.${accTail}`, JSON.stringify(m)); + accTail += 1; + } + kvStore.set('acceptanceQueue', JSON.stringify([accHead, accTail])); + + const stats = JSON.parse(kvStore.get('kernelStats')); + stats.runQueueLength += runQueue.length; + stats.runQueueLengthUp += runQueue.length; + stats.runQueueLengthMax = runQueue.length; + stats.acceptanceQueueLength += acceptanceQueue.length; + stats.acceptanceQueueLengthUp += acceptanceQueue.length; + stats.acceptanceQueueLengthMax = acceptanceQueue.length; + kvStore.set('kernelStats', JSON.stringify(stats)); + + await commit(); + + const data = { ...debug.dump().kvEntries }; + + // confirm that this state is too old for the kernel to use + await t.throwsAsync(() => makeSwingsetController(kernelStorage), { + message: /kernel DB is too old/, + }); + + // upgrade it + const { modified } = upgradeSwingset(kernelStorage); + t.true(modified); + + // there should be `upgradeEvents` in the kvStore + const expected = [ + { type: 'notify', vatID, kpid: p1 }, + { type: 'notify', vatID, kpid: p4 }, + { type: 'notify', vatID, kpid: p5 }, + ]; + t.deepEqual(JSON.parse(kvStore.get('upgradeEvents')), expected); + + // DB updated, now we should be ready to build the controller + const controller = await makeSwingsetController(kernelStorage); + + // but the events remain until the controller is told to inject them + t.deepEqual(JSON.parse(kvStore.get('upgradeEvents')), expected); + + // and then they're removed + controller.injectQueuedUpgradeEvents(); + t.is(kvStore.get('upgradeEvents'), undefined); + + // check state by mutating our dumped copy and then comparing + // against a new dump + + t.deepEqual({ a: 1, b: 2 }, { b: 2, a: 1 }); + // expect notifies for p1/p4/p5 in acceptance queue + const [head, tail] = JSON.parse(kvStore.get('acceptanceQueue')); + t.is(head, accHead); + t.is(tail, accTail + 3); + data.acceptanceQueue = JSON.stringify([accHead, accTail + 3]); + // note: we aren't JSON-parsing the entries, so this depends upon + // the properties being assigned in this exact order + const np1 = JSON.stringify({ type: 'notify', vatID, kpid: p1 }); + data[`acceptanceQueue.${tail - 3}`] = np1; + const np4 = JSON.stringify({ type: 'notify', vatID, kpid: p4 }); + data[`acceptanceQueue.${tail - 2}`] = np4; + const np5 = JSON.stringify({ type: 'notify', vatID, kpid: p5 }); + data[`acceptanceQueue.${tail - 1}`] = np5; + + // note: the in-RAM copy of kernelStats will have the elevated + // acceptance-queue counters, but these are not written to the + // kvStore until a crank is executed, so the data we're comparing + // won't see them + // + // stats = JSON.parse(data.kernelStats); + // stats.acceptanceQueueLength += 3; + // stats.acceptanceQueueLengthUp += 3; + // stats.acceptanceQueueLengthMax = stats.acceptanceQueueLength; + // data.kernelStats = JSON.stringify(stats); + + // the refcounts should now be one larger, because of the queued + // notifies + data[`${p1}.refCount`] = '2'; + data[`${p2}.refCount`] = '2'; + data[`${p3}.refCount`] = '2'; + data[`${p4}.refCount`] = '3'; + data[`${p5}.refCount`] = '2'; + data[`${p6}.refCount`] = '1'; + + // the version is bumped, indicating we don't need to perform this + // remediation again (because the bug is fixed and we won't be + // creating similar corruption in the future) + data.version = '3'; + + // no other state changes are expected + + const newData = { ...debug.dump().kvEntries }; + t.deepEqual(data, newData); +}); diff --git a/packages/SwingSet/test/upgrade/bootstrap-scripted-upgrade.js b/packages/SwingSet/test/upgrade/bootstrap-scripted-upgrade.js index c93cfa6b568..8e4ee211ab9 100644 --- a/packages/SwingSet/test/upgrade/bootstrap-scripted-upgrade.js +++ b/packages/SwingSet/test/upgrade/bootstrap-scripted-upgrade.js @@ -43,6 +43,8 @@ export const buildRootObject = () => { vatAdmin = await E(vats.vatAdmin).createVatAdminService(devices.vatAdmin); }, + nop: () => 0, + getMarker: () => marker, getImportSensors: () => importSensors, @@ -78,8 +80,16 @@ export const buildRootObject = () => { p1.catch(() => 'hush'); const p2 = E(ulrikRoot).getEternalPromise(); p2.catch(() => 'hush'); + const p3 = E(ulrikRoot).getWatchedDecidedPromise(); + + // Create our own promises that capture the rejection data in a + // way that the test harness can read, because the originals + // will be GCed when the rejection notifications finish + // delivery. + const p1w = Promise.resolve().then(() => p1); + const p2w = Promise.resolve().then(() => p2); - return { version, data, p1, p2, retain, ...parameters }; + return { version, data, p1, p2, p3, p1w, p2w, retain, ...parameters }; }, upgradeV2: async () => { @@ -145,8 +155,17 @@ export const buildRootObject = () => { resolve(`message for your predecessor, don't freak out`); const newDur = await E(ulrikRoot).getNewDurandal(); - - return { version, data, remoerr, newDur, upgradeResult, ...parameters }; + const watcherResult = await E(ulrikRoot).getWatcherResult(); + + return { + version, + data, + remoerr, + newDur, + upgradeResult, + watcherResult, + ...parameters, + }; }, buildV1WithLostKind: async () => { @@ -265,5 +284,26 @@ export const buildRootObject = () => { return [paramA, paramB]; }, + + buildV1WithVatParameters: async () => { + const bcap1 = await E(vatAdmin).getNamedBundleCap('ulrik1'); + const vp1 = { number: 1, marker }; + const options1 = { vatParameters: vp1 }; + const res = await E(vatAdmin).createVat(bcap1, options1); + ulrikAdmin = res.adminNode; + ulrikRoot = res.root; + const param1 = await E(ulrikRoot).getParameters(); + return param1; + }, + + upgradeV2WithVatParameters: async () => { + const bcap2 = await E(vatAdmin).getNamedBundleCap('ulrik2'); + const vp2 = { number: 2, marker }; + const options2 = { vatParameters: vp2 }; + await E(ulrikAdmin).upgrade(bcap2, options2); + const param2 = await E(ulrikRoot).getParameters(); + + return param2; + }, }); }; diff --git a/packages/SwingSet/test/upgrade/upgrade.test.js b/packages/SwingSet/test/upgrade/upgrade.test.js index aca1efbbd89..564453a375c 100644 --- a/packages/SwingSet/test/upgrade/upgrade.test.js +++ b/packages/SwingSet/test/upgrade/upgrade.test.js @@ -363,6 +363,11 @@ const testUpgrade = async (t, defaultManagerType, options = {}) => { // grab the promises that should be rejected const v1p1Kref = verifyPresence(v1result.p1); const v1p2Kref = verifyPresence(v1result.p2); + const v1p1wKref = verifyPresence(v1result.p1w); + const v1p2wKref = verifyPresence(v1result.p2w); + const v1p3Kref = verifyPresence(v1result.p3); + c.kpRegisterInterest(v1p1wKref); + c.kpRegisterInterest(v1p2wKref); // grab krefs for the exported durable/virtual objects to check their abandonment const retainedKrefs = objectMap(v1result.retain, obj => verifyPresence(obj)); const retainedNames = 'dur1 vir2 vir5 vir7 vc1 vc3 dc4 rem1 rem2 rem3'.split( @@ -461,16 +466,34 @@ const testUpgrade = async (t, defaultManagerType, options = {}) => { const newDurKref = verifyPresence(v2result.newDur); t.not(newDurKref, dur1Kref); - // the old version's non-durable promises should be rejected - t.is(c.kpStatus(v1p1Kref), 'rejected'); + // The old version's non-durable promises should be rejected. The + // original kpids will be GCed. Bug #9039 failed to GC them. + t.is(kvStore.get(`${v1p1Kref}.refCount`), undefined); + t.is(kvStore.get(`${v1p2Kref}.refCount`), undefined); + t.is(kvStore.get(`${vatID}.c.${v1p1Kref}`), undefined); + t.is(kvStore.get(`${vatID}.c.${v1p2Kref}`), undefined); + t.is(kvStore.get(`${v1p1Kref}.state`), undefined); + t.is(kvStore.get(`${v1p2Kref}.state`), undefined); + // but vat-bootstrap wraps them (with Promise.all() so we can + // examine their rejection data. + t.is(c.kpStatus(v1p1wKref), 'rejected'); const vatUpgradedError = { name: 'vatUpgraded', upgradeMessage: 'test upgrade', incarnationNumber: 0, }; - t.deepEqual(kunser(c.kpResolution(v1p1Kref)), vatUpgradedError); - t.is(c.kpStatus(v1p2Kref), 'rejected'); - t.deepEqual(kunser(c.kpResolution(v1p2Kref)), vatUpgradedError); + t.deepEqual(kunser(c.kpResolution(v1p1wKref)), vatUpgradedError); + t.is(c.kpStatus(v1p2wKref), 'rejected'); + t.deepEqual(kunser(c.kpResolution(v1p2wKref)), vatUpgradedError); + + // The local-promise watcher should have fired. If vat-upgrade is + // too aggressive about removing c-list entries (i.e. it doesn't + // check for self-subscription first), the kernel-provoked + // dispatch.notify won't be delivered, and the new incarnation won't + // fire the durable watcher. + t.deepEqual(v2result.watcherResult, ['reject', vatUpgradedError]); + // and the promise it was watching should be GCed + t.is(kvStore.get(`${v1p3Kref}.refCount`), undefined); // verify export abandonment/garbage collection/etc. // This used to be MUCH more extensive, but GC was cut to the bone @@ -930,3 +953,69 @@ test('failed vatAdmin upgrade - bad replacement code', async t => { // Just a taste to verify that the create went right; other tests check the rest t.deepEqual(v1result.data, ['some', 'data']); }); + +test('vat upgrade retains vatParameters', async t => { + const config = makeConfigFromPaths('bootstrap-scripted-upgrade.js', { + defaultManagerType: 'xs-worker', + defaultReapInterval: 'never', + bundlePaths: { + ulrik1: 'vat-ulrik-1.js', + ulrik2: 'vat-ulrik-2.js', + }, + }); + const kft = await initKernelForTest(t, t.context.data, config); + const { controller: c, kvStore } = kft; + + // create initial version + const kpid1 = c.queueToVatRoot('bootstrap', 'buildV1WithVatParameters', []); + await c.run(); + // incref=false to keep it from adding a refcount to the marker + const params1 = kunser(c.kpResolution(kpid1, { incref: false })); + // Free kpid1, else it will hold an extra refcount on the + // marker. The c.kpResolution() decremented the kpid1 refcount to 0, + // but we need to provoke a call to processRefcounts(), which only + // happens at end-of-crank + c.queueToVatRoot('bootstrap', 'nop', []); + // BOYD to get vat-admin to drop it's copy of the marker + c.reapAllVats(); + await c.run(); + + t.is(params1.number, 1); + const marker = params1.marker; + t.deepEqual(params1, { number: 1, marker }); + const kref = marker.getKref(); + + // confirm the vatParameters were recorded in kvStore + const vatID = JSON.parse(kvStore.get('vat.dynamicIDs'))[0]; + const vp1cd = kvStore.get(`${vatID}.vatParameters`); + const vp1 = kunser(JSON.parse(vp1cd)); + t.is(vp1.number, 1); + t.is(vp1.marker.getKref(), kref); + + // Ideally, the refcount should be 2: one for the importing vat's + // c-list, and a second for the retained vNN.vatParameters. But it + // will also get a refcount from device-vat-admin, as a conduit for + // the upgrade() call, because devices don't do GC. + + t.is(kvStore.get(`${kref}.refCount`), '3,3'); + + // upgrade + const kpid2 = c.queueToVatRoot('bootstrap', 'upgradeV2WithVatParameters', []); + await c.run(); + const params2 = kunser(c.kpResolution(kpid2, { incref: false })); + c.queueToVatRoot('bootstrap', 'nop', []); + c.reapAllVats(); + await c.run(); + + t.is(params2.number, 2); + t.is(params2.marker.getKref(), kref); + // kvStore should now hold the new vatParameters + const vp2cd = kvStore.get(`${vatID}.vatParameters`); + const vp2 = kunser(JSON.parse(vp2cd)); + t.is(vp2.number, 2); + t.is(vp2.marker.getKref(), kref); + + // same refcount: the old retained vatParameters should be swapped + // out + t.is(kvStore.get(`${kref}.refCount`), '3,3'); +}); diff --git a/packages/SwingSet/test/upgrade/vat-ulrik-1.js b/packages/SwingSet/test/upgrade/vat-ulrik-1.js index 89c08c286df..71f54b02030 100644 --- a/packages/SwingSet/test/upgrade/vat-ulrik-1.js +++ b/packages/SwingSet/test/upgrade/vat-ulrik-1.js @@ -8,6 +8,7 @@ import { defineKind, makeScalarBigMapStore, makeScalarBigWeakMapStore, + watchPromise, } from '@agoric/vat-data'; // we set up a lot of ephemeral, merely-virtual, and durable objects @@ -29,6 +30,20 @@ const holderMethods = { const makeVir = defineKind('virtual', initHolder, holderMethods); const makeDur = defineDurableKind(durandalHandle, initHolder, holderMethods); +const initWatcher = () => harden({ result: ['unresolved'] }); +const watcherMethods = { + onFulfilled: ({ state }, data) => (state.result = harden(['fulfill', data])), + onRejected: ({ state }, reason) => + (state.result = harden(['reject', reason])), + getResult: ({ state }) => state.result, +}; +const watcherHandle = makeKindHandle('watcher'); +const makeWatcher = defineDurableKind( + watcherHandle, + initWatcher, + watcherMethods, +); + // TODO: explore 'export modRetains' // eslint-disable-next-line no-unused-vars let modRetains; @@ -134,6 +149,12 @@ const buildExports = (baggage, imp) => { export const buildRootObject = (_vatPowers, vatParameters, baggage) => { const { promise: p1 } = makePromiseKit(); const { promise: p2 } = makePromiseKit(); + const { promise: p3 } = makePromiseKit(); + const watcher = makeWatcher(); + watchPromise(p3, watcher); + baggage.init('watcher', watcher); + baggage.init('wh', watcherHandle); + let heldPromise; let counter = 0; @@ -165,6 +186,7 @@ export const buildRootObject = (_vatPowers, vatParameters, baggage) => { }, getEternalPromiseInArray: () => [p1], getEternalPromise: () => p2, + getWatchedDecidedPromise: () => p3, makeLostKind: () => { makeKindHandle('unhandled'); diff --git a/packages/SwingSet/test/upgrade/vat-ulrik-2.js b/packages/SwingSet/test/upgrade/vat-ulrik-2.js index e843e2154e3..51412d8c31e 100644 --- a/packages/SwingSet/test/upgrade/vat-ulrik-2.js +++ b/packages/SwingSet/test/upgrade/vat-ulrik-2.js @@ -61,6 +61,18 @@ export const buildRootObject = (_vatPowers, vatParameters, baggage) => { } } + const watcherHandle = baggage.get('wh'); + const initWatcher = () => harden({ result: ['unresolved'] }); + const watcherMethods = { + onFulfilled: ({ state }, data) => + (state.result = harden(['fulfill', data])), + onRejected: ({ state }, reason) => + (state.result = harden(['reject', reason])), + getResult: ({ state }) => state.result, + }; + defineDurableKind(watcherHandle, initWatcher, watcherMethods); + const watcher = baggage.get('watcher'); + const root = Far('root', { getVersion: () => 'v2', getParameters: () => vatParameters, @@ -89,6 +101,7 @@ export const buildRootObject = (_vatPowers, vatParameters, baggage) => { return E(handler).ping(`ping ${counter}`); }, getNewDurandal: () => newDur, + getWatcherResult: () => watcher.getResult(), }); // buildRootObject() is allowed to return a Promise, as long as it fulfills // promptly (we added this in #5246 to allow ZCF to use the async diff --git a/packages/SwingSet/test/vat-admin/create-vat.test.js b/packages/SwingSet/test/vat-admin/create-vat.test.js index 40b3325cc58..0fe7547bd2d 100644 --- a/packages/SwingSet/test/vat-admin/create-vat.test.js +++ b/packages/SwingSet/test/vat-admin/create-vat.test.js @@ -276,13 +276,20 @@ test('createVat holds refcount', async t => { const { c, idRC, kernelStorage } = await doTestSetup(t, false, printSlog); const { kvStore } = kernelStorage; + // we typicaly get: v1-bootstrap, v2-vatAdmin, v6-export-held + // for (const name of JSON.parse(kvStore.get('vat.names'))) { + // console.log(`${kvStore.get(`vat.name.${name}`)}: ${name}`); + // } + // and d7-vatAdmin + // The bootstrap vat starts by fetching 'held' from vat-export-held, during // doTestSetup(), and retains it throughout the entire test. When we send // it refcount(), it will send VatAdminService.getBundleCap(), wait for the // response, then send VatAdminService.createVat(held). VAS will tell // device-vat-admin to push a create-vat event (including 'held') on the // run-queue. Some time later, the create-vat event reaches the front, and - // the new vat is created, receiving 'held' in vatParametesr. + // the new vat is created, receiving 'held' in vatParameters. The kernel + // retains a copy of those vatParameters for any future unilateral upgrade. // We want to check the refcounts during this sequence, to confirm that the // create-vat event holds a reference. Otherwise, 'held' might get GCed @@ -292,7 +299,7 @@ test('createVat holds refcount', async t => { // nothing ever decrements it: devices hold eternal refcounts on their // c-list entries, and nothing ever removes a device c-list entry. But some // day when we fix that, we'll rely upon the create-vat event's refcount to - // keep these things alive. + // keep these things alive.) // We happen to know that 'held' will be allocated ko27, but we use // `getHeld()` to obtain the real value in case e.g. a new built-in vat is @@ -365,9 +372,11 @@ test('createVat holds refcount', async t => { expectedRefcount -= 1; // kpid1 deleted, drops ref to 'held', now 2,2 // it also does syscall.send(createVat), which holds a new reference expectedRefcount += 1; // arg to 'createVat' - // now we should see 3,3: v1-bootstrap, the kpResolution pin, and the - // send(createVat) arguments. Two of these are c-lists. + // now we should see 3,3: v1-bootstrap, the kpResolution pin, and + // the send(createVat) arguments. For c-lists, it is present in v1.c + // (import) and v6.c (export) const r1 = findRefs(kvStore, held); + // console.log(`r1:`, JSON.stringify(r1)); t.deepEqual(r1.refcount, [expectedRefcount, expectedRefcount]); t.is(r1.refs.length, expectedCLists); // console.log(`---`); @@ -384,13 +393,13 @@ test('createVat holds refcount', async t => { await stepUntil(seeCreateVat); // console.log(`---`); - // We should see 5,5: v2-bootstrap, the kpResolution pin, vat-vat-admin, - // device-vat-admin, and the create-vat run-queue event. Three of these are - // c-lists. - expectedRefcount += 1; // vat-vat-admin c-list - expectedCLists += 1; // vat-vat-admin c-list - expectedRefcount += 1; // device-vat-admin c-list - expectedCLists += 1; // device-vat-admin c-list + // We should see 5,5: v1-bootstrap, the kpResolution pin, + // v2-vatAdmin, d7-vatAdmin, and the create-vat run-queue + // event. c-lists: v1/v2/d7 imports, v6 export + expectedRefcount += 1; // v2-vatAdmin c-list + expectedCLists += 1; // v2-vatAdmin c-list + expectedRefcount += 1; // d7-vatAdmin c-list + expectedCLists += 1; // d7-vatAdmin c-list const r2 = findRefs(kvStore, held); // console.log(`r2:`, JSON.stringify(r2)); @@ -400,8 +409,8 @@ test('createVat holds refcount', async t => { // Allow the vat-admin bringOutYourDead to be delivered, which // allows it to drop its reference to 'held'. - expectedRefcount -= 1; // vat-vat-admin retires - expectedCLists -= 1; // vat-vat-admin retires + expectedRefcount -= 1; // v2-vatAdmin retires + expectedCLists -= 1; // v2-vatAdmin retires // In addition, device-vat-admin does not yet participate in GC, and holds // its references forever. So this -=1 remains commented out until we @@ -415,25 +424,27 @@ test('createVat holds refcount', async t => { t.deepEqual(c.dump().reapQueue, []); // console.log(`---`); - // At this point we expected to see 5,5: v2-bootstrap, kpResolution pin, - // vat-vat-admin (because of the non-dropping bug), device-vat-admin - // (because of unimplemented GC), and the create-vat run-queue event. Two - // are c-lists. + // At this point we expected to see 4,4: v1-bootstrap, kpResolution + // pin, d7-vatAdmin (because of unimplemented GC), and the + // create-vat run-queue event. c-lists: v1/d7 imports, v6 export const r3 = findRefs(kvStore, held); // console.log(`r3:`, JSON.stringify(r3)); t.deepEqual(r3.refcount, [expectedRefcount, expectedRefcount]); t.is(r3.refs.length, expectedCLists); - // Allow create-vat to be processed, removing the create-vat reference and - // adding a reference from the new vat's c-list + // Allow create-vat to be processed, removing the create-vat + // reference and adding a reference from the new vat's c-list, plus + // a second reference from the retained vatParameters await c.step(); expectedRefcount -= 1; // remove send(createVat) argument expectedRefcount += 1; // new-vat c-list + expectedRefcount += 1; // retained vatParameters expectedCLists += 1; // new-vat c-list // console.log(`---`); - // v2-bootstrap, kpResolution pin, device-vat-admin, new-vat + // v1-bootstrap, kpResolution pin, d7-vatAdmin, new-vat, retained + // vatParameters const r4 = findRefs(kvStore, held); // console.log(`r4:`, JSON.stringify(r4)); t.deepEqual(r4.refcount, [expectedRefcount, expectedRefcount]); diff --git a/packages/SwingSet/test/vat-admin/slow-termination/bootstrap-slow-terminate.js b/packages/SwingSet/test/vat-admin/slow-termination/bootstrap-slow-terminate.js index 135124209ef..cff9c0ad02d 100644 --- a/packages/SwingSet/test/vat-admin/slow-termination/bootstrap-slow-terminate.js +++ b/packages/SwingSet/test/vat-admin/slow-termination/bootstrap-slow-terminate.js @@ -1,4 +1,5 @@ import { Far, E } from '@endo/far'; +import { makePromiseKit } from '@endo/promise-kit'; export function buildRootObject(_vatPowers) { let root; @@ -21,11 +22,22 @@ export function buildRootObject(_vatPowers) { } // set up 20 "dude exports, bootstrap imports" c-list entries - for (let i = 0; i < 20; i += 1) { myImports.push(await E(root).sendExport()); } + // also 10 imported promises + for (let i = 0; i < 10; i += 1) { + await E(root).acceptImports(makePromiseKit().promise); + } + + // and 10 exported promises + for (let i = 0; i < 10; i += 1) { + const p = E(root).forever(); + myImports.push(p); + p.catch(_err => 0); // hush + } + // ask dude to creates 20 vatstore entries (in addition to the // built-in liveslots stuff) await E(root).makeVatstore(20); diff --git a/packages/SwingSet/test/vat-admin/slow-termination/slow-termination.test.js b/packages/SwingSet/test/vat-admin/slow-termination/slow-termination.test.js index 336176024c6..2e5b4909fb7 100644 --- a/packages/SwingSet/test/vat-admin/slow-termination/slow-termination.test.js +++ b/packages/SwingSet/test/vat-admin/slow-termination/slow-termination.test.js @@ -63,7 +63,7 @@ async function doSlowTerminate(t, mode) { defaultManagerType: 'xsnap', defaultReapInterval: 'never', snapshotInitial: 2, // same as the default - snapshotInterval: 10, // ensure multiple spans+snapshots + snapshotInterval: 11, // ensure multiple spans+snapshots bootstrap: 'bootstrap', bundles: { dude: { @@ -117,13 +117,16 @@ async function doSlowTerminate(t, mode) { t.is(countCleanups, 0); // bootstrap adds a fair amount of vat-dude state: - // * we have c-list entries for 20 imports and 20 exports, each of - // which need two kvStore entries, so 80 kvStore total + // * we have c-list entries for 20 object imports and 20 object + // exports, each of which need two kvStore entries, so 80 kvStore + // total + // * c-list entries for 10 promise imports and 10 promise exports, + // * so 40 kvStore total // * the vat has created 20 baggage entries, all of which go into // the vatstore, adding 20 kvStore // * an empty vat has about 29 kvStore entries just to track // counters, the built-in collection types, baggage itself, etc - // * by sending 40-plus deliveries into an xsnap vat with + // * by sending 60-plus deliveries into an xsnap vat with // snapInterval=5, we get 8-ish transcript spans (7 old, 1 // current), and each old span generates a heap snapshot record // Slow vat termination means deleting these entries slowly. @@ -148,18 +151,28 @@ async function doSlowTerminate(t, mode) { .pluck() .get(vatID); - // 20*2 for imports, 21*2 for exports, 20*1 for vatstore = 102. - // Plus 21 for the usual liveslots stuff, and 6 for kernel stuff - // like vNN.source/options + // 20*2 for imports, 21*2 for exports, 20*2 for promises, 20*1 for + // vatstore = 142. Plus 21 for the usual liveslots stuff, and 7 for + // kernel stuff like vNN.source/options + const initialKVCount = 170; - t.is(remainingKV().length, 129); + t.is(remainingKV().length, initialKVCount); t.false(JSON.parse(kvStore.get('vats.terminated')).includes(vatID)); + // we get one span for snapshotInitial (=2), then a span every - // snapshotInterval (=10). Each non-current span creates a + // snapshotInterval (=11). Each non-current span creates a // snapshot. - t.is(remainingTranscriptSpans(), 6); - t.is(remainingTranscriptItems(), 59); - t.is(remainingSnapshots(), 5); + let expectedRemainingItems = 85; + let expectedRemainingSpans = 8; + let expectedRemainingSnapshots = 7; + + const checkTS = () => { + t.is(remainingTranscriptSpans(), expectedRemainingSpans); + t.is(remainingTranscriptItems(), expectedRemainingItems); + t.is(remainingSnapshots(), expectedRemainingSnapshots); + }; + checkTS(); + const remaining = () => remainingKV().length + remainingSnapshots() + @@ -167,11 +180,19 @@ async function doSlowTerminate(t, mode) { remainingTranscriptSpans(); // note: mode=dieHappy means we send one extra message to the vat, - // which adds a single transcript item (but this doesn't happen to trigger an extra span) + // but we've tuned snapshotInterval to avoid this triggering a BOYD + // and save/load snapshot, so it increases our expected transcript + // items, but leaves the spans/snapshots alone + if (mode === 'dieHappy') { + expectedRemainingItems += 1; + } const kpid = controller.queueToVatRoot('bootstrap', 'kill', [mode]); await controller.run(noCleanupPolicy); await commit(); + + checkTS(); + t.is(controller.kpStatus(kpid), 'fulfilled'); t.deepEqual( controller.kpResolution(kpid), @@ -182,7 +203,7 @@ async function doSlowTerminate(t, mode) { t.true(JSON.parse(kvStore.get('vats.terminated')).includes(vatID)); // no cleanups were allowed, so nothing should be removed yet t.truthy(kernelStorage.kvStore.get(`${vatID}.options`)); - t.is(remainingKV().length, 129); + t.is(remainingKV().length, initialKVCount); // now do a series of cleanup runs, each with budget=5 const clean = async (budget = 5) => { @@ -223,64 +244,73 @@ async function doSlowTerminate(t, mode) { // the non-clist kvstore keys should still be present t.truthy(kernelStorage.kvStore.get(`${vatID}.options`)); - // there are no imports, so this clean(budget.default=5) will delete - // the first five of our 47 other kv entries (20 vatstore plus 27 - // liveslots overhead + // there are no remaining imports, so this clean(budget.default=5) + // will delete the first five of our promise c-list entries, each + // with two kv entries + await cleanKV(10, 5); // 5 c-list promises + await cleanKV(10, 5); // 5 c-list promises + await cleanKV(10, 5); // 5 c-list promises + await cleanKV(10, 5); // 5 c-list promises + + // that finishes the promises, so the next clean will delete the + // first five of our 48 other kv entries (20 vatstore plus 28 + // overhead) await cleanKV(5, 5); // 5 other kv - // now there are 42 other kv entries left - t.is(remainingKV().length, 42); + // now there are 43 other kv entries left + t.is(remainingKV().length, 43); await cleanKV(5, 5); // 5 other kv - t.is(remainingKV().length, 37); + t.is(remainingKV().length, 38); await cleanKV(5, 5); // 5 other kv - t.is(remainingKV().length, 32); + t.is(remainingKV().length, 33); await cleanKV(5, 5); // 5 other kv - t.is(remainingKV().length, 27); + t.is(remainingKV().length, 28); await cleanKV(5, 5); // 5 other kv - t.is(remainingKV().length, 22); + t.is(remainingKV().length, 23); await cleanKV(5, 5); // 5 other kv - t.is(remainingKV().length, 17); - t.is(remainingSnapshots(), 5); + t.is(remainingKV().length, 18); + checkTS(); await cleanKV(5, 5); // 5 other kv - t.is(remainingKV().length, 12); + t.is(remainingKV().length, 13); await cleanKV(5, 5); // 5 other kv - t.is(remainingKV().length, 7); + t.is(remainingKV().length, 8); await cleanKV(5, 5); // 5 other kv - t.is(remainingKV().length, 2); - t.is(remainingSnapshots(), 5); + t.is(remainingKV().length, 3); + + checkTS(); - // there are two kv left, so this clean will delete those, then all 5 snapshots - await cleanKV(2, 7); // 2 final kv, and 5 snapshots + // there are three kv left, so this clean will delete those, then 5 + // of the 7 snapshots + await cleanKV(3, 8); // 3 final kv, and 5 snapshots t.deepEqual(remainingKV(), []); t.is(kernelStorage.kvStore.get(`${vatID}.options`), undefined); - t.is(remainingSnapshots(), 0); - - t.is(remainingTranscriptSpans(), 6); - let ts = 59; - if (mode === 'dieHappy') { - ts = 60; - } - t.is(remainingTranscriptItems(), ts); - - // the next clean (with the default budget of 5) will delete the - // five most recent transcript spans, starting with the isCurrent=1 - // one (which had 9 or 10 items), leaving just the earliest (which - // had 4, due to `snapshotInitial`) + expectedRemainingSnapshots -= 5; + checkTS(); + + // the next clean gets the 2 remaining snapshots, and the five most + // recent transcript spans, starting with the isCurrent=1 one (which + // had 9 or 10 items), leaving the earliest (which had 4, due to + // `snapshotInitial`) and the next two (with 13 each, due to + // snapshotInterval plus 2 for BOYD/snapshot overhead ). let cleanups = await clean(); - t.is(cleanups, 5); - t.is(remainingTranscriptSpans(), 1); - t.is(remainingTranscriptItems(), 4); + + t.is(cleanups, expectedRemainingSnapshots + 5); + expectedRemainingSnapshots = 0; + expectedRemainingItems = 4 + 13 + 13; + expectedRemainingSpans = 3; + checkTS(); // not quite done t.true(JSON.parse(kvStore.get('vats.terminated')).includes(vatID)); - // the final clean deletes the remaining span, and finishes by + // the final clean deletes the remaining spans, and finishes by // removing the "still being deleted" bookkeeping, and the .options cleanups = await clean(); - t.is(cleanups, 1); - t.is(remainingTranscriptSpans(), 0); - t.is(remainingTranscriptItems(), 0); + t.is(cleanups, 3); + expectedRemainingItems = 0; + expectedRemainingSpans = 0; + checkTS(); t.is(remaining(), 0); t.false(JSON.parse(kvStore.get('vats.terminated')).includes(vatID)); } diff --git a/packages/SwingSet/test/vat-admin/slow-termination/vat-slow-terminate.js b/packages/SwingSet/test/vat-admin/slow-termination/vat-slow-terminate.js index 78ec9a74e47..0ad2e1f5b4e 100644 --- a/packages/SwingSet/test/vat-admin/slow-termination/vat-slow-terminate.js +++ b/packages/SwingSet/test/vat-admin/slow-termination/vat-slow-terminate.js @@ -1,4 +1,5 @@ import { Far } from '@endo/far'; +import { makePromiseKit } from '@endo/promise-kit'; export function buildRootObject(vatPowers, _vatParameters, baggage) { const hold = []; @@ -7,6 +8,7 @@ export function buildRootObject(vatPowers, _vatParameters, baggage) { dieHappy: completion => vatPowers.exitVat(completion), sendExport: () => Far('dude export', {}), acceptImports: imports => hold.push(imports), + forever: () => makePromiseKit().promise, makeVatstore: count => { for (let i = 0; i < count; i += 1) { baggage.init(`key-${i}`, i); diff --git a/packages/SwingSet/test/vat-admin/terminate/bootstrap-die-cleanly.js b/packages/SwingSet/test/vat-admin/terminate/bootstrap-die-cleanly.js index 8b67c11b9a1..9be8f7af7e1 100644 --- a/packages/SwingSet/test/vat-admin/terminate/bootstrap-die-cleanly.js +++ b/packages/SwingSet/test/vat-admin/terminate/bootstrap-die-cleanly.js @@ -1,16 +1,24 @@ import { Far, E } from '@endo/far'; +import { makePromiseKit } from '@endo/promise-kit'; export function buildRootObject() { let dude; + const hold = []; const self = Far('root', { async bootstrap(vats, devices) { const vatMaker = E(vats.vatAdmin).createVatAdminService(devices.vatAdmin); - // create a dynamic vat, send it a message and let it respond, to make - // sure everything is working + // Create a dynamic vat, send it a message and let it respond, + // to make sure everything is working. Give them a promise to + // follow, to check that its refcount is cleaned up. dude = await E(vatMaker).createVatByName('dude'); await E(dude.root).foo(1); + const p = makePromiseKit().promise; + await E(dude.root).holdPromise(p); + const p2 = E(dude.root).never(); + p2.catch(_err => 'hush'); + hold.push(p2); return 'bootstrap done'; }, async phase2() { diff --git a/packages/SwingSet/test/vat-admin/terminate/terminate.test.js b/packages/SwingSet/test/vat-admin/terminate/terminate.test.js index a4759943e47..f764441aac4 100644 --- a/packages/SwingSet/test/vat-admin/terminate/terminate.test.js +++ b/packages/SwingSet/test/vat-admin/terminate/terminate.test.js @@ -471,6 +471,23 @@ test.serial('dead vat state removed', async t => { t.is(kvStore.get('vat.dynamicIDs'), '["v6"]'); t.is(kvStore.get('ko26.owner'), 'v6'); t.is(Array.from(enumeratePrefixedKeys(kvStore, 'v6.')).length > 10, true); + // find the two promises in the new vat's c-list, they should both + // have refCount=2 (one for bootstrap, one for the dynamic vat) + let decidedKPID; + let subscribedKPID; + for (const key of enumeratePrefixedKeys(kvStore, 'v6.')) { + if (key.startsWith('v6.c.kp')) { + const kpid = key.slice('v6.c.'.length); + const refCount = Number(kvStore.get(`${kpid}.refCount`)); + const decider = kvStore.get(`${kpid}.decider`); + if (decider === 'v6') { + decidedKPID = kpid; + } else { + subscribedKPID = kpid; + } + t.is(refCount, 2, `${kpid}.refCount=${refCount}, not 2`); + } + } const beforeDump = debug.dump(true); t.truthy(beforeDump.transcripts.v6); t.truthy(beforeDump.snapshots.v6); @@ -483,6 +500,16 @@ test.serial('dead vat state removed', async t => { const afterDump = debug.dump(true); t.falsy(afterDump.transcripts.v6); t.falsy(afterDump.snapshots.v6); + // the promise that v6 was deciding will be removed from v6's + // c-list, rejected by the kernel, and the reject notification to + // vat-bootstrap will remove it from that c-list, so the end state + // should be no references, and a completely deleted kernel promise + // table entry + t.is(kvStore.get(`${decidedKPID}.refCount`), undefined); + // the promise that v6 received from vat-bootstrap should be removed + // from v6's c-list, but it is still being exported by + // vat-bootstrap, so the refcount should finish at 1 + t.is(kvStore.get(`${subscribedKPID}.refCount`), '1'); }); test.serial('terminate with presence', async t => { diff --git a/packages/SwingSet/test/vat-admin/terminate/vat-dude-terminate.js b/packages/SwingSet/test/vat-admin/terminate/vat-dude-terminate.js index 42da13a3cfb..af731fb682b 100644 --- a/packages/SwingSet/test/vat-admin/terminate/vat-dude-terminate.js +++ b/packages/SwingSet/test/vat-admin/terminate/vat-dude-terminate.js @@ -6,12 +6,18 @@ export function buildRootObject(vatPowers) { // to be cut off const { testLog } = vatPowers; + const hold = []; + return Far('root', { foo(arg) { testLog(`FOO ${arg}`); return `FOO SAYS ${arg}`; }, + holdPromise(p) { + hold.push(p); + }, + never() { return makePromiseKit().promise; // never fires }, diff --git a/packages/boot/test/bootstrapTests/ec-membership-update.test.ts b/packages/boot/test/bootstrapTests/ec-membership-update.test.ts index 164b7f710e8..3865cb87432 100644 --- a/packages/boot/test/bootstrapTests/ec-membership-update.test.ts +++ b/packages/boot/test/bootstrapTests/ec-membership-update.test.ts @@ -59,6 +59,23 @@ export const makeZoeTestContext = async t => { await EV.vat('bootstrap').consumeItem('vaultFactoryKit'); console.timeLog('DefaultTestContext', 'vaultFactoryKit'); + // replaceElectorate relies on these values from the auction upgrade. Insert + // them manually since this bootstrap test doesn't run the auction upgrade. + const governedKits = await EV.vat('bootstrap').consumeItem( + 'governedContractKits', + ); + const auctioneerKit = await EV.vat('bootstrap').consumeItem('auctioneerKit'); + const auctionInstance = await auctioneerKit.instance; + const aKit = await EV(governedKits).get(auctionInstance); + await EV.vat('bootstrap').produceItem( + 'auctionUpgradeNewInstance', + aKit.instance, + ); + await EV.vat('bootstrap').produceItem( + 'auctionUpgradeNewGovCreator', + aKit.governorCreatorFacet, + ); + // has to be late enough for agoricNames data to have been published const agoricNamesRemotes = makeAgoricNamesRemotesFromFakeStorage(storage); console.timeLog('DefaultTestContext', 'agoricNamesRemotes'); diff --git a/packages/builders/scripts/inter-protocol/replace-electorate-core.js b/packages/builders/scripts/inter-protocol/replace-electorate-core.js index 6b2fbbac1f1..4545cbacfbc 100644 --- a/packages/builders/scripts/inter-protocol/replace-electorate-core.js +++ b/packages/builders/scripts/inter-protocol/replace-electorate-core.js @@ -16,25 +16,6 @@ import { makeHelpers } from '@agoric/deploy-script-support'; import { getManifestForReplaceAllElectorates } from '@agoric/inter-protocol/src/proposals/replaceElectorate.js'; -/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */ -export const defaultProposalBuilder = async ({ publishRef, install }, opts) => { - return harden({ - sourceSpec: '@agoric/inter-protocol/src/proposals/replaceElectorate.js', - getManifestCall: [ - getManifestForReplaceAllElectorates.name, - { - ...opts, - economicCommitteeRef: publishRef( - install( - '@agoric/governance/src/committee.js', - '../bundles/bundle-committee.js', - ), - ), - }, - ], - }); -}; - const configurations = { MAINNET: { committeeName: 'Economic Committee', @@ -68,10 +49,12 @@ const configurations = { committeeName: 'Economic Committee', voterAddresses: { gov1: 'agoric1ee9hr0jyrxhy999y755mp862ljgycmwyp4pl7q', + gov2: 'agoric1wrfh296eu2z34p6pah7q04jjuyj3mxu9v98277', + gov4: 'agoric1c9gyu460lu70rtcdp95vummd6032psmpdx7wdy', }, highPrioritySendersConfig: { - addressesToAdd: [], - addressesToRemove: ['agoric1wrfh296eu2z34p6pah7q04jjuyj3mxu9v98277'], + addressesToAdd: ['agoric1c9gyu460lu70rtcdp95vummd6032psmpdx7wdy'], + addressesToRemove: ['agoric1ydzxwh6f893jvpaslmaz6l8j2ulup9a7x8qvvq'], }, }, BOOTSTRAP_TEST: { @@ -94,21 +77,39 @@ const configurations = { const { keys } = Object; const Usage = `agoric run replace-electorate-core.js ${keys(configurations).join(' | ')}`; +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */ +export const defaultProposalBuilder = async ({ publishRef, install }, opts) => { + const config = configurations[opts.variant]; + if (!config) { + console.error(Usage); + process.exit(1); + } + return harden({ + sourceSpec: '@agoric/inter-protocol/src/proposals/replaceElectorate.js', + getManifestCall: [ + getManifestForReplaceAllElectorates.name, + { + ...config, + economicCommitteeRef: publishRef( + install( + '@agoric/governance/src/committee.js', + '../bundles/bundle-committee.js', + ), + ), + }, + ], + }); +}; /** @type {import('@agoric/deploy-script-support/src/externalTypes.js').DeployScriptFunction} */ export default async (homeP, endowments) => { const { scriptArgs } = endowments; const variant = scriptArgs?.[0]; - const config = configurations[variant]; - if (!config) { - console.error(Usage); - process.exit(1); - } - console.log('replace-committee', scriptArgs, config); + console.log('replace-committee', scriptArgs, variant); const { writeCoreEval } = await makeHelpers(homeP, endowments); await writeCoreEval(`replace-committee-${variant}`, (utils, opts) => - defaultProposalBuilder(utils, { ...opts, ...config }), + defaultProposalBuilder(utils, { ...opts, variant }), ); }; diff --git a/packages/builders/scripts/inter-protocol/updatePriceFeeds.js b/packages/builders/scripts/inter-protocol/updatePriceFeeds.js index 866a4865882..c6a96157abf 100644 --- a/packages/builders/scripts/inter-protocol/updatePriceFeeds.js +++ b/packages/builders/scripts/inter-protocol/updatePriceFeeds.js @@ -1,5 +1,3 @@ -/* global process */ - import { makeHelpers } from '@agoric/deploy-script-support'; import { getManifestForPriceFeeds } from '@agoric/inter-protocol/src/proposals/deploy-price-feeds.js'; @@ -38,14 +36,21 @@ const configurations = { }, }; +const { keys } = Object; +const Usage = `agoric run updatePriceFeed.js ${keys(configurations).join(' | ')}`; /** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */ export const defaultProposalBuilder = async ({ publishRef, install }, opts) => { + const config = configurations[opts.variant]; + if (!config) { + console.error(Usage); + throw Error(Usage); + } return harden({ sourceSpec: '@agoric/inter-protocol/src/proposals/deploy-price-feeds.js', getManifestCall: [ getManifestForPriceFeeds.name, { - ...opts, + ...config, priceAggregatorRef: publishRef( install( '@agoric/inter-protocol/src/price/fluxAggregatorContract.js', @@ -63,26 +68,15 @@ export const defaultProposalBuilder = async ({ publishRef, install }, opts) => { }); }; -const { keys } = Object; -const Usage = `agoric run updatePriceFeed.js ${keys(configurations).join(' | ')}`; - /** @type {import('@agoric/deploy-script-support/src/externalTypes.js').DeployScriptFunction} */ export default async (homeP, endowments) => { const { scriptArgs } = endowments; - assert(scriptArgs, 'expected script args endowment'); - const config = configurations[scriptArgs[0]]; - if (!config) { - console.error(Usage); - process.exit(1); - } - console.log('UPPrices', scriptArgs, config); + const variant = scriptArgs?.[0]; + console.log('updatePriceFeeds', scriptArgs, variant); const { writeCoreEval } = await makeHelpers(homeP, endowments); - const match = scriptArgs[0].match(/UNRELEASED_(.*)/); - const variant = match ? match[1] : scriptArgs; - await writeCoreEval(`gov-price-feeds-${variant}`, (utils, opts) => - defaultProposalBuilder(utils, { ...opts, ...config }), + defaultProposalBuilder(utils, { ...opts, variant }), ); }; diff --git a/packages/cosmic-swingset/src/launch-chain.js b/packages/cosmic-swingset/src/launch-chain.js index c18f5fa5d7c..c8b113d5528 100644 --- a/packages/cosmic-swingset/src/launch-chain.js +++ b/packages/cosmic-swingset/src/launch-chain.js @@ -215,7 +215,7 @@ export async function buildSwingset( } const pendingCoreProposals = await ensureSwingsetInitialized(); - upgradeSwingset(kernelStorage); + const { modified } = upgradeSwingset(kernelStorage); const controller = await makeSwingsetController( kernelStorage, deviceEndowments, @@ -236,6 +236,7 @@ export async function buildSwingset( return { coreProposals: pendingCoreProposals, controller, + kernelHasUpgradeEvents: modified, mb: mailboxDevice, bridgeInbound: bridgeDevice.deliverInbound, timer: timerDevice, @@ -408,6 +409,7 @@ export async function launch({ const { coreProposals: bootstrapCoreProposals, controller, + kernelHasUpgradeEvents, mb, bridgeInbound, timer, @@ -510,6 +512,14 @@ export async function launch({ await commit(); } + async function doKernelUpgradeEvents(inboundNum) { + controller.writeSlogObject({ + type: 'cosmic-swingset-inject-kernel-upgrade-events', + inboundNum, + }); + controller.injectQueuedUpgradeEvents(); + } + async function deliverInbound(sender, messages, ack, inboundNum) { Array.isArray(messages) || Fail`inbound given non-Array: ${messages}`; controller.writeSlogObject({ @@ -629,6 +639,11 @@ export async function launch({ break; } + case ActionType.KERNEL_UPGRADE_EVENTS: { + p = doKernelUpgradeEvents(inboundNum); + break; + } + case ActionType.INSTALL_BUNDLE: { p = installBundle(action.bundle); break; @@ -934,6 +949,22 @@ export async function launch({ await doBootstrap(action); } + // The reboot-time upgradeSwingset() may have generated some + // remediation events that need to be injected at the right + // time (after catchup, before proposals). Push them onto + // runThisBlock before anything else goes there. + if (kernelHasUpgradeEvents) { + isBootstrap || + upgradeDetails || + Fail`Unexpected kernel upgrade events outside of consensus start`; + const txHash = 'x/kernel-upgrade-events'; + const context = { blockHeight, txHash, msgIdx: 0 }; + runThisBlock.push({ + action: { type: ActionType.KERNEL_UPGRADE_EVENTS }, + context, + }); + } + // Concatenate together any pending core proposals from chain bootstrap // with any from this inbound init action, then execute them all. const coreProposals = mergeCoreProposals( diff --git a/packages/governance/src/committee.js b/packages/governance/src/committee.js index 8eb967d75fb..78441d926ae 100644 --- a/packages/governance/src/committee.js +++ b/packages/governance/src/committee.js @@ -1,6 +1,5 @@ import { makeStoredPublishKit } from '@agoric/notifier'; import { M } from '@agoric/store'; -import { natSafeMath } from '@agoric/zoe/src/contractSupport/index.js'; import { E } from '@endo/eventual-send'; import { StorageNodeShape } from '@agoric/internal'; @@ -20,8 +19,6 @@ import { prepareVoterKit } from './voterKit.js'; * @import {ElectorateCreatorFacet, CommitteeElectoratePublic, QuestionDetails, OutcomeRecord, AddQuestion} from './types.js'; */ -const { ceilDivide } = natSafeMath; - /** * @typedef { ElectorateCreatorFacet & { * getVoterInvitations: () => Promise>[] @@ -139,7 +136,7 @@ export const start = (zcf, privateArgs, baggage) => { const quorumThreshold = quorumRule => { switch (quorumRule) { case QuorumRule.MAJORITY: - return ceilDivide(committeeSize, 2); + return Math.ceil((committeeSize + 1) / 2); case QuorumRule.ALL: return committeeSize; case QuorumRule.NO_QUORUM: diff --git a/packages/governance/test/unitTests/committee.test.js b/packages/governance/test/unitTests/committee.test.js index 5e50c9d300e..39ff48d7832 100644 --- a/packages/governance/test/unitTests/committee.test.js +++ b/packages/governance/test/unitTests/committee.test.js @@ -27,7 +27,9 @@ const dirname = path.dirname(new URL(import.meta.url).pathname); const electorateRoot = `${dirname}/../../src/committee.js`; const counterRoot = `${dirname}/../../src/binaryVoteCounter.js`; -const setupContract = async () => { +const setupContract = async ( + terms = { committeeName: 'illuminati', committeeSize: 13 }, +) => { const zoe = makeZoeForTest(); const mockChainStorageRoot = makeMockChainStorageRoot(); @@ -45,7 +47,6 @@ const setupContract = async () => { E(zoe).install(electorateBundle), E(zoe).install(counterBundle), ]); - const terms = { committeeName: 'illuminati', committeeSize: 13 }; const electorateStartResult = await E(zoe).startInstance( electorateInstallation, {}, @@ -56,7 +57,12 @@ const setupContract = async () => { }, ); - return { counterInstallation, electorateStartResult, mockChainStorageRoot }; + return { + counterInstallation, + electorateStartResult, + mockChainStorageRoot, + zoe, + }; }; test('committee-open no questions', async t => { @@ -233,3 +239,164 @@ test('committee-open question:mixed, with snapshot', async t => { }; await documentStorageSchema(t, mockChainStorageRoot, doc); }); + +const setUpVoterAndVote = async (invitation, zoe, qHandle, choice) => { + const seat = E(zoe).offer(invitation); + const { voter } = E.get(E(seat).getOfferResult()); + return E(voter).castBallotFor(qHandle, [choice]); +}; + +test('committee-tie outcome', async t => { + const { + electorateStartResult: { creatorFacet }, + counterInstallation: counter, + zoe, + } = await setupContract({ committeeName: 'halfDozen', committeeSize: 6 }); + + const timer = buildZoeManualTimer(t.log); + + const positions = [harden({ text: 'guilty' }), harden({ text: 'innocent' })]; + const questionSpec = coerceQuestionSpec( + harden({ + method: ChoiceMethod.UNRANKED, + issue: { text: 'guilt' }, + positions, + electionType: ElectionType.SURVEY, + maxChoices: 1, + maxWinners: 1, + closingRule: { + timer, + deadline: 2n, + }, + quorumRule: QuorumRule.MAJORITY, + tieOutcome: positions[1], + }), + ); + + const qResult = await E(creatorFacet).addQuestion(counter, questionSpec); + + const invites = await E(creatorFacet).getVoterInvitations(); + const votes = []; + for (const i of [...Array(6).keys()]) { + votes.push( + setUpVoterAndVote( + invites[i], + zoe, + qResult.questionHandle, + positions[i % 2], + ), + ); + } + + await Promise.all(votes); + await E(timer).tick(); + await E(timer).tick(); + + // if half vote each way, the tieOutcome prevails + await E.when(E(qResult.publicFacet).getOutcome(), async outcomes => + t.deepEqual(outcomes, { + text: 'innocent', + }), + ); +}); + +test('committee-half vote', async t => { + const { + electorateStartResult: { creatorFacet }, + counterInstallation: counter, + zoe, + } = await setupContract({ committeeName: 'halfDozen', committeeSize: 6 }); + + const timer = buildZoeManualTimer(t.log); + + const positions = [harden({ text: 'guilty' }), harden({ text: 'innocent' })]; + const questionSpec = coerceQuestionSpec( + harden({ + method: ChoiceMethod.UNRANKED, + issue: { text: 'guilt' }, + positions, + electionType: ElectionType.SURVEY, + maxChoices: 1, + maxWinners: 1, + closingRule: { + timer, + deadline: 2n, + }, + quorumRule: QuorumRule.MAJORITY, + tieOutcome: positions[1], + }), + ); + + const qResult = await E(creatorFacet).addQuestion(counter, questionSpec); + + const invites = await E(creatorFacet).getVoterInvitations(); + const votes = []; + for (const i of [...Array(3).keys()]) { + votes.push( + setUpVoterAndVote(invites[i], zoe, qResult.questionHandle, positions[0]), + ); + } + + await Promise.all(votes); + await E(timer).tick(); + await E(timer).tick(); + + // if only half the voters vote, there is no quorum + await E.when( + E(qResult.publicFacet).getOutcome(), + async _outcomes => { + t.fail('expect no quorum'); + }, + e => { + t.is(e, 'No quorum'); + }, + ); +}); + +test('committee-half plus one vote', async t => { + const { + electorateStartResult: { creatorFacet }, + counterInstallation: counter, + zoe, + } = await setupContract({ committeeName: 'halfDozen', committeeSize: 6 }); + + const timer = buildZoeManualTimer(t.log); + + const positions = [harden({ text: 'guilty' }), harden({ text: 'innocent' })]; + const questionSpec = coerceQuestionSpec( + harden({ + method: ChoiceMethod.UNRANKED, + issue: { text: 'guilt' }, + positions, + electionType: ElectionType.SURVEY, + maxChoices: 1, + maxWinners: 1, + closingRule: { + timer, + deadline: 2n, + }, + quorumRule: QuorumRule.MAJORITY, + tieOutcome: positions[1], + }), + ); + + const qResult = await E(creatorFacet).addQuestion(counter, questionSpec); + + const invites = await E(creatorFacet).getVoterInvitations(); + const votes = []; + for (const i of [...Array(4).keys()]) { + votes.push( + setUpVoterAndVote(invites[i], zoe, qResult.questionHandle, positions[0]), + ); + } + + await Promise.all(votes); + await E(timer).tick(); + await E(timer).tick(); + + await E.when(E(qResult.publicFacet).getOutcome(), async outcomes => + t.deepEqual(outcomes, { + text: 'guilty', + }), + ); +}); diff --git a/packages/inter-protocol/src/proposals/add-auction.js b/packages/inter-protocol/src/proposals/add-auction.js index 89769e4cb25..abecb842390 100644 --- a/packages/inter-protocol/src/proposals/add-auction.js +++ b/packages/inter-protocol/src/proposals/add-auction.js @@ -9,6 +9,7 @@ const trace = makeTracer('NewAuction', true); /** * @typedef {PromiseSpaceOf<{ * auctionUpgradeNewInstance: Instance; + * auctionUpgradeNewGovCreator: any; * newContractGovBundleId: string; * }>} interlockPowers */ @@ -33,12 +34,14 @@ export const addAuction = async ( chainTimerService, economicCommitteeCreatorFacet: electorateCreatorFacet, econCharterKit, + governedContractKits: governedContractKitsP, priceAuthority8400, zoe, }, produce: { auctioneerKit: produceAuctioneerKit, auctionUpgradeNewInstance, + auctionUpgradeNewGovCreator, newContractGovBundleId, }, instance: { @@ -197,7 +200,10 @@ export const addAuction = async ( governedInstance, ); + const governedContractKits = await governedContractKitsP; + governedContractKits.init(kit.instance, kit); auctionUpgradeNewInstance.resolve(governedInstance); + auctionUpgradeNewGovCreator.resolve(kit.governorCreatorFacet); newContractGovBundleId.resolve(contractGovernorBundle.bundleID); }; @@ -211,12 +217,14 @@ export const ADD_AUCTION_MANIFEST = harden({ chainTimerService: true, econCharterKit: true, economicCommitteeCreatorFacet: true, + governedContractKits: true, priceAuthority8400: true, zoe: true, }, produce: { auctioneerKit: true, auctionUpgradeNewInstance: true, + auctionUpgradeNewGovCreator: true, newContractGovBundleId: true, }, instance: { diff --git a/packages/inter-protocol/src/proposals/deploy-price-feeds.js b/packages/inter-protocol/src/proposals/deploy-price-feeds.js index a80c50edf43..f98f62223f2 100644 --- a/packages/inter-protocol/src/proposals/deploy-price-feeds.js +++ b/packages/inter-protocol/src/proposals/deploy-price-feeds.js @@ -222,6 +222,9 @@ export const deployPriceFeeds = async (powers, config) => { ); const { priceAuthorityAdmin, priceAuthority } = powers.consume; + + trace({ oracleAddresses }); + trace({ inBrandNames }); for (const inBrandName of inBrandNames) { const AGORIC_INSTANCE_NAME = oracleBrandFeedName(inBrandName, outBrandName); const brandIn = await ensureOracleBrand(powers, { @@ -263,6 +266,7 @@ export const deployPriceFeeds = async (powers, config) => { options: { scaledPARef }, }); + trace('resolving priceAuthority8400'); // cf. #8400 QuotePayments storage leak powers.produce.priceAuthority8400.resolve(priceAuthority); }; diff --git a/packages/inter-protocol/src/proposals/replaceElectorate.js b/packages/inter-protocol/src/proposals/replaceElectorate.js index ede08ff8cb4..cb862be5a67 100644 --- a/packages/inter-protocol/src/proposals/replaceElectorate.js +++ b/packages/inter-protocol/src/proposals/replaceElectorate.js @@ -305,24 +305,42 @@ const startNewEconCharter = async ({ return startResult; }; +/** + * @typedef {PromiseSpaceOf<{ + * auctionUpgradeNewInstance: Instance; + * auctionUpgradeNewGovCreator: any; + * }>} interlockPowers + */ + /** * Adds governors to an existing Economic Committee Charter * - * - @param {EconomyBootstrapPowers} powers - The resources and capabilities - * required to start the committee. + * @param {EconomyBootstrapPowers & interlockPowers} powers + * + * - The resources and capabilities required to start the committee. * * @param {{ * options: { * econCharterKit: EconCharterStartResult; * }; - * }} config + * }} options * - Configuration object containing the name and size of the committee. * * @returns {Promise} A promise that resolves once all the governors have * been successfully added to the economic charter */ const addGovernorsToEconCharter = async ( - { consume: { psmKit, governedContractKits } }, + { + consume: { + auctionUpgradeNewInstance: auctionUpgradeNewInstanceP, + auctionUpgradeNewGovCreator: auctionUpgradeNewGovCreatorP, + psmKit, + governedContractKits, + }, + produce: { + auctionUpgradeNewGovCreator: auctionUpgradeNewGovCreatorProduce, + }, + }, { options: { econCharterKit } }, ) => { const { creatorFacet: ecCreatorFacet } = E.get(econCharterKit); @@ -334,23 +352,45 @@ const addGovernorsToEconCharter = async ( } const governedContractKitMap = await governedContractKits; - for (const { instance, governorCreatorFacet, label, } of governedContractKitMap.values()) { - await E(ecCreatorFacet).addInstance(instance, governorCreatorFacet, label); + // The auctioneer was updated in this same release, getting values directly + // (there might be more than one auctioneer instance, but the others don't + // need to be registered.) + if (label !== 'auctioneer') { + await E(ecCreatorFacet).addInstance( + instance, + governorCreatorFacet, + label, + ); + } } + + const [auctionUpgradeNewInstance, auctionUpgradeNewGovCreator] = + await Promise.all([ + auctionUpgradeNewInstanceP, + auctionUpgradeNewGovCreatorP, + ]); + // reset after use. auctionUpgradeNewInstance is reset by upgrade-vault.js + auctionUpgradeNewGovCreatorProduce.reset(); + + await E(ecCreatorFacet).addInstance( + auctionUpgradeNewInstance, + auctionUpgradeNewGovCreator, + 'auctioneer', + ); }; /** * Replaces the electorate for governance contracts by creating a new Economic * Committee and updating contracts with the new electorate's creator facet. * - * @param {EconomyBootstrapPowers} permittedPowers - The resources and - * capabilities needed for operations, including access to governance - * contracts and the PSM kit. + * @param {EconomyBootstrapPowers & interlockPowers} permittedPowers - The + * resources and capabilities needed for operations, including access to + * governance contracts and the PSM kit. * @param {{ * options: { * committeeName: string; @@ -445,6 +485,8 @@ export const getManifestForReplaceAllElectorates = async ( manifest: { [replaceAllElectorates.name]: { consume: { + auctionUpgradeNewGovCreator: true, + auctionUpgradeNewInstance: true, psmKit: true, governedContractKits: true, chainStorage: true, @@ -458,6 +500,7 @@ export const getManifestForReplaceAllElectorates = async ( econCharterKit: true, economicCommitteeKit: true, economicCommitteeCreatorFacet: true, + auctionUpgradeNewGovCreator: true, }, installation: { consume: { diff --git a/packages/inter-protocol/src/proposals/upgrade-vaults.js b/packages/inter-protocol/src/proposals/upgrade-vaults.js index ab516d4bc37..febaa8cfa35 100644 --- a/packages/inter-protocol/src/proposals/upgrade-vaults.js +++ b/packages/inter-protocol/src/proposals/upgrade-vaults.js @@ -39,9 +39,6 @@ export const upgradeVaults = async ( auctionUpgradeNewInstance: auctionUpgradeNewInstanceProducer, newContractGovBundleId: newContractGovBundleIdErasor, }, - instance: { - consume: { auctioneer: auctioneerInstanceP }, - }, }, { options: { VaultFactoryBundle: vaultBundleRef } }, ) => { @@ -52,12 +49,7 @@ export const upgradeVaults = async ( await priceAuthority8400; - const [auctionOldInstance, auctionNewInstance] = await Promise.all([ - auctioneerInstanceP, - auctionUpgradeNewInstance, - ]); - auctionOldInstance !== auctionNewInstance || - Fail`Auction instance didn't change`; + const auctionNewInstance = await auctionUpgradeNewInstance; auctionUpgradeNewInstanceProducer.reset(); const publicFacet = E(zoe).getPublicFacet(auctionNewInstance); /** @type {import('@agoric/inter-protocol/src/auction/scheduler.js').FullSchedule} */ @@ -202,7 +194,6 @@ export const getManifestForUpgradeVaults = async ( auctionUpgradeNewInstance: uV, newContractGovBundleId: uV, }, - instance: { consume: { auctioneer: uV } }, }, }, installations: { VaultFactory: restoreRef(VaultFactoryRef) }, diff --git a/packages/internal/src/action-types.js b/packages/internal/src/action-types.js index f07a3fd5592..30e363f5117 100644 --- a/packages/internal/src/action-types.js +++ b/packages/internal/src/action-types.js @@ -16,3 +16,4 @@ export const WALLET_ACTION = 'WALLET_ACTION'; export const WALLET_SPEND_ACTION = 'WALLET_SPEND_ACTION'; export const INSTALL_BUNDLE = 'INSTALL_BUNDLE'; export const VTRANSFER_IBC_EVENT = 'VTRANSFER_IBC_EVENT'; +export const KERNEL_UPGRADE_EVENTS = 'KERNEL_UPGRADE_EVENTS'; diff --git a/packages/internal/src/node/fs-stream.js b/packages/internal/src/node/fs-stream.js index eb7f335f45c..8e7ae36a48b 100644 --- a/packages/internal/src/node/fs-stream.js +++ b/packages/internal/src/node/fs-stream.js @@ -1,8 +1,11 @@ import { createWriteStream } from 'node:fs'; +import process from 'node:process'; import { open } from 'node:fs/promises'; /** - * @param {import('fs').ReadStream | import('fs').WriteStream} stream + * @param {import('fs').ReadStream + * | import('fs').WriteStream + * | import('net').Socket} stream * @returns {Promise} */ export const fsStreamReady = stream => @@ -48,45 +51,51 @@ export const makeFsStreamWriter = async filePath => { return undefined; } - const handle = await open(filePath, 'a'); + const handle = await (filePath !== '-' ? open(filePath, 'a') : undefined); - const stream = createWriteStream(noPath, { fd: handle.fd }); + const stream = handle + ? createWriteStream(noPath, { fd: handle.fd }) + : process.stdout; await fsStreamReady(stream); let flushed = Promise.resolve(); let closed = false; - const write = async data => { - if (closed) { - throw Error('Stream closed'); - } - - /** @type {Promise} */ - const written = new Promise((resolve, reject) => { - stream.write(data, err => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); + const updateFlushed = p => { flushed = flushed.then( - () => written, - async err => - Promise.reject( - written.then( - () => err, - writtenError => AggregateError([err, writtenError]), - ), + () => p, + err => + p.then( + () => Promise.reject(err), + pError => + Promise.reject( + pError !== err ? AggregateError([err, pError]) : err, + ), ), ); + flushed.catch(() => {}); + }; + + const write = async data => { + /** @type {Promise} */ + const written = closed + ? Promise.reject(Error('Stream closed')) + : new Promise((resolve, reject) => { + stream.write(data, err => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); + updateFlushed(written); return written; }; const flush = async () => { await flushed; - await handle.sync().catch(err => { + await handle?.sync().catch(err => { if (err.code === 'EINVAL') { return; } @@ -95,10 +104,14 @@ export const makeFsStreamWriter = async filePath => { }; const close = async () => { + // TODO: Consider creating a single Error here to use a write rejection closed = true; await flush(); - stream.close(); + // @ts-expect-error calling a possibly missing method + stream.close?.(); }; + stream.on('error', err => updateFlushed(Promise.reject(err))); + return harden({ write, flush, close }); }; diff --git a/packages/telemetry/package.json b/packages/telemetry/package.json index 4ca8fa5f285..cd0cb0585c0 100644 --- a/packages/telemetry/package.json +++ b/packages/telemetry/package.json @@ -29,9 +29,12 @@ "@endo/marshal": "^1.6.1", "@endo/stream": "^1.2.7", "@opentelemetry/api": "~1.3.0", + "@opentelemetry/api-logs": "0.53.0", "@opentelemetry/exporter-prometheus": "~0.35.0", + "@opentelemetry/exporter-logs-otlp-http": "0.53.0", "@opentelemetry/exporter-trace-otlp-http": "~0.35.0", "@opentelemetry/resources": "~1.9.0", + "@opentelemetry/sdk-logs": "0.53.0", "@opentelemetry/sdk-metrics": "~1.9.0", "@opentelemetry/sdk-trace-base": "~1.9.0", "@opentelemetry/semantic-conventions": "~1.27.0", diff --git a/packages/telemetry/src/context-aware-slog-file.js b/packages/telemetry/src/context-aware-slog-file.js new file mode 100644 index 00000000000..003680fc2bd --- /dev/null +++ b/packages/telemetry/src/context-aware-slog-file.js @@ -0,0 +1,42 @@ +/* eslint-env node */ + +import { makeFsStreamWriter } from '@agoric/internal/src/node/fs-stream.js'; +import { makeContextualSlogProcessor } from './context-aware-slog.js'; +import { serializeSlogObj } from './serialize-slog-obj.js'; + +/** + * @param {import('./index.js').MakeSlogSenderOptions} options + */ +export const makeSlogSender = async options => { + const { CHAIN_ID, CONTEXTUAL_SLOGFILE } = options.env || {}; + if (!CONTEXTUAL_SLOGFILE) + return console.warn( + 'Ignoring invocation of slogger "context-aware-slog-file" without the presence of "CONTEXTUAL_SLOGFILE"', + ); + + const stream = await makeFsStreamWriter(CONTEXTUAL_SLOGFILE); + + if (!stream) + return console.error( + `Couldn't create a write stream on file "${CONTEXTUAL_SLOGFILE}"`, + ); + + const contextualSlogProcessor = makeContextualSlogProcessor({ + 'chain-id': CHAIN_ID, + }); + + /** + * @param {import('./context-aware-slog.js').Slog} slog + */ + const slogSender = slog => { + const contextualizedSlog = contextualSlogProcessor(slog); + + // eslint-disable-next-line prefer-template + stream.write(serializeSlogObj(contextualizedSlog) + '\n').catch(() => {}); + }; + + return Object.assign(slogSender, { + forceFlush: () => stream.flush(), + shutdown: () => stream.close(), + }); +}; diff --git a/packages/telemetry/src/context-aware-slog.js b/packages/telemetry/src/context-aware-slog.js new file mode 100644 index 00000000000..34bb4867422 --- /dev/null +++ b/packages/telemetry/src/context-aware-slog.js @@ -0,0 +1,345 @@ +/* eslint-env node */ + +/** + * @typedef {Partial<{ + * 'block.height': Slog['blockHeight']; + * 'block.time': Slog['blockTime']; + * 'crank.deliveryNum': Slog['deliveryNum']; + * 'crank.num': Slog['crankNum']; + * 'crank.type': Slog['crankType']; + * 'crank.vatID': Slog['vatID']; + * init: boolean; + * replay: boolean; + * 'run.id': string; + * 'run.num': string | null; + * 'run.trigger.blockHeight': Slog['blockHeight']; + * 'run.trigger.msgIdx': number; + * 'run.trigger.sender': Slog['sender']; + * 'run.trigger.source': Slog['source']; + * 'run.trigger.time': Slog['blockTime']; + * 'run.trigger.txHash': string; + * 'run.trigger.type': string; + * }> + * } Context + * + * @typedef {{ + * 'crank.syscallNum'?: Slog['syscallNum']; + * 'process.uptime': Slog['monotime']; + * } & Context} LogAttributes + * + * @typedef {{ + * blockHeight?: number; + * blockTime?: number; + * crankNum?: bigint; + * crankType?: string; + * deliveryNum?: bigint; + * inboundNum?: string; + * monotime: number; + * remainingBeans?: bigint; + * replay?: boolean; + * runNum?: number; + * sender?: string; + * source?: string; + * syscallNum?: number; + * time: number; + * type: string; + * vatID?: string; + * }} Slog + */ + +const SLOG_TYPES = { + CLIST: 'clist', + CONSOLE: 'console', + COSMIC_SWINGSET: { + AFTER_COMMIT_STATS: 'cosmic-swingset-after-commit-stats', + BEGIN_BLOCK: 'cosmic-swingset-begin-block', + BOOTSTRAP_BLOCK: { + FINISH: 'cosmic-swingset-bootstrap-block-finish', + START: 'cosmic-swingset-bootstrap-block-start', + }, + BRIDGE_INBOUND: 'cosmic-swingset-bridge-inbound', + COMMIT: { + FINISH: 'cosmic-swingset-commit-finish', + START: 'cosmic-swingset-commit-start', + }, + DELIVER_INBOUND: 'cosmic-swingset-deliver-inbound', + END_BLOCK: { + FINISH: 'cosmic-swingset-end-block-finish', + START: 'cosmic-swingset-end-block-start', + }, + // eslint-disable-next-line no-restricted-syntax + RUN: { + FINISH: 'cosmic-swingset-run-finish', + START: 'cosmic-swingset-run-start', + }, + }, + CRANK: { + FINISH: 'crank-finish', + START: 'crank-start', + }, + DELIVER: 'deliver', + DELIVER_RESULT: 'deliver-result', + KERNEL: { + INIT: { + FINISH: 'kernel-init-finish', + START: 'kernel-init-start', + }, + }, + REPLAY: { + FINISH: 'finish-replay', + START: 'start-replay', + }, + SYSCALL: 'syscall', + SYSCALL_RESULT: 'syscall-result', +}; + +/** + * @template {Record} [T={}] + * @param {T} [staticContext] + * @param {Partial<{ persistContext: (context: Context) => void; restoreContext: () => Context | null; }>} [persistenceUtils] + */ +export const makeContextualSlogProcessor = ( + staticContext, + persistenceUtils = {}, +) => { + /** @type Array */ + let [ + blockContext, + crankContext, + initContext, + lastPersistedTriggerContext, + replayContext, + triggerContext, + ] = [null, null, null, null, null, null]; + + /** + * @param {Context} context + */ + const persistContext = context => { + lastPersistedTriggerContext = context; + return persistenceUtils?.persistContext?.(context); + }; + + const restoreContext = () => { + if (!lastPersistedTriggerContext) + lastPersistedTriggerContext = + persistenceUtils?.restoreContext?.() || null; + return lastPersistedTriggerContext; + }; + + /** + * @param {Slog} slog + * @returns {{ attributes: T & LogAttributes, body: Partial; timestamp: Slog['time'] }} + */ + const slogProcessor = ({ monotime, time: timestamp, ...body }) => { + const finalBody = { ...body }; + + /** @type {{'crank.syscallNum'?: Slog['syscallNum']}} */ + const eventLogAttributes = {}; + + /** + * Add any before report operations here + * like setting context data + */ + switch (body.type) { + case SLOG_TYPES.KERNEL.INIT.START: { + initContext = { init: true }; + break; + } + case SLOG_TYPES.COSMIC_SWINGSET.BEGIN_BLOCK: { + blockContext = { + 'block.height': finalBody.blockHeight, + 'block.time': finalBody.blockTime, + }; + break; + } + case SLOG_TYPES.COSMIC_SWINGSET.BOOTSTRAP_BLOCK.START: { + blockContext = { + 'block.height': finalBody.blockHeight || 0, + 'block.time': finalBody.blockTime, + }; + break; + } + case SLOG_TYPES.COSMIC_SWINGSET.END_BLOCK.START: + case SLOG_TYPES.COSMIC_SWINGSET.END_BLOCK.FINISH: + case SLOG_TYPES.COSMIC_SWINGSET.BOOTSTRAP_BLOCK.FINISH: + case SLOG_TYPES.COSMIC_SWINGSET.COMMIT.START: + case SLOG_TYPES.COSMIC_SWINGSET.COMMIT.FINISH: + case SLOG_TYPES.COSMIC_SWINGSET.AFTER_COMMIT_STATS: { + assert(!!blockContext && !triggerContext); + break; + } + case SLOG_TYPES.COSMIC_SWINGSET.BRIDGE_INBOUND: + case SLOG_TYPES.COSMIC_SWINGSET.DELIVER_INBOUND: { + const [blockHeight, txHash, msgIdx] = ( + finalBody.inboundNum || '' + ).split('-'); + const [, triggerType] = + /cosmic-swingset-([^-]+)-inbound/.exec(body.type) || []; + + triggerContext = { + 'run.num': undefined, + 'run.id': `${triggerType}-${finalBody.inboundNum}`, + 'run.trigger.type': triggerType, + 'run.trigger.source': finalBody.source, + 'run.trigger.sender': finalBody.sender, + 'run.trigger.blockHeight': Number(blockHeight), + 'run.trigger.txHash': txHash, + 'run.trigger.msgIdx': Number(msgIdx), + }; + break; + } + // eslint-disable-next-line no-restricted-syntax + case SLOG_TYPES.COSMIC_SWINGSET.RUN.START: { + if (!finalBody.runNum) { + assert(!triggerContext); + triggerContext = restoreContext(); // Restore persisted context if any + } else if (!triggerContext) { + assert(!!blockContext); + // TODO: add explicit slog events of both timer poll and install bundle + // https://github.com/Agoric/agoric-sdk/issues/10332 + triggerContext = { + 'run.num': undefined, + 'run.id': `unknown-${finalBody.blockHeight}-${finalBody.runNum}`, + 'run.trigger.type': 'unknown', + 'run.trigger.blockHeight': finalBody.blockHeight, + }; + } + + if (!triggerContext) triggerContext = {}; + triggerContext['run.num'] = `${finalBody.runNum}`; + + break; + } + case SLOG_TYPES.CRANK.START: { + crankContext = { + 'crank.num': finalBody.crankNum, + 'crank.type': finalBody.crankType, + }; + break; + } + case SLOG_TYPES.CLIST: { + assert(!!crankContext); + crankContext['crank.vatID'] = finalBody.vatID; + break; + } + case SLOG_TYPES.REPLAY.START: + case SLOG_TYPES.REPLAY.FINISH: { + replayContext = { replay: true, 'crank.vatID': finalBody.vatID }; + break; + } + case SLOG_TYPES.DELIVER: { + if (replayContext) { + assert(finalBody.replay); + replayContext = { + ...replayContext, + 'crank.vatID': finalBody.vatID, + 'crank.deliveryNum': finalBody.deliveryNum, + }; + } else { + assert(!!crankContext && !finalBody.replay); + crankContext = { + ...crankContext, + 'crank.vatID': finalBody.vatID, + 'crank.deliveryNum': finalBody.deliveryNum, + }; + } + + delete finalBody.deliveryNum; + delete finalBody.replay; + + break; + } + case SLOG_TYPES.DELIVER_RESULT: { + delete finalBody.deliveryNum; + delete finalBody.replay; + + break; + } + case SLOG_TYPES.SYSCALL: + case SLOG_TYPES.SYSCALL_RESULT: { + eventLogAttributes['crank.syscallNum'] = finalBody.syscallNum; + + delete finalBody.deliveryNum; + delete finalBody.replay; + delete finalBody.syscallNum; + + break; + } + case SLOG_TYPES.CONSOLE: { + delete finalBody.crankNum; + delete finalBody.deliveryNum; + + break; + } + default: + // All other log types are logged as is (using existing contexts) without + // any change to the slogs or any contributions to the contexts. This also + // means that any unexpected slog type will pass through. To fix that, add + // all remaining cases of expected slog types above with a simple break + // statement and log a warning here + break; + } + + const logAttributes = { + ...staticContext, + 'process.uptime': monotime, + ...initContext, // Optional prelude + ...blockContext, // Block is the first level of execution nesting + ...triggerContext, // run and trigger info is nested next + ...crankContext, // Finally cranks are the last level of nesting + ...replayContext, // Replay is a substitute for crank context during vat page in + ...eventLogAttributes, + }; + + /** + * Add any after report operations here + * like resetting context data + */ + switch (body.type) { + case SLOG_TYPES.KERNEL.INIT.FINISH: { + initContext = null; + break; + } + case SLOG_TYPES.COSMIC_SWINGSET.BOOTSTRAP_BLOCK.START: { + triggerContext = { + 'run.num': undefined, + 'run.id': `bootstrap-${finalBody.blockTime}`, + 'run.trigger.type': 'bootstrap', + 'run.trigger.time': finalBody.blockTime, + }; + break; + } + case SLOG_TYPES.COSMIC_SWINGSET.AFTER_COMMIT_STATS: + case SLOG_TYPES.COSMIC_SWINGSET.BOOTSTRAP_BLOCK.FINISH: { + blockContext = null; + break; + } + // eslint-disable-next-line no-restricted-syntax + case SLOG_TYPES.COSMIC_SWINGSET.RUN.FINISH: { + assert(!!triggerContext); + persistContext(finalBody.remainingBeans ? {} : triggerContext); + triggerContext = null; + break; + } + case SLOG_TYPES.CRANK.FINISH: { + crankContext = null; + break; + } + case SLOG_TYPES.REPLAY.FINISH: { + replayContext = null; + break; + } + default: + break; + } + + return { + attributes: /** @type {T & LogAttributes} */ (logAttributes), + body: finalBody, + timestamp, + }; + }; + + return slogProcessor; +}; diff --git a/packages/telemetry/src/ingest-slog-entrypoint.js b/packages/telemetry/src/ingest-slog-entrypoint.js index 3a10d8463f2..ee6c6fca36e 100755 --- a/packages/telemetry/src/ingest-slog-entrypoint.js +++ b/packages/telemetry/src/ingest-slog-entrypoint.js @@ -11,7 +11,8 @@ import { makeSlogSender } from './make-slog-sender.js'; const LINE_COUNT_TO_FLUSH = 10000; const ELAPSED_MS_TO_FLUSH = 3000; -const MAX_LINE_COUNT_PER_PERIOD = 1000; +const MAX_LINE_COUNT_PER_PERIOD = 10000; +const MAX_BLOCKS_PER_PERIOD = 10; const PROCESSING_PERIOD = 1000; async function run() { @@ -29,7 +30,7 @@ async function run() { return; } - const [slogFile] = args; + const slogFile = args[0] === '-' ? undefined : args[0]; const slogSender = await makeSlogSender({ serviceName, stateDir: '.', @@ -56,14 +57,21 @@ async function run() { const lines = readline.createInterface({ input: slogF }); const slogFileName = slogFile || '*stdin*'; - const progressFileName = `${slogFileName}.ingest-progress`; - if (!fs.existsSync(progressFileName)) { - const progress = { virtualTimeOffset: 0, lastSlogTime: 0 }; - fs.writeFileSync(progressFileName, JSON.stringify(progress)); + const progressFileName = slogFile && `${slogFileName}.ingest-progress`; + const progress = { virtualTimeOffset: 0, lastSlogTime: 0 }; + if (progressFileName) { + if (!fs.existsSync(progressFileName)) { + fs.writeFileSync(progressFileName, JSON.stringify(progress)); + } else { + Object.assign( + progress, + JSON.parse(fs.readFileSync(progressFileName).toString()), + ); + } } - const progress = JSON.parse(fs.readFileSync(progressFileName).toString()); let linesProcessedThisPeriod = 0; + let blocksInThisPeriod = 0; let startOfLastPeriod = 0; let lastTime = Date.now(); @@ -75,10 +83,12 @@ async function run() { return; } await slogSender.forceFlush?.(); - fs.writeFileSync(progressFileName, JSON.stringify(progress)); + if (progressFileName) { + fs.writeFileSync(progressFileName, JSON.stringify(progress)); + } }; - console.log(`parsing`, slogFileName); + console.warn(`parsing`, slogFileName); let update = false; const maybeUpdateStats = async now => { @@ -106,9 +116,14 @@ async function run() { continue; } + const isAfterCommit = obj.type === 'cosmic-swingset-after-commit-stats'; + // Maybe wait for the next period to process a bunch of lines. let maybeWait; - if (linesProcessedThisPeriod >= MAX_LINE_COUNT_PER_PERIOD) { + if ( + linesProcessedThisPeriod >= MAX_LINE_COUNT_PER_PERIOD || + blocksInThisPeriod >= MAX_BLOCKS_PER_PERIOD + ) { const delayMS = PROCESSING_PERIOD - (now - startOfLastPeriod); maybeWait = new Promise(resolve => setTimeout(resolve, delayMS)); } @@ -118,8 +133,8 @@ async function run() { if (now - startOfLastPeriod >= PROCESSING_PERIOD) { startOfLastPeriod = now; linesProcessedThisPeriod = 0; + blocksInThisPeriod = 0; } - linesProcessedThisPeriod += 1; if (progress.virtualTimeOffset) { const virtualTime = obj.time + progress.virtualTimeOffset; @@ -133,10 +148,17 @@ async function run() { // Use the original. slogSender(obj); } + + linesProcessedThisPeriod += 1; + if (isAfterCommit) { + blocksInThisPeriod += 1; + lastTime = Date.now(); + await stats(true); + } } await stats(true); - console.log( + console.warn( `done parsing`, slogFileName, `(${lineCount} lines, ${byteCount} bytes)`, diff --git a/packages/telemetry/src/otel-context-aware-slog.js b/packages/telemetry/src/otel-context-aware-slog.js new file mode 100644 index 00000000000..51891460eba --- /dev/null +++ b/packages/telemetry/src/otel-context-aware-slog.js @@ -0,0 +1,131 @@ +/* eslint-env node */ +import { logs, SeverityNumber } from '@opentelemetry/api-logs'; +import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http'; +import { Resource } from '@opentelemetry/resources'; +import { + LoggerProvider, + SimpleLogRecordProcessor, +} from '@opentelemetry/sdk-logs'; +import { readFileSync, writeFileSync } from 'fs'; +import { makeContextualSlogProcessor } from './context-aware-slog.js'; +import { getResourceAttributes } from './index.js'; +import { serializeSlogObj } from './serialize-slog-obj.js'; + +const DEFAULT_CONTEXT_FILE = 'slog-context.json'; +const FILE_ENCODING = 'utf8'; + +/** + * @param {string} filePath + */ +export const getContextFilePersistenceUtils = filePath => { + console.warn(`Using file ${filePath} for slogger context`); + + return { + /** + * @param {import('./context-aware-slog.js').Context} context + */ + persistContext: context => { + try { + writeFileSync(filePath, serializeSlogObj(context), FILE_ENCODING); + } catch (err) { + console.error('Error writing context to file: ', err); + } + }, + + /** + * @returns {import('./context-aware-slog.js').Context | null} + */ + restoreContext: () => { + try { + return JSON.parse(readFileSync(filePath, FILE_ENCODING)); + } catch (parseErr) { + console.error('Error reading context from file: ', parseErr); + return null; + } + }, + }; +}; + +/** + * @param {import('./index.js').MakeSlogSenderOptions} options + */ +export const makeSlogSender = async options => { + const { CHAIN_ID, OTEL_EXPORTER_OTLP_ENDPOINT } = options.env || {}; + if (!(OTEL_EXPORTER_OTLP_ENDPOINT && options.stateDir)) + return console.error( + 'Ignoring invocation of slogger "context-aware-slog" without the presence of "OTEL_EXPORTER_OTLP_ENDPOINT" and "stateDir"', + ); + + const loggerProvider = new LoggerProvider({ + resource: new Resource(getResourceAttributes(options)), + }); + + const otelLogExporter = new OTLPLogExporter({ keepAlive: true }); + const logRecordProcessor = new SimpleLogRecordProcessor(otelLogExporter); + + loggerProvider.addLogRecordProcessor(logRecordProcessor); + + logs.setGlobalLoggerProvider(loggerProvider); + const logger = logs.getLogger('default'); + + const persistenceUtils = getContextFilePersistenceUtils( + process.env.SLOG_CONTEXT_FILE_PATH || + `${options.stateDir}/${DEFAULT_CONTEXT_FILE}`, + ); + + const contextualSlogProcessor = makeContextualSlogProcessor( + { 'chain-id': CHAIN_ID }, + persistenceUtils, + ); + + /** + * @param {import('./context-aware-slog.js').Slog} slog + */ + const slogSender = slog => { + const { timestamp, ...logRecord } = contextualSlogProcessor(slog); + + const [secondsStr, fractionStr] = String(timestamp).split('.'); + const seconds = parseInt(secondsStr, 10); + const nanoSeconds = parseInt( + (fractionStr || String(0)).padEnd(9, String(0)).slice(0, 9), + 10, + ); + + logger.emit({ + ...JSON.parse(serializeSlogObj(logRecord)), + severityNumber: SeverityNumber.INFO, + timestamp: [seconds, nanoSeconds], + }); + }; + + const shutdown = async () => { + await Promise.resolve(); + const errors = []; + + try { + await logRecordProcessor.shutdown(); + } catch (err) { + errors.push(err); + } + + try { + await otelLogExporter.forceFlush(); + } catch (err) { + errors.push(err); + } + + switch (errors.length) { + case 0: + return; + case 1: + throw errors[0]; + default: + throw AggregateError(errors); + } + }; + + return Object.assign(slogSender, { + forceFlush: () => otelLogExporter.forceFlush(), + shutdown, + }); +}; diff --git a/packages/telemetry/src/slog-file.js b/packages/telemetry/src/slog-file.js index 3c091211a5f..e7b49d559bd 100644 --- a/packages/telemetry/src/slog-file.js +++ b/packages/telemetry/src/slog-file.js @@ -11,7 +11,7 @@ export const makeSlogSender = async ({ env: { SLOGFILE } = {} } = {}) => { const slogSender = (slogObj, jsonObj = serializeSlogObj(slogObj)) => { // eslint-disable-next-line prefer-template - void stream.write(jsonObj + '\n'); + stream.write(jsonObj + '\n').catch(() => {}); }; return Object.assign(slogSender, { diff --git a/yarn.lock b/yarn.lock index f2059f6e6bc..bb8a5ec3a88 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3042,6 +3042,13 @@ dependencies: "@octokit/openapi-types" "^18.0.0" +"@opentelemetry/api-logs@0.53.0": + version "0.53.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.53.0.tgz#c478cbd8120ec2547b64edfa03a552cfe42170be" + integrity sha512-8HArjKx+RaAI8uEIgcORbZIPklyh1YLjPSBus8hjRmvLi6DeFzgOcdZ7KwPabKj8mXF8dX0hyfAyGfycz0DbFw== + dependencies: + "@opentelemetry/api" "^1.0.0" + "@opentelemetry/api@^1.0.0": version "1.4.1" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.1.tgz#ff22eb2e5d476fbc2450a196e40dd243cc20c28f" @@ -3052,6 +3059,13 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.3.0.tgz#27c6f776ac3c1c616651e506a89f438a0ed6a055" integrity sha512-YveTnGNsFFixTKJz09Oi4zYkiLT5af3WpZDu4aIUM7xX+2bHAkOJayFTVQd6zB8kkWPpbua4Ha6Ql00grdLlJQ== +"@opentelemetry/core@1.26.0": + version "1.26.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.26.0.tgz#7d84265aaa850ed0ca5813f97d831155be42b328" + integrity sha512-1iKxXXE8415Cdv0yjG3G6hQnB5eVEsJce3QaawX8SjDn0mAS0ZM8fAbZZJD4ajvhC15cePvosSCut404KrIIvQ== + dependencies: + "@opentelemetry/semantic-conventions" "1.27.0" + "@opentelemetry/core@1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.9.1.tgz#e343337e1a7bf30e9a6aef3ef659b9b76379762a" @@ -3066,6 +3080,17 @@ dependencies: "@opentelemetry/semantic-conventions" "1.15.2" +"@opentelemetry/exporter-logs-otlp-http@0.53.0": + version "0.53.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.53.0.tgz#1b4a152ea427ec4581532880fd0d620cc559cb11" + integrity sha512-cSRKgD/n8rb+Yd+Cif6EnHEL/VZg1o8lEcEwFji1lwene6BdH51Zh3feAD9p2TyVoBKrl6Q9Zm2WltSp2k9gWQ== + dependencies: + "@opentelemetry/api-logs" "0.53.0" + "@opentelemetry/core" "1.26.0" + "@opentelemetry/otlp-exporter-base" "0.53.0" + "@opentelemetry/otlp-transformer" "0.53.0" + "@opentelemetry/sdk-logs" "0.53.0" + "@opentelemetry/exporter-prometheus@~0.35.0": version "0.35.1" resolved "https://registry.yarnpkg.com/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.35.1.tgz#c2fd5fcd17dac8106b33e5d354d98c47ebaa8804" @@ -3093,6 +3118,14 @@ dependencies: "@opentelemetry/core" "1.9.1" +"@opentelemetry/otlp-exporter-base@0.53.0": + version "0.53.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.53.0.tgz#dfe51874b869c687c3cb463b70cddda7de282762" + integrity sha512-UCWPreGQEhD6FjBaeDuXhiMf6kkBODF0ZQzrk/tuQcaVDJ+dDQ/xhJp192H9yWnKxVpEjFrSSLnpqmX4VwX+eA== + dependencies: + "@opentelemetry/core" "1.26.0" + "@opentelemetry/otlp-transformer" "0.53.0" + "@opentelemetry/otlp-transformer@0.35.1": version "0.35.1" resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-transformer/-/otlp-transformer-0.35.1.tgz#d4333b71324b83dbb1b0b3a4cfd769b3e214c6f9" @@ -3103,6 +3136,27 @@ "@opentelemetry/sdk-metrics" "1.9.1" "@opentelemetry/sdk-trace-base" "1.9.1" +"@opentelemetry/otlp-transformer@0.53.0": + version "0.53.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/otlp-transformer/-/otlp-transformer-0.53.0.tgz#55d435db5ed5cf56b99c010827294dd4921c45c2" + integrity sha512-rM0sDA9HD8dluwuBxLetUmoqGJKSAbWenwD65KY9iZhUxdBHRLrIdrABfNDP7aiTjcgK8XFyTn5fhDz7N+W6DA== + dependencies: + "@opentelemetry/api-logs" "0.53.0" + "@opentelemetry/core" "1.26.0" + "@opentelemetry/resources" "1.26.0" + "@opentelemetry/sdk-logs" "0.53.0" + "@opentelemetry/sdk-metrics" "1.26.0" + "@opentelemetry/sdk-trace-base" "1.26.0" + protobufjs "^7.3.0" + +"@opentelemetry/resources@1.26.0": + version "1.26.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.26.0.tgz#da4c7366018bd8add1f3aa9c91c6ac59fd503cef" + integrity sha512-CPNYchBE7MBecCSVy0HKpUISEeJOniWqcHaAHpmasZ3j9o6V3AyBzhRc90jdmemq0HOxDr6ylhUbDhBqqPpeNw== + dependencies: + "@opentelemetry/core" "1.26.0" + "@opentelemetry/semantic-conventions" "1.27.0" + "@opentelemetry/resources@1.9.1", "@opentelemetry/resources@~1.9.0": version "1.9.1" resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.9.1.tgz#5ad3d80ba968a3a0e56498ce4bc82a6a01f2682f" @@ -3111,6 +3165,23 @@ "@opentelemetry/core" "1.9.1" "@opentelemetry/semantic-conventions" "1.9.1" +"@opentelemetry/sdk-logs@0.53.0": + version "0.53.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-logs/-/sdk-logs-0.53.0.tgz#ec8b69278c4e683c13c58ed4285a47c27f5799c6" + integrity sha512-dhSisnEgIj/vJZXZV6f6KcTnyLDx/VuQ6l3ejuZpMpPlh9S1qMHiZU9NMmOkVkwwHkMy3G6mEBwdP23vUZVr4g== + dependencies: + "@opentelemetry/api-logs" "0.53.0" + "@opentelemetry/core" "1.26.0" + "@opentelemetry/resources" "1.26.0" + +"@opentelemetry/sdk-metrics@1.26.0": + version "1.26.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics/-/sdk-metrics-1.26.0.tgz#37bb0afb1d4447f50aab9cdd05db6f2d8b86103e" + integrity sha512-0SvDXmou/JjzSDOjUmetAAvcKQW6ZrvosU0rkbDGpXvvZN+pQF6JbK/Kd4hNdK4q/22yeruqvukXEJyySTzyTQ== + dependencies: + "@opentelemetry/core" "1.26.0" + "@opentelemetry/resources" "1.26.0" + "@opentelemetry/sdk-metrics@1.9.1", "@opentelemetry/sdk-metrics@~1.9.0": version "1.9.1" resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics/-/sdk-metrics-1.9.1.tgz#babc162a81df9884c16b1e67c2dd26ab478f3080" @@ -3120,6 +3191,15 @@ "@opentelemetry/resources" "1.9.1" lodash.merge "4.6.2" +"@opentelemetry/sdk-trace-base@1.26.0": + version "1.26.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.26.0.tgz#0c913bc6d2cfafd901de330e4540952269ae579c" + integrity sha512-olWQldtvbK4v22ymrKLbIcBi9L2SpMO84sCPY54IVsJhP9fRsxJT194C/AVaAuJzLE30EdhhM1VmvVYR7az+cw== + dependencies: + "@opentelemetry/core" "1.26.0" + "@opentelemetry/resources" "1.26.0" + "@opentelemetry/semantic-conventions" "1.27.0" + "@opentelemetry/sdk-trace-base@1.9.1", "@opentelemetry/sdk-trace-base@~1.9.0": version "1.9.1" resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.9.1.tgz#c349491b432a7e0ae7316f0b48b2d454d79d2b84" @@ -3134,16 +3214,16 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz#3bafb5de3e20e841dff6cb3c66f4d6e9694c4241" integrity sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw== +"@opentelemetry/semantic-conventions@1.27.0", "@opentelemetry/semantic-conventions@~1.27.0": + version "1.27.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz#1a857dcc95a5ab30122e04417148211e6f945e6c" + integrity sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg== + "@opentelemetry/semantic-conventions@1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.9.1.tgz#ad3367684a57879392513479e0a436cb2ac46dad" integrity sha512-oPQdbFDmZvjXk5ZDoBGXG8B4tSB/qW5vQunJWQMFUBp7Xe8O1ByPANueJ+Jzg58esEBegyyxZ7LRmfJr7kFcFg== -"@opentelemetry/semantic-conventions@~1.27.0": - version "1.27.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz#1a857dcc95a5ab30122e04417148211e6f945e6c" - integrity sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg== - "@parcel/watcher@2.0.4": version "2.0.4" resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.4.tgz#f300fef4cc38008ff4b8c29d92588eced3ce014b" @@ -10052,7 +10132,7 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= -protobufjs@^6.8.8, protobufjs@^7.2.4, protobufjs@^7.2.6: +protobufjs@^6.8.8, protobufjs@^7.2.4, protobufjs@^7.2.6, protobufjs@^7.3.0: version "7.4.0" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.4.0.tgz#7efe324ce9b3b61c82aae5de810d287bc08a248a" integrity sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==