Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

Wrapper/Reentrancy unit tests, etc. #2042

Merged
merged 33 commits into from
Aug 13, 2019

Conversation

dorothy-zbornak
Copy link
Contributor

@dorothy-zbornak dorothy-zbornak commented Aug 8, 2019

Description

tape

This PR introduces a couple things:

Exchange Wrapper Unit Tests

You'll find many new unit tests for Exchange wrapper functions in wrapper_unit_tests.ts.

This is accomplished by overriding a few Exchange internal functions in TestWrappersFunctions.sol so we test only the logic of the wrapper functions themselves.

Automatic Reentrancy Guard Testing

You'll find some lovely tests in reentrancy_tests.ts that automatically 🤖 checks if public Exchange functions are secured by the nonReentrant modifier. This voodoo is accomplished by the ReentrancyTester.sol contract.

By default, it will check all mutator public functions that are not whitelisted in test/utils/constants.ts, and it'll even check that those whitelisted functions are actually reentrant. This makes it much harder to accidentally expose a reentrant public function.

I've also removed a lot of the legacy reentrancy tests scattered around the tests because I feel like the new tests cover them. This is might be controversial. 😇

Other goodies:

  • marketBuyOrders and marketSellOrders have been removed and their noThrow variants have replaced them.
  • Signature validating functions in MixinSignatureValidator will now throw an InvalidSigner if the signerAddress == 0. This is to prevent people from using 0x0 as a signerAddress and passing bad signature data to ecrecover(), which will happily return 0x0.
  • There is no more assembly in fillOrderNoThrow 🎉
  • Updated the legacy Wallet signature type (which we still support in V3) to use the magic bytes pattern from v2.1 of the Exchange.
  • Added all mutator functions to constants.ExchangeFunctions in @0x/contracts/exchange/test/utils.ts.
  • Blacklisted yet another file from solhint because of abi.decode syntax: contracts/coordinator/contracts/src/MixinCoordinatorApprovalVerifier.sol. 😞

Some Thoughts

I think it might be cleaner to move ALL integration tests out of the exchange package and into its own package (like a protocol-tests package?), when we have more time. There's a lot of integration tests that pull in many external contract packages so things are looking a little spaghetti...

Testing instructions

You know the drill.

Types of changes

  • New feature (non-breaking change which adds functionality)

  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • Prefix PR title with [WIP] if necessary.
  • Add tests to cover changes as needed.
  • Update documentation as needed.
  • Add new entries to the relevant CHANGELOG.jsons.

@dorothy-zbornak dorothy-zbornak force-pushed the feature/3.0/exchange/wrapper-tests branch from 8169d02 to f9814b0 Compare August 8, 2019 07:24
@coveralls
Copy link

coveralls commented Aug 8, 2019

Coverage Status

Coverage increased (+0.003%) to 78.686% when pulling ca33090 on feature/3.0/exchange/wrapper-tests into 8adfa52 on 3.0.

@@ -22,9 +22,6 @@ pragma solidity ^0.5.9;
contract LibExchangeSelectors {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can probably ditch this file, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 the current approach is error-prone and not worth the small gas optimization over .selector.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed it entirely from the Exchange contracts. Coordinator and DevUtils still use it quite heavily, so I added it to the backlog.

@dorothy-zbornak dorothy-zbornak marked this pull request as ready for review August 8, 2019 07:45
@dorothy-zbornak dorothy-zbornak changed the title Wrapper/Reentrancy tests, etc. Wrapper/Reentrancy unit tests, etc. Aug 8, 2019
@dorothy-zbornak dorothy-zbornak force-pushed the feature/3.0/exchange/wrapper-tests branch from f9814b0 to 7bc7090 Compare August 8, 2019 15:59
@hysz
Copy link
Contributor

hysz commented Aug 8, 2019

I think it might be cleaner to move ALL integration tests out of the exchange package and into its own package.

I really like the idea of consolidating integration tests into a single package. We'd get an overview of protocol-wide test coverage without having to sift through all of our packages.

Copy link
Contributor

@hysz hysz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of great stuff in this PR (~‾▿‾)~

@@ -22,9 +22,6 @@ pragma solidity ^0.5.9;
contract LibExchangeSelectors {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 the current approach is error-prone and not worth the small gas optimization over .selector.

/// Because this function uses the `nonReentrant` modifier, if
/// the function being called is also guarded by the `nonReentrant` modifier,
/// it will revert when it returns.
function testReentrantFunction(bytes calldata fnCallData)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this only called by isReentrant? If so, why not apply the nonReentrant modifier directly to isReentrant?

Copy link
Contributor Author

@dorothy-zbornak dorothy-zbornak Aug 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly for UX reasons. Because our reentrancy guard is lazy, only second to the last non-reentrant call will revert. If I made isReentrant() nonReentrant, I wouldn't be able to capture and compare the revert data within the contract and provide a nice boolean result.

@@ -0,0 +1,1129 @@
import { ReferenceFunctions as LibReferenceFunctions } from '@0x/contracts-exchange-libs';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Readability lvl 💯

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

@dorothy-zbornak dorothy-zbornak force-pushed the feature/3.0/exchange/wrapper-tests branch from 7bc7090 to 5e9fe4f Compare August 10, 2019 01:27
@dorothy-zbornak dorothy-zbornak changed the base branch from feature/3.0/exchange/fill-order-unit-tests to 3.0 August 10, 2019 01:52
`@0x/contracts-exchange`: Add more `wrapper_unit_tests` tests.
…match v2.1.

`@0x/contracts-exchange`: Add EOA tests to `signature_validator`.
`@0x/contracts-exchange`: Remove old reentrancy tests.
`@0x/contracts-exchange`: Remove `ReentrantERC20Token` contract.
`@0x/contracts-coordinator`: Add `MixinCoordinatorApprovalVerifier.sol` to `.solhintignore` because of `abi.decode` issues.
`@0x/contracts-test-utils`: Remove unecessary wait timeout in
`LogDecoder`.
`@0x/contracts-exchange`: Remove references to `marketXOrdersNoThrow`.
`@0x/contracts-exchange`: Update reentrancy tests.
`@0x/contracts-exchange`: Add all mutator functions to
`ExchangeFunctions` type.
`@0x/contracts-tes-utils`: Remove unused import.
… constant in `TestValidatorWallet.sol`.

`@0x/contracts-exchange`: Remove references to `LibExchangeSelectors` in the Exchange.
@dorothy-zbornak dorothy-zbornak force-pushed the feature/3.0/exchange/wrapper-tests branch from 5e9fe4f to ca33090 Compare August 10, 2019 02:15
@@ -0,0 +1,2 @@
# solhint can't parse `abi.decode` syntax.
contracts/src/MixinCoordinatorApprovalVerifier.sol
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just disable that specific block of code? The rest of the contract is still being linted correctly right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried but solhint throws a parser error when it hits that contract. I'm a little confused about why this just started happening and with only certain forms of abi.decode(). I don't think it's just my setup because the same thing happened on CI (that's how I first noticed it).

contracts/exchange/contracts/src/MixinWrapperFunctions.sol Outdated Show resolved Hide resolved
contracts/exchange/contracts/test/TestValidatorWallet.sol Outdated Show resolved Hide resolved

import { constants as TestConstants } from './utils/constants';

blockchainTests.resets('Reentrancy Tests', env => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Having this all in one place is so much cleaner.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

contracts/exchange/test/utils/types.ts Outdated Show resolved Hide resolved
@@ -0,0 +1,1129 @@
import { ReferenceFunctions as LibReferenceFunctions } from '@0x/contracts-exchange-libs';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link
Contributor

@jalextowle jalextowle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! This is much cleaner than reentrancy previously was. I had a few nits, but it looks good to go once those are addressed.

{}

/// @dev Calls a public function to check if it is reentrant.
function isReentrant(bytes calldata fnCallData)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that it would make sense to start compiling a list of functions that need this modifier so that we can manually check during code reviews. That would also make it really easy when we start using static analysis.

contracts/exchange/contracts/test/ReentrancyTester.sol Outdated Show resolved Hide resolved
@@ -1,388 +0,0 @@
/*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥 So glad we're getting rid of this contract


import { constants as TestConstants } from './utils/constants';

blockchainTests.resets('Reentrancy Tests', env => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants