Skip to content

Commit

Permalink
chore(liquidationVisibility): Add a3p proposal
Browse files Browse the repository at this point in the history
Refs: #35
  • Loading branch information
anilhelvaci committed Jun 21, 2024
1 parent 2d3f580 commit 37c45b5
Show file tree
Hide file tree
Showing 46 changed files with 6,999 additions and 0 deletions.
2 changes: 2 additions & 0 deletions a3p-integration/proposals/d:liquidation-visibility/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.yarn
artifacts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
88 changes: 88 additions & 0 deletions a3p-integration/proposals/d:liquidation-visibility/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Liquidation Visibility
This proposal aims to perform a `core-eval` upgrade to `vaultFactory`. The purpose of the upgrade is to
improve the visibility of liquidating vaults. For the full spec please see [this issue](https://github.com/Jorge-Lopes/liquidation-visibility/issues/2)
and for minor additional specs see [this one](https://github.com/Jorge-Lopes/agoric-sdk/issues/36).

## Work Backlog
* Main workspace => https://github.com/Jorge-Lopes/agoric-sdk
* PR => https://github.com/Agoric/agoric-sdk/pull/8994
* Release => https://github.com/Jorge-Lopes/agoric-sdk/releases/tag/liq-visibility-a3p-v0.2

## Test Structure
We follow the usual `a3p` flow where we first perform our upgrade at `EVAL` phase then run our tests in `TEST`
phase. Notice that we don't run anything under `USE` phase as we don't want the changes we make to create the
appropriate test circumstances to effect future builds.

To execute our core-eval we use;
* [test-liquidation-visibility.js](./test-liquidation-visibility.js)

Our main challenge in testing the visibility of liquidations is to trigger an actual liquidation in our post
eval tests. Below files are used to set up that environment;
* [post.liquidation.js](./post.liquidation.js)
* [post.test.js](./post.test.js)

And test our desired behavior we use;
* [post.scenario.js](./post.scenario.js)

## How to trigger liquidation?
In the below diagram, we describe the steps we go thorough to trigger a fake liquidation.
The reason why it is problematic subject is;
1. We need to be able to control the time and `chainTimerService` does not allow that
2. Auctioneer at this version is not upgradable so we have to deploy a new one
3. We have to make sure `vaultFactory` does use our fake auctioneer and the timer we want
* The problem with this one is with making sure while we adjust the code base to serve
our needs to create a suitable liquidation environment, we also have to make sure our tests
are actually reliable(we test what we will be deploying to the mainnet).


### Addressing problem #3
In order to get around this problem we've created a tarball file ([visibilityFeaturesProof.tar](./visibilityFeaturesProof.tar))
that has what we've developed and put the same file in both the [relase](https://github.com/Jorge-Lopes/agoric-sdk/releases/tag/liq-visibility-a3p-v0.2).
Basically what we do with that file is;
* Check the local and remote versions match using `cksum`
* Move the incarnation 1 code to `/usr/src/agoric-sdk`
* Bundle `inter-protocol` again
* Make sure the vaultFactory bundle hash matches the one we use to perform our core-eval
* At this stage we're sure that the version we have in `/usr/src/agoric-sdk` is the same
as the one we deployed
* Adjust VF code programmatically in [post.liquidation.js](./post.liquidation.js) to
make sure it uses the fake auctioneer and manual timer so that we can trigger the liquidation

### The whole proposal flow

```mermaid
sequenceDiagram
participant image as ghcr.io/agoric/agoric-3-proposals
participant web as release
Note over image,image: vaultFactory: inc 0 code
Note over testFlow,agd: PHASE: EVAL
testFlow ->>+ agd: test-liquidation-visibility.js
agd -->>- testFlow: upgrade successful
Note over agd,agd: vaultFactory: inc 1
Note over testFlow,agd: PHASE: TEST
Note over testFlow,testFlow: post.liquidation.js running
testFlow ->>+ web: fetch vaultFactory inc 1 code
web -->>- testFlow: incarnation 1 code
testFlow ->> testFlow: make sure inc 1 code matches the local one
testFlow ->> testFlow: unarchive local inc 1 code
testFlow ->> image: copy vaultFactory inc 1 code
Note over image,image: vaultFactory: inc 1 code
testFlow ->> image: bundle vaultFactory
testFlow ->> testFlow: check bundle hash from the image matches inc 1's bundle hash
Note over testFlow, testFlow: we must use a manual timer to trigger liquidation
testFlow ->> testFlow: update vaultFactory.js, vaultDirector.js, vaultManager.js
Note over testFlow, testFlow: we must deploy a new auctioneer as well that uses the same timer as VF
testFlow ->> testFlow: build proposal to deploy changes required to trigger liquidation
testFlow ->> agd: Execute built proposal
Note over agd,agd: vaultFactory: inc 2
Note over testFlow,testFlow: post.test.js running
testFlow ->> agd: deploy a new vaultManager for the asset STARS
Note over testFlow, agd: we use addSTARS.js from. the sdk
testFlow ->> testFlow: accept oracle invitations
testFlow ->> testFlow: push STARS/IST price
Note over testFlow,testFlow: post.scenario.js running
testFlow ->> testFlow: open STARS vaults
testFlow ->> testFlow: place bids to fake auctioneer
testFlow ->> testFlow: make sure aution is run and bids are settled
testFlow ->> testFlow: assert the visibility of the liquidation
```

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"consume": {
"vaultFactoryKit": "to upgrade vaultFactory using its adminFacet",
"agoricNamesAdmin": "makeCoreProposalBehavior",
"vatAdminSvc": "makeCoreProposalBehavior",
"zoe": "makeCoreProposalBehavior"
},
"evaluateBundleCap": "makeCoreProposalBehavior",
"installation": {
"produce": "makeCoreProposalBehavior"
},
"modules": {
"utils": {
"runModuleBehaviors": "makeCoreProposalBehavior"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "upgrade-vaults-liq-visibility",
"script": "upgrade-vaults-liq-visibility.js",
"permit": "upgrade-vaults-liq-visibility-permit.json",
"bundles": [
{
"entrypoint": "../src/vaultFactory/vaultFactory.js",
"bundleID": "b1-0daeb28abf2bb95cd27bebe80cdcd53ecd670244cb4ca6fe07784697fa8b40bcbc8f3ab1fd92a6d7ce8197efa0d2a28716737f77c68ab2eba88b3c72179f15e0",
"fileName": "/Users/anil/.agoric/cache/b1-0daeb28abf2bb95cd27bebe80cdcd53ecd670244cb4ca6fe07784697fa8b40bcbc8f3ab1fd92a6d7ce8197efa0d2a28716737f77c68ab2eba88b3c72179f15e0.json"
},
{
"entrypoint": "../src/proposals/vaultsUpgrade.js",
"bundleID": "b1-88b8532be656b66ebc0298f916802fae523a263bd1935160ee0042cf0cb4136bdba57165a7ca70b78b37402404aaafc02400019383c6d6b076a7236a352a6ba3",
"fileName": "/Users/anil/.agoric/cache/b1-88b8532be656b66ebc0298f916802fae523a263bd1935160ee0042cf0cb4136bdba57165a7ca70b78b37402404aaafc02400019383c6d6b076a7236a352a6ba3.json"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// This is generated by writeCoreProposal; please edit!
/* eslint-disable */

const manifestBundleRef = {bundleID:"b1-88b8532be656b66ebc0298f916802fae523a263bd1935160ee0042cf0cb4136bdba57165a7ca70b78b37402404aaafc02400019383c6d6b076a7236a352a6ba3"};
const getManifestCall = harden([
"getManifestVaultsUpgrade",
{
vaultFactoryRef: {
bundleID: "b1-0daeb28abf2bb95cd27bebe80cdcd53ecd670244cb4ca6fe07784697fa8b40bcbc8f3ab1fd92a6d7ce8197efa0d2a28716737f77c68ab2eba88b3c72179f15e0",
},
},
]);
const customManifest = {
upgradeVaults: {
consume: {
vaultFactoryKit: "to upgrade vaultFactory using its adminFacet",
},
},
};

// Make a behavior function and "export" it by way of script completion value.
// It is constructed by an anonymous invocation to ensure the absence of a global binding
// for makeCoreProposalBehavior, which may not be necessary but preserves behavior pre-dating
// https://github.com/Agoric/agoric-sdk/pull/8712 .
const behavior = (({
manifestBundleRef,
getManifestCall: [manifestGetterName, ...manifestGetterArgs],
customManifest,
E,
log = console.info,
customRestoreRef,
}) => {
const { entries, fromEntries } = Object;

// deeplyFulfilled is a bit overkill for what we need.
const shallowlyFulfilled = async obj => {
if (!obj) {
return obj;
}
const ents = await Promise.all(
entries(obj).map(async ([key, valueP]) => {
const value = await valueP;
return [key, value];
}),
);
return fromEntries(ents);
};

const makeRestoreRef = (vatAdminSvc, zoe) => {
/** @type {(ref: import\('./externalTypes.js').ManifestBundleRef) => Promise<Installation<unknown>>} */
const defaultRestoreRef = async bundleRef => {
// extract-proposal.js creates these records, and bundleName is
// the optional name under which the bundle was installed into
// config.bundles
const bundleIdP =
'bundleName' in bundleRef
? E(vatAdminSvc).getBundleIDByName(bundleRef.bundleName)
: bundleRef.bundleID;
const bundleID = await bundleIdP;
const label = bundleID.slice(0, 8);
return E(zoe).installBundleID(bundleID, label);
};
return defaultRestoreRef;
};

/** @param {ChainBootstrapSpace & BootstrapPowers & { evaluateBundleCap: any }} powers */
const coreProposalBehavior = async powers => {
// NOTE: `powers` is expected to match or be a superset of the above `permits` export,
// which should therefore be kept in sync with this deconstruction code.
// HOWEVER, do note that this function is invoked with at least the *union* of powers
// required by individual moduleBehaviors declared by the manifest getter, which is
// necessary so it can use `runModuleBehaviors` to provide the appropriate subset to
// each one (see ./writeCoreProposal.js).
// Handle `powers` with the requisite care.
const {
consume: { vatAdminSvc, zoe, agoricNamesAdmin },
evaluateBundleCap,
installation: { produce: produceInstallations },
modules: {
utils: { runModuleBehaviors },
},
} = powers;

// Get the on-chain installation containing the manifest and behaviors.
log('evaluateBundleCap', {
manifestBundleRef,
manifestGetterName,
vatAdminSvc,
});
let bcapP;
if ('bundleName' in manifestBundleRef) {
bcapP = E(vatAdminSvc).getNamedBundleCap(manifestBundleRef.bundleName);
} else if ('bundleID' in manifestBundleRef) {
bcapP = E(vatAdminSvc).getBundleCap(manifestBundleRef.bundleID);
} else {
const keys = Reflect.ownKeys(manifestBundleRef).map(key =>
typeof key === 'string' ? JSON.stringify(key) : String(key),
);
const keysStr = `[${keys.join(', ')}]`;
throw Error(
`bundleRef must have own bundleName or bundleID, missing in ${keysStr}`,
);
}
const bundleCap = await bcapP;

const proposalNS = await evaluateBundleCap(bundleCap);

// Get the manifest and its metadata.
log('execute', {
manifestGetterName,
bundleExports: Object.keys(proposalNS),
});
const restoreRef = customRestoreRef || makeRestoreRef(vatAdminSvc, zoe);
const {
manifest,
options: rawOptions,
installations: rawInstallations,
} = await proposalNS[manifestGetterName](
harden({ restoreRef }),
...manifestGetterArgs,
);

// Await references in the options or installations.
const [options, installations] = await Promise.all(
[rawOptions, rawInstallations].map(shallowlyFulfilled),
);

// Publish the installations for our dependencies.
const installationEntries = entries(installations || {});
if (installationEntries.length > 0) {
const installAdmin = E(agoricNamesAdmin).lookupAdmin('installation');
await Promise.all(
installationEntries.map(([key, value]) => {
produceInstallations[key].resolve(value);
return E(installAdmin).update(key, value);
}),
);
}

// Evaluate the manifest.
return runModuleBehaviors({
// Remember that `powers` may be arbitrarily broad.
allPowers: powers,
behaviors: proposalNS,
manifest: customManifest || manifest,
makeConfig: (name, _permit) => {
log('coreProposal:', name);
return { options };
},
});
};

return coreProposalBehavior;
})({ manifestBundleRef, getManifestCall, customManifest, E });
behavior;
Loading

0 comments on commit 37c45b5

Please sign in to comment.