Skip to content

Commit

Permalink
Refactor: cleanup for release (#2)
Browse files Browse the repository at this point in the history
* refactor: clean up code and tests

* docs: update `DropSpell` natspec

* refactor: remove custom test script

* refactor: update Github actions to run automatically

* forge install: dss-test

* refactor: remove forge-std direct dependency

* refactor: replace forge-st/Test with dss-test/DssTest

* feat: add deployment script

* refactor: reorganize helper test contracts

* refactor: remove redundant internal function

* refactor: extract `EmergencyDropSpell` into its own file

* test: add aftermath condition to `Protego.drop` test cases

* refactor: remove methods relying on conforming spells

* chore: update NatSpec and remove unused interfaces

* refactor: add test coverage for drop spell `done()`

* docs: fix README format [skip CI]

* docs: update README structure

* refactor: reorg and add events to `EmergencyDropSpell`

* refactor: add explicity error msg for `expectRevert`

* refactor: remove unsed method from `test/NonConformingSpell`

* docs: improve natspec structure (skip ci)
  • Loading branch information
amusingaxl authored May 27, 2024
1 parent 5d3d6bb commit fc1f94c
Show file tree
Hide file tree
Showing 20 changed files with 550 additions and 492 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FOUNDRY_ROOT_CHAINID='number: the ID of the chain'
FOUNDRY_EXPORTS_OVERWRITE_LATEST='bool: whether to override the latest deployment or not'
8 changes: 7 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
name: test

on: workflow_dispatch
on:
push:
branches:
- master
- main
pull_request:

env:
FOUNDRY_PROFILE: ci
ETH_RPC_URL: ${{ secrets.ETH_RPC_URL }}

jobs:
check:
Expand Down
14 changes: 10 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@
cache/
out/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/
# Ignores broadcast logs
broadcast/

# Ignores script config
script/input/**/*.json
!script/input/**/template-*.json
script/output/**/*.json

# Docs
docs/

# Dotenv file
.env
7 changes: 3 additions & 4 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
branch = v1.1.1
[submodule "lib/dss-test"]
path = lib/dss-test
url = https://github.com/makerdao/dss-test
3 changes: 0 additions & 3 deletions Makefile

This file was deleted.

71 changes: 41 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,59 @@
# protego
# Protego

Protego is a tool to permissionlessly deploy a spell which can be used to drop a particular plan in MakerDAO's Pause contract, or in extreme cases the protego contract can itself be lifted to the hat, allowing any user to permissionlessly drop any scheduled plan.
Protego is a tool to permissionlessly deploy a spell which can be used to drop a particular plan in the `MCD_PAUSE`
contract, or in extreme cases the `Protego` contract can itself be lifted to the hat, allowing any user to
permissionlessly drop any scheduled plan.

## Usage

### Drop an individual plan

Protego contains a factory to permissionlessly create a spell that is able to drop a single plan in the [pause](https://github.com/dapphub/ds-pause).

* `function deploy(DSSpellLike _spell) external returns (address)`
## Context

Used to deploy a single spell that can be elected to a privileged position. This requires that the `_spell` address parameter conforms to a MakerDAO conformant [spell](https://github.com/makerdao/spells-mainnet)
- A `plan` is a scheduled `delegatecall` that exists in [`MCD_PAUSE`](https://github.com/makerdao/ds-pause) and can be
permissionlessly executed.
- Plans in `MCD_PAUSE` are [identified by a unique 32 byte hash](https://github.com/makerdao/ds-pause/blob/master/src/pause.sol#L99).
- A conforming spell here is one where the values returned by `action()`, `tag()`, `sig()` and `eta()` are equal to the
corresponding `plan`, `usr`, `tag`, `fax` and `eta` values respectively. Bad actors could potentially manipulate the
return values of these functions such that they are not equal, which would result in a non-conforming spell.
- Users interacting with `Protego` should **always** assume the spell they want to drop is non-conforming and fetch the
parameters directly from the logs created by [`pause.plot()`](https://etherscan.deth.net/address/0xbe286431454714f511008713973d3b053a2d38f3#L189-L203)

* `function deploy(address _usr, bytes32 _tag, bytes memory _fax, uint256 _eta) external returns (address)`

Used to deploy a single spell that can be elected to a privileged position. This can be used to drop a non-conformant spell by reproducing the associated components of the plan.

### Drop any plan
## Usage

In the case of a governance attack, many spells could be plotted quickly by an attacker. To ameliorate this risk, the `protego` contract itself can be elected to a hat role, which permits any user to permissionlessly drop any plan.
### 1. Create a single drop spell

* `function drop(DSSpellLike _spell) external`
```solidity
deploy(address _usr, bytes32 _tag, bytes memory _fax, uint256 _eta)(address)
```

Drop a conformant spell from the pause.
If there is sufficient ecosystem interest to cancel (`drop`) a particular scheduled set of actions (the target `plan`),
Protego contains a factory to permissionlessly create a spell ("Emergency Drop Spell"), which – upon being given
the hat – is able to drop the targeted plan in `MCD_PAUSE`.

* `function drop(address _usr, bytes32 _tag, bytes memory _fax, uint256 _eta) public`
### 2. Enable permissionless dropping of any plan

Drop a plan from the pause by reproducing the associated components of the plan.
```solidity
drop(address _usr, bytes32 _tag, bytes memory _fax, uint256 _eta)
```

## Additional Functions
In case of a governance attack, numerous spells could be created and planned by an attacker at a rate faster than a
single hat spell can `drop` them. To mitigate this risk, the `Protego` contract itself can be given the hat, which
allows any user to permissionlessly drop any plan.

### ID
It will **immediately** drop a plan in `MCD_PAUSE` using the parameters provided.

* `function id(DSSpellLike _spell) public view returns (bytes32)`
* `function id(address _usr, bytes32 _tag, bytes memory _fax, uint256 _eta) public pure returns (bytes32)`
## Additional functions

Return the `bytes32` id of a plan in the pause.
### `id()`

### Planned
```solidity
id(address _usr, bytes32 _tag, bytes memory _fax, uint256 _eta)(bytes32)
```

* `function planned(DSSpellLike _spell) public view returns (bool)`
* `function planned(address _usr, bytes32 _tag, bytes memory _fax, uint256 _eta) public view returns (bool)`
* `function planned(bytes32 _id) public view returns (bool)`
Returns the `bytes32` id of a given plan using the same method that `hash` does in `MCD_PAUSE`.

Returns `true` if a plan has been plotted.
### `planned()`

```solidity
planned(address _usr, bytes32 _tag, bytes memory _fax, uint256 _eta)(bool)
planned(bytes32 _id)(bool)
```

Returns `true` if a plan has been scheduled for execution.
18 changes: 14 additions & 4 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
[profile.default]
src = 'src'
out = 'out'
libs = ['lib']
src = "src"
out = "out"
script = 'script'
libs = ["lib"]
solc = '0.8.16'
optimizer = true

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
fs_permissions = [
{ access = "read", path = "./out/" },
{ access = "read", path = "./script/input/" },
{ access = "read-write", path = "./script/output/" }
]

[rpc_endpoints]
mainnet = "${ETH_RPC_URL}"
1 change: 1 addition & 0 deletions lib/dss-test
Submodule dss-test added at 6d4029
1 change: 0 additions & 1 deletion lib/forge-std
Submodule forge-std deleted from 054a25
43 changes: 43 additions & 0 deletions script/ProtegoDeploy.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: © 2023 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.16;

import {Script} from "forge-std/Script.sol";
import {MCD, DssInstance} from "dss-test/MCD.sol";
import {ScriptTools} from "dss-test/ScriptTools.sol";
import {ProtegoDeploy, ProtegoDeployParams} from "./dependencies/ProtegoDeploy.sol";
import {ProtegoInstance} from "./dependencies/ProtegoInstance.sol";

contract ProtegoDeployScript is Script {
using ScriptTools for string;

string constant NAME = "protego";

address constant CHAINLOG = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F;
DssInstance dss = MCD.loadFromChainlog(CHAINLOG);
address pause = dss.chainlog.getAddress("MCD_PAUSE");
ProtegoInstance inst;

function run() external {
vm.startBroadcast();

inst = ProtegoDeploy.deploy(ProtegoDeployParams({pause: pause}));

vm.stopBroadcast();

ScriptTools.exportContract(NAME, "protego", inst.protego);
}
}
30 changes: 30 additions & 0 deletions script/dependencies/ProtegoDeploy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: © 2023 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.16;

import {ScriptTools} from "dss-test/ScriptTools.sol";
import {Protego} from "src/Protego.sol";
import {ProtegoInstance} from "./ProtegoInstance.sol";

struct ProtegoDeployParams {
address pause;
}

library ProtegoDeploy {
function deploy(ProtegoDeployParams memory p) internal returns (ProtegoInstance memory r) {
r.protego = address(new Protego(p.pause));
}
}
21 changes: 21 additions & 0 deletions script/dependencies/ProtegoInstance.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: © 2023 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.16;

struct ProtegoInstance {
address protego;
}

1 change: 1 addition & 0 deletions script/input/1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Inputs for Mainnet scripts.
1 change: 1 addition & 0 deletions script/output/1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Outputs for Mainnet scripts.
13 changes: 0 additions & 13 deletions script/test.sh

This file was deleted.

Loading

0 comments on commit fc1f94c

Please sign in to comment.