Skip to content

Commit

Permalink
zoe adminFacet can terminate contract (#10267)
Browse files Browse the repository at this point in the history
closes: #9716

## Description

Add the ability to terminate a vat if you have the adminFacet.

### Security Considerations

Doesn't add authority, because anyone with the adminFacet could also upgrade the contract to something that calls `zcf.shutdown()`.  In practice, this will be controlled by governance or the bootstrap space.

### Scaling Considerations

The scaling issue is about cleaning up abandoned vats that are holding memory we'd like to release.

### Documentation Considerations

We should (not urgently) update the zoe docs to mention this ability.

### Testing Considerations

Adding a unit test wouldn't show anything, since it relies on the adminNode. 

A SwingSet test shows that the vat is dead.

I will pass this to @warner as a draft, so he can do extensive testing of the cleanup process.

### Upgrade Considerations

The upgrade requires only merging the code and upgrading Zoe. This will redefine all the existing `adminFacets`. The holders of those facets (in the bootstrap space) will then be able to invoke the method and observe the results.
  • Loading branch information
mergify[bot] authored Oct 17, 2024
2 parents 94aaebc + 507c979 commit 55a8847
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 150 deletions.
1 change: 1 addition & 0 deletions packages/zoe/src/typeGuards.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ export const AdminFacetI = M.interface('ZcfAdminFacet', {
getVatShutdownPromise: M.call().returns(M.promise()),
restartContract: M.call().optional(M.any()).returns(M.promise()),
upgradeContract: M.call(M.string()).optional(M.any()).returns(M.promise()),
terminateContract: M.call(M.error()).returns(M.promise()),
});

export const SeatDataShape = M.splitRecord(
Expand Down
5 changes: 5 additions & 0 deletions packages/zoe/src/zoeService/startInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,11 @@ export const makeStartInstance = (
E(state.adminNode).upgrade(bCap, { vatParameters }),
);
},
terminateContract(reason) {
const { state } = this;

return E(state.adminNode).terminateWithFailure(reason);
},
},
);

Expand Down
1 change: 1 addition & 0 deletions packages/zoe/src/zoeService/utils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export type AdminFacet<SF extends ContractStartFunction> = FarRef<{
restartContract: Parameters<SF>[1] extends undefined
? () => Promise<VatUpgradeResults>
: (newPrivateArgs: Parameters<SF>[1]) => Promise<VatUpgradeResults>;
terminateContract: (Error) => void;
}>;

export type StartParams<SF> = SF extends ContractStartFunction
Expand Down
5 changes: 4 additions & 1 deletion packages/zoe/test/swingsetTests/zoe/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const makeVats = (log, vats, zoe, installations, startingValues) => {
aliceP,
bobP,
};
let logMessage = 'alice and bob';

if (carolValues) {
const carolP = E(vats.carol).build(
Expand All @@ -61,6 +62,7 @@ const makeVats = (log, vats, zoe, installations, startingValues) => {
timer,
);
result.carolP = carolP;
logMessage = 'alice, bob, and carol';
}

if (daveValues) {
Expand All @@ -72,9 +74,10 @@ const makeVats = (log, vats, zoe, installations, startingValues) => {
timer,
);
result.daveP = daveP;
logMessage = 'alice, bob, carol and dave';
}

log(`=> alice, bob, carol and dave are set up`);
log(`=> ${logMessage} are set up`);
return harden(result);
};

Expand Down
194 changes: 194 additions & 0 deletions packages/zoe/test/swingsetTests/zoe/snapshots/zoe.test.js.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# Snapshot report for `test/swingsetTests/zoe/zoe.test.js`

The actual snapshot is saved in `zoe.test.js.snap`.

Generated by [AVA](https://avajs.dev).

## zoe - automaticRefund - valid inputs

> Snapshot 1
[
'=> alice and bob are set up',
'=> alice.doCreateAutomaticRefund called',
'The offer was accepted',
'The offer was accepted',
'bobMoolaPurse: balance {"brand":{},"value":0}',
'bobSimoleanPurse: balance {"brand":{},"value":17}',
'aliceMoolaPurse: balance {"brand":{},"value":3}',
'aliceSimoleanPurse: balance {"brand":{},"value":0}',
]

## zoe - coveredCall - valid inputs

> Snapshot 1
[
'=> alice and bob are set up',
'=> alice.doCreateCoveredCall called',
'@@ schedule task for:1, currently: 0 @@',
'The option was exercised. Please collect the assets in your payout.',
'covered call was shut down due to "Swap completed."',
'bobMoolaPurse: balance {"brand":{},"value":3}',
'bobSimoleanPurse: balance {"brand":{},"value":0}',
'aliceMoolaPurse: balance {"brand":{},"value":0}',
'aliceSimoleanPurse: balance {"brand":{},"value":7}',
]

## zoe - swapForOption - valid inputs

> Snapshot 1
[
'=> alice, bob, carol and dave are set up',
'=> alice.doSwapForOption called',
'call option made',
'@@ schedule task for:100, currently: 0 @@',
'swap invitation made',
'The offer has been accepted. Once the contract has been completed, please check your payout',
'The option was exercised. Please collect the assets in your payout.',
'daveMoolaPurse: balance {"brand":{},"value":3}',
'daveSimoleanPurse: balance {"brand":{},"value":0}',
'daveBucksPurse: balance {"brand":{},"value":0}',
'bobMoolaPurse: balance {"brand":{},"value":0}',
'bobSimoleanPurse: balance {"brand":{},"value":0}',
'bobBucksPurse;: balance {"brand":{},"value":1}',
'aliceMoolaPurse: balance {"brand":{},"value":0}',
'aliceSimoleanPurse: balance {"brand":{},"value":7}',
]

## zoe - secondPriceAuction - valid inputs

> Snapshot 1
[
'=> alice, bob, carol and dave are set up',
'Carol: The offer has been accepted. Once the contract has been completed, please check your payout',
'Bob: The offer has been accepted. Once the contract has been completed, please check your payout',
'@@ schedule task for:1, currently: 0 @@',
'Dave: The offer has been accepted. Once the contract has been completed, please check your payout',
'@@ tick:1 @@',
'carolMoolaPurse: balance {"brand":{},"value":0}',
'bobMoolaPurse: balance {"brand":{},"value":1}',
'daveMoolaPurse: balance {"brand":{},"value":0}',
'carolSimoleanPurse: balance {"brand":{},"value":7}',
'bobSimoleanPurse: balance {"brand":{},"value":4}',
'daveSimoleanPurse: balance {"brand":{},"value":5}',
'aliceMoolaPurse: balance {"brand":{},"value":0}',
'aliceSimoleanPurse: balance {"brand":{},"value":7}',
]

## zoe - atomicSwap - valid inputs

> Snapshot 1
[
'=> alice and bob are set up',
'The offer has been accepted. Once the contract has been completed, please check your payout',
'aliceMoolaPurse: balance {"brand":{},"value":0}',
'bobMoolaPurse: balance {"brand":{},"value":3}',
'aliceSimoleanPurse: balance {"brand":{},"value":7}',
'bobSimoleanPurse: balance {"brand":{},"value":0}',
]

## zoe - simpleExchange - valid inputs

> Snapshot 1
[
'=> alice and bob are set up',
'Order Added',
'Order Added',
'bobMoolaPurse: balance {"brand":{},"value":3}',
'bobSimoleanPurse: balance {"brand":{},"value":3}',
'aliceMoolaPurse: balance {"brand":{},"value":0}',
'aliceSimoleanPurse: balance {"brand":{},"value":4}',
]

## zoe - simpleExchange - state Update

> Snapshot 1
[
'=> alice and bob are set up',
'{"buys":[],"sells":[]}',
'{"buys":[],"sells":[{"give":{"Asset":{"brand":{},"value":3}},"want":{"Price":{"brand":{},"value":4}}}]}',
'Order Added',
'{"buys":[],"sells":[]}',
'Order Added',
'bobMoolaPurse: balance {"brand":{},"value":0}',
'bobSimoleanPurse: balance {"brand":{},"value":20}',
'{"buys":[{"give":{"Price":{"brand":{},"value":2}},"want":{"Asset":{"brand":{},"value":8}}}],"sells":[]}',
'Order Added',
'bobMoolaPurse: balance {"brand":{},"value":3}',
'bobSimoleanPurse: balance {"brand":{},"value":18}',
'{"buys":[{"give":{"Price":{"brand":{},"value":2}},"want":{"Asset":{"brand":{},"value":8}}},{"give":{"Price":{"brand":{},"value":13}},"want":{"Asset":{"brand":{},"value":20}}}],"sells":[]}',
'Order Added',
'bobMoolaPurse: balance {"brand":{},"value":3}',
'bobSimoleanPurse: balance {"brand":{},"value":5}',
'{"buys":[{"give":{"Price":{"brand":{},"value":2}},"want":{"Asset":{"brand":{},"value":8}}},{"give":{"Price":{"brand":{},"value":13}},"want":{"Asset":{"brand":{},"value":20}}},{"give":{"Price":{"brand":{},"value":2}},"want":{"Asset":{"brand":{},"value":5}}}],"sells":[]}',
'Order Added',
'bobMoolaPurse: balance {"brand":{},"value":3}',
'bobSimoleanPurse: balance {"brand":{},"value":3}',
'aliceMoolaPurse: balance {"brand":{},"value":0}',
'aliceSimoleanPurse: balance {"brand":{},"value":4}',
]

## zoe - autoswap - valid inputs

> Snapshot 1
[
'=> alice and bob are set up',
'Added liquidity.',
'simoleanAmounts {"brand":{},"value":1}',
'Swap successfully completed.',
'moola proceeds {"brand":{},"value":5}',
'Swap successfully completed.',
'bobMoolaPurse: balance {"brand":{},"value":5}',
'bobSimoleanPurse: balance {"brand":{},"value":5}',
'simoleans required {"brand":{},"value":5}',
'Liquidity successfully removed.',
'poolAmounts{"Central":{"brand":{},"value":0},"Liquidity":{"brand":{},"value":10},"Secondary":{"brand":{},"value":0}}',
'aliceMoolaPurse: balance {"brand":{},"value":8}',
'aliceSimoleanPurse: balance {"brand":{},"value":7}',
'aliceLiquidityTokenPurse: balance {"brand":{},"value":0}',
]

## zoe - sellTickets - valid inputs

> Snapshot 1
[
'=> alice and bob are set up',
'availableTickets: {"brand":{},"value":[{"number":3,"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm"},{"number":2,"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm"},{"number":1,"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm"}]}',
'boughtTicketAmount: {"brand":{},"value":[{"number":1,"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm"}]}',
'after ticket1 purchased: {"brand":{},"value":[{"number":3,"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm"},{"number":2,"show":"Steven Universe, the Opera","start":"Wed, March 25th 2020 at 8pm"}]}',
'alice earned: {"brand":{},"value":22}',
]

## zoe - otcDesk - valid inputs

> Snapshot 1
[
'=> alice and bob are set up',
'Inventory added',
'@@ schedule task for:1, currently: 0 @@',
'The option was exercised. Please collect the assets in your payout.',
'{"brand":{},"value":3}',
'{"brand":{},"value":0}',
'Inventory removed',
'{"brand":{},"value":2}',
]

## zoe - shutdown autoswap

> Snapshot 1
[
'=> alice and bob are set up',
'vat terminated',
'aliceMoolaPurse: balance {"brand":{},"value":0}',
'aliceSimoleanPurse: balance {"brand":{},"value":0}',
]
Binary file not shown.
28 changes: 28 additions & 0 deletions packages/zoe/test/swingsetTests/zoe/vat-alice.js
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,31 @@ const build = async (log, zoe, issuers, payments, installations, timer) => {
await showPurseBalance(simoleanPurseP, 'aliceSimoleanPurse', log);
};

const doShutdownAutoswap = async () => {
const issuerKeywordRecord = harden({
Central: moolaIssuer,
Secondary: simoleanIssuer,
});
const { publicFacet, adminFacet } = await E(zoe).startInstance(
installations.autoswap,
issuerKeywordRecord,
);

console.log(' ALICE terminating autoswap');
await E(adminFacet).terminateContract(Error('end of the line'));

try {
const poolAmountsPre = await E(publicFacet).getPoolAllocation();
console.log('ALICE', poolAmountsPre);
} catch (e) {
console.log('ALICE caught', e.message, e);
log(e.message);
}

await showPurseBalance(moolaPurseP, 'aliceMoolaPurse', log);
await showPurseBalance(simoleanPurseP, 'aliceSimoleanPurse', log);
};

return Far('build', {
startTest: async (testName, bobP, carolP, daveP) => {
switch (testName) {
Expand Down Expand Up @@ -576,6 +601,9 @@ const build = async (log, zoe, issuers, payments, installations, timer) => {
case 'badTimer': {
return doBadTimer();
}
case 'shutdownAutoswap': {
return doShutdownAutoswap();
}
default: {
assert.fail(X`testName ${testName} not recognized`);
}
Expand Down
Loading

0 comments on commit 55a8847

Please sign in to comment.