manual deployment, optimization, formatting, need to update deploy script

Signed-off-by: AlienTornadosaurusHex <>
This commit is contained in:
AlienTornadosaurusHex 2023-05-24 23:59:14 +00:00
parent 75cf27d7d4
commit f14e8d988b
9 changed files with 147 additions and 103 deletions

View File

@ -5,6 +5,11 @@ artifacts
build build
dist dist
README.md README.md
contracts/v2-vault-and-gas/libraries/EtherSend.sol
coverage coverage
coverage.json coverage.json
diffs
contracts/v2-vault-and-gas
contracts/v3-relayer-registry
contracts/v4-patch/TornadoStakingRewards.sol
contracts/v4-patch/TornadoStakingRewards.sol

View File

@ -8,8 +8,7 @@
{ {
"files": "*.sol", "files": "*.sol",
"options": { "options": {
"singleQuote": false, "singleQuote": false
"printWidth": 130
} }
} }
] ]

View File

@ -2,4 +2,10 @@ module.exports = {
governance: '0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce', governance: '0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce',
TORN: '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C', TORN: '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
tornWhale: '0xF977814e90dA44bFA03b6295A0616a897441aceC', tornWhale: '0xF977814e90dA44bFA03b6295A0616a897441aceC',
ens: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
feeManager: '0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7',
staking: '0x2FC93484614a34f26F7970CBB94615bA109BB4bf',
registry: '0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2',
compensations: '0xFA4C1f3f7D5dd7c12a9Adb82Cd7dDA542E3d59ef',
vault: '0x2F50508a8a3D323B91336FA3eA6ae50E55f32185',
} }

View File

@ -38,7 +38,10 @@ contract GovernancePatchUpgrade is GovernanceStakingUpgrade {
proposalCodehash := extcodehash(target) proposalCodehash := extcodehash(target)
} }
require(proposalCodehash == proposalCodehashes[proposalId], "Governance::propose: metamorphic contracts not allowed"); require(
proposalCodehash == proposalCodehashes[proposalId],
"Governance::propose: metamorphic contracts not allowed"
);
super.execute(proposalId); super.execute(proposalId);
} }

View File

@ -3,10 +3,8 @@
pragma solidity ^0.6.12; pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { LoopbackProxy } from "../v1/LoopbackProxy.sol"; import { LoopbackProxy } from "../v1/LoopbackProxy.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { GovernancePatchUpgrade } from "./GovernancePatchUpgrade.sol"; import { GovernancePatchUpgrade } from "./GovernancePatchUpgrade.sol";
import { TornadoStakingRewards } from "./TornadoStakingRewards.sol"; import { TornadoStakingRewards } from "./TornadoStakingRewards.sol";
@ -16,96 +14,56 @@ interface Proxy {
function upgradeTo(address newImplementation) external; function upgradeTo(address newImplementation) external;
} }
/**
* @notice Contract which should help the proposal deploy the necessary contracts.
*/
contract PatchProposalContractsFactory {
/**
* @notice Create a new TornadoStakingRewards contract.
* @param governance The address of Tornado Cash Goveranance.
* @param torn The torn token address.
* @param registry The address of the relayer registry.
* @return The address of the new staking contract.
*/
function createStakingRewards(
address governance,
address torn,
address registry
) external returns (address) {
return address(new TornadoStakingRewards(governance, torn, registry));
}
/**
* @notice Create a new RelayerRegistry contract.
* @param torn The torn token address.
* @param governance The address of Tornado Cash Goveranance.
* @param ens The ens registrar address.
* @param staking The TornadoStakingRewards contract address.
* @return The address of the new registry contract.
*/
function createRegistryContract(
address torn,
address governance,
address ens,
address staking,
address feeManager
) external returns (address) {
return address(new RelayerRegistry(torn, governance, ens, staking, feeManager));
}
}
/** /**
* @notice Proposal which should patch governance against the metamorphic contract replacement vulnerability. * @notice Proposal which should patch governance against the metamorphic contract replacement vulnerability.
*/ */
contract PatchProposal { contract PatchProposal {
using SafeMath for uint256; // Address of the staking proxy
using Address for address; address public constant stakingProxyAddress = 0x2FC93484614a34f26F7970CBB94615bA109BB4bf;
address public constant feeManagerAddress = 0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7; // Address of the registry proxy
address public constant ensAddress = 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e; address public constant registryProxyAddress = 0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2;
address public immutable registry = 0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2;
// Address of the gas compensation vault
address public constant gasCompensationVaultAddress = 0xFA4C1f3f7D5dd7c12a9Adb82Cd7dDA542E3d59ef;
// Address of the user vault (not replaced)
address public constant userVaultAddress = 0x2F50508a8a3D323B91336FA3eA6ae50E55f32185;
// Address of the governance proxy
address payable public constant governanceProxyAddress = 0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce;
// Torn token
IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C); IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C);
PatchProposalContractsFactory public immutable patchProposalContractsFactory; // These are the contracts we deployed
address public immutable deployedStakingContractAddress;
address public immutable deployedRelayerRegistryImplementationAddress;
constructor(address _patchProposalContractsFactory) public { constructor(address _deployedStakingContractAddress, address _deployedRelayerRegistryImplementationAddress)
patchProposalContractsFactory = PatchProposalContractsFactory(_patchProposalContractsFactory); public
{
deployedStakingContractAddress = _deployedStakingContractAddress;
deployedRelayerRegistryImplementationAddress = _deployedRelayerRegistryImplementationAddress;
} }
/// @notice Function to execute the proposal. /// @notice Function to execute the proposal.
function executeProposal() external { function executeProposal() external {
// address(this) has to be governance
address payable governance = payable(address(this));
// Get the two contracts gov depends on
address gasComp = address(GovernancePatchUpgrade(governance).gasCompensationVault());
address vault = address(GovernancePatchUpgrade(governance).userVault());
// Get the old staking contract // Get the old staking contract
TornadoStakingRewards oldStaking = TornadoStakingRewards(address(GovernancePatchUpgrade(governance).Staking())); TornadoStakingRewards oldStaking = TornadoStakingRewards(stakingProxyAddress);
// Get the small amount of TORN left // Get the small amount of TORN left
oldStaking.withdrawTorn(TORN.balanceOf(address(oldStaking))); oldStaking.withdrawTorn(TORN.balanceOf(address(oldStaking)));
// And create a new staking contract // And create a new staking contract
TornadoStakingRewards newStaking = TornadoStakingRewards( TornadoStakingRewards newStaking = TornadoStakingRewards(deployedStakingContractAddress);
patchProposalContractsFactory.createStakingRewards(address(governance), address(TORN), registry)
);
// And a new registry implementation
address newRegistryImplementationAddress = patchProposalContractsFactory.createRegistryContract(
address(TORN),
address(governance),
ensAddress,
address(newStaking),
feeManagerAddress
);
// Upgrade the registry proxy // Upgrade the registry proxy
Proxy(registry).upgradeTo(newRegistryImplementationAddress); Proxy(registryProxyAddress).upgradeTo(deployedRelayerRegistryImplementationAddress);
// Now upgrade the governance to the latest stuff // Now upgrade the governance to the latest stuff
LoopbackProxy(payable(governance)).upgradeTo(address(new GovernancePatchUpgrade(address(newStaking), gasComp, vault))); LoopbackProxy(governanceProxyAddress).upgradeTo(
address(new GovernancePatchUpgrade(address(newStaking), gasCompensationVaultAddress, userVaultAddress))
);
} }
} }

View File

@ -127,16 +127,22 @@ contract MetamorphicContractFactory {
*/ */
constructor(bytes memory transientContractInitializationCode) public { constructor(bytes memory transientContractInitializationCode) public {
// assign the initialization code for the metamorphic contract. // assign the initialization code for the metamorphic contract.
_metamorphicContractInitializationCode = (hex"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3"); _metamorphicContractInitializationCode = (
hex"5860208158601c335a63aaf10f428752fa158151803b80938091923cf3"
);
// calculate and assign keccak256 hash of metamorphic initialization code. // calculate and assign keccak256 hash of metamorphic initialization code.
_metamorphicContractInitializationCodeHash = keccak256(abi.encodePacked(_metamorphicContractInitializationCode)); _metamorphicContractInitializationCodeHash = keccak256(
abi.encodePacked(_metamorphicContractInitializationCode)
);
// store the initialization code for the transient contract. // store the initialization code for the transient contract.
_transientContractInitializationCode = transientContractInitializationCode; _transientContractInitializationCode = transientContractInitializationCode;
// calculate and assign keccak256 hash of transient initialization code. // calculate and assign keccak256 hash of transient initialization code.
_transientContractInitializationCodeHash = keccak256(abi.encodePacked(_transientContractInitializationCode)); _transientContractInitializationCodeHash = keccak256(
abi.encodePacked(_transientContractInitializationCode)
);
} }
/* solhint-disable function-max-lines */ /* solhint-disable function-max-lines */
@ -214,7 +220,10 @@ contract MetamorphicContractFactory {
} /* solhint-enable no-inline-assembly */ } /* solhint-enable no-inline-assembly */
// ensure that the contracts were successfully deployed. // ensure that the contracts were successfully deployed.
require(deployedMetamorphicContract == metamorphicContractAddress, "Failed to deploy the new metamorphic contract."); require(
deployedMetamorphicContract == metamorphicContractAddress,
"Failed to deploy the new metamorphic contract."
);
// initialize the new metamorphic contract if any data or value is provided. // initialize the new metamorphic contract if any data or value is provided.
if (data.length > 0 || msg.value > 0) { if (data.length > 0 || msg.value > 0) {
@ -281,7 +290,10 @@ contract MetamorphicContractFactory {
} /* solhint-enable no-inline-assembly */ } /* solhint-enable no-inline-assembly */
// ensure that the contracts were successfully deployed. // ensure that the contracts were successfully deployed.
require(deployedMetamorphicContract == metamorphicContractAddress, "Failed to deploy the new metamorphic contract."); require(
deployedMetamorphicContract == metamorphicContractAddress,
"Failed to deploy the new metamorphic contract."
);
// initialize the new metamorphic contract if any data or value is provided. // initialize the new metamorphic contract if any data or value is provided.
if (data.length > 0 || msg.value > 0) { if (data.length > 0 || msg.value > 0) {
@ -412,7 +424,11 @@ contract MetamorphicContractFactory {
* @param salt bytes32 The nonce passed into CREATE2 by metamorphic contract. * @param salt bytes32 The nonce passed into CREATE2 by metamorphic contract.
* @return Address of the corresponding metamorphic contract. * @return Address of the corresponding metamorphic contract.
*/ */
function findMetamorphicContractAddress(bytes32 salt) external view returns (address metamorphicContractAddress) { function findMetamorphicContractAddress(bytes32 salt)
external
view
returns (address metamorphicContractAddress)
{
// determine the address where the metamorphic contract will be deployed. // determine the address where the metamorphic contract will be deployed.
metamorphicContractAddress = _getMetamorphicContractAddress(salt); metamorphicContractAddress = _getMetamorphicContractAddress(salt);
} }
@ -424,7 +440,11 @@ contract MetamorphicContractFactory {
* transient contract. * transient contract.
* @return Address of the corresponding transient contract. * @return Address of the corresponding transient contract.
*/ */
function findTransientContractAddress(bytes32 salt) external view returns (address transientContractAddress) { function findTransientContractAddress(bytes32 salt)
external
view
returns (address transientContractAddress)
{
// determine the address where the transient contract will be deployed. // determine the address where the transient contract will be deployed.
transientContractAddress = _getTransientContractAddress(salt); transientContractAddress = _getTransientContractAddress(salt);
} }
@ -442,14 +462,20 @@ contract MetamorphicContractFactory {
returns (address metamorphicContractAddress) returns (address metamorphicContractAddress)
{ {
// determine the address of the metamorphic contract. // determine the address of the metamorphic contract.
metamorphicContractAddress = _getMetamorphicContractAddressWithConstructor(_getTransientContractAddress(salt)); metamorphicContractAddress = _getMetamorphicContractAddressWithConstructor(
_getTransientContractAddress(salt)
);
} }
/** /**
* @dev View function for retrieving the initialization code of metamorphic * @dev View function for retrieving the initialization code of metamorphic
* contracts for purposes of verification. * contracts for purposes of verification.
*/ */
function getMetamorphicContractInitializationCode() external view returns (bytes memory metamorphicContractInitializationCode) { function getMetamorphicContractInitializationCode()
external
view
returns (bytes memory metamorphicContractInitializationCode)
{
return _metamorphicContractInitializationCode; return _metamorphicContractInitializationCode;
} }
@ -469,7 +495,11 @@ contract MetamorphicContractFactory {
* @dev View function for retrieving the initialization code of transient * @dev View function for retrieving the initialization code of transient
* contracts for purposes of verification. * contracts for purposes of verification.
*/ */
function getTransientContractInitializationCode() external view returns (bytes memory transientContractInitializationCode) { function getTransientContractInitializationCode()
external
view
returns (bytes memory transientContractInitializationCode)
{
return _transientContractInitializationCode; return _transientContractInitializationCode;
} }
@ -477,7 +507,11 @@ contract MetamorphicContractFactory {
* @dev View function for retrieving the keccak256 hash of the initialization * @dev View function for retrieving the keccak256 hash of the initialization
* code of transient contracts for purposes of verification. * code of transient contracts for purposes of verification.
*/ */
function getTransientContractInitializationCodeHash() external view returns (bytes32 transientContractInitializationCodeHash) { function getTransientContractInitializationCodeHash()
external
view
returns (bytes32 transientContractInitializationCodeHash)
{
return _transientContractInitializationCodeHash; return _transientContractInitializationCodeHash;
} }
@ -532,7 +566,11 @@ contract MetamorphicContractFactory {
* that has been deployed via a transient contract given the address of the * that has been deployed via a transient contract given the address of the
* transient contract. * transient contract.
*/ */
function _getMetamorphicContractAddressWithConstructor(address transientContractAddress) internal pure returns (address) { function _getMetamorphicContractAddressWithConstructor(address transientContractAddress)
internal
pure
returns (address)
{
// determine the address of the metamorphic contract. // determine the address of the metamorphic contract.
return return
address( address(
@ -560,7 +598,10 @@ contract MetamorphicContractFactory {
modifier containsCaller(bytes32 salt) { modifier containsCaller(bytes32 salt) {
// prevent contract submissions from being stolen from tx.pool by requiring // prevent contract submissions from being stolen from tx.pool by requiring
// that the first 20 bytes of the submitted salt match msg.sender. // that the first 20 bytes of the submitted salt match msg.sender.
require(address(bytes20(salt)) == msg.sender, "Invalid salt - first 20 bytes of the salt must match calling address."); require(
address(bytes20(salt)) == msg.sender,
"Invalid salt - first 20 bytes of the salt must match calling address."
);
_; _;
} }
} }

View File

@ -16,25 +16,36 @@ const config = require('./config')
module.exports = { module.exports = {
solidity: { solidity: {
compilers: [ compilers: [
{
version: '0.6.12',
settings: {
optimizer: {
enabled: true,
runs: 1000,
},
},
},
{ {
version: '0.5.6', version: '0.5.6',
settings: { settings: {
optimizer: { optimizer: {
enabled: true, enabled: true,
runs: 1000, runs: 1_000,
},
},
},
{
version: '0.6.12',
settings: {
optimizer: {
enabled: true,
runs: 1_000_000,
}, },
}, },
}, },
], ],
overrides: {
'contracts/v4-patch/PatchProposal.sol': {
version: '0.6.12',
settings: {
optimizer: {
enabled: true,
runs: 1,
},
},
},
},
}, },
networks: { networks: {
hardhat: { hardhat: {

View File

@ -17,6 +17,7 @@
"test:f": "yarn prettier:fix && yarn test", "test:f": "yarn prettier:fix && yarn test",
"clean": "yarn prettier:fix && yarn lint", "clean": "yarn prettier:fix && yarn lint",
"compile": "yarn prettier:fix && yarn hardhat compile", "compile": "yarn prettier:fix && yarn hardhat compile",
"_compile": "yarn hardhat compile",
"coverage": "yarn hardhat coverage", "coverage": "yarn hardhat coverage",
"layout": "yarn hardhat check" "layout": "yarn hardhat check"
}, },

View File

@ -54,11 +54,16 @@ describe('Gov Exploit Patch Upgrade Tests', () => {
let proposalContractsDeployer let proposalContractsDeployer
let proposalDeployer let proposalDeployer
let registryDeployer
let stakingDeployer
let proposerBalanceInitial let proposerBalanceInitial
let torn let torn
let governance let governance
let metamorphicFactory let metamorphicFactory
let registryImplementation
let staking
let exploit = { let exploit = {
hacker: undefined, hacker: undefined,
@ -134,7 +139,10 @@ describe('Gov Exploit Patch Upgrade Tests', () => {
initialProposalDeployer = await ethers.getContractFactory('InitialProposal') initialProposalDeployer = await ethers.getContractFactory('InitialProposal')
maliciousProposalDeployer = await ethers.getContractFactory('MaliciousProposal') maliciousProposalDeployer = await ethers.getContractFactory('MaliciousProposal')
proposalContractsDeployer = await ethers.getContractFactory('PatchProposalContractsFactory')
stakingDeployer = await ethers.getContractFactory('TornadoStakingRewards')
registryDeployer = await ethers.getContractFactory('RelayerRegistry')
proposalDeployer = await ethers.getContractFactory('PatchProposal') proposalDeployer = await ethers.getContractFactory('PatchProposal')
// Metamorphic & Exploit // Metamorphic & Exploit
@ -146,6 +154,16 @@ describe('Gov Exploit Patch Upgrade Tests', () => {
initialProposalImpl = await initialProposalDeployer.deploy() initialProposalImpl = await initialProposalDeployer.deploy()
maliciousProposalImpl = await maliciousProposalDeployer.deploy() maliciousProposalImpl = await maliciousProposalDeployer.deploy()
staking = await stakingDeployer.deploy(config.governance, config.TORN, config.registry)
registryImplementation = await registryDeployer.deploy(
config.TORN,
config.governance,
config.ens,
config.staking,
config.feeManager,
)
exploit.address = await metamorphicFactory.findMetamorphicContractAddress(exploit.salt) exploit.address = await metamorphicFactory.findMetamorphicContractAddress(exploit.salt)
// Snapshot // Snapshot
@ -159,16 +177,16 @@ describe('Gov Exploit Patch Upgrade Tests', () => {
snapshotId = await takeSnapshot() snapshotId = await takeSnapshot()
}) })
it('Should be able to execute the proposal', async () => { it.only('Should be able to execute the proposal', async () => {
// Load these storage variables for comparison // Load these storage variables for comparison
const oldVaultAddr = await governance.userVault() const oldVaultAddr = await governance.userVault()
const oldGasCompAddr = await governance.gasCompensationVault() const oldGasCompAddr = await governance.gasCompensationVault()
const oldStaking = await governance.Staking() const oldStaking = await governance.Staking()
// Start proposing // Deploy the proposal
const proposal = await proposalDeployer.deploy((await proposalContractsDeployer.deploy()).address) const proposal = await proposalDeployer.deploy(staking.address, registryImplementation.address)
// Propose // Propose
@ -194,7 +212,9 @@ describe('Gov Exploit Patch Upgrade Tests', () => {
await ethers.provider.send('evm_mine', []) await ethers.provider.send('evm_mine', [])
await governance.execute(proposalId) const response = await governance.execute(proposalId)
console.log(await response.wait())
const newVaultAddr = await governance.userVault() const newVaultAddr = await governance.userVault()
const newGasCompAddr = await governance.gasCompensationVault() const newGasCompAddr = await governance.gasCompensationVault()