In @0x/contracts-exchange: Add tests for LibExchangeRichErrorDecoder.

This commit is contained in:
Lawrence Forman 2019-04-23 17:46:47 -04:00 committed by Amir Bandeali
parent a2846faa61
commit fdb6bee65f
9 changed files with 364 additions and 55 deletions

View File

@ -39,10 +39,10 @@
"src/interfaces/IValidator.sol",
"src/interfaces/IWallet.sol",
"src/interfaces/IWrapperFunctions.sol",
"src/libs/LibExchangeRichErrorDecoder.sol",
"test/ReentrantERC20Token.sol",
"test/TestAssetProxyDispatcher.sol",
"test/TestExchangeInternals.sol",
"test/TestLibExchangeRichErrorDecoder.sol",
"test/TestRevertReceiver.sol",
"test/TestSignatureValidator.sol",
"test/TestStaticCallReceiver.sol"

View File

@ -25,26 +25,12 @@ import "../mixins/MExchangeRichErrorTypes.sol";
contract LibExchangeRichErrorDecoder is
MExchangeRichErrorTypes
{
/// @dev Decompose an ABI-encoded StandardError.
/// This is the standard, string revert() error.
/// @param encoded ABI-encoded revert error.
/// @param encoded ABI-encoded revert error.
/// @return message The error message.
function decomposeStandardError(bytes memory encoded)
public
pure
returns (string memory message)
{
_assertSelectorBytes(encoded, STANDARD_ERROR_SELECTOR);
message = _readErrorParameterAsString(encoded, 0);
}
/// @dev Decompose an ABI-encoded SignatureError.
/// @param encoded ABI-encoded revert error.
/// @return errorCode The error code.
/// @return signer The expected signer of the hash.
/// @return signature The full signature.
function decomposeSignatureError(bytes memory encoded)
function decodeSignatureError(bytes memory encoded)
public
pure
returns (
@ -63,15 +49,13 @@ contract LibExchangeRichErrorDecoder is
/// @dev Decompose an ABI-encoded SignatureValidatorError.
/// @param encoded ABI-encoded revert error.
/// @return errorCode The error code.
/// @return signer The expected signer of the hash.
/// @return signature The full signature bytes.
/// @return errorData The revert data thrown by the validator contract.
function decomposeSignatureValidatorError(bytes memory encoded)
function decodeSignatureValidatorError(bytes memory encoded)
public
pure
returns (
SignatureErrorCodes errorCode,
bytes32 hash,
address signer,
bytes memory signature,
@ -79,11 +63,10 @@ contract LibExchangeRichErrorDecoder is
)
{
_assertSelectorBytes(encoded, SIGNATURE_VALIDATOR_ERROR_SELECTOR);
errorCode = SignatureErrorCodes(_readErrorParameterAsUint256(encoded, 0));
hash = _readErrorParameterAsBytes32(encoded, 1);
signer = _readErrorParameterAsAddress(encoded, 2);
signature = _readErrorParameterAsBytes(encoded, 3);
errorData = _readErrorParameterAsBytes(encoded, 4);
hash = _readErrorParameterAsBytes32(encoded, 0);
signer = _readErrorParameterAsAddress(encoded, 1);
signature = _readErrorParameterAsBytes(encoded, 2);
errorData = _readErrorParameterAsBytes(encoded, 3);
}
/// @dev Decompose an ABI-encoded SignatureWalletError.
@ -92,11 +75,10 @@ contract LibExchangeRichErrorDecoder is
/// @return signer The expected signer of the hash.
/// @return signature The full signature bytes.
/// @return errorData The revert data thrown by the validator contract.
function decomposeSignatureWalletError(bytes memory encoded)
function decodeSignatureWalletError(bytes memory encoded)
public
pure
returns (
SignatureErrorCodes errorCode,
bytes32 hash,
address signer,
bytes memory signature,
@ -104,11 +86,10 @@ contract LibExchangeRichErrorDecoder is
)
{
_assertSelectorBytes(encoded, SIGNATURE_WALLET_ERROR_SELECTOR);
errorCode = SignatureErrorCodes(_readErrorParameterAsUint256(encoded, 0));
hash = _readErrorParameterAsBytes32(encoded, 1);
signer = _readErrorParameterAsAddress(encoded, 2);
signature = _readErrorParameterAsBytes(encoded, 3);
errorData = _readErrorParameterAsBytes(encoded, 4);
hash = _readErrorParameterAsBytes32(encoded, 0);
signer = _readErrorParameterAsAddress(encoded, 1);
signature = _readErrorParameterAsBytes(encoded, 2);
errorData = _readErrorParameterAsBytes(encoded, 3);
}
/// @dev Decompose an ABI-encoded SignatureOrderValidatorError.
@ -117,11 +98,10 @@ contract LibExchangeRichErrorDecoder is
/// @return signer The expected signer of the hash.
/// @return signature The full signature bytes.
/// @return errorData The revert data thrown by the validator contract.
function decomposeSignatureOrderValidatorError(bytes memory encoded)
function decodeSignatureOrderValidatorError(bytes memory encoded)
public
pure
returns (
SignatureErrorCodes errorCode,
bytes32 hash,
address signer,
bytes memory signature,
@ -129,11 +109,10 @@ contract LibExchangeRichErrorDecoder is
)
{
_assertSelectorBytes(encoded, SIGNATURE_ORDER_VALIDATOR_ERROR_SELECTOR);
errorCode = SignatureErrorCodes(_readErrorParameterAsUint256(encoded, 0));
hash = _readErrorParameterAsBytes32(encoded, 1);
signer = _readErrorParameterAsAddress(encoded, 2);
signature = _readErrorParameterAsBytes(encoded, 3);
errorData = _readErrorParameterAsBytes(encoded, 4);
hash = _readErrorParameterAsBytes32(encoded, 0);
signer = _readErrorParameterAsAddress(encoded, 1);
signature = _readErrorParameterAsBytes(encoded, 2);
errorData = _readErrorParameterAsBytes(encoded, 3);
}
/// @dev Decompose an ABI-encoded SignatureWalletOrderValidatorError.
@ -142,11 +121,10 @@ contract LibExchangeRichErrorDecoder is
/// @return signer The expected signer of the hash.
/// @return signature The full signature bytes.
/// @return errorData The revert data thrown by the validator contract.
function decomposeSignatureWalletOrderValidatorError(bytes memory encoded)
function decodeSignatureWalletOrderValidatorError(bytes memory encoded)
public
pure
returns (
SignatureErrorCodes errorCode,
bytes32 hash,
address signer,
bytes memory signature,
@ -154,18 +132,17 @@ contract LibExchangeRichErrorDecoder is
)
{
_assertSelectorBytes(encoded, SIGNATURE_WALLET_ORDER_VALIDATOR_ERROR_SELECTOR);
errorCode = SignatureErrorCodes(_readErrorParameterAsUint256(encoded, 0));
hash = _readErrorParameterAsBytes32(encoded, 1);
signer = _readErrorParameterAsAddress(encoded, 2);
signature = _readErrorParameterAsBytes(encoded, 3);
errorData = _readErrorParameterAsBytes(encoded, 4);
hash = _readErrorParameterAsBytes32(encoded, 0);
signer = _readErrorParameterAsAddress(encoded, 1);
signature = _readErrorParameterAsBytes(encoded, 2);
errorData = _readErrorParameterAsBytes(encoded, 3);
}
/// @dev Decompose an ABI-encoded OrderStatusError.
/// @param encoded ABI-encoded revert error.
/// @return orderHash The order hash.
/// @return orderStatus The order status.
function decomposeOrderStatusError(bytes memory encoded)
function decodeOrderStatusError(bytes memory encoded)
public
pure
returns (
@ -181,8 +158,8 @@ contract LibExchangeRichErrorDecoder is
/// @dev Decompose an ABI-encoded InvalidSenderError.
/// @param encoded ABI-encoded revert error.
/// @return orderHash The order hash.
/// @return sender The sender of the order.
function decomposeInvalidSenderError(bytes memory encoded)
/// @return sender The sender.
function decodeInvalidSenderError(bytes memory encoded)
public
pure
returns (
@ -199,7 +176,7 @@ contract LibExchangeRichErrorDecoder is
/// @param encoded ABI-encoded revert error.
/// @return orderHash The order hash.
/// @return maker The maker of the order.
function decomposeInvalidMakerError(bytes memory encoded)
function decodeInvalidMakerError(bytes memory encoded)
public
pure
returns (
@ -207,16 +184,33 @@ contract LibExchangeRichErrorDecoder is
address maker
)
{
_assertSelectorBytes(encoded, INVALID_SENDER_ERROR_SELECTOR);
_assertSelectorBytes(encoded, INVALID_MAKER_ERROR_SELECTOR);
orderHash = _readErrorParameterAsBytes32(encoded, 0);
maker = _readErrorParameterAsAddress(encoded, 1);
}
/// @dev Decompose an ABI-encoded InvalidTaker.
/// @param encoded ABI-encoded revert error.
/// @return orderHash The order hash.
/// @return taker The taker of the order.
function decodeInvalidTakerError(bytes memory encoded)
public
pure
returns (
bytes32 orderHash,
address taker
)
{
_assertSelectorBytes(encoded, INVALID_TAKER_ERROR_SELECTOR);
orderHash = _readErrorParameterAsBytes32(encoded, 0);
taker = _readErrorParameterAsAddress(encoded, 1);
}
/// @dev Decompose an ABI-encoded FillError.
/// @param encoded ABI-encoded revert error.
/// @return errorCode The error code.
/// @return orderHash The order hash.
function decomposeFillError(bytes memory encoded)
function decodeFillError(bytes memory encoded)
public
pure
returns (
@ -229,6 +223,165 @@ contract LibExchangeRichErrorDecoder is
orderHash = _readErrorParameterAsBytes32(encoded, 0);
}
/// @dev Decompose an ABI-encoded OrderEpochError.
/// @param encoded ABI-encoded revert error.
/// @return maker The order maker.
/// @return sender The sender.
/// @return currentEpoch The current epoch for the maker.
function decodeOrderEpochError(bytes memory encoded)
public
pure
returns (
address maker,
address sender,
uint256 currentEpoch
)
{
_assertSelectorBytes(encoded, ORDER_EPOCH_ERROR_SELECTOR);
maker = _readErrorParameterAsAddress(encoded, 0);
sender = _readErrorParameterAsAddress(encoded, 1);
currentEpoch = _readErrorParameterAsUint256(encoded, 2);
}
/// @dev Decompose an ABI-encoded AssetProxyExistsError.
/// @param encoded ABI-encoded revert error.
/// @return proxyAddress The address of the asset proxy.
function decodeAssetProxyExistsError(bytes memory encoded)
public
pure
returns (
address assetProxyAddress
)
{
_assertSelectorBytes(encoded, ASSET_PROXY_EXISTS_ERROR_SELECTOR);
assetProxyAddress = _readErrorParameterAsAddress(encoded, 0);
}
/// @dev Decompose an ABI-encoded AssetProxyDispatchError.
/// @param encoded ABI-encoded revert error.
/// @return errorCode The error code.
/// @return orderHash Hash of the order being dispatched.
/// @return assetData Asset data of the order being dispatched.
function decodeAssetProxyDispatchError(bytes memory encoded)
public
pure
returns (
AssetProxyDispatchErrorCodes errorCode,
bytes32 orderHash,
bytes memory assetData
)
{
_assertSelectorBytes(encoded, ASSET_PROXY_DISPATCH_ERROR_SELECTOR);
errorCode = AssetProxyDispatchErrorCodes(_readErrorParameterAsUint256(encoded, 0));
orderHash = _readErrorParameterAsBytes32(encoded, 1);
assetData = _readErrorParameterAsBytes(encoded, 2);
}
/// @dev Decompose an ABI-encoded AssetProxyTransferError.
/// @param encoded ABI-encoded revert error.
/// @return orderHash Hash of the order being dispatched.
/// @return assetData Asset data of the order being dispatched.
/// @return errorData ABI-encoded revert data from the asset proxy.
function decodeAssetProxyTransferError(bytes memory encoded)
public
pure
returns (
bytes32 orderHash,
bytes memory assetData,
bytes memory errorData
)
{
_assertSelectorBytes(encoded, ASSET_PROXY_TRANSFER_ERROR_SELECTOR);
orderHash = _readErrorParameterAsBytes32(encoded, 0);
assetData = _readErrorParameterAsBytes(encoded, 1);
errorData = _readErrorParameterAsBytes(encoded, 2);
}
/// @dev Decompose an ABI-encoded NegativeSpreadError.
/// @param encoded ABI-encoded revert error.
/// @return leftOrderHash Hash of the left order being matched.
/// @return rightOrderHash Hash of the right order being matched.
function decodeNegativeSpreadError(bytes memory encoded)
public
pure
returns (
bytes32 leftOrderHash,
bytes32 rightOrderHash
)
{
_assertSelectorBytes(encoded, NEGATIVE_SPREAD_ERROR_SELECTOR);
leftOrderHash = _readErrorParameterAsBytes32(encoded, 0);
rightOrderHash = _readErrorParameterAsBytes32(encoded, 1);
}
/// @dev Decompose an ABI-encoded TransactionError.
/// @param encoded ABI-encoded revert error.
/// @return errorCode The error code.
/// @return transactionHash Hash of the transaction.
function decodeTransactionError(bytes memory encoded)
public
pure
returns (
TransactionErrorCodes errorCode,
bytes32 transactionHash
)
{
_assertSelectorBytes(encoded, TRANSACTION_ERROR_SELECTOR);
errorCode = TransactionErrorCodes(_readErrorParameterAsUint256(encoded, 0));
transactionHash = _readErrorParameterAsBytes32(encoded, 1);
}
/// @dev Decompose an ABI-encoded TransactionSignatureError.
/// @param encoded ABI-encoded revert error.
/// @return transactionHash Hash of the transaction.
/// @return signer Signer of the transaction.
/// @return signature Full signature for the transaction.
function decodeTransactionSignatureError(bytes memory encoded)
public
pure
returns (
bytes32 transactionHash,
address signer,
bytes memory signature
)
{
_assertSelectorBytes(encoded, TRANSACTION_SIGNATURE_ERROR_SELECTOR);
transactionHash = _readErrorParameterAsBytes32(encoded, 0);
signer = _readErrorParameterAsAddress(encoded, 1);
signature = _readErrorParameterAsBytes(encoded, 2);
}
/// @dev Decompose an ABI-encoded TransactionExecutionError.
/// @param encoded ABI-encoded revert error.
/// @return transactionHash Hash of the transaction.
/// @return errorData Error thrown by exeucteTransaction().
function decodeTransactionExecutionError(bytes memory encoded)
public
pure
returns (
bytes32 transactionHash,
bytes memory errorData
)
{
_assertSelectorBytes(encoded, TRANSACTION_EXECUTION_ERROR_SELECTOR);
transactionHash = _readErrorParameterAsBytes32(encoded, 0);
errorData = _readErrorParameterAsBytes(encoded, 1);
}
/// @dev Decompose an ABI-encoded IncompleteFillError.
/// @param encoded ABI-encoded revert error.
/// @return orderHash Hash of the order being filled.
function decodeIncompleteFillError(bytes memory encoded)
public
pure
returns (
bytes32 orderHash
)
{
_assertSelectorBytes(encoded, INCOMPLETE_FILL_ERROR_SELECTOR);
orderHash = _readErrorParameterAsBytes32(encoded, 0);
}
/// @dev Revert if the leading 4 bytes of `encoded` is not `selector`.
function _assertSelectorBytes(bytes memory encoded, bytes4 selector)
private
@ -237,7 +390,7 @@ contract LibExchangeRichErrorDecoder is
bytes4 actualSelector = LibBytes.readBytes4(encoded, 0);
require(
actualSelector == selector,
"INVALID_SELECTOR"
"BAD_SELECTOR"
);
}

View File

@ -0,0 +1,27 @@
/*
Copyright 2018 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.5;
import "../src/libs/LibExchangeRichErrorDecoder.sol";
// solhint-disable no-empty-blocks
contract TestLibExchangeRichErrorDecoder is
LibExchangeRichErrorDecoder
{}

View File

@ -34,7 +34,7 @@
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
},
"config": {
"abis": "./generated-artifacts/@(Exchange|ExchangeWrapper|IAssetProxyDispatcher|IExchange|IExchangeCore|IMatchOrders|IOrderValidator|ISignatureValidator|ITransactions|IValidator|IWallet|IWrapperFunctions|ReentrantERC20Token|TestAssetProxyDispatcher|TestExchangeInternals|TestRevertReceiver|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|Whitelist).json",
"abis": "./generated-artifacts/@(Exchange|ExchangeWrapper|IAssetProxyDispatcher|IExchange|IExchangeCore|IMatchOrders|IOrderValidator|ISignatureValidator|ITransactions|IValidator|IWallet|IWrapperFunctions|ReentrantERC20Token|TestAssetProxyDispatcher|TestExchangeInternals|TestLibExchangeRichErrorDecoder|TestRevertReceiver|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|Whitelist).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {

View File

@ -20,6 +20,7 @@ import * as IWrapperFunctions from '../generated-artifacts/IWrapperFunctions.jso
import * as ReentrantERC20Token from '../generated-artifacts/ReentrantERC20Token.json';
import * as TestAssetProxyDispatcher from '../generated-artifacts/TestAssetProxyDispatcher.json';
import * as TestExchangeInternals from '../generated-artifacts/TestExchangeInternals.json';
import * as TestLibExchangeRichErrorDecoder from '../generated-artifacts/TestLibExchangeRichErrorDecoder.json';
import * as TestRevertReceiver from '../generated-artifacts/TestRevertReceiver.json';
import * as TestSignatureValidator from '../generated-artifacts/TestSignatureValidator.json';
import * as TestStaticCallReceiver from '../generated-artifacts/TestStaticCallReceiver.json';
@ -48,4 +49,5 @@ export const artifacts = {
TestRevertReceiver: TestRevertReceiver as ContractArtifact,
TestSignatureValidator: TestSignatureValidator as ContractArtifact,
TestStaticCallReceiver: TestStaticCallReceiver as ContractArtifact,
TestLibExchangeRichErrorDecoder: TestLibExchangeRichErrorDecoder as ContractArtifact,
};

View File

@ -18,6 +18,7 @@ export * from '../generated-wrappers/i_wrapper_functions';
export * from '../generated-wrappers/reentrant_erc20_token';
export * from '../generated-wrappers/test_asset_proxy_dispatcher';
export * from '../generated-wrappers/test_exchange_internals';
export * from '../generated-wrappers/test_lib_exchange_rich_error_decoder';
export * from '../generated-wrappers/test_revert_receiver';
export * from '../generated-wrappers/test_signature_validator';
export * from '../generated-wrappers/test_static_call_receiver';

View File

@ -220,8 +220,6 @@ describe('Exchange core', () => {
};
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
// Grant the reentrant ERC20 token a
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();

View File

@ -0,0 +1,127 @@
import {
addressUtils,
chaiSetup,
OrderStatus,
orderUtils,
provider,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils, ExchangeRevertErrors, orderHashUtils } from '@0x/order-utils';
import { RevertError } from '@0x/utils';
import * as chai from 'chai';
import * as crypto from 'crypto';
import * as _ from 'lodash';
import {
artifacts,
TestLibExchangeRichErrorDecoderContract,
} from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe.only('LibExchangeRichErrorDecoder', () => {
let decoder: TestLibExchangeRichErrorDecoderContract;
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
decoder = await TestLibExchangeRichErrorDecoderContract.deployFrom0xArtifactAsync(
artifacts.TestLibExchangeRichErrorDecoder,
provider,
txDefaults,
);
});
function generateRandomBytes(length: number) {
const bytes = crypto.randomBytes(length).toString('hex');
return `0x${bytes}`;
}
function createDecodeTest(
revertType: new(...args: any[]) => RevertError,
parameters: any[],
) {
const revert = new revertType(...parameters);
const encoded = revert.encode();
const endpointName = `decode${revert.name}`;
const callAsync = (encoded: string) => {
return (decoder as any)[endpointName].callAsync.call(
(decoder as any)[endpointName],
encoded,
);
};
describe(`${endpointName}()`, async () => {
it('decodes encoded parameters', async () => {
const results = await callAsync(encoded);
return expect(results).to.deep.equal(parameters);
});
it('reverts if selector does not match', async () => {
// Replace the selector with null bytes.
const NULL_SELECTOR = '00000000';
const withBadSelector = `0x${NULL_SELECTOR}${encoded.substr(10)}`;
return expect(callAsync(withBadSelector)).to.revertWith('BAD_SELECTOR');
});
});
};
(() => {
const errorCode = ExchangeRevertErrors.SignatureErrorCode.Illegal;
const orderHash = orderUtils.generatePseudoRandomOrderHash();
const signer = addressUtils.generatePseudoRandomAddress();
const signature = generateRandomBytes(66);
const errorData = generateRandomBytes(4+32+32+32);
createDecodeTest(
ExchangeRevertErrors.SignatureError,
[
errorCode,
orderHash,
signer,
signature,
],
);
createDecodeTest(
ExchangeRevertErrors.SignatureValidatorError,
[
orderHash,
signer,
signature,
errorData,
],
);
createDecodeTest(
ExchangeRevertErrors.SignatureWalletError,
[
orderHash,
signer,
signature,
errorData,
],
);
createDecodeTest(
ExchangeRevertErrors.SignatureOrderValidatorError,
[
orderHash,
signer,
signature,
errorData,
],
);
createDecodeTest(
ExchangeRevertErrors.SignatureWalletOrderValidatorError,
[
orderHash,
signer,
signature,
errorData,
],
);
})();
});

View File

@ -18,6 +18,7 @@
"generated-artifacts/ReentrantERC20Token.json",
"generated-artifacts/TestAssetProxyDispatcher.json",
"generated-artifacts/TestExchangeInternals.json",
"generated-artifacts/TestLibExchangeRichErrorDecoder.json",
"generated-artifacts/TestRevertReceiver.json",
"generated-artifacts/TestSignatureValidator.json",
"generated-artifacts/TestStaticCallReceiver.json",