Skip to content

Commit

Permalink
ETH app upgradeability (paritytech#554)
Browse files Browse the repository at this point in the history
* ETHApp upgradeability and test

* upgradeable erc20 app

* upgradeable erc721 app

* upgradeable DOT app

* add upgrade task

* add renounce task

* add events

* CHANNEL_UPGRADE_ROLE can change CHANNEL_UPGRADE_ROLE

* Fix spelling
  • Loading branch information
wildmolasses authored Jan 26, 2022
1 parent 31ae72d commit 5f02215
Show file tree
Hide file tree
Showing 14 changed files with 448 additions and 26 deletions.
39 changes: 36 additions & 3 deletions ethereum/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,48 @@ cp env.template .env
Run tests on the hardhat network:

```bash
npx hardhat test
yarn test
```

## Deployment

Example: Deploy contracts to a local ganache instance
### Local

Example: Run a local hardhat instance with deployments

```
yarn hardhat node
```
npx hardhat deploy --network localhost

### Ropsten, Mainnet

```
yarn hardhat deploy --network ropsten
```

## Tasks

### Upgrade an app

Example: upgrade an app to use a different set of channels

```sh
# Do something first to deploy new channels, then provide addresses below
yarn hardhat upgrade \
--appaddr 0x9A676e781A523b5d0C0e43731313A708CB607508 \
--basicinbound 0x0000000000000000000000000000000000000001 \
--basicoutbound 0x0000000000000000000000000000000000000002 \
--incinbound 0x0000000000000000000000000000000000000003 \
--incoutbound 0x0000000000000000000000000000000000000004 \
--network localhost # change to ropsten or mainnet accordingly
```

### Renounce ownership of the CHANNEL_UPGRADE_ROLE

```sh
yarn hardhat renounce \
--appaddr 0x9A676e781A523b5d0C0e43731313A708CB607508 \
--network localhost
```

## Autogenerated Documentation
Expand Down
31 changes: 31 additions & 0 deletions ethereum/contracts/DOTApp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ contract DOTApp is FeeSource, AccessControl {
bytes32 public constant INBOUND_CHANNEL_ROLE =
keccak256("INBOUND_CHANNEL_ROLE");

bytes32 public constant CHANNEL_UPGRADE_ROLE =
keccak256("CHANNEL_UPGRADE_ROLE");

event Upgraded(
address upgrader,
Channel basic,
Channel incentivized
);

struct Channel {
address inbound;
address outbound;
Expand All @@ -46,6 +55,9 @@ contract DOTApp is FeeSource, AccessControl {
c2.inbound = _incentivized.inbound;
c2.outbound = _incentivized.outbound;

_setupRole(CHANNEL_UPGRADE_ROLE, msg.sender);
_setRoleAdmin(INBOUND_CHANNEL_ROLE, CHANNEL_UPGRADE_ROLE);
_setRoleAdmin(CHANNEL_UPGRADE_ROLE, CHANNEL_UPGRADE_ROLE);
_setupRole(FEE_BURNER_ROLE, feeBurner);
_setupRole(INBOUND_CHANNEL_ROLE, _basic.inbound);
_setupRole(INBOUND_CHANNEL_ROLE, _incentivized.inbound);
Expand Down Expand Up @@ -97,4 +109,23 @@ contract DOTApp is FeeSource, AccessControl {
_amount.encode256()
);
}

function upgrade(
Channel memory _basic,
Channel memory _incentivized
) external onlyRole(CHANNEL_UPGRADE_ROLE) {
Channel storage c1 = channels[ChannelId.Basic];
Channel storage c2 = channels[ChannelId.Incentivized];
// revoke old channel
revokeRole(INBOUND_CHANNEL_ROLE, c1.inbound);
revokeRole(INBOUND_CHANNEL_ROLE, c2.inbound);
// set new channel
c1.inbound = _basic.inbound;
c1.outbound = _basic.outbound;
c2.inbound = _incentivized.inbound;
c2.outbound = _incentivized.outbound;
grantRole(INBOUND_CHANNEL_ROLE, _basic.inbound);
grantRole(INBOUND_CHANNEL_ROLE, _incentivized.inbound);
emit Upgraded(msg.sender, c1, c2);
}
}
32 changes: 32 additions & 0 deletions ethereum/contracts/ERC20App.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ contract ERC20App is AccessControl {
uint256 amount
);

event Upgraded(
address upgrader,
Channel basic,
Channel incentivized
);

struct Channel {
address inbound;
address outbound;
Expand All @@ -47,6 +53,10 @@ contract ERC20App is AccessControl {
bytes32 public constant INBOUND_CHANNEL_ROLE =
keccak256("INBOUND_CHANNEL_ROLE");

bytes32 public constant CHANNEL_UPGRADE_ROLE =
keccak256("CHANNEL_UPGRADE_ROLE");


constructor(Channel memory _basic, Channel memory _incentivized) {
Channel storage c1 = channels[ChannelId.Basic];
c1.inbound = _basic.inbound;
Expand All @@ -56,6 +66,9 @@ contract ERC20App is AccessControl {
c2.inbound = _incentivized.inbound;
c2.outbound = _incentivized.outbound;

_setupRole(CHANNEL_UPGRADE_ROLE, msg.sender);
_setRoleAdmin(INBOUND_CHANNEL_ROLE, CHANNEL_UPGRADE_ROLE);
_setRoleAdmin(CHANNEL_UPGRADE_ROLE, CHANNEL_UPGRADE_ROLE);
_setupRole(INBOUND_CHANNEL_ROLE, _basic.inbound);
_setupRole(INBOUND_CHANNEL_ROLE, _incentivized.inbound);
}
Expand Down Expand Up @@ -149,4 +162,23 @@ contract ERC20App is AccessControl {
_paraId.encode32()
);
}

function upgrade(
Channel memory _basic,
Channel memory _incentivized
) external onlyRole(CHANNEL_UPGRADE_ROLE) {
Channel storage c1 = channels[ChannelId.Basic];
Channel storage c2 = channels[ChannelId.Incentivized];
// revoke old channel
revokeRole(INBOUND_CHANNEL_ROLE, c1.inbound);
revokeRole(INBOUND_CHANNEL_ROLE, c2.inbound);
// set new channel
c1.inbound = _basic.inbound;
c1.outbound = _basic.outbound;
c2.inbound = _incentivized.inbound;
c2.outbound = _incentivized.outbound;
grantRole(INBOUND_CHANNEL_ROLE, _basic.inbound);
grantRole(INBOUND_CHANNEL_ROLE, _incentivized.inbound);
emit Upgraded(msg.sender, c1, c2);
}
}
30 changes: 30 additions & 0 deletions ethereum/contracts/ERC721App.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ contract ERC721App is AccessControl {
address recipient
);

event Upgraded(
address upgrader,
Channel basic,
Channel incentivized
);

struct Channel {
address inbound;
address outbound;
Expand All @@ -41,6 +47,8 @@ contract ERC721App is AccessControl {
bytes32 public constant INBOUND_CHANNEL_ROLE =
keccak256("INBOUND_CHANNEL_ROLE");

bytes32 public constant CHANNEL_UPGRADE_ROLE =
keccak256("CHANNEL_UPGRADE_ROLE");
constructor(Channel memory _basic, Channel memory _incentivized) {
Channel storage c1 = channels[ChannelId.Basic];
c1.inbound = _basic.inbound;
Expand All @@ -50,6 +58,9 @@ contract ERC721App is AccessControl {
c2.inbound = _incentivized.inbound;
c2.outbound = _incentivized.outbound;

_setupRole(CHANNEL_UPGRADE_ROLE, msg.sender);
_setRoleAdmin(INBOUND_CHANNEL_ROLE, CHANNEL_UPGRADE_ROLE);
_setRoleAdmin(CHANNEL_UPGRADE_ROLE, CHANNEL_UPGRADE_ROLE);
_setupRole(INBOUND_CHANNEL_ROLE, _basic.inbound);
_setupRole(INBOUND_CHANNEL_ROLE, _incentivized.inbound);
}
Expand Down Expand Up @@ -131,4 +142,23 @@ contract ERC721App is AccessControl {
bytes1(0x00) // Use an empty _tokenURI instead of SCALE encoded _tokenURI
);
}

function upgrade(
Channel memory _basic,
Channel memory _incentivized
) external onlyRole(CHANNEL_UPGRADE_ROLE) {
Channel storage c1 = channels[ChannelId.Basic];
Channel storage c2 = channels[ChannelId.Incentivized];
// revoke old channel
revokeRole(INBOUND_CHANNEL_ROLE, c1.inbound);
revokeRole(INBOUND_CHANNEL_ROLE, c2.inbound);
// set new channel
c1.inbound = _basic.inbound;
c1.outbound = _basic.outbound;
c2.inbound = _incentivized.inbound;
c2.outbound = _incentivized.outbound;
grantRole(INBOUND_CHANNEL_ROLE, _basic.inbound);
grantRole(INBOUND_CHANNEL_ROLE, _incentivized.inbound);
emit Upgraded(msg.sender, c1, c2);
}
}
31 changes: 31 additions & 0 deletions ethereum/contracts/ETHApp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ contract ETHApp is RewardSource, AccessControl {

event Unlocked(bytes32 sender, address recipient, uint256 amount);

event Upgraded(
address upgrader,
Channel basic,
Channel incentivized
);

bytes2 constant MINT_CALL = 0x4101;

bytes32 public constant REWARD_ROLE = keccak256("REWARD_ROLE");
Expand All @@ -39,6 +45,9 @@ contract ETHApp is RewardSource, AccessControl {
bytes32 public constant INBOUND_CHANNEL_ROLE =
keccak256("INBOUND_CHANNEL_ROLE");

bytes32 public constant CHANNEL_UPGRADE_ROLE =
keccak256("CHANNEL_UPGRADE_ROLE");

constructor(
address rewarder,
Channel memory _basic,
Expand All @@ -53,6 +62,9 @@ contract ETHApp is RewardSource, AccessControl {
c2.inbound = _incentivized.inbound;
c2.outbound = _incentivized.outbound;

_setupRole(CHANNEL_UPGRADE_ROLE, msg.sender);
_setRoleAdmin(INBOUND_CHANNEL_ROLE, CHANNEL_UPGRADE_ROLE);
_setRoleAdmin(CHANNEL_UPGRADE_ROLE, CHANNEL_UPGRADE_ROLE);
_setupRole(REWARD_ROLE, rewarder);
_setupRole(INBOUND_CHANNEL_ROLE, _basic.inbound);
_setupRole(INBOUND_CHANNEL_ROLE, _incentivized.inbound);
Expand Down Expand Up @@ -148,4 +160,23 @@ contract ETHApp is RewardSource, AccessControl {
(bool success, ) = _recipient.call{value: _amount}("");
require(success, "Unable to send Ether");
}

function upgrade(
Channel memory _basic,
Channel memory _incentivized
) external onlyRole(CHANNEL_UPGRADE_ROLE) {
Channel storage c1 = channels[ChannelId.Basic];
Channel storage c2 = channels[ChannelId.Incentivized];
// revoke old channel
revokeRole(INBOUND_CHANNEL_ROLE, c1.inbound);
revokeRole(INBOUND_CHANNEL_ROLE, c2.inbound);
// set new channel
c1.inbound = _basic.inbound;
c1.outbound = _basic.outbound;
c2.inbound = _incentivized.inbound;
c2.outbound = _incentivized.outbound;
grantRole(INBOUND_CHANNEL_ROLE, _basic.inbound);
grantRole(INBOUND_CHANNEL_ROLE, _incentivized.inbound);
emit Upgraded(msg.sender, c1, c2);
}
}
10 changes: 5 additions & 5 deletions ethereum/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import "@nomiclabs/hardhat-web3";
import "@nomiclabs/hardhat-etherscan";
import "hardhat-deploy";
import { HardhatUserConfig } from "hardhat/config";
import "./tasks/upgrade";
import "./tasks/renounce";

const getenv = (name: string) => {
if (name in process.env) {
Expand All @@ -25,15 +27,13 @@ const etherscanKey = getenv("ETHERSCAN_API_KEY");

const config: HardhatUserConfig = {
networks: {
hardhat: {
throwOnTransactionFailures: true,
},
hardhat: {},
localhost: {
url: "http://127.0.0.1:8545",
accounts: {
mnemonic: "stone speak what ritual switch pigeon weird dutch burst shaft nature shove",
mnemonic: "stone speak what ritual switch pigeon weird dutch burst shaft nature shove"
},
chainId: 15,
chainId: 31337,
},
ropsten: {
chainId: 3,
Expand Down
8 changes: 4 additions & 4 deletions ethereum/package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"license": "Apache-2.0",
"scripts": {
"test": "npx hardhat test",
"coverage": "npx hardhat coverage",
"lint": "npx solhint 'contracts/**/*.sol'"
"test": "hardhat test",
"coverage": "hardhat coverage",
"lint": "solhint 'contracts/**/*.sol'"
},
"dependencies": {
"@iarna/toml": "^2.2.5",
Expand Down Expand Up @@ -47,4 +47,4 @@
"typescript": "^4.3.2",
"web3": "^1.3.6"
}
}
}
14 changes: 14 additions & 0 deletions ethereum/tasks/renounce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { task } from "hardhat/config";
import { TaskArguments } from "hardhat/types";

task("renounce")
.addParam("appaddr", "The app you want to renounce ownership of")
.setAction(async ({appaddr}: TaskArguments, { ethers }) => {
const accounts = await ethers.getSigners();
const app = await ethers.getContractAt(["function renounceRole(bytes32 role, address account)"], appaddr)
const tx = await app.renounceRole(ethers.utils.keccak256(ethers.utils.toUtf8Bytes("CHANNEL_UPGRADE_ROLE")), accounts[0].address);
console.log(`tx submitted... https://etherscan.io/tx/${tx.hash}`);
const receipt = await tx.wait();
console.log(`tx mined, receipt:`);
console.log(JSON.stringify(receipt, undefined, 2))
});
21 changes: 21 additions & 0 deletions ethereum/tasks/upgrade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Signer } from "@ethersproject/abstract-signer";
import { task } from "hardhat/config";
import { TaskArguments } from "hardhat/types";
task("upgrade")
.addParam("appaddr", "The app you want to upgrade")
.addParam("basicinbound")
.addParam("basicoutbound")
.addParam("incinbound")
.addParam("incoutbound")
.setAction(async ({appaddr, basicinbound, basicoutbound, incinbound, incoutbound}: TaskArguments, { ethers }) => {
const accounts: Signer[] = await ethers.getSigners();
const app = await ethers.getContractAt(["function upgrade(tuple(address inbound, address outbound) _basic, tuple(address inbound, address outbound) _incentivized)"], appaddr)
const tx = await app.upgrade(
[basicinbound, basicoutbound],
[incinbound, incoutbound]
);
console.log(`tx submitted... https://etherscan.io/tx/${tx.hash}`);
const receipt = await tx.wait();
console.log(`tx mined, receipt:`);
console.log(JSON.stringify(receipt, undefined, 2))
});
Loading

0 comments on commit 5f02215

Please sign in to comment.