diff --git a/contracts/utils/compiler.json b/contracts/utils/compiler.json index 7d494ae78d..a81005b841 100644 --- a/contracts/utils/compiler.json +++ b/contracts/utils/compiler.json @@ -30,13 +30,13 @@ "src/LibEIP712.sol", "src/Ownable.sol", "src/ReentrancyGuard.sol", - "src/RichErrors.sol", "src/SafeMath.sol", "src/interfaces/IOwnable.sol", "test/TestConstants.sol", "test/TestLibAddressArray.sol", "test/TestLibBytes.sol", "test/TestOwnable.sol", + "test/TestReentrancyGuard.sol", "test/TestSafeMath.sol" ] } diff --git a/contracts/utils/contracts/src/RichErrors.sol b/contracts/utils/contracts/src/RichErrors.sol deleted file mode 100644 index ebba8b7d4f..0000000000 --- a/contracts/utils/contracts/src/RichErrors.sol +++ /dev/null @@ -1,58 +0,0 @@ -/* - - Copyright 2019 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity ^0.5.9; - - -contract RichErrors { - - // bytes4(keccak256("Error(string)")) - bytes4 internal constant STANDARD_ERROR_SELECTOR = - 0x08c379a0; - - // solhint-disable func-name-mixedcase - /// @dev ABI encode a standard, string revert error payload. - /// This is the same payload that would be included by a `revert(string)` - /// solidity statement. It has the function signature `Error(string)`. - /// @param message The error string. - /// @return The ABI encoded error. - function StandardError( - string memory message - ) - internal - pure - returns (bytes memory) - { - return abi.encodeWithSelector( - STANDARD_ERROR_SELECTOR, - bytes(message) - ); - } - // solhint-enable func-name-mixedcase - - /// @dev Reverts an encoded rich revert reason `errorData`. - /// @param errorData ABI encoded error data. - function _rrevert(bytes memory errorData) - internal - pure - { - assembly { - revert(add(errorData, 0x20), mload(errorData)) - } - } -} diff --git a/contracts/utils/contracts/test/TestReentrancyGuard.sol b/contracts/utils/contracts/test/TestReentrancyGuard.sol new file mode 100644 index 0000000000..54394f712d --- /dev/null +++ b/contracts/utils/contracts/test/TestReentrancyGuard.sol @@ -0,0 +1,54 @@ +/* + + Copyright 2019 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.5.9; + +import "../src/ReentrancyGuard.sol"; + + +contract TestReentrancyGuard is + ReentrancyGuard +{ + uint256 internal counter = 2; + + function guarded(bool shouldBeAttacked) + external + nonReentrant + { + if (shouldBeAttacked) { + this.exploitive(); + } else { + this.nonExploitive(); + } + } + + function exploitive() + external + { + if (counter > 0) { + counter--; + this.guarded(true); + } else { + counter = 2; + } + } + + function nonExploitive() + external + {} // solhint-disable-line no-empty-blocks +} diff --git a/contracts/utils/package.json b/contracts/utils/package.json index 9ddbceb9d1..b2a95da17a 100644 --- a/contracts/utils/package.json +++ b/contracts/utils/package.json @@ -34,7 +34,7 @@ "lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol" }, "config": { - "abis": "./generated-artifacts/@(IOwnable|LibAddress|LibBytes|LibEIP1271|LibEIP712|Ownable|ReentrancyGuard|RichErrors|SafeMath|TestConstants|TestLibAddressArray|TestLibBytes|TestOwnable|TestSafeMath).json", + "abis": "./generated-artifacts/@(IOwnable|LibAddress|LibBytes|LibEIP1271|LibEIP712|Ownable|ReentrancyGuard|SafeMath|TestConstants|TestLibAddressArray|TestLibBytes|TestOwnable|TestReentrancyGuard|TestSafeMath).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/utils/src/artifacts.ts b/contracts/utils/src/artifacts.ts index 9267a5b6b7..2f171de7a5 100644 --- a/contracts/utils/src/artifacts.ts +++ b/contracts/utils/src/artifacts.ts @@ -12,12 +12,12 @@ import * as LibEIP1271 from '../generated-artifacts/LibEIP1271.json'; import * as LibEIP712 from '../generated-artifacts/LibEIP712.json'; import * as Ownable from '../generated-artifacts/Ownable.json'; import * as ReentrancyGuard from '../generated-artifacts/ReentrancyGuard.json'; -import * as RichErrors from '../generated-artifacts/RichErrors.json'; import * as SafeMath from '../generated-artifacts/SafeMath.json'; import * as TestConstants from '../generated-artifacts/TestConstants.json'; import * as TestLibAddressArray from '../generated-artifacts/TestLibAddressArray.json'; import * as TestLibBytes from '../generated-artifacts/TestLibBytes.json'; import * as TestOwnable from '../generated-artifacts/TestOwnable.json'; +import * as TestReentrancyGuard from '../generated-artifacts/TestReentrancyGuard.json'; import * as TestSafeMath from '../generated-artifacts/TestSafeMath.json'; export const artifacts = { LibAddress: LibAddress as ContractArtifact, @@ -26,12 +26,12 @@ export const artifacts = { LibEIP712: LibEIP712 as ContractArtifact, Ownable: Ownable as ContractArtifact, ReentrancyGuard: ReentrancyGuard as ContractArtifact, - RichErrors: RichErrors as ContractArtifact, SafeMath: SafeMath as ContractArtifact, IOwnable: IOwnable as ContractArtifact, TestConstants: TestConstants as ContractArtifact, TestLibAddressArray: TestLibAddressArray as ContractArtifact, TestLibBytes: TestLibBytes as ContractArtifact, TestOwnable: TestOwnable as ContractArtifact, + TestReentrancyGuard: TestReentrancyGuard as ContractArtifact, TestSafeMath: TestSafeMath as ContractArtifact, }; diff --git a/contracts/utils/src/wrappers.ts b/contracts/utils/src/wrappers.ts index 5dffb7b8f4..8b4176ab7f 100644 --- a/contracts/utils/src/wrappers.ts +++ b/contracts/utils/src/wrappers.ts @@ -10,10 +10,10 @@ export * from '../generated-wrappers/lib_e_i_p1271'; export * from '../generated-wrappers/lib_e_i_p712'; export * from '../generated-wrappers/ownable'; export * from '../generated-wrappers/reentrancy_guard'; -export * from '../generated-wrappers/rich_errors'; export * from '../generated-wrappers/safe_math'; export * from '../generated-wrappers/test_constants'; export * from '../generated-wrappers/test_lib_address_array'; export * from '../generated-wrappers/test_lib_bytes'; export * from '../generated-wrappers/test_ownable'; +export * from '../generated-wrappers/test_reentrancy_guard'; export * from '../generated-wrappers/test_safe_math'; diff --git a/contracts/utils/test/reentrancy_guard.ts b/contracts/utils/test/reentrancy_guard.ts new file mode 100644 index 0000000000..304c755a8b --- /dev/null +++ b/contracts/utils/test/reentrancy_guard.ts @@ -0,0 +1,40 @@ +import { chaiSetup, provider, txDefaults, web3Wrapper } from '@0x/contracts-test-utils'; +import { BlockchainLifecycle } from '@0x/dev-utils'; +import { ReentrancyGuardRevertErrors } from '@0x/utils'; +import * as chai from 'chai'; +import * as _ from 'lodash'; + +import { artifacts, TestReentrancyGuardContract } from '../src'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('ReentrancyGuard', () => { + let guard: TestReentrancyGuardContract; + + before(async () => { + await blockchainLifecycle.startAsync(); + // Deploy TestReentrancyGuard + guard = await TestReentrancyGuardContract.deployFrom0xArtifactAsync( + artifacts.TestReentrancyGuard, + provider, + txDefaults, + ); + }); + + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + + describe('nonReentrant', () => { + it('should throw if reentrancy occurs', async () => { + const expectedError = new ReentrancyGuardRevertErrors.IllegalReentrancyError(); + return expect(guard.guarded.sendTransactionAsync(true)).to.revertWith(expectedError); + }); + + it('should succeed if reentrancy does not occur', async () => { + expect(guard.guarded.sendTransactionAsync(false)).to.be.fulfilled; // tslint:disable-line:no-unused-expression + }); + }); +}); diff --git a/contracts/utils/tsconfig.json b/contracts/utils/tsconfig.json index 3f1913dd7e..891fbe342f 100644 --- a/contracts/utils/tsconfig.json +++ b/contracts/utils/tsconfig.json @@ -10,12 +10,12 @@ "generated-artifacts/LibEIP712.json", "generated-artifacts/Ownable.json", "generated-artifacts/ReentrancyGuard.json", - "generated-artifacts/RichErrors.json", "generated-artifacts/SafeMath.json", "generated-artifacts/TestConstants.json", "generated-artifacts/TestLibAddressArray.json", "generated-artifacts/TestLibBytes.json", "generated-artifacts/TestOwnable.json", + "generated-artifacts/TestReentrancyGuard.json", "generated-artifacts/TestSafeMath.json" ], "exclude": ["./deploy/solc/solc_bin"]