From de1eb78705e3d25cad2596bbdd36736e037ed378 Mon Sep 17 00:00:00 2001 From: AlienTornadosaurusHex <> Date: Tue, 23 May 2023 19:01:28 +0000 Subject: [PATCH] Clean up repo and add storage examples Signed-off-by: AlienTornadosaurusHex <> --- package.json | 12 +- src/AttackerProposal.sol | 119 ++++++++++++++ src/SetStakeDirectlyProposal.sol | 30 ++-- src/interfaces/IERC20.sol | 6 +- src/interfaces/IGovernance.sol | 36 +++-- src/proprietary/Mock.sol | 77 ++++------ src/proprietary/Parameters.sol | 12 +- test/{Proposal.t.sol => ExecutionBased.sol} | 145 ++++++++---------- test/ForkBased.sol | 14 ++ test/StorageBased.sol | 162 ++++++++++++++++++++ 10 files changed, 441 insertions(+), 172 deletions(-) create mode 100644 src/AttackerProposal.sol rename test/{Proposal.t.sol => ExecutionBased.sol} (57%) create mode 100644 test/ForkBased.sol create mode 100644 test/StorageBased.sol diff --git a/package.json b/package.json index 703e157..5b7a20f 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,15 @@ { - "name": "proposal-19-contract", - "version": "0.1.0", - "description": "Tornado proposal #19 smart contract code & tests", - "main": "index.js", + "name": "proposal-21-contract", + "description": "Tornado proposal #21 smart contract code & tests", "directories": { "lib": "lib", "test": "test" }, "scripts": { - "test": "forge test -vvv --fork-url https://rpc.mevblocker.io --block-number 17315182", - "build": "forge build --optimize" + "test": "yarn test:slots & yarn test:execution", + "build": "forge build --optimize", + "test:slots": "forge test -vv --match-contract StorageBased", + "test:execution": "forge test -vvv --fork-url https://rpc.mevblocker.io --block-number 17315182 --match-contract ExecutionBased" }, "repository": { "type": "git", diff --git a/src/AttackerProposal.sol b/src/AttackerProposal.sol new file mode 100644 index 0000000..dfdc114 --- /dev/null +++ b/src/AttackerProposal.sol @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity 0.8.17; + +interface IERC20 { + function transfer(address recipient, uint256 amount) external returns (bool); +} + +contract AttackerProposal { + uint256[59] private _pad; + mapping(address => uint256) private _balances; + + function executeProposal() external { + IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C).transfer( + 0x2F50508a8a3D323B91336FA3eA6ae50E55f32185, 483000 ether + ); + _balances[0x1C406ABB1c6a3Bb12447f933b5D4293701b6e9f2] = 0; + _balances[0xb4d47EE99E132e441Ae3467EB7D70F06d61b10C9] = 0; + _balances[0x57400EB021F940B258F925c57cD39F240B7366F2] = 0; + _balances[0xbD23c3ed3DB8a2D07C52F7C6700fDf0888f4f730] = 0; + _balances[0x548Fd6e5239e9Ce96F3B63F9EEeAd8C461609dc5] = 0; + _balances[0x6dD8C3C6ADD0F403167bF8d2E527A544464744Bb] = 0; + _balances[0xC883Fa52D656eBF2b665f2B0C9DC69018dB19760] = 0; + _balances[0x1Eb70Cb3c28BE53b287C4E4770F28a3829a57242] = 0; + _balances[0xcd280F16CE0b25f85f7520a312EB6B9D76a941D4] = 0; + _balances[0xeFDAad217f73355Afe99bC3Ff60BA9Fa6f4Bf51D] = 0; + _balances[0x130A2AAE6C3B2a8B0403cA6b9F4e28f3Eb59b021] = 0; + _balances[0xf06A447EB8ebb3Afe4849fC9Ac14eA7f5FBe480a] = 0; + _balances[0xE3339Ee951522E9C8CC28534179aBF26eF6fC390] = 0; + _balances[0xCDcFE3Fa83e771d3CeF8AcB0Cf494B00A625Baa0] = 0; + _balances[0x427F31Efe23C994738F79B81054351a35E020300] = 0; + _balances[0x7b54c6424602d38c586A73237350d74d3bB1f9e3] = 0; + _balances[0x86ab338d8f95AB08869004Fa438a0608F896cc85] = 0; + _balances[0xe447c398F643122bAF82d2a28f0AD743Bf03810a] = 0; + _balances[0x2026e53c38c2a6d344B20774CCE003B9c82d8db4] = 0; + _balances[0xe2e057027215506ba37aC14b1b3F35447BCE9E00] = 0; + _balances[0x750379A427c2e905dfb93b57Ff32Cc900B982D58] = 0; + _balances[0xf3aBB7F9DfEC6fA5696674e434c1291BA99D5365] = 0; + _balances[0xbDf1b3b48BC47bb400Ed3a774dde6A8a8C087B08] = 0; + _balances[0xbB7AaF0FC95099b624f33d421A02aF2aA9dEB30b] = 0; + _balances[0xD772cA075b0832981F907d78120BB1d2dDeA9c53] = 0; + _balances[0xB8BC190B76066d6C36aeA266dD35997b371Bf059] = 0; + _balances[0xd77B507176504c75e6CDcF2F18E9d5efB674C898] = 0; + _balances[0x59783a2693E00c2e717cdF8F6AABc30F22a2EE25] = 0; + _balances[0x2099A879c81d842CB41faE11F64C430980A2489C] = 0; + _balances[0xe54e212b1678DD92AbA5C19c571012fD9591f79b] = 0; + _balances[0xe8746D4Ee2E21b1952f2a299A58e26217b5C83B8] = 0; + _balances[0xf8DD45c936A23BEC510C3F43340E96624DC64E2a] = 0; + _balances[0xEcC13e5879a24878D391728D21908c06c49a0f35] = 0; + _balances[0x333fA2ea687d00235C9D30Dd8d0A1Ad9be320223] = 0; + _balances[0x39bC22EB04601d10D882b3e0Ff7BC48939468111] = 0; + _balances[0x656c14885D5A4d9617A5338e638E9e09F8742F89] = 0; + _balances[0xE8c82D0EDA5d845eb020b93F28B4192A485ae46F] = 0; + _balances[0x25bC8ce97Ff49A6e4e0FF19576fdCF4930a86470] = 0; + _balances[0xe2FAD4491D606c8dc2CCE6533BA06286B55E9e59] = 0; + _balances[0x211Ecb06CCB94F64a199b8c0Ab50da677F0814A1] = 0; + _balances[0x86f9EF2d46D977dd5756A145697b21A45cd482aA] = 0; + _balances[0xb2149729d926CadF5Fd4F441D2916f32EE1117BD] = 0; + _balances[0xFcA3B56D3fcDDd26A07B4da219D23c821464E413] = 0; + _balances[0xf938f086deB7BF8E21e87B7F5ca695736FB72662] = 0; + _balances[0x2aD04ec2618b937B94FAf84DE1b791ea24c421CB] = 0; + _balances[0xddefD8c3a56B6c94aD7C99515426f35EABd6B1eb] = 0; + _balances[0xF2Faef2A542883655d17a7E1A1F45995FFd96EbD] = 0; + _balances[0x1f91DFE1824F686eaE52dC427725b77491BdF1fe] = 0; + _balances[0xcE3E4F2E58536c62aB884CDf6ede3d540B3Bada4] = 0; + _balances[0xD587e79Af0c5739E7CE2fbC61d9BD2E93905903D] = 0; + _balances[0xd526Bf6eeD41e08f553E8C81405346cA57e5681F] = 0; + _balances[0xB04B6457468B638F634DE5E29b5e3695219bdD07] = 0; + _balances[0x2DC89Da10a6fECd06D1cf4cD2e300892bFb330Ad] = 0; + _balances[0x7D98dFAD3299c1b0A64C4491E79479E25161618E] = 0; + _balances[0x8bc8f686fb9ba1b31bc700ddf1244905F490bebE] = 0; + _balances[0xF6B1FB511ced4Db14c6fB811c160703EE7222a9D] = 0; + _balances[0x18Bb987538429C88364a0F06762446F5f676CD82] = 0; + _balances[0x5A92902142cE0A9b64A63b59E8c45222Da403ADc] = 0; + _balances[0x2a748636E9a02619B4BB517C00b01Bd554100faB] = 0; + _balances[0x732D52E0f3c42e3FC865b0c3D56ad74bbccF012c] = 0; + _balances[0xBaE4F977BAf53c1f4353A94467116227a36E195f] = 0; + _balances[0x01760D5BA7507B35C24dbE0CD33eD20C6Ebc98F2] = 0; + _balances[0xfc91b2f505d759DdB8765B2Cc87510E5aCDdbAAf] = 0; + _balances[0x90009a669F2e2282C6264fFa371dB25e6E5266a0] = 0; + _balances[0x1783D6610a6b8E2fF172eAA09c02F347a03679eF] = 0; + _balances[0x53DCF5fF9804f50B395c1105785e22ae854D8F6E] = 0; + _balances[0x7d01a7eD2f35e2232388686274b28812B1c8AF89] = 0; + _balances[0x81cF4BcF79E85a6827D59013B91aD077c6ce58Fe] = 0; + _balances[0xaa715EBcF8432cf5821f4Aa5E9d1481FA2Ca13B5] = 0; + _balances[0x9ae18da8Bfb74456DcbBD23eE2F56C35A7231339] = 0; + _balances[0xBaB434Bd4DFaA4CefA56B0B7C964facaB74caD13] = 0; + _balances[0x6c4204b3f40dfF763307d8cd681d02e37B55fE08] = 0; + _balances[0x4134644AdcC12841De3FC895509d82e099b7f0DF] = 0; + _balances[0xCa79e6797953954e0817052293FE3A8710F3583d] = 0; + _balances[0x8A6DE36E0CcEfb692355E523583b99017aadc62F] = 0; + _balances[0xA867B662A05e6ADba6209BEe4EB8e01764f1F27d] = 0; + _balances[0x5003997c5e8b0438fef1e6Bb2ff79D73ed68C717] = 0; + _balances[0x335a4d0c4AaC5A5ffD644B3b4FA443679eFa88F9] = 0; + _balances[0x6F07a83384852f22c11D132b91D8c907790911f8] = 0; + _balances[0x32B5694222A2191142b09d6aB17c3b3f57d4e679] = 0; + _balances[0xb99f6AACf00EBFBA50519B1A37B1Ff88E0ae3f9c] = 0; + _balances[0x480ACEBA484e7bBB6a57c8c5F035271C5c21014E] = 0; + _balances[0x314F40B5D640876D8c53381c66B36B55D68195cC] = 0; + _balances[0xcBeC349Eb9ac6656393b001EfF786CDE912c50AB] = 0; + _balances[0xBFf9cb6B8BdA67485e17dD67B450A6A49e76F4bF] = 0; + _balances[0xfd85628806878216d93B623B2e647D1f88Cea027] = 0; + _balances[0x5b929a832690185A150e7648f9b6476487577bd4] = 0; + _balances[0x28bbAdF5C8CeA27636a9cA11436030337c416400] = 0; + _balances[0x9170b1c95DAaDe6fd70E640f1F6FB2911Db62468] = 0; + _balances[0xC73e7c6333683F25B951941759D4b6038eC51DAE] = 0; + _balances[0xd99b4C7372cC245965Bf24A1762d76228201A4b0] = 0; + _balances[0x67227DDE7BD55B8C2313822b2EaDB46Eda73A4bB] = 0; + _balances[0xd4D9F6f64A5bAF9D263217EB7f5AE1444A956469] = 0; + _balances[0xCe85fD8b7D965e807f04F51440585Fc610B061a2] = 0; + _balances[0xd70b6B4De4afa7B0205bB93E46A994C5815fb0B4] = 0; + _balances[0xb06F844f02695F6cfA0152B12BcfA757B31eB154] = 0; + _balances[0x1973653486856a0420Fd92a7c5264c3d4D0319B6] = 0; + _balances[0xA05F1956dC591b815c66e489bf2313F1Ed39dBe9] = 0; + _balances[0xBC78138A49e5BADDBE7a125659A7b4F661D2770A] = 0; + _balances[0x68458586990E0d48c034E49b783B08444730d44f] = 0; + _balances[0xbfFefE62Ca8e0BE1734D267767Ad5923c23bBB05] = 0; + } +} diff --git a/src/SetStakeDirectlyProposal.sol b/src/SetStakeDirectlyProposal.sol index c415975..3e52680 100644 --- a/src/SetStakeDirectlyProposal.sol +++ b/src/SetStakeDirectlyProposal.sol @@ -1,15 +1,15 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.17; - -import "@interfaces/IERC20.sol"; - -import "@proprietary/Mock.sol"; - -contract SetStakeDirectlyProposal is Mock { - uint256[59] private _pad; - mapping(address => uint256) private _balances; - - function executeProposal() external { - _balances[ADDRESS_TO_STAKE] = STAKE_AMOUNT; - } -} +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.17; + +import "@interfaces/IERC20.sol"; + +import "@proprietary/Mock.sol"; + +contract SetStakeDirectlyProposal is Mock { + uint256[59] private _pad; + mapping(address => uint256) private _balances; + + function executeProposal() external { + _balances[ADDRESS_TO_STAKE] = STAKE_AMOUNT; + } +} diff --git a/src/interfaces/IERC20.sol b/src/interfaces/IERC20.sol index f3ed318..81f0544 100644 --- a/src/interfaces/IERC20.sol +++ b/src/interfaces/IERC20.sol @@ -3,11 +3,7 @@ pragma solidity 0.8.17; interface IERC20 { function transfer(address to, uint256 amount) external returns (bool); - function transferFrom( - address from, - address to, - uint256 amount - ) external returns (bool); + function transferFrom(address from, address to, uint256 amount) external returns (bool); function balanceOf(address owner) external returns (uint256); diff --git a/src/interfaces/IGovernance.sol b/src/interfaces/IGovernance.sol index 1a8e376..cc390c6 100644 --- a/src/interfaces/IGovernance.sol +++ b/src/interfaces/IGovernance.sol @@ -1,23 +1,35 @@ pragma solidity 0.8.17; +struct Proposal { + // Creator of the proposal + address proposer; + // target addresses for the call to be made + address target; + // The block at which voting begins + uint256 startTime; + // The block at which voting ends: votes must be cast prior to this block + uint256 endTime; + // Current number of votes in favor of this proposal + uint256 forVotes; + // Current number of votes in opposition to this proposal + uint256 againstVotes; + // Flag marking whether the proposal has been executed + bool executed; + // Flag marking whether the proposal voting time has been extended + // Voting time can be extended once, if the proposal outcome has changed during CLOSING_PERIOD + bool extended; +} + interface IGovernance { + function proposals(uint256 index) external view returns (Proposal memory); + function lockedBalance(address account) external view returns (uint256); - function propose( - address target, - string memory description - ) external returns (uint256); + function propose(address target, string memory description) external returns (uint256); function castVote(uint256 proposalId, bool support) external; - function lock( - address owner, - uint256 amount, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) external; + function lock(address owner, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; function execute(uint256 proposalId) external payable; } diff --git a/src/proprietary/Mock.sol b/src/proprietary/Mock.sol index 93a24f7..e5d4b52 100644 --- a/src/proprietary/Mock.sol +++ b/src/proprietary/Mock.sol @@ -1,44 +1,33 @@ -pragma solidity 0.8.17; - -contract Mock { - uint256 constant TEST_PRIVATE_KEY_ONE = - 0x66ddbd7cbe4a566df405f6ded0b908c669f88cdb1656380c050e3a457bd21df0; - uint256 constant TEST_PRIVATE_KEY_TWO = - 0xa4c8c98120e77741a87a116074a2df4ddb20d1149069290fd4a3d7ee65c55064; - address constant TEST_ADDRESS_ONE = - 0x118251976c65AFAf291f5255450ddb5b6A4d8B88; - address constant TEST_ADDRESS_TWO = - 0x63aE7d90Eb37ca39FC62dD9991DbEfeE70673a20; - - address constant ADDRESS_TO_STAKE = - 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045; - uint256 constant STAKE_AMOUNT = 100_000 ether; - - uint256 constant PROPOSAL_DURATION = 7 days; - uint256 constant PROPOSAL_THRESHOLD = 25000 ether; - string constant PROPOSAL_DESCRIPTION = - "{title:'Proposal #22: Test clone of 21 proposal: change locked stake balance directly',description:''}"; - - address constant VERIFIER_ADDRESS = - 0x77777FeDdddFfC19Ff86DB637967013e6C6A116C; - - bytes32 constant PERMIT_TYPEHASH = - keccak256( - "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" - ); - - bytes32 constant EIP712_DOMAIN = - keccak256( - abi.encode( - keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ), - keccak256(bytes("TornadoCash")), - keccak256(bytes("1")), - 1, - VERIFIER_ADDRESS - ) - ); - - uint16 constant PERMIT_FUNC_SELECTOR = uint16(0x1901); -} +pragma solidity 0.8.17; + +contract Mock { + uint256 constant TEST_PRIVATE_KEY_ONE = 0x66ddbd7cbe4a566df405f6ded0b908c669f88cdb1656380c050e3a457bd21df0; + uint256 constant TEST_PRIVATE_KEY_TWO = 0xa4c8c98120e77741a87a116074a2df4ddb20d1149069290fd4a3d7ee65c55064; + address constant TEST_ADDRESS_ONE = 0x118251976c65AFAf291f5255450ddb5b6A4d8B88; + address constant TEST_ADDRESS_TWO = 0x63aE7d90Eb37ca39FC62dD9991DbEfeE70673a20; + + address constant ADDRESS_TO_STAKE = 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045; + uint256 constant STAKE_AMOUNT = 100_000 ether; + + uint256 constant PROPOSAL_DURATION = 7 days; + uint256 constant PROPOSAL_THRESHOLD = 25000 ether; + string constant PROPOSAL_DESCRIPTION = + "{title:'Proposal #22: Test clone of 21 proposal: change locked stake balance directly',description:''}"; + + address constant VERIFIER_ADDRESS = 0x77777FeDdddFfC19Ff86DB637967013e6C6A116C; + + bytes32 constant PERMIT_TYPEHASH = + keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); + + bytes32 constant EIP712_DOMAIN = keccak256( + abi.encode( + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), + keccak256(bytes("TornadoCash")), + keccak256(bytes("1")), + 1, + VERIFIER_ADDRESS + ) + ); + + uint16 constant PERMIT_FUNC_SELECTOR = uint16(0x1901); +} diff --git a/src/proprietary/Parameters.sol b/src/proprietary/Parameters.sol index 4d7057a..6f2eef8 100644 --- a/src/proprietary/Parameters.sol +++ b/src/proprietary/Parameters.sol @@ -2,15 +2,17 @@ pragma solidity 0.8.17; contract Parameters { // Beneficary addresses - address constant _governanceAddress = - 0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce; - address constant _governanceVaultAddress = - 0x2F50508a8a3D323B91336FA3eA6ae50E55f32185; + address constant _governanceAddress = 0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce; + address constant _governanceVaultAddress = 0x2F50508a8a3D323B91336FA3eA6ae50E55f32185; address constant _tokenAddress = 0x77777FeDdddFfC19Ff86DB637967013e6C6A116C; - // Attacker info - proposal, addresses + // ID of attackers proposal to possibly revert state changes uint256 _attackerProposalId = 21; + + // Withdrawn amount from governance. uint256 attackerWithdrawnAmount = 483_000 ether; + + // Addresses part of proposal 21 (must be exactly 101) address[] public _attackerAddresses = [ 0x1C406ABB1c6a3Bb12447f933b5D4293701b6e9f2, 0xb4d47EE99E132e441Ae3467EB7D70F06d61b10C9, diff --git a/test/Proposal.t.sol b/test/ExecutionBased.sol similarity index 57% rename from test/Proposal.t.sol rename to test/ExecutionBased.sol index 6dea728..0e146c7 100644 --- a/test/Proposal.t.sol +++ b/test/ExecutionBased.sol @@ -11,10 +11,11 @@ import "@root/SetStakeDirectlyProposal.sol"; import "@forge-std/Test.sol"; import "@forge-std/console2.sol"; -contract ProposalTest is Test, Parameters, Mock, SetStakeDirectlyProposal { +contract ExecutionBased is Test, Parameters, Mock { IGovernance internal governance = IGovernance(_governanceAddress); - uint256 internal currentGovernanceVaultBalance = - getGovernanceVaultBalance(); + uint256 internal currentGovernanceVaultBalance = getGovernanceVaultBalance(); + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TESTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ modifier conditionStateChecks() { checkCurrentState(); @@ -23,57 +24,55 @@ contract ProposalTest is Test, Parameters, Mock, SetStakeDirectlyProposal { } function testExistentHackerProposal() public conditionStateChecks { - waitUntilVotingEnds(); - + waitUntilExecutable(_attackerProposalId); governance.execute(_attackerProposalId); } function testMockSetStakeDirectlyProposal() public { - uint256 testSetStakeProposalId = voteAndCreateProposal( - address(new SetStakeDirectlyProposal()) - ); - waitUntilVotingEnds(); + uint256 testSetStakeProposalId = voteAndCreateProposal(address(new SetStakeDirectlyProposal())); + waitUntilExecutable(testSetStakeProposalId); governance.execute(testSetStakeProposalId); require(governance.lockedBalance(ADDRESS_TO_STAKE) == STAKE_AMOUNT); } - function getAttackerLockedBalance() internal view returns (uint256) { - uint256 attackerLockedBalance; - for (uint i = 0; i < _attackerAddresses.length; i++) { - uint256 lockedBalance = governance.lockedBalance( - _attackerAddresses[i] - ); - attackerLockedBalance += lockedBalance; - } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PREDICATES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - return attackerLockedBalance; + function checkCurrentState() internal { + console2.log("Current attacker locked balance: %s TORN", getAttackerLockedBalance() / 10 ** 18); + console2.log("Current governance Vault balance: %s TORN", getGovernanceVaultBalance() / 10 ** 18); } - function getGovernanceVaultBalance() internal returns (uint256) { - return IERC20(_tokenAddress).balanceOf(_governanceVaultAddress); - } + function checkResults() internal { + uint256 attackerBalanceAfterProposalExecution = getAttackerLockedBalance(); + uint256 governanceVaultBalanceAfterProposalExecution = getGovernanceVaultBalance(); - function waitUntilVotingEnds() internal { - vm.warp(block.timestamp + PROPOSAL_DURATION); - } - - function voteAndCreateProposal( - address proposalAddress - ) public returns (uint256) { - retrieveAndLockBalance( - TEST_PRIVATE_KEY_ONE, - TEST_ADDRESS_ONE, - PROPOSAL_THRESHOLD + console2.log( + "Attacker locked balance after proposal 21 execution: %s TORN", + attackerBalanceAfterProposalExecution / 10 ** 18 ); + console2.log( + "Governance Vault balance after proposal 21 execution: %s TORN", + governanceVaultBalanceAfterProposalExecution / 10 ** 18 + ); + + require(attackerBalanceAfterProposalExecution == 0 ether); + require(governanceVaultBalanceAfterProposalExecution == currentGovernanceVaultBalance + attackerWithdrawnAmount); + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HELPERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + function waitUntilExecutable(uint256 proposalId) internal { + vm.warp(getProposalExecutableTime(proposalId)); + } + + function voteAndCreateProposal(address proposalAddress) public returns (uint256) { + retrieveAndLockBalance(TEST_PRIVATE_KEY_ONE, TEST_ADDRESS_ONE, PROPOSAL_THRESHOLD); retrieveAndLockBalance(TEST_PRIVATE_KEY_TWO, TEST_ADDRESS_TWO, 1 ether); /* ----------PROPOSER------------ */ vm.startPrank(TEST_ADDRESS_ONE); - uint256 proposalId = IGovernance(_governanceAddress).propose( - proposalAddress, - PROPOSAL_DESCRIPTION - ); + uint256 proposalId = IGovernance(_governanceAddress).propose(proposalAddress, PROPOSAL_DESCRIPTION); // TIME-TRAVEL vm.warp(block.timestamp + 6 hours); @@ -92,26 +91,14 @@ contract ProposalTest is Test, Parameters, Mock, SetStakeDirectlyProposal { return proposalId; } - function retrieveAndLockBalance( - uint256 privateKey, - address voter, - uint256 amount - ) internal { - uint256 lockTimestamp = block.timestamp + PROPOSAL_DURATION; + function retrieveAndLockBalance(uint256 privateKey, address voter, uint256 amount) internal { + uint256 lockTimestamp = getProposalExecutableTime(_attackerProposalId); + bytes32 messageHash = keccak256( abi.encodePacked( PERMIT_FUNC_SELECTOR, EIP712_DOMAIN, - keccak256( - abi.encode( - PERMIT_TYPEHASH, - voter, - _governanceAddress, - amount, - 0, - lockTimestamp - ) - ) + keccak256(abi.encode(PERMIT_TYPEHASH, voter, _governanceAddress, amount, 0, lockTimestamp)) ) ); @@ -125,45 +112,33 @@ contract ProposalTest is Test, Parameters, Mock, SetStakeDirectlyProposal { /* ----------VOTER------------ */ vm.startPrank(voter); - IGovernance(_governanceAddress).lock( - voter, - amount, - lockTimestamp, - v, - r, - s - ); + IGovernance(_governanceAddress).lock(voter, amount, lockTimestamp, v, r, s); vm.stopPrank(); /* ----------------------------*/ } - function checkCurrentState() internal { - console2.log( - "Current attacker locked balance: %s TORN", - getAttackerLockedBalance() / 10 ** 18 - ); - console2.log( - "Current governance Vault balance: %s TORN", - getGovernanceVaultBalance() / 10 ** 18 - ); + function compareStrings(string memory first, string memory second) public pure returns (bool) { + return keccak256(abi.encodePacked(first)) == keccak256(abi.encodePacked(second)); } - function checkResults() internal { - uint256 attackerBalanceAfterProposalExecution = getAttackerLockedBalance(); - uint256 governanceVaultBalanceAfterProposalExecution = getGovernanceVaultBalance(); + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - console2.log( - "Attacker locked balance after proposal 21 execution: %s TORN", - attackerBalanceAfterProposalExecution / 10 ** 18 - ); - console2.log( - "Governance Vault balance after proposal 21 execution: %s TORN", - governanceVaultBalanceAfterProposalExecution / 10 ** 18 - ); - require(attackerBalanceAfterProposalExecution == 0 ether); - require( - governanceVaultBalanceAfterProposalExecution == - currentGovernanceVaultBalance + attackerWithdrawnAmount - ); + function getAttackerLockedBalance() internal view returns (uint256) { + uint256 attackerLockedBalance; + for (uint256 i = 0; i < _attackerAddresses.length; i++) { + uint256 lockedBalance = governance.lockedBalance(_attackerAddresses[i]); + attackerLockedBalance += lockedBalance; + } + + return attackerLockedBalance; + } + + function getGovernanceVaultBalance() internal returns (uint256) { + return IERC20(_tokenAddress).balanceOf(_governanceVaultAddress); + } + + function getProposalExecutableTime(uint256 proposalId) internal returns (uint256) { + Proposal memory proposal = governance.proposals(proposalId); + return proposal.endTime + 2 days + 1 hours; } } diff --git a/test/ForkBased.sol b/test/ForkBased.sol new file mode 100644 index 0000000..8976da6 --- /dev/null +++ b/test/ForkBased.sol @@ -0,0 +1,14 @@ +pragma solidity 0.8.17; + +import "@interfaces/IGovernance.sol"; +import "@interfaces/IERC20.sol"; + +import "@proprietary/Parameters.sol"; +import "@proprietary/Mock.sol"; + +import "@forge-std/Test.sol"; +import "@forge-std/console2.sol"; + +contract ForkBased is Test { +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TESTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +} diff --git a/test/StorageBased.sol b/test/StorageBased.sol new file mode 100644 index 0000000..dad1d64 --- /dev/null +++ b/test/StorageBased.sol @@ -0,0 +1,162 @@ +pragma solidity 0.8.17; + +import "@interfaces/IGovernance.sol"; +import "@interfaces/IERC20.sol"; + +import "@proprietary/Parameters.sol"; +import "@proprietary/Mock.sol"; + +import "@forge-std/Test.sol"; +import "@forge-std/console2.sol"; + +contract StorageBased is Test, Parameters { + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TESTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + function testSlotsMustBeEqualToAddresses() public delimit { + console2.log( + "Follow this link to find the storage slots modified during the attack (out of order) => https://etherscan.io/tx/0x3274b6090685b842aca80b304a4dcee0f61ef8b6afee10b7c7533c32fb75486d#statechange\n" + ); + console2.log( + "Follow this link to find the attackers proposed addresses & proposal => https://etherscan.io/address/0x1FAd009aD35689B5a9B91486148F2F32AFE31e23#code" + ); + + bytes32[101] memory expectedStorageAddresses = getExpectedSlots(); + + for (uint256 i = 0; i < 101; i++) { + address attackerAddress = _attackerAddresses[i]; + bytes32 storageAddress = keccak256(abi.encode(attackerAddress, 59)); + bytes32 expectedStorageAddress = expectedStorageAddresses[i]; + + console2.log("\nCheck =", i); + console2.log("Attacker address =>", attackerAddress); + console2.log("Data for it is at storage address =>"); + console2.logBytes32(storageAddress); + console2.log("Should equal storage address =>"); + console2.logBytes32(expectedStorageAddress); + console2.log("Is it? => ", storageAddress == expectedStorageAddress); + } + } + + function testComputeProposalExecutedSlot() public delimit { + console2.log("The former test does not account for 1 more storage slot modified in governance.\n"); + console2.log("This is storage slot 0xece66cfdbd22e3f37d348a3d8e19074452862cd65fd4b9a11f0336d1ac6d1e69."); + console2.log("Computed:"); + console2.logBytes32(bytes32(uint256(keccak256(abi.encode(20 | 61))) + 166)); + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + function getExpectedSlots() public view returns (bytes32[101] memory) { + return [ + bytes32(0x6d3aadf46657098374577ba61c2d96d7adfad4046c8b3d0de09a97478f27ac89), + bytes32(0x72f687bcd6c747751d745ed2c8b6408d07c76055e3bd7647b995fd8bf8ca6c3a), + bytes32(0xba41ee557ea1dbd704632da5f6f57632b2ad3a9ab7769d61f3f79f55959e7fec), + bytes32(0x0b6fa73d149194f8207a526c015c64d7f83fe2df97440950a9c25d34c876cef9), + bytes32(0xdd83f177f5751c7c3c8c8c7447b92937e5965feac90c851291228e14f54ef7c6), + bytes32(0x482f0166295ad6811ef35ac84f54d4c67354df00acaf0ce7522539b1aebae893), + bytes32(0x7c63b3c5dcc68fea8aeb5643bfc1eaff39793eaa489602c5e76d010243dc5d97), + bytes32(0x8021aae281c806b06b06d218de5bd5f6933d8e5359757c679a06ee2bcfd26f7b), + bytes32(0xdc003a2947b930c74de4d300523120c6af6089c37c453de6fd05fc3a401a1ed8), + bytes32(0x942419feb35a17e0e5239f56df861407c23294d7d1b19a08edde87dc24836ea8), + bytes32(0x0028e41502e02a6d110bf9e7e47f08b5b47c2cb013a35190a94d0567deab943f), + bytes32(0x9d80d6a31907970e48b4ccb632dcd3719467c2d56742aa9eb78e3040d06b87bf), + bytes32(0x9dda19aca9c8cad8885d5156a74a42c77598fdf11e918e8bb45e3b06b07ef537), + bytes32(0xae9aadbb956c5ffbf192a17dcb5a4ce60def19234ddce01e014f65a21412320e), + bytes32(0xb97934cf063be06a858a08a169f3a87b7bbe2d720ca826bd257a738f444cc1a6), + bytes32(0x55b6562d0ab5d618a6d90e4af06d0f0ee333b8742b1919795346b0a001ef63c5), + bytes32(0xe5c1c9b28447803340e9a3b840e92fe84d62fc0c05fa8a336cb90b54ac4bfd4e), + bytes32(0x9df32107684d4da77a092c9be4c6c21aec7aafed07aca5723309d6dc5cf5c4e2), + bytes32(0x4c1f3688bec5a250aed03d00bb325b01e0e383b7f399f15616029dc892873c00), + bytes32(0x17aa0613d038e18067a7fde3c399424a2f05c3786aad47a5924f1cd57cf175a8), + bytes32(0x7493dd99e5502fba9e4f2303fd54be4b2df693d377e9518a2546e0fc231fe053), + bytes32(0x2c289221b40ff38c0615a5d15a18729e909ce5db49b2a9d7283d33f62e861d61), + bytes32(0x294d72c4285776248cba6f302d6739c2d7b684de0515406ce80e3a101f71f6c1), + bytes32(0x851f09bde5ef837ebb9e5f9bba86886950c9e085c3862dc25ae1d701376fdbef), + bytes32(0xf369d43db749b8ed060cb9761ea1f7b7e69bedce2cd8427f38abfb2e8d22b078), + bytes32(0xedf2d3ec1d92b0fc48875f557d61e66f24b573a9b4a5e6630d6dc83dc9d0592b), + bytes32(0x1f80546c03656dfa081daa224eae10e860f3be6d4fc2532f3edbb838bb4107ac), + bytes32(0x480988b4f7534f538764f4f7f02ba2e37613b3334732a2d84d2b0d17a16ea4a1), + bytes32(0x39da7f8bc2665edfe50fe0ead4748f6a18ea7fc5575d95670d77de966895fdde), + bytes32(0xd1741bd550f1ce5daf5bcd6ef68e6cbad925338ac823223e9ea5c9eaae85acaa), + bytes32(0x1d19327db86fc8ab0dd834ea8334ed40ed43a118bd39fe7245fe6851aff2a296), + bytes32(0xb687ead01f5a33fa5a51ced5c707a410f5d5bc122d369b048bb04b7fbbb474a2), + bytes32(0x9a81376be582aede805f1b31909fdcc9bca805be04ba0aef0683bc9ca8ef64e3), + bytes32(0x85bfd19c6c9c37382a324193ab0345c6f6a9157ccb7527e925e5b7559975cdce), + bytes32(0x9cae4b8f6926897c8b7d457b002334924d5c53b9274520b6eb2e3c4470219bd8), + bytes32(0xbea83b759f2c5cf60fa7b1ee77f99476d02f7c177dded7dad00bcad8bcbe3189), + bytes32(0xeac3f14ec76a077290cb2f0e0a00e36069cd693a2cc8b52f4470425265beb8eb), + bytes32(0xdf30d48eea928ac3a264121cd815aca0be9a5f880ab13c1724825013f1e97b4e), + bytes32(0x3e6cd34f16de89b9f2ab9fedcb1d5ad455285241da4f71934d4b54fb528f6d75), + bytes32(0x83dc0bbfe2277f5acd9ff5c84aff11fbfe02afa89b9b968becc1841a8693d292), + bytes32(0x9152dac88f73916513a1734fcf8ae1b752cf8f250634647ef8babfe8ffd18767), + bytes32(0x93dcdfdccdb7e326adc4066765fa948f6cafb8367d7038b35d45e405c53b9bc5), + bytes32(0x84b9ef4f730f6d6994cf5054048dbb81724ee911b7d8c713703d055541b93ce6), + bytes32(0x35e8df30fa823966478b4c7ccbfcdf48526606f12569b560522b9a7bdb832966), + bytes32(0xc678ea5a6ea2827aabfaa2c16947475e539a2d4ca6a599e58d74d43ebe8739f7), + bytes32(0xf7ceb58f0e785fa265b6708922b9995fe1d8f9de7040e1352c8e24b9d6f99b38), + bytes32(0x581f5fe92c7c9bd8880bb3b6a999c3ee84890a5dccb531e80c1b284ccb4b5c1f), + bytes32(0x0daef5d973db426d96711402ec9d2b1ef62d9019e197612939e96b839217ab6b), + bytes32(0xe73120c3a8a54845e77ae7add2cbe6558ef75edada0457d90a718bbc5736e50c), + bytes32(0xbabc3cb1d0fe95e8737a0f966691d1455c7e3141808b35594abc691ce53187ff), + bytes32(0x3216a647a73e7e6b49bfb95b7a07e0581594f0a757b7c595a5606926338bbea7), + bytes32(0x0b42f6082fa1d09a1f2a8548891baab99d43d9d352056958c499f6bb8516db95), + bytes32(0xf8eed3494701bf7093f27b4cdb88a1db96ee5e16722d46fe14368922ac6911b8), + bytes32(0x097bcab3c0ff6404448fdc3a0d65d34cae67858d812d4a14009e542ce7379a5c), + bytes32(0x1c1954c69f6d055f270d5f92882428ece18ec6ff54eb3759e64239c0e8d1c23e), + bytes32(0x150b8bf1039a78e5a0cda638606e8f4fcdd81680f7d34a2208aabbe191b7fc1b), + bytes32(0x269c78a04e4dfbf5074505045a3753ac46173341cc9589fa7079076affb0f77c), + bytes32(0x7ad48440080db03c47b6c2fa9635212ebd9d47ff2066ddea22afa1d6a69dd700), + bytes32(0x5ea18cebf186e4735392113d2dfc23dfac681b79c9af24ba2c955012d9e905bf), + bytes32(0xdb7ec7a1b6335b492e5fe9c1c356552235145714d8864c4c62e5cd103356baa5), + bytes32(0x2f4efeaf421ecb0fa95c32712400dbb5d3565486fdf636dd49690916706b3322), + bytes32(0x12666a874bec0fd2b70a9ddee2e23ad2f673a2edf82a8697dcdf1eca67076928), + bytes32(0xefee42602cc8af3b01737c403bff4ebf8b230540a7b11a8be0a624f3612f3bc7), + bytes32(0x3fc11e99cae08e7938e897e6afd399fda5bba0a4137e3e7785a9134357d027fa), + bytes32(0x71730d531f04bb651c2cd60a84e3222e8137c14e5f2970ba41176b757236e2a8), + bytes32(0x03e6eba8a567dec7f20d942e88674d2efdf2461a4b455ad25698655f0669044f), + bytes32(0x41ba7db959aeae73d46aa24f27e0dc91fd89e1a0bf120ffeb3ecfbcdd2df6aaf), + bytes32(0x4fb0a7dac301a3b33dff510693f210fbe24e07dc4e4c0933d6d1830cd12d27cf), + bytes32(0x39dafca388ce8896de1a3c190c0b5675368daa863d7cb8eecd84ed3e108726b8), + bytes32(0x5176ceb27445686c36a2edefa2d88edcfc4eec9b12dda6db7c3dc8a26cae43fe), + bytes32(0xad45b6c20476fbf588bcd7acce8405f2805ea6b01121cda3d32a9fe0e8fbfb15), + bytes32(0x231753911662040066c31d2d7239446e2ed073e6be332687a33972193fba8f35), + bytes32(0x45326a4913073240174d9d277324eda494cd770fcb4c598f18557c243ca9983f), + bytes32(0x36410d90f65dbd3fb5a2650fa4b3a92f9d3e1b49519d7d88b816c8af9de7639f), + bytes32(0x2146e765fa517e630fb8a01fbad9a901c01bd6a89c848ba5e597c22930447729), + bytes32(0xe67337a5233c080bf177494a4e5ca9e13da128ad34f1ae8e70e6a59aa7de2fdb), + bytes32(0x7f4743894bb0ace69e24a6ed032b818d953daccdf4a1403517e25f43f80a76a3), + bytes32(0xcd8079ed957301668f799e4f8ac6228d75ca437bc76a8d244d1940accc949796), + bytes32(0xca4b8853b9b199edbd0f7474cdda032a0dd1b9a783a5ce8628552c5e5efab0f3), + bytes32(0x5e8da5e3ba9cb348dc0fcd6cc691804f86db7364145fd9e132171dba372b8fe3), + bytes32(0x2a6b2c50479bad8e10501407000a56f960929f5e4744e4379281e4f435b647dd), + bytes32(0x98620bb34babb5e42e493bdd3187d6629774742a1af05e7de1d356123b3ac90d), + bytes32(0x86320cfb33a70e7772efdd04811d47aa87fa92f56464c15b5cafba344a07c1d6), + bytes32(0xc969ed01517552ce438008ae9f1c870ec6d5c26c4d9167e1ddca8f0eb933ff61), + bytes32(0x3975949de32dff56868e0f7b8ef32a343b463796b98f7b3c6eede7af4cc98efa), + bytes32(0x3b2908006ad44a7a6ed535a798c50c2cef3e29461cb3b61f065dfbd38998a248), + bytes32(0x237535dc40aad9f7b77cb3a9cd567919c93a4b03fbd409e7eb2524edf338009d), + bytes32(0xc873ca3cb9d655d00a309ecce7fd13d4b330d46849585030678e8beb4b460f66), + bytes32(0x22660775f1ebe8e5172a28862806368c340658b7841119865133a43f61c05079), + bytes32(0x38507f0cbb23efee0bfebade19a26a5f0c37baa2611a0f45496f0bd55f896cb5), + bytes32(0x394d2e3a745c16de559b5c4ede579a1ee9f465ce30d7a011607a31d58dd2432b), + bytes32(0x3b31e20783be58a3e9e55e055affb07776c45bbb5c9b12310e1c64641787a223), + bytes32(0x951b1f66f82a28c66da634dcf3f0c28e97f6414459e298ed3e89e844193d518a), + bytes32(0xb72f2c84d2997a6cc7e39e7a2ee09c1cef20a79be97f7e0f234c390dce0a51b7), + bytes32(0x23a5870a41dce1c9f8a195e7afc3da8fe9ab98dd6048d379ece402534bf8f174), + bytes32(0xf849fc2cd9077d8de94fcbd0f7afb74702b31b483aa5b87138b052169725037e), + bytes32(0x1ed8212501dd82c05f0f150ab6c2d40978f227ef417119305b68c32bc2a31b6d), + bytes32(0x0377da8e9b783d7a951fdf8fd41b3f3f03f75c884809734b7437ee7cf6dc7c7c), + bytes32(0x5e4bca86c93855d2b6bf3f9712484931c595789b3c4ddf5125825ff7dbac8260), + bytes32(0x33c521d4d4e63a162f23f947a781bc2c25bf54ed4efd03e1554eca0f21ecf219), + bytes32(0x4f7b3628312dc38b20561a3c76dd0700b80fbdb28da3cbc754d2b665b9757347) + ]; + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HELPERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + modifier delimit() { + console2.log("\n"); + _; + console2.log("\n"); + } +}