diff --git a/contracts/CrossChainUpgradeableProxy.sol b/contracts/CrossChainUpgradeableProxy.sol index 1b3e21b..07c8cc2 100644 --- a/contracts/CrossChainUpgradeableProxy.sol +++ b/contracts/CrossChainUpgradeableProxy.sol @@ -13,7 +13,7 @@ interface iOVM_CrossDomainMessenger { */ contract CrossChainUpgradeableProxy is TransparentUpgradeableProxy { // https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts/deployments/README.md - iOVM_CrossDomainMessenger public constant messenger = iOVM_CrossDomainMessenger(0x4200000000000000000000000000000000000007); + iOVM_CrossDomainMessenger public immutable messenger; /** * @dev Initializes an upgradeable proxy backed by the implementation at `_logic`. @@ -21,8 +21,11 @@ contract CrossChainUpgradeableProxy is TransparentUpgradeableProxy { constructor( address _logic, address _admin, - bytes memory _data - ) TransparentUpgradeableProxy(_logic, _admin, _data) {} + bytes memory _data, + iOVM_CrossDomainMessenger _messenger + ) TransparentUpgradeableProxy(_logic, _admin, _data) { + messenger = _messenger; + } /** * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the cross chain admin. diff --git a/contracts/Mocks/MockOVM_CrossDomainMessenger.sol b/contracts/Mocks/MockOVM_CrossDomainMessenger.sol new file mode 100644 index 0000000..f8ec03c --- /dev/null +++ b/contracts/Mocks/MockOVM_CrossDomainMessenger.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract MockOVM_CrossDomainMessenger { + address public xDomainMessageSender; + + constructor(address _xDomainMessageSender) { + xDomainMessageSender = _xDomainMessageSender; + } + + function execute(address _who, bytes calldata _calldata) external returns (bool success, bytes memory result) { + (success, result) = _who.call(_calldata); + } +} diff --git a/contracts/TornadoPool.sol b/contracts/TornadoPool.sol index f291c4d..b586d65 100644 --- a/contracts/TornadoPool.sol +++ b/contracts/TornadoPool.sol @@ -12,6 +12,7 @@ pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; interface IVerifier { function verifyProof(bytes memory _proof, uint256[10] memory _input) external view returns (bool); @@ -23,7 +24,7 @@ interface ERC20 { function transfer(address to, uint256 value) external returns (bool); } -contract TornadoPool { +contract TornadoPool is Initializable { uint256 public constant FIELD_SIZE = 21888242871839275222246405745257275088548364400416034343698204186575808495617; uint256 public constant MAX_EXT_AMOUNT = 2**248 - 1; @@ -66,15 +67,13 @@ contract TornadoPool { @dev The constructor @param _verifier2 the address of SNARK verifier for 2 inputs @param _verifier16 the address of SNARK verifier for 16 inputs - @param _currentRoot root of an empty Merkle tree */ - constructor( - IVerifier _verifier2, - IVerifier _verifier16, - bytes32 _currentRoot - ) { + constructor(IVerifier _verifier2, IVerifier _verifier16) { verifier2 = _verifier2; verifier16 = _verifier16; + } + + function initialize(bytes32 _currentRoot) external initializer { currentRoot = _currentRoot; } diff --git a/package.json b/package.json index 98fada7..3b2ea75 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@nomiclabs/hardhat-ethers": "^2.0.2", "@nomiclabs/hardhat-waffle": "^2.0.1", "@openzeppelin/contracts": "git+https://github.com/tornadocash/openzeppelin-contracts.git#6e46aa6946a7f215e7604169ddf46e1aebea850f", + "@openzeppelin/contracts-upgradeable": "3.4.1", "bignumber.js": "^9.0.0", "chai": "^4.3.4", "circom": "^0.5.45", diff --git a/src/utils.js b/src/utils.js index 45fb17e..632c1a7 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,3 +1,4 @@ +/* global network */ const crypto = require('crypto') const { ethers } = require('hardhat') const BigNumber = ethers.BigNumber @@ -73,6 +74,15 @@ async function revertSnapshot(id) { await ethers.provider.send('evm_revert', [id]) } +async function getSignerFromAddress(address) { + await network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [address], + }) + + return await ethers.provider.getSigner(address) +} + module.exports = { FIELD_SIZE, randomBN, @@ -84,4 +94,5 @@ module.exports = { takeSnapshot, revertSnapshot, shuffle, + getSignerFromAddress, } diff --git a/test/full.test.js b/test/full.test.js index 99518d2..d34a75d 100644 --- a/test/full.test.js +++ b/test/full.test.js @@ -12,11 +12,11 @@ const { transaction, registerAndTransact } = require('../src/index') const { Keypair } = require('../src/keypair') describe('TornadoPool', () => { - let snapshotId, tornadoPool, sender + let snapshotId, tornadoPool, sender, gov, proxy, messenger /* prettier-ignore */ before(async function () { - ;[sender] = await ethers.getSigners() + ;[sender, gov] = await ethers.getSigners() const Verifier2 = await ethers.getContractFactory('Verifier2') const verifier2 = await Verifier2.deploy() @@ -30,10 +30,37 @@ describe('TornadoPool', () => { const root = await tree.root() const Pool = await ethers.getContractFactory('TornadoPool') - tornadoPool = await Pool.deploy(verifier2.address, verifier16.address, toFixedHex(root)) + const tornadoPoolImpl = await Pool.deploy(verifier2.address, verifier16.address) + + const OVM_Messenger = await ethers.getContractFactory('MockOVM_CrossDomainMessenger') + messenger = await OVM_Messenger.deploy(gov.address) + await messenger.deployed() + + const CrossChainUpgradeableProxy = await ethers.getContractFactory('CrossChainUpgradeableProxy') + proxy = await CrossChainUpgradeableProxy.deploy(tornadoPoolImpl.address, gov.address, [], messenger.address) + await proxy.deployed() + + tornadoPool = await Pool.attach(proxy.address) + + await tornadoPool.initialize(toFixedHex(root)) snapshotId = await takeSnapshot() }) + describe('Upgradeability tests', () => { + it('admin should be gov', async () => { + const { data } = await proxy.populateTransaction.admin() + const { result } = await messenger.callStatic.execute(proxy.address, data) + expect('0x' + result.slice(26)).to.be.equal(gov.address.toLowerCase()) + }) + + it('non admin cannot call', async () => { + await proxy + .admin() + .should.be.revertedWith( + "Transaction reverted: function selector was not recognized and there's no fallback function", + ) + }) + }) it('encrypt -> decrypt should work', () => { const data = Buffer.from([0xff, 0xaa, 0x00, 0x01]) diff --git a/yarn.lock b/yarn.lock index 9714918..65a8b22 100644 --- a/yarn.lock +++ b/yarn.lock @@ -683,6 +683,11 @@ "@types/sinon-chai" "^3.2.3" "@types/web3" "1.0.19" +"@openzeppelin/contracts-upgradeable@3.4.1": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-3.4.1.tgz#38dfdfa86fda0a088c6fcdebe6870cfaf897b471" + integrity sha512-wBGlUzEkOxcj/ghtcF2yKc8ZYh+PTUtm1mK38zoENulJ6aplij7eH8quo3lMugfzPJy+V6V5qI8QhdQmCn7hkQ== + "@openzeppelin/contracts@git+https://github.com/tornadocash/openzeppelin-contracts.git#6e46aa6946a7f215e7604169ddf46e1aebea850f": version "3.4.1-solc-0.7-2" resolved "git+https://github.com/tornadocash/openzeppelin-contracts.git#6e46aa6946a7f215e7604169ddf46e1aebea850f"