Skip to content

Commit

Permalink
docs: Adds wip offboarding docs
Browse files Browse the repository at this point in the history
  • Loading branch information
pmerkleplant committed Sep 25, 2024
1 parent cd77972 commit 56f0f00
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 71 deletions.
70 changes: 70 additions & 0 deletions docs/Management.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ This document describes how to manage deployed `Scribe` and `ScribeOptimistic` i
- [`IAuth::deny`](#iauthdeny)
- [`IToll::kiss`](#itollkiss)
- [`IToll::diss`](#itolldiss)
- [Offboarding](#offboarding)
- [Deactivation](#deactivation)
- [Fund Rescue](#fund-resuce)
- [Killing](#killing)

## Environment Variables

Expand Down Expand Up @@ -275,3 +279,69 @@ $ forge script \
-vvv \
script/${SCRIBE_FLAVOUR}.s.sol:${SCRIBE_FLAVOUR}Script
```

## Offboarding

Offboarding a Scribe(Optimistic) instance simply means _Chronicle Protocol_ is not guaranteeing pokes anymore, ie the oracle is not being updated anymore.

However, to ensure an offboarded Scribe(Optimistic) instance may not behave unexpectedly it needs to be deactivated. Furthermore, if the contract is a ScribeOptimistic instance ETH held by the contract may need to be rescued. If its certain the contract will never be used again it is recommended to kill it.

### Deactivation

Deactivating a Scribe(Optimistic) instance means its value is set to zero, leading all `read()` calls to revert/fail, no feeds are lifted, and `bar` is set to `255`.

Note that one or more addresses still hold `auth` on the contract meaning the instance can be reactivated via `lift`-ing feeds and updating `bar` again.

> [!IMPORTANT]
>
> Deactivation requires running two distinct `forge script` commands.
>
> It is of utmost importance to run both commands and NOT leave the Scribe(Optimistic) instance in an undefined state.
Step 1:

```bash
$ forge script \
--keystore "$KEYSTORE" \
--password "$KEYSTORE_PASSWORD" \
--broadcast \
--rpc-url "$RPC_URL" \
--sig $(cast calldata "deactivate_Step1(address)" "$SCRIBE") \
-vvv \
script/${SCRIBE_FLAVOUR}.s.sol:${SCRIBE_FLAVOUR}Script
```

Step 2:

```bash
$ forge script \
--keystore "$KEYSTORE" \
--password "$KEYSTORE_PASSWORD" \
--broadcast \
--rpc-url "$RPC_URL" \
--sig $(cast calldata "deactivate_Step2(address)" "$SCRIBE") \
-vvv \
script/${SCRIBE_FLAVOUR}.s.sol:${SCRIBE_FLAVOUR}Script
```

### Fund Rescue

TODO: Rescuing funds

### Killing

Killing a deactivated Scribe(Optimistic) instance ensures it cannot be activated again. Note that killing an instance makes the contract's state immutable via `deny`-ing `auth` for every address.

Run:

```bash
$ forge script \
--keystore "$KEYSTORE" \
--password "$KEYSTORE_PASSWORD" \
--broadcast \
--rpc-url "$RPC_URL" \
--sig $(cast calldata "kill(address)" "$SCRIBE") \
--sender $(cast wallet address --keystore $KEYSTORE --password $KEYSTORE_PASSWORD) \
-vvv \
script/${SCRIBE_FLAVOUR}.s.sol:${SCRIBE_FLAVOUR}Script
```
107 changes: 36 additions & 71 deletions script/Scribe.s.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

import {Vm} from "forge-std/Vm.sol";
import {Script} from "forge-std/Script.sol";
import {console2} from "forge-std/console2.sol";

Expand Down Expand Up @@ -268,42 +269,22 @@ contract ScribeScript is Script {

// -- Offboarding Functions --

/// @dev !!! DANGER !!!
///
/// @dev Deactivates instance `self`.
/// @dev Step 1 of deactivating instance `self`.
///
/// @dev Deactivating an instance means:
/// - Its value is zero
/// - There are no lifted feeds
/// - Bar is set to type(uint8).max
///
/// @dev Note that function _must_ be executed with the `--slow` and
/// `--skip-simulation` flags!
///
/// @dev Expected environment variables:
/// - PRIVATE_KEY
/// - The private key to use
/// - RPC_URL
/// - The RPC URL of an EVM node
/// - SCRIBE
/// - The Scribe instance to deactivate
/// - SCRIBE_FLAVOUR
/// - The Scribe instance's flavour, either "Scribe" or
/// "ScribeOptimistic"
/// @dev Note that deactivation MUST be executed via two distinct
/// `forge script` due to forge script's hard requirement to perform
/// a simulation. However, simulating deactivation fails because
/// the `poke(0)` MUST happen after (in terms of timestamp) the
/// `setBar(1)` call. Note that `forge script` simulations run as a
/// single tx and therefore at the same timestamp.
///
/// @dev Run via:
///
/// ```bash
/// $ forge script \
/// --private-key $PRIVATE_KEY \
/// --broadcast \
/// --rpc-url $RPC_URL \
/// --sig $(cast calldata "deactivate(address)" "$SCRIBE") \
/// --slow \
/// --skip-simulation \
/// script/${SCRIBE_FLAVOUR}.s.sol:${SCRIBE_FLAVOUR}Script
/// ```
function deactivate(address self) public {
/// For more info, see https://github.com/foundry-rs/foundry/issues/6825.
function deactivate_Step1(address self) public {
// Get lifted feeds and compute their feed ids.
address[] memory feeds = IScribe(self).feeds();
uint8[] memory feedIds = new uint8[](feeds.length);
Expand All @@ -316,6 +297,14 @@ contract ScribeScript is Script {
IScribe(self).drop(feedIds);
vm.stopBroadcast();

// Set bar to 1.
vm.startBroadcast();
IScribe(self).setBar(uint8(1));
vm.stopBroadcast();
}

/// @dev Step 2 of deactivating instance `self`.
function deactivate_Step2(address self) public {
// Create new random private key.
uint privKeySeed = LibRandom.readUint();
uint privKey = _bound(privKeySeed, 1, LibSecp256k1.Q() - 1);
Expand All @@ -332,11 +321,6 @@ contract ScribeScript is Script {
IScribe(self).lift(feed.pubKey, ecdsaData);
vm.stopBroadcast();

// Set bar to 1.
vm.startBroadcast();
IScribe(self).setBar(uint8(1));
vm.stopBroadcast();

// Create and sign pokeData with value of zero.
//
// Note that this disables Scribe's read functions.
Expand Down Expand Up @@ -364,54 +348,35 @@ contract ScribeScript is Script {
vm.stopBroadcast();
}

/// @dev !!! DANGER !!!
///
/// @dev Kills instance `self`.
///
/// @dev Killing an instance means:
/// - Its deactivated
/// - There are no auth'ed addresses
///
/// Note that this means the ownership of the contract is waived while
/// ensuring its value can never be updated again.
///
/// @dev Note that function _must_ be executed with the `--slow` and
/// `--skip-simulation` flags!
/// @dev Expects instance `self` to be deactivated.
///
/// @dev Expected environment variables:
/// - PRIVATE_KEY
/// - The private key to use
/// - RPC_URL
/// - The RPC URL of an EVM node
/// - SCRIBE
/// - The Scribe instance to kill
/// - SCRIBE_FLAVOUR
/// - The Scribe instance's flavour, either "Scribe" or
/// "ScribeOptimistic"
///
/// @dev Run via:
///
/// ```bash
/// $ forge script \
/// --private-key $PRIVATE_KEY \
/// --broadcast \
/// --rpc-url $RPC_URL \
/// --sig $(cast calldata "kill(address)" "$SCRIBE") \
/// --slow \
/// --skip-simulation \
/// script/${SCRIBE_FLAVOUR}.s.sol:${SCRIBE_FLAVOUR}Script
/// ```
/// @dev Note to only call using the `--sender` flag!
function kill(address self) public {
// Deactivate self.
deactivate(self);
// Require self to be deactivated.
{
vm.prank(address(0));
(bool ok, /*val*/ ) = IScribe(self).tryRead();
require(!ok, "Instance not deactivated: read() does not fail");

require(
IScribe(self).feeds().length == 0,
"Instance not deactivated: Feeds still lifted"
);
require(
IScribe(self).bar() == 255,
"Instance not deactivated: Bar not type(uint8).max"
);
}

// Get list of auth'ed addresses.
address[] memory authed = IAuth(self).authed();

// Renounce auth for each address except msg.sender.
//
// Note that msg.sender refers to the current script's caller, i.e. the
// address signing the actual txs.
// address signing the actual txs, iff the `--sender` flag was used.
for (uint i; i < authed.length; i++) {
if (authed[i] == msg.sender) {
continue;
Expand Down

0 comments on commit 56f0f00

Please sign in to comment.