Skip to content

Commit

Permalink
doc: Address comments on deploy steps doc
Browse files Browse the repository at this point in the history
  • Loading branch information
victorges committed Oct 11, 2023
1 parent b2f44e0 commit 0174938
Show file tree
Hide file tree
Showing 2 changed files with 285 additions and 16 deletions.
80 changes: 64 additions & 16 deletions doc/deploy_delta.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
# Livepeer Delta upgrade deployment steps

## Step 1: Deploy contracts
## Step 1: Setup deployer account

Configure the private key for an account that has enough ETH to run the deploy transactions.

```
export PRIVATE_KEY=... (no 0x prefix)
```

Alternatively you can set it in the `.env` file in your repo. All commands listed below expect the same account to be configured.

## Step 2: Deploy contracts

Run the deployment script to deploy the new contracts:

Expand All @@ -10,7 +20,21 @@ npx hardhat deploy --tags DELTA_UPGRADE --network arbitrumMainnet

You must configure the private key for an account that has enough ETH to run the transactions.

## Step 2: Prepare governance update
## Step 3: Verify contracts source code

Verify the contracts source code on Arbiscan:

```
yarn etherscan-verify --network arbitrumMainnet BondingVotesTarget BondingVotes Treasury LivepeerGovernorTarget LivepeerGovernor BondingManagerTarget
```

Then check the contracts on Arbiscan to make sure the source code is verified and matches the expected code (compare
with `npx hardhat flatten`).

Also check the `Treasury` configuration, the only non-proxied contract, to make sure it wasn't manipulated by anyone
before the `initialize` function was called.

## Step 4: Prepare governance update

1. Grab the full git hash from the current repository where you ran the deployment script, or get it from the output of
the pending governance actions.
Expand All @@ -20,39 +44,63 @@ You must configure the private key for an account that has enough ETH to run the
a) the `0xPENDING_ADDRESS` address entries on the `updates/addresses.js` file with the deployed contracts.
b) the `0xPENDING_GIT_HASH` references in `updates/l2-lip-delta-91-92.js` with the git hash from the protocol
repository.
4. Commit and push the changes to the [`governance-scripts` PR](https://github.com/livepeer/governor-scripts/pull/7) and later merge it before deploy.

## Step 5.1: Simulate governance update on a fork (optional)

Make a fork of mainnet after the contracts deploy and make sure the Governance script can run cleanly. Also run the
validation script from Step 8 below to make sure everything is configured correctly.

## Step 6: Renounce deployer admin role

Once the governance update has been executed and the update validated (e.g. wait for next round and some treasury
contributions to take place as expected), the deployer admin role should be renounced.

## Step 3: Run governance update
To do so, run the following command with the same account with which you ran the deploy:

```
npx hardhat treasury-renounce-admin-role --network arbitrumMainnet
```

## Step 7: Run governance update

In the `governance-scripts` repository, run the governance update script:

```
node index.js create ./updates/l2-lip-delta-91-92.js 0
```

This will print out the transaction that should be run by the governor owner to stage the update
on the governance contract.
This will print out the transaction that should be run by the **protocol** governor owner to stage the update. Note that
this refers to the existing `Governor` contract that manages the protocol governance, not the new `LivepeerGovernor`
that will only manage the treasury for now.

This should now be provided to the wallet interface to stage and then execute the governance update.
This output should be provided to the governor owner wallet interface to stage and then execute the governance update.

## Step 4: Validate the update
## Step 8: Validate the update

You can run the task at
[https://github.com/livepeer/protocol/blob/vg/fork-test-2/tasks/verify-delta-deployment.ts](verify-delta-deployment.ts)
[../tasks/verify-delta-deployment.ts](verify-delta-deployment.ts)
to verify the deployment and governance update.

To do so, copy that task to your local task folder and then run:
To do so, run this with the same `deployer` account with which you ran the deploy:

```
npx hardhat verify-delta-deployment --network arbitrumMainnet
```

## Step 5: Renounce deployer admin role
Keep in mind that it makes a voting power checkpoint of the top-stake orchestrator, in case they don't have one yet.

Once the governance update has been executed and the update validated (e.g. wait for next round and some treasury
contributions to take place as expected), the deployer admin role should be renounced.
## Step 9: Monitor the behavior

To do so, run the following command with the same account with which you ran the deploy:
Now wait until the next round so that the treasury reward cut rate gets updated. Check reward calls from orchestrators
and make sure they are being properly discounted by the expected 10% cut.

```
npx hardhat treasury-renounce-admin-role --network arbitrumMainnet
```
Also worth running the `verify-delta-deployment` script again and checking its output.

## Step 10: Deploy subgraph and explorer

Update the subgraph and explorer changes with any new contract addresses, merge them and deploy to production. This is
less risky as we can iterate quickly. Open PRs:

- https://github.com/livepeer/subgraph/pull/157
- https://github.com/livepeer/explorer/pull/224
221 changes: 221 additions & 0 deletions tasks/verify-delta-deployment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
import {task} from "hardhat/config"
import {
BondingManager,
BondingVotes,
Controller,
LivepeerGovernor,
RoundsManager,
Treasury
} from "../typechain"
import {contractId} from "../utils/helpers"
import {constants} from "../utils/constants"
import {ethers} from "ethers"

const expected = {
bondingManager: {
nextRoundTreasuryRewardCutRate: constants.PERC_DIVISOR_PRECISE.div(10),
treasuryBalanceCeiling: ethers.utils.parseEther("750000")
},
livepeerGovernor: {
name: "LivepeerGovernor",
votingDelay: 1,
votingPeriod: 10,
proposalThreshold: ethers.utils.parseEther("100"),
quorumNumerator: 333300, // 33.33%
quorumDenominator: 1000000,
quota: 500000 // 50%
},
treasury: {
minDelay: 0
}
}

task(
"verify-delta-deployment",
"Verifies deployment of Delta upgrade contracts (LIP-91 and LIP-92)"
)
.addOptionalPositionalParam("substep", "Substep to verify (unimplemented)")
.setAction(async (taskArgs, hre) => {
const {ethers, deployments} = hre

const controller = await deployments.get("Controller")
const Controller: Controller = await hre.ethers.getContractAt(
"Controller",
controller.address
)

const getContract = async <T extends ethers.Contract>(
name: string
): Promise<T> => {
const address = await Controller.getContract(contractId(name))
return await ethers.getContractAt(name, address)
}

const checkParam = async (
name: string,
actual: { toString: () => string },
expected: { toString: () => string }
) => {
console.log(`${name} is ${actual}`)

if (actual.toString() !== expected.toString()) {
throw new Error(`${name} is ${actual} but expected ${expected}`)
}
}

const BondingManager: BondingManager = await getContract(
"BondingManager"
)

const params = {
treasuryRewardCutRate: await BondingManager.treasuryRewardCutRate(),
nextRoundTreasuryRewardCutRate:
await BondingManager.nextRoundTreasuryRewardCutRate(),
treasuryBalanceCeiling:
await BondingManager.treasuryBalanceCeiling()
}

await checkParam(
"BondingManager.nextRoundTreasuryRewardCutRate",
params.nextRoundTreasuryRewardCutRate,
expected.bondingManager.nextRoundTreasuryRewardCutRate
)

await checkParam(
"BondingManager.treasuryBalanceCeiling",
params.treasuryBalanceCeiling,
expected.bondingManager.treasuryBalanceCeiling
)

if (
params.treasuryRewardCutRate.eq(
params.nextRoundTreasuryRewardCutRate
)
) {
console.log(
"Treasury reward cut rate of 10% already propagated to current round"
)
} else {
console.log("Treasury reward cut rate hasn't propagated yet")

const RoundsManager: RoundsManager = await getContract(
"RoundsManager"
)
const initialized = await RoundsManager.currentRoundInitialized()
if (!initialized) {
console.log(
"Missing only current round initialization. Call RoundsManager.initializeRound()"
)
} else {
const currentRound = await RoundsManager.currentRound()
const nextRound = currentRound.add(1)
const currRoundStartBlock =
await RoundsManager.currentRoundStartBlock()
const nextRoundStartBlock = currRoundStartBlock.add(
await RoundsManager.roundLength()
)
const currBlock = await RoundsManager.blockNum()

console.log(
`Cut rate will be initialized on round ${nextRound} starting at block ${nextRoundStartBlock} (${nextRoundStartBlock.sub(
currBlock
)} blocks left)`
)
}
}

const LivepeerGovernor: LivepeerGovernor = await getContract(
"LivepeerGovernor"
)
const actual = {
name: await LivepeerGovernor.name(),
votingDelay: await LivepeerGovernor.votingDelay(),
votingPeriod: await LivepeerGovernor.votingPeriod(),
proposalThreshold: await LivepeerGovernor.proposalThreshold(),
quorumNumerator: await LivepeerGovernor["quorumNumerator()"](),
quorumDenominator: await LivepeerGovernor.quorumDenominator(),
quota: await LivepeerGovernor.quota()
}

const allParams = Object.keys(
expected.livepeerGovernor
) as (keyof typeof expected.livepeerGovernor)[] // ts sorcery
for (const param of allParams) {
await checkParam(
`LivepeerGovernor.${param}`,
actual[param],
expected.livepeerGovernor[param]
)
}

const Treasury: Treasury = await getContract("Treasury")
await checkParam(
"LivepeerGovernor.timelock",
await LivepeerGovernor.timelock(),
Treasury.address
)
await checkParam(
"Treasury.minDelay",
await Treasury.getMinDelay(),
expected.treasury.minDelay
)

const roles = {
proposer: await Treasury.PROPOSER_ROLE(),
canceller: await Treasury.CANCELLER_ROLE(),
executor: await Treasury.EXECUTOR_ROLE(),
admin: await Treasury.TIMELOCK_ADMIN_ROLE()
}
const checkRole = async (role: keyof typeof roles) => {
const hasRole = await Treasury.hasRole(
roles[role],
LivepeerGovernor.address
)
if (!hasRole) {
throw new Error(
`Treasury does not provide ${role} role for governor`
)
}
console.log(`Treasury provides ${role} role for governor`)
}

await checkRole("proposer")
await checkRole("canceller")
await checkRole("executor")

const {deployer} = await hre.getNamedAccounts() // Fetch named accounts from hardhat.config.ts
const deployerHasAdmin = await Treasury.hasRole(roles.admin, deployer)
if (deployerHasAdmin) {
console.error(
`WARNING: Treasury still provides ADMIN role to deployer ${deployer}`
)
} else {
console.log(
`Treasury does not provide admin role for deployer ${deployer}`
)
}

const BondingVotes: BondingVotes = await getContract("BondingVotes")

const topTranscoder = await BondingManager.getFirstTranscoderInPool()
if (!(await BondingVotes.hasCheckpoint(topTranscoder))) {
console.log(`Checkpointing top transcoder ${topTranscoder}`)
await BondingManager.checkpointBondingState(topTranscoder).then(
tx => tx.wait()
)
}

await checkParam(
"BondingVotes.hasCheckpoint(topTranscoder)",
await BondingVotes.hasCheckpoint(topTranscoder),
true
)

await checkParam(
"BondingVotes.getVotes(topTranscoder)",
await BondingVotes.getVotes(topTranscoder),
await BondingManager.transcoderTotalStake(topTranscoder)
)

console.log("All good!")
})

0 comments on commit 0174938

Please sign in to comment.