oDAO Membership Interest - peteris

@peteris love your work.

I figured I’d take a stab at the last link, which is connecting from the audits to the now-verified contracts.

Consensys audit

Diff from reviewed commit 2023/11/23 to latest: Comparing f26996f..a08da96 · rocket-pool/rocketpool · GitHub
80 changed files with 2,299 additions and 582 deletions.

Sigma Prime Audit

Not available on GitHub - sigp/public-audits: Collection of public security reviews at this time, so link is to RP-hosted file. Would like to be able to get it from the 3rd party.

Diff from reviewed commit 2024/02/28 to latest: Comparing 7215562c..a08da96 · rocket-pool/rocketpool · GitHub
63 changed files with 1,346 additions and 273 deletions.

“Two follow up reviews were also conducted on Rocket Pool repository and were strictly limited to changes introduced in the commits e760442 and 7161d1c”. These commits are from 2024/05/07 and 2024/05/14 respectively.

Chainsafe audit

Reviewed at 2 commits initially, and then follow up after some additional updates by the dev team. The initial commits were from 2023/06/21 (6a9dbfd) and 2024/01/11 (60684a7) respectively. The additional verification commit was from 2024/04/22 (84ac198) and represents the latest audited point I see.

Diff from last reviewed commit: Comparing 84ac198..a08da96 · rocket-pool/rocketpool · GitHub
24 changed files with 365 additions and 126 deletions.

My own look at diffs

I’ll start from the latest audited point (84ac198) and comment on each commit after that. My looks are pretty cursory here, tbh. I am not deep into SC details, let alone this specific snapshot of the SCs. The main goal, cuz we don’t like trust in web3, is to see how much surface area there was for changes, especially unreviewed changes.

  • e760442 was reviewed by Sigma Prime; seems like it fixes (a) an issue with a malicious withdrawal address preventing distribution and (b) some mismatch between implementation and RPIP-31
    • pretty broad – definitely glad it got 3rd party review
  • e4fc9c8 claims to implement Sigma Prime’s recommendations in RPH-14; it looks like the changes do what it says on the tin
    • many changes, but they are all copies of the same change per RPH-14; quite narrow
  • 7161d1c was reviewed by Sigma Prime; says it corrects a vote power issue in phase 2 (the phase when delegates can be overridden). Frankly, I think I tracked in the end, but I’m less than 100.
    • voting details are important, so glad it got review; narrow scope
  • 5746fde increases the gas allowed to be used by a withdrawal address to receive a distribution from 2300 to 10000; straightforward
    • a single operative change and it is teeny
  • a08da963 fixes a comment
    • no operative changes

TL; DR – I’m satisfied that the audits+contract verification cover the necessary ground

5 Likes

Houston hotfix upgrade contract verification

Checking whether the source code published on GitHub matches what’s deployed on-chain.

This is similar to my verification of previous upgrades. Previous posts have more details how this works.

Payload

You can see the decoded payload of the proposal on Rocketscan:

Or you can verify it yourself:

new Interface(['function proposalUpgrade(string action, string contractName, string contractABI, address contractAddress)']).decodeFunctionData('proposalUpgrade', '0xdfc970ef...')

You should see:

  • Contract name: rocketUpgradeOneDotThreeDotOne
  • Contract address: 0xc2C81454427b1E53Fdf5d3B45561e3c18F90f9eD
  • Contract ABI: eJzNmMtu4jAU...

The contract ABI is compressed with zlib and base64-encoded. Here is a script to decode the ABI. You can also run it in your browser on replit.

Script to decode the payload and contract ABI
const ethers = require('ethers')
const pako = require('pako')

// proposal payload
// https://etherscan.io/tx/0x5d5737e5c5fe3aee8ce232c6462c4b2753be6c4833567d024dbbe7b21bc1000c
const payload = '0xdfc970ef000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000c2c81454427b1e53fdf5d3b45561e3c18f90f9ed000000000000000000000000000000000000000000000000000000000000000b616464436f6e7472616374000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e726f636b6574557067726164654f6e65446f745468726565446f744f6e65000000000000000000000000000000000000000000000000000000000000000003d0654a7a4e6d4d7475346a4155686c396c6c44554c4f334553707a7330624762526d564537367161714b73632b526c597a4e6e4b63586a5471753439546f454249434c5249395135382f622f7a4838655832332b52306f76473164484662667654676457732b764f79674f6769346b593779376a37646d5834413768725a79796277342b326b5751636f6b6d6b32642b3234623364626a4156776b4a642b32713348496574436c37764a6c48746d49504c78724653566371392b46707439494b39734c4b435451382f632b3173772f324176724257633831635937733172354e4436746d376a4c564b62635142635a4f3945667a664f4d3232426842514f62627075717076735659742f4744666a6258416e544c614e7a534e57386b37476c3032657431376d7873396379516b467a6b6177573636716a64444e2f75432b627661656b66756544512f47387a68574f37483656484255322b4533724b42615264642b4a79416273517745524a7a7a48596a746f47485a2b434e677a50376c4747533453496c49374f4b772f45756a616e36504877725032755545737753796d6b787048634f3771702f655a2b534c73642b4454344977536d4b63523450427233794247663257644a4549466f4f5a6c63375a54677563356d6741754634534b32477036584c732b6d7633395973544d327173437757575a7a524241597437694f596c756f77684e394c6c4a37334d6178717a6f70414742434d4342794a344177335661426d674341693575496a4a4b475a6b73516b4b784e2b43736f314f4f6546314e4e6d6636662f636d2b794a4d754141663445554741574156436134504b6b64624d6d576d646459427457495341565a54363436783646464a684e5259706c7776684a69586344566b6b464e69783353696c497a464879455a4c4154496c4a586c425a35714d6f6c307172685439337a4b43434f65736369372f6345656d2f30426e6d34312f704c6b5a676470522b2b30383553592f6d7547546148376f4457783873497a496a36666870736b4d526d426d70774468465a447970666f4a374d7662687872696c756f4373344c496f736344794e4962416a4d4245466854612b2f38596842457741372f374b52655744515352784e382f78772f45577753426d534170532f4a692b454b376733447432454e776130477947464f4230536b45675a6d517979497559734750653979387664766f756c2b56776635376e472f5738794b336c4c3837524b6e714c6c2f6265525043476e59583375666655564b4b557044444a2b46487350586f4861743932715239486930727a72747245483836464b6e507372762f63764c3263673d3d00000000000000000000000000000000'

// https://github.com/rocket-pool/rocketpool/blob/master/contracts/interface/dao/node/RocketDAONodeTrustedProposalsInterface.sol
const iface = new ethers.Interface([
  'function proposalInvite(string id, string url, address nodeAddress)',
  'function proposalLeave(address nodeAddress)',
  'function proposalKick(address nodeAddress, uint256 rplFine)',
  'function proposalSettingUint(string settingContractName, string settingPath, uint256 value)',
  'function proposalSettingBool(string settingContractName, string settingPath, bool value)',
  'function proposalUpgrade(string type, string name, string contractABI, address contractAddress)'
])

const result = iface.decodeFunctionData(iface.getFunction(payload.substring(0, 10)), payload).toObject()
console.log(result)

if (result.type == 'addContract' || result.type == 'upgradeContract') {
  const abi = JSON.parse(pako.inflate(Buffer.from(result.contractABI, 'base64'), { to: 'string' }))
  //console.log(abi) // json format
  console.log(new ethers.Interface(abi).format('full'))
}

Source code

Checking whether the source code published on Etherscan matches what’s on GitHub.

I ran the team’s verification tool.

Instructions
$ git clone https://github.com/rocket-pool/verify-1.3.1.git
$ cd verify-1.3.1/

$ git log --oneline | head -n1
30d5829 Update readme

$ cp .env.example .env
$ cat .env
ETH_RPC=http://xxx
NETWORK=mainnet
ETHERSCAN_API_KEY=xxx

# change .gitmodules to clone over HTTPS not SSH
$ cat .gitmodules
[submodule "rocketpool"]
	path = rocketpool
	url = https://github.com/rocket-pool/rocketpool.git
$ git submodule sync

Here is the output:

$ docker run --rm -it -v $(pwd):/app -w /app node:20 sh -c 'npm install && ./verify.sh'
...
Cloning into '/app/rocketpool'...
Submodule path 'rocketpool': checked out '8d4d5c0f1b810f97ca42cd1ceece0e7c81eb81ee'
...
added 998 packages, and audited 999 packages in 19s
...
✔ Verified contract at 0xc2C81454427b1E53Fdf5d3B45561e3c18F90f9eD matches RocketUpgradeOneDotThreeDotOne
✔ Verified contract at 0x1e94e6131Ba5B4F193d2A1067517136C52ddF102 matches RocketDAOProposal
✔ Verified contract at 0x2D627A50Dc1C4EDa73E42858E8460b0eCF300b25 matches RocketDAOProtocolProposal
✔ Verified contract at 0xd1f7e573cdC64FC0B201ca37aB50bC7Dd880040A matches RocketDAOProtocolVerifier
✔ Verified contract at 0x59cd103DF1BE2EBd80D45c54a3cDE8d4F812C034 matches RocketDAOProtocolSettingsProposals
✔ Verified contract at 0x364F989A3C9a1F66cB51b9043680974eA08C0d18 matches RocketDAOProtocolAuction
✔ Verified contract at 0xF82991Bd8976c243eB3b7CDDc52AB0Fc8dc1246C matches RocketMinipoolManager
✔ Verified contract at 0xF18Dc176C10Ff6D8b5A17974126D43301F8EEB95 matches RocketNodeStaking
✔ Verified contract at 0x03d30466d199Ef540823fe2a22CAE2E3b9343bb0 matches RocketMinipoolDelegate
✔ Verified contract at 0x672335B91b4f2096D897cA1B12Ef4ec9346A5ff4 matches RocketNodeDeposit
✔ Verified contract at 0x77cF0f32BDd06242465eb3318a81196194a13daA matches RocketNetworkVoting
ETH matched corrections:
 1: 0x9796dAd6a55c9501F83B0Dc41676bdC6d001dd32 = 16000000000000000000
 2: 0x70D06394f33D56B6310778eC4E61033585038997 = 16000000000000000000
 3: 0x4efc3E587A4c3Ae0899a0F6e20a78393FC9E39C8 = 16000000000000000000
✔ Verification successful

8d4d5c0 is the commit with the v1.3.1 tag.

Bytecode

This verifies whether the bytecode of the contracts on-chain matches the bytecode produced by compiling the source code from GitHub.

Script

Get the source code and compile the contracts:

git clone https://github.com/rocket-pool/rocketpool.git
cd rocketpool
git checkout 8d4d5c0
npm install
npm run compile

Install dependencies for the verification script:

npm install --no-save ethers@5 glob@10 ganache-cli

Fork mainnet and impersonate Rocket Pool’s deployer address:

npx ganache-cli --fork https://cloudflare-eth.com --unlock 0x27e80db1f5a975f4c43c5ec163114e796cdb603d --gasPrice 1

Create a file called verify.js:

const fs = require('node:fs/promises')
const { glob } = require('glob') // npm install glob@10
const { Contract, ContractFactory } = require('ethers') // npm install ethers@5
const { StaticJsonRpcProvider } = require('@ethersproject/providers')

async function main() {
  const providerUrl = 'http://127.0.0.1:8545'

  const deployer = '0x27e80db1f5a975f4c43c5ec163114e796cdb603d'
  const storageAddress = '0x1d8f8f00cfa6758d7bE78336684788Fb0ee0Fa46'
  const upgradeContractAddress = '0xc2C81454427b1E53Fdf5d3B45561e3c18F90f9eD'

  const upgradeContractABI = [
    'function newRocketDAOProposal() view returns (address)',
    'function newRocketDAOProtocolProposal() view returns (address)',
    'function newRocketDAOProtocolSettingsAuction() view returns (address)',
    'function newRocketDAOProtocolSettingsProposals() view returns (address)',
    'function newRocketDAOProtocolVerifier() view returns (address)',
    'function newRocketMinipoolDelegate() view returns (address)',
    'function newRocketMinipoolManager() view returns (address)',
    'function newRocketNetworkVoting() view returns (address)',
    'function newRocketNodeDeposit() view returns (address)',
    'function newRocketNodeStaking() view returns (address)'
  ]

  const provider = new StaticJsonRpcProvider(providerUrl)

  const upgradeContract = new Contract(upgradeContractAddress, upgradeContractABI, provider)

  const contracts = [
    ['rocketUpgradeOneDotThreeDotOne', upgradeContractAddress],
    ...(await Promise.all(
      Object.values(upgradeContract.interface.functions).map(async f => [
        f.name.replace('new', ''),
        await upgradeContract[f.name]()
      ])
    ))
  ]

  for (const [contractName, address] of contracts) {
    const blockchainCode = await provider.getCode(address)

    const artifactName = contractName[0].toUpperCase() + contractName.substring(1) // rocketContract => RocketContract
    const artifactFilename = (await glob(`artifacts/**/${artifactName}.sol/${artifactName}.json`))[0]
    const artifact = JSON.parse(await fs.readFile(artifactFilename, 'utf-8'))

    const factory = ContractFactory.fromSolidity(artifact, provider.getSigner(deployer))
    const deployment = artifactName.includes('Delegate') ? await factory.deploy() : await factory.deploy(storageAddress)
    await deployment.deployTransaction.wait()
    const deployedArtifactCode = await provider.getCode(deployment.address)

    const match = blockchainCode === deployedArtifactCode
    console.log(match ? '✅' : '❌', artifactName, address)
  }
}

main()

Run the verification script:

node verify.js

Script output for 8d4d5c0f1b810f97ca42cd1ceece0e7c81eb81ee:

✅ RocketUpgradeOneDotThreeDotOne 0xc2C81454427b1E53Fdf5d3B45561e3c18F90f9eD
✅ RocketDAOProposal 0x1e94e6131Ba5B4F193d2A1067517136C52ddF102
✅ RocketDAOProtocolProposal 0x2D627A50Dc1C4EDa73E42858E8460b0eCF300b25
✅ RocketDAOProtocolSettingsAuction 0x364F989A3C9a1F66cB51b9043680974eA08C0d18
✅ RocketDAOProtocolSettingsProposals 0x59cd103DF1BE2EBd80D45c54a3cDE8d4F812C034
✅ RocketDAOProtocolVerifier 0xd1f7e573cdC64FC0B201ca37aB50bC7Dd880040A
✅ RocketMinipoolDelegate 0x03d30466d199Ef540823fe2a22CAE2E3b9343bb0
✅ RocketMinipoolManager 0xF82991Bd8976c243eB3b7CDDc52AB0Fc8dc1246C
✅ RocketNetworkVoting 0x77cF0f32BDd06242465eb3318a81196194a13daA
✅ RocketNodeDeposit 0x672335B91b4f2096D897cA1B12Ef4ec9346A5ff4
✅ RocketNodeStaking 0xF18Dc176C10Ff6D8b5A17974126D43301F8EEB95

Everything matches.

Settings

The upgrade contract was deployed with 0x1d8f8f00cfa6758d7bE78336684788Fb0ee0Fa46 as the RocketStorage address which is correct.

Upgraded contract addresses were set once in a separate transaction and I’ve checked them in this post.

There were 3 ETH matched corrections added but I have not checked if they are correct or not.

Finally the upgrade contract was locked which means contract addresses cannot be changed and no more corrections can be added.

The upgrade can be executed by the Protocol DAO guardian (an EOA controlled by the team) at their convenience which is scheduled for October 28 at 00:00 UTC.

Conclusion

I have verified myself that the contracts whose source code is published on GitHub are the same ones that are deployed on-chain and will be used for the Houston hotfix upgrade.

4 Likes