@0x/contracts-exchange: Always check OrderValidator and WalletOrderValidator signature types on every fill

`@0x/contracts-exchange`: Add `validatorAddress` field to `SignatureValidatorError` and `SignatureOrderValidatorError` rich reverts
`@0x/contracts-exchange`: Add separate `SignatureOrderValidatorNotApprovedError` for `OrderValidator` signatures
`@0x/contracts-exchange`: Consolidate Wallet and Validator test contracts into a single configurable `TestValidatorWallet` contract.
`@0x/contracts-exchange`: Rewrite many tests in `signature_validator.ts` for brevity.
This commit is contained in:
Lawrence Forman
2019-06-20 21:18:34 -04:00
committed by Amir Bandeali
parent 5f8ebc3601
commit 42f7b7cc19
18 changed files with 834 additions and 708 deletions

View File

@@ -25,8 +25,6 @@
},
"contracts": [
"examples/ExchangeWrapper.sol",
"examples/Validator.sol",
"examples/Wallet.sol",
"examples/Whitelist.sol",
"src/Exchange.sol",
"src/interfaces/IAssetProxyDispatcher.sol",
@@ -43,8 +41,7 @@
"test/TestAssetProxyDispatcher.sol",
"test/TestExchangeInternals.sol",
"test/TestLibExchangeRichErrorDecoder.sol",
"test/TestRevertReceiver.sol",
"test/TestSignatureValidator.sol",
"test/TestStaticCallReceiver.sol"
"test/TestValidatorWallet.sol"
]
}

View File

@@ -1,79 +0,0 @@
/*
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;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "../src/interfaces/IValidator.sol";
import "../src/interfaces/IOrderValidator.sol";
contract Validator is
IValidator,
IOrderValidator
{
// The single valid signer for this validator.
// solhint-disable-next-line var-name-mixedcase
address internal VALID_SIGNER;
/// @dev constructs a new `Validator` with a single valid signer.
/// @param validSigner The sole, valid signer.
constructor (address validSigner) public {
VALID_SIGNER = validSigner;
}
// solhint-disable no-unused-vars
/// @dev Verifies that a signature is valid. `signer` must match `VALID_SIGNER`.
/// @param hash Message hash that is signed.
/// @param signerAddress Address that should have signed the given hash.
/// @param signature Proof of signing.
/// @return Validity of signature.
function isValidSignature(
bytes32 hash,
address signerAddress,
bytes calldata signature
)
external
view
returns (bool isValid)
{
return (signerAddress == VALID_SIGNER);
}
// solhint-enable no-unused-vars
// solhint-disable no-unused-vars
/// @dev Verifies that an order and signature is valid. `signer` must match `VALID_SIGNER`.
/// @param order The order.
/// @param orderHash The order hash.
/// @param signature Proof of signing.
/// @return Validity of signature.
function isValidOrderSignature(
LibOrder.Order calldata order,
bytes32 orderHash,
bytes calldata signature
)
external
view
returns (bool isValid)
{
return (order.makerAddress == VALID_SIGNER);
}
// solhint-enable no-unused-vars
}

View File

@@ -1,100 +0,0 @@
/*
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;
pragma experimental ABIEncoderV2;
import "../src/interfaces/IWallet.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
contract Wallet is
IWallet
{
using LibBytes for bytes;
// The owner of this wallet.
// solhint-disable-next-line var-name-mixedcase
address internal WALLET_OWNER;
/// @dev constructs a new `Wallet` with a single owner.
/// @param walletOwner The owner of this wallet.
constructor (address walletOwner) public {
WALLET_OWNER = walletOwner;
}
/// @dev Validates an EIP712 signature.
/// The signer must match the owner of this wallet.
/// @param hash Message hash that is signed.
/// @param signature Proof of signing.
/// @return Validity of signature.
function isValidSignature(
bytes32 hash,
bytes calldata signature
)
external
view
returns (bool isValid)
{
require(
signature.length == 65,
"LENGTH_65_REQUIRED"
);
return _validateEIP712Signature(hash, signature);
}
/// @dev Validates an order AND EIP712 signature.
/// The signer must match the owner of this wallet.
/// @param order The order.
/// @param orderHash The order hash.
/// @param signature Proof of signing.
/// @return Validity of order and signature.
function isValidOrderSignature(
LibOrder.Order calldata order,
bytes32 orderHash,
bytes calldata signature
)
external
view
returns (bool isValid)
{
// Ensure order hash is correct.
require(
order.makerAddress == WALLET_OWNER,
"INVALID_ORDER_MAKER"
);
return _validateEIP712Signature(orderHash, signature);
}
function _validateEIP712Signature(
bytes32 hash,
bytes memory signature
)
private
view
returns (bool isValid)
{
uint8 v = uint8(signature[0]);
bytes32 r = signature.readBytes32(1);
bytes32 s = signature.readBytes32(33);
address recoveredAddress = ecrecover(hash, v, r, s);
return WALLET_OWNER == recoveredAddress;
}
}

View File

@@ -47,9 +47,40 @@ contract MixinExchangeRichErrors is
);
}
function SignatureValidatorNotApprovedError(
address signerAddress,
address validatorAddress
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
SIGNATURE_VALIDATOR_NOT_APPROVED_ERROR_SELECTOR,
signerAddress,
validatorAddress
);
}
function SignatureOrderValidatorNotApprovedError(
address signerAddress,
address validatorAddress
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
SIGNATURE_ORDER_VALIDATOR_NOT_APPROVED_ERROR_SELECTOR,
signerAddress,
validatorAddress
);
}
function SignatureValidatorError(
bytes32 hash,
address signerAddress,
address validatorAddress,
bytes memory signature,
bytes memory errorData
)
@@ -61,6 +92,7 @@ contract MixinExchangeRichErrors is
SIGNATURE_VALIDATOR_ERROR_SELECTOR,
hash,
signerAddress,
validatorAddress,
signature,
errorData
);
@@ -88,6 +120,7 @@ contract MixinExchangeRichErrors is
function SignatureOrderValidatorError(
bytes32 orderHash,
address signerAddress,
address validatorAddress,
bytes memory signature,
bytes memory errorData
)
@@ -99,6 +132,7 @@ contract MixinExchangeRichErrors is
SIGNATURE_ORDER_VALIDATOR_ERROR_SELECTOR,
orderHash,
signerAddress,
validatorAddress,
signature,
errorData
);

View File

@@ -360,7 +360,10 @@ contract MixinSignatureValidator is
address validatorAddress = signature.readAddress(signatureLength - 21);
// Ensure signer has approved validator.
if (!allowedValidators[signerAddress][validatorAddress]) {
return false;
_rrevert(SignatureValidatorNotApprovedError(
signerAddress,
validatorAddress
));
}
// Shave the validator address and signature type from the signature.
assembly {
@@ -387,6 +390,7 @@ contract MixinSignatureValidator is
_rrevert(SignatureValidatorError(
hash,
signerAddress,
validatorAddress,
signature,
returnData
));
@@ -468,7 +472,10 @@ contract MixinSignatureValidator is
address validatorAddress = signature.readAddress(signatureLength - 21);
// Ensure signer has approved validator.
if (!allowedOrderValidators[signerAddress][validatorAddress]) {
return false;
_rrevert(SignatureOrderValidatorNotApprovedError(
signerAddress,
validatorAddress
));
}
// Shave the validator address and signature type from the signature.
assembly {
@@ -495,6 +502,7 @@ contract MixinSignatureValidator is
_rrevert(SignatureOrderValidatorError(
orderHash,
signerAddress,
validatorAddress,
signature,
returnData
));

View File

@@ -33,17 +33,25 @@ contract IExchangeRichErrors {
bytes4 internal constant SIGNATURE_ERROR_SELECTOR =
0x7e5a2318;
// bytes4(keccak256("SignatureValidatorError(bytes32,address,bytes,bytes)"))
// bytes4(keccak256("SignatureValidatorNotApprovedError(address,address)"))
bytes4 internal constant SIGNATURE_VALIDATOR_NOT_APPROVED_ERROR_SELECTOR =
0xa15c0d06;
// bytes4(keccak256("SignatureOrderValidatorNotApprovedError(address,address)"))
bytes4 internal constant SIGNATURE_ORDER_VALIDATOR_NOT_APPROVED_ERROR_SELECTOR =
0x6d273c5a;
// bytes4(keccak256("SignatureValidatorError(bytes32,address,address,bytes,bytes)"))
bytes4 internal constant SIGNATURE_VALIDATOR_ERROR_SELECTOR =
0x169fad8c;
0xa23838b8;
// bytes4(keccak256("SignatureWalletError(bytes32,address,bytes,bytes)"))
bytes4 internal constant SIGNATURE_WALLET_ERROR_SELECTOR =
0x1b8388f7;
// bytes4(keccak256("SignatureOrderValidatorError(bytes32,address,bytes,bytes)"))
// bytes4(keccak256("SignatureOrderValidatorError(bytes32,address,address,bytes,bytes)"))
bytes4 internal constant SIGNATURE_ORDER_VALIDATOR_ERROR_SELECTOR =
0xfabf4577;
0xf45375b7;
// bytes4(keccak256("SignatureWalletOrderValidatorError(bytes32,address,bytes,bytes)"))
bytes4 internal constant SIGNATURE_WALLET_ORDER_VALIDATOR_ERROR_SELECTOR =

View File

@@ -23,7 +23,7 @@ import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "../interfaces/IExchangeRichErrors.sol";
contract LibExchangeRichErrorDecoder is
contract LibExchangeRichErrorDecoder is
IExchangeRichErrors
{
/// @dev Decompose an ABI-encoded SignatureError.
@@ -59,6 +59,7 @@ contract LibExchangeRichErrorDecoder is
returns (
bytes32 hash,
address signerAddress,
address validatorAddress,
bytes memory signature,
bytes memory errorData
)
@@ -66,8 +67,43 @@ contract LibExchangeRichErrorDecoder is
_assertSelectorBytes(encoded, SIGNATURE_VALIDATOR_ERROR_SELECTOR);
hash = _readErrorParameterAsBytes32(encoded, 0);
signerAddress = _readErrorParameterAsAddress(encoded, 1);
signature = _readErrorParameterAsBytes(encoded, 2);
errorData = _readErrorParameterAsBytes(encoded, 3);
validatorAddress = _readErrorParameterAsAddress(encoded, 2);
signature = _readErrorParameterAsBytes(encoded, 3);
errorData = _readErrorParameterAsBytes(encoded, 4);
}
/// @dev Decompose an ABI-encoded SignatureValidatorNotApprovedError.
/// @param encoded ABI-encoded revert error.
/// @return signerAddress The expected signer of the hash.
/// @return validatorAddress The expected validator.
function decodeSignatureValidatorNotApprovedError(bytes memory encoded)
public
pure
returns (
address signerAddress,
address validatorAddress
)
{
_assertSelectorBytes(encoded, SIGNATURE_VALIDATOR_NOT_APPROVED_ERROR_SELECTOR);
signerAddress = _readErrorParameterAsAddress(encoded, 0);
validatorAddress = _readErrorParameterAsAddress(encoded, 1);
}
/// @dev Decompose an ABI-encoded SignatureOrderValidatorNotApprovedError.
/// @param encoded ABI-encoded revert error.
/// @return signerAddress The expected signer of the hash.
/// @return validatorAddress The expected validator.
function decodeSignatureOrderValidatorNotApprovedError(bytes memory encoded)
public
pure
returns (
address signerAddress,
address validatorAddress
)
{
_assertSelectorBytes(encoded, SIGNATURE_ORDER_VALIDATOR_NOT_APPROVED_ERROR_SELECTOR);
signerAddress = _readErrorParameterAsAddress(encoded, 0);
validatorAddress = _readErrorParameterAsAddress(encoded, 1);
}
/// @dev Decompose an ABI-encoded SignatureWalletError.
@@ -105,6 +141,7 @@ contract LibExchangeRichErrorDecoder is
returns (
bytes32 hash,
address signerAddress,
address validatorAddress,
bytes memory signature,
bytes memory errorData
)
@@ -112,8 +149,9 @@ contract LibExchangeRichErrorDecoder is
_assertSelectorBytes(encoded, SIGNATURE_ORDER_VALIDATOR_ERROR_SELECTOR);
hash = _readErrorParameterAsBytes32(encoded, 0);
signerAddress = _readErrorParameterAsAddress(encoded, 1);
signature = _readErrorParameterAsBytes(encoded, 2);
errorData = _readErrorParameterAsBytes(encoded, 3);
validatorAddress = _readErrorParameterAsAddress(encoded, 2);
signature = _readErrorParameterAsBytes(encoded, 3);
errorData = _readErrorParameterAsBytes(encoded, 4);
}
/// @dev Decompose an ABI-encoded SignatureWalletOrderValidatorError.

View File

@@ -1,76 +0,0 @@
/*
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;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
// solhint-disable no-unused-vars
contract TestRevertReceiver {
string constant public REVERT_REASON = "you shall not pass";
/// @dev Reverts with `REVERT_REASON`. Intended to be used with `Validator` signature type.
/// @param hash Message hash that is signed.
/// @param signerAddress Address that should have signed the given hash.
/// @param signature Proof of signing.
/// @return Validity of order signature.
function isValidSignature(
bytes32 hash,
address signerAddress,
bytes calldata signature
)
external
returns (bool isValid)
{
revert(REVERT_REASON);
}
/// @dev Reverts with `REVERT_REASON`. Intended to be used with `Wallet` signature type.
/// @param hash Message hash that is signed.
/// @param signature Proof of signing.
/// @return Validity of order signature.
function isValidSignature(
bytes32 hash,
bytes calldata signature
)
external
returns (bool isValid)
{
revert(REVERT_REASON);
}
/// @dev Reverts with `REVERT_REASON`. Intended to be used with `OrderValidator` signature type.
/// @param order The order.
/// @param hash Message hash that is signed.
/// @param signature Proof of signing.
/// @return Validity of order signature.
function isValidOrderSignature(
LibOrder.Order calldata order,
bytes32 hash,
bytes calldata signature
)
external
returns (bool isValid)
{
revert(REVERT_REASON);
}
}

View File

@@ -1,100 +0,0 @@
/*
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;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
// solhint-disable no-unused-vars
contract TestStaticCallReceiver {
uint256 internal state = 1;
/// @dev Updates state and returns true. Intended to be used with `Validator` signature type.
/// @param hash Message hash that is signed.
/// @param signerAddress Address that should have signed the given hash.
/// @param signature Proof of signing.
/// @return Validity of order signature.
function isValidSignature(
bytes32 hash,
address signerAddress,
bytes calldata signature
)
external
returns (bool isValid)
{
_updateState();
return true;
}
/// @dev Updates state and returns true. Intended to be used with `Wallet` signature type.
/// @param hash Message hash that is signed.
/// @param signature Proof of signing.
/// @return Validity of order signature.
function isValidSignature(
bytes32 hash,
bytes calldata signature
)
external
returns (bool isValid)
{
_updateState();
return true;
}
/// @dev Updates state and returns true. Intended to be used with `OrderValidator` signature type.
/// @param order The order.
/// @param orderHash The order hash.
/// @param signature Proof of signing.
/// @return Validity of order signature.
function isValidOrderSignature(
LibOrder.Order calldata order,
bytes32 orderHash,
bytes calldata signature
)
external
returns (bool isValid)
{
_updateState();
return true;
}
/// @dev Approves an ERC20 token to spend tokens from this address.
/// @param token Address of ERC20 token.
/// @param spender Address that will spend tokens.
/// @param value Amount of tokens spender is approved to spend.
function approveERC20(
address token,
address spender,
uint256 value
)
external
{
IERC20Token(token).approve(spender, value);
}
/// @dev Increments state variable.
function _updateState()
internal
{
state++;
}
}

View File

@@ -0,0 +1,200 @@
/*
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;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
// solhint-disable no-unused-vars
contract TestValidatorWallet {
using LibBytes for bytes;
string constant public REVERT_REASON = "you shall not pass";
enum ValidatorAction {
// Return false (default)
Reject,
// Return true
Allow,
// Revert
Revert,
// Update state
UpdateState,
// Validate signature
ValidateSignature,
NTypes
}
/// @dev Internal state to modify.
uint256 internal _state = 1;
/// @dev What action to execute when a hash is validated .
mapping (bytes32=>ValidatorAction) internal _hashActions;
/// @dev Allowed signers for `ValidateSignature` actions using `Wallet` signature types.
mapping (bytes32=>address) internal _hashWalletSigners;
/// @dev Set the action to take when validating a hash.
/// @param hash The hash.
/// @param action action to take.
/// @param allowedSigner Signer that must be recovered with
/// `ValidateSignature` action type and `Wallet` or
/// `OrderWallet` signature types.
function setValidateAction(
bytes32 hash,
ValidatorAction action,
address allowedSigner
)
external
{
if (uint8(action) >= uint8(ValidatorAction.NTypes)) {
revert('UNSUPPORTED_VALIDATE_ACTION');
}
_hashActions[hash] = action;
_hashWalletSigners[hash] = allowedSigner;
}
/// @dev Validates a hash with the `Validator` signature type.
/// @param hash Message hash that is signed.
/// @param signerAddress Address that should have signed the given hash.
/// @param signature Proof of signing.
/// @return Validity of order signature.
function isValidSignature(
bytes32 hash,
address signerAddress,
bytes memory signature
)
public
returns (bool isValid)
{
ValidatorAction action = _hashActions[hash];
if (action == ValidatorAction.Reject) {
isValid = false;
} else if (action == ValidatorAction.Allow) {
isValid = true;
} else if (action == ValidatorAction.Revert) {
revert(REVERT_REASON);
} else if (action == ValidatorAction.ValidateSignature) {
isValid = _isSignedBy(hash, signature, signerAddress);
} else { // if (action == ValidatorAction.UpdateState) {
_updateState();
}
}
/// @dev Validates a hash with the `Wallet` signature type.
/// @param hash Message hash that is signed.
/// @param signature Proof of signing.
/// @return Validity of order signature.
function isValidSignature(
bytes32 hash,
bytes memory signature
)
public
returns (bool isValid)
{
ValidatorAction action = _hashActions[hash];
if (action == ValidatorAction.Reject) {
isValid = false;
} else if (action == ValidatorAction.Allow) {
isValid = true;
} else if (action == ValidatorAction.Revert) {
revert(REVERT_REASON);
} else if (action == ValidatorAction.ValidateSignature) {
isValid = _isSignedBy(hash, signature, _hashWalletSigners[hash]);
} else { // if (action == ValidatorAction.UpdateState) {
_updateState();
}
}
/// @dev Validates a hash with the `OrderValidator` signature type.
/// @param order The order.
/// @param orderHash The order hash.
/// @param signature Proof of signing.
/// @return Validity of order signature.
function isValidOrderSignature(
LibOrder.Order memory order,
bytes32 orderHash,
bytes memory signature
)
public
returns (bool isValid)
{
ValidatorAction action = _hashActions[orderHash];
if (action == ValidatorAction.Reject) {
isValid = false;
} else if (action == ValidatorAction.Allow) {
isValid = true;
} else if (action == ValidatorAction.Revert) {
revert(REVERT_REASON);
} else if (action == ValidatorAction.ValidateSignature) {
isValid = _isSignedBy(orderHash, signature, order.makerAddress);
} else { // if (action == ValidatorAction.UpdateState) {
_updateState();
}
}
/// @dev Approves an ERC20 token to spend tokens from this address.
/// @param token Address of ERC20 token.
/// @param spender Address that will spend tokens.
/// @param value Amount of tokens spender is approved to spend.
function approveERC20(
address token,
address spender,
uint256 value
)
external
{
IERC20Token(token).approve(spender, value);
}
/// @dev Increments state variable.
function _updateState()
internal
{
_state++;
}
/// @dev Verifies the signer of a hash is correct.
function _isSignedBy(
bytes32 hash,
bytes memory signature,
address signerAddress
)
internal
pure
returns (bool isSignedBy)
{
require(signature.length == 65, "LENGTH_65_REQUIRED");
uint8 v = uint8(signature[0]);
bytes32 r = signature.readBytes32(1);
bytes32 s = signature.readBytes32(33);
// Try a naked signature.
address recovered = ecrecover(hash, v, r, s);
if (recovered != signerAddress) {
// Try an eth_sign signature.
bytes32 ethSignHash = keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n32",
hash
)
);
recovered = ecrecover(ethSignHash, v, r, s);
}
isSignedBy = recovered == signerAddress;
}
}

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|TestLibExchangeRichErrorDecoder|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|TestSignatureValidator|TestValidatorWallet|Whitelist).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {

View File

@@ -21,16 +21,11 @@ import * as ReentrantERC20Token from '../generated-artifacts/ReentrantERC20Token
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';
import * as Validator from '../generated-artifacts/Validator.json';
import * as Wallet from '../generated-artifacts/Wallet.json';
import * as TestValidatorWallet from '../generated-artifacts/TestValidatorWallet.json';
import * as Whitelist from '../generated-artifacts/Whitelist.json';
export const artifacts = {
ExchangeWrapper: ExchangeWrapper as ContractArtifact,
Validator: Validator as ContractArtifact,
Wallet: Wallet as ContractArtifact,
Whitelist: Whitelist as ContractArtifact,
Exchange: Exchange as ContractArtifact,
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
@@ -47,7 +42,6 @@ export const artifacts = {
TestAssetProxyDispatcher: TestAssetProxyDispatcher as ContractArtifact,
TestExchangeInternals: TestExchangeInternals as ContractArtifact,
TestLibExchangeRichErrorDecoder: TestLibExchangeRichErrorDecoder as ContractArtifact,
TestRevertReceiver: TestRevertReceiver as ContractArtifact,
TestSignatureValidator: TestSignatureValidator as ContractArtifact,
TestStaticCallReceiver: TestStaticCallReceiver as ContractArtifact,
TestValidatorWallet: TestValidatorWallet as ContractArtifact,
};

View File

@@ -19,9 +19,6 @@ 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';
export * from '../generated-wrappers/validator';
export * from '../generated-wrappers/wallet';
export * from '../generated-wrappers/test_validator_wallet';
export * from '../generated-wrappers/whitelist';

View File

@@ -47,7 +47,8 @@ import {
ExchangeContract,
ExchangeWrapper,
ReentrantERC20TokenContract,
TestStaticCallReceiverContract,
TestValidatorWalletContract,
ValidatorWalletAction,
} from '../src';
chaiSetup.configure();
@@ -73,10 +74,7 @@ describe('Exchange core', () => {
let erc721Proxy: ERC721ProxyContract;
let erc1155Proxy: ERC721ProxyContract;
let multiAssetProxy: MultiAssetProxyContract;
let staticCallProxy: StaticCallProxyContract;
let staticCallTarget: TestStaticCallTargetContract;
let maliciousWallet: TestStaticCallReceiverContract;
let maliciousValidator: TestStaticCallReceiverContract;
let validatorWallet: TestValidatorWalletContract;
let erc1155Contract: ERC1155MintableContract;
let signedOrder: SignedOrder;
@@ -141,8 +139,8 @@ describe('Exchange core', () => {
txDefaults,
new BigNumber(chainId),
);
maliciousWallet = maliciousValidator = await TestStaticCallReceiverContract.deployFrom0xArtifactAsync(
artifacts.TestStaticCallReceiver,
validatorWallet = await TestValidatorWalletContract.deployFrom0xArtifactAsync(
artifacts.TestValidatorWallet,
provider,
txDefaults,
);
@@ -251,27 +249,143 @@ describe('Exchange core', () => {
};
describe('fillOrder reentrancy tests', () => reentrancyTest(exchangeConstants.FUNCTIONS_WITH_MUTEX));
it('should throw if signature is invalid', async () => {
signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
describe('signatures', () => {
beforeEach(async () => {
// Approve the ERC20 proxy with the test validator wallet.
await validatorWallet.approveERC20.awaitTransactionSuccessAsync(
erc20TokenA.address,
erc20Proxy.address,
constants.INITIAL_ERC20_ALLOWANCE,
);
// Mint some ERC20 tokens to the test validator wallet.
await erc20TokenA.setBalance.awaitTransactionSuccessAsync(
validatorWallet.address,
constants.INITIAL_ERC20_BALANCE,
);
// Approve the validator.
await exchange.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
validatorWallet.address,
true,
{ from: makerAddress },
);
});
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const v = ethUtil.toBuffer(signedOrder.signature.slice(0, 4));
const invalidR = ethUtil.sha3('invalidR');
const invalidS = ethUtil.sha3('invalidS');
const signatureType = ethUtil.toBuffer(`0x${signedOrder.signature.slice(-2)}`);
const invalidSigBuff = Buffer.concat([v, invalidR, invalidS, signatureType]);
const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`;
signedOrder.signature = invalidSigHex;
const expectedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.BadSignature,
orderHashHex,
signedOrder.makerAddress,
invalidSigHex,
);
const tx = exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
return expect(tx).to.revertWith(expectedError);
it('should revert if signature is invalid', async () => {
signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
});
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const v = ethUtil.toBuffer(signedOrder.signature.slice(0, 4));
const invalidR = ethUtil.sha3('invalidR');
const invalidS = ethUtil.sha3('invalidS');
const signatureType = ethUtil.toBuffer(`0x${signedOrder.signature.slice(-2)}`);
const invalidSigBuff = Buffer.concat([v, invalidR, invalidS, signatureType]);
const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`;
signedOrder.signature = invalidSigHex;
const expectedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.BadSignature,
orderHashHex,
signedOrder.makerAddress,
invalidSigHex,
);
const tx = exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
return expect(tx).to.revertWith(expectedError);
});
it('should revert if validator tries to update state when SignatureType=Wallet', async () => {
signedOrder = await orderFactory.newSignedOrderAsync({
makerAddress: validatorWallet.address,
makerFee: constants.ZERO_AMOUNT,
});
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
signedOrder.signature = `0x0${SignatureType.Wallet}`;
validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
orderHashHex,
ValidatorWalletAction.UpdateState,
makerAddress,
);
const expectedError = new ExchangeRevertErrors.SignatureWalletError(
orderHashHex,
signedOrder.makerAddress,
signedOrder.signature,
constants.NULL_BYTES,
);
const tx = exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
return expect(tx).to.revertWith(expectedError);
});
it('should revert if validator tries to update state when SignatureType=Validator', async () => {
signedOrder.signature = `${validatorWallet.address}0${SignatureType.Validator}`;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
orderHashHex,
ValidatorWalletAction.UpdateState,
makerAddress,
);
const expectedError = new ExchangeRevertErrors.SignatureValidatorError(
orderHashHex,
signedOrder.makerAddress,
signedOrder.signature,
constants.NULL_BYTES,
);
const tx = exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
return expect(tx).to.revertWith(expectedError);
});
it('should revert if validator is not approved when SignatureType=Validator', async () => {
signedOrder.signature = `${validatorWallet.address}0${SignatureType.Validator}`;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
orderHashHex,
ValidatorWalletAction.Allow,
makerAddress,
);
// Disapprove the validator.
await exchange.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
validatorWallet.address,
false,
{ from: makerAddress },
);
const expectedError = new ExchangeRevertErrors.SignatureValidatorNotApprovedError(
signedOrder.makerAddress,
signedOrder.signature,
);
const tx = exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
return expect(tx).to.revertWith(expectedError);
});
it('should revert if `OrderValidator` signature type rejects during a second fill', async () => {
signedOrder.signature = `${validatorWallet.address}0${SignatureType.OrderValidator}`;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
await validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
orderHashHex,
ValidatorWalletAction.Allow,
makerAddress,
);
const fillAmount = signedOrder.takerAssetAmount.div(10);
await exchangeWrapper.fillOrderAsync(
signedOrder,
takerAddress,
{ takerAssetFillAmount: fillAmount },
);
await validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
orderHashHex,
ValidatorWalletAction.Reject,
makerAddress,
);
const tx = exchangeWrapper.fillOrderAsync(
signedOrder,
takerAddress,
{ takerAssetFillAmount: fillAmount },
);
const expectedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.BadSignature,
signedOrder.makerAddress,
signedOrder.signature,
);
return expect(tx).to.revertWith(expectedError);
});
});
it('should throw if fully filled', async () => {
@@ -283,52 +397,6 @@ describe('Exchange core', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should revert if `isValidSignature` tries to update state when SignatureType=Wallet', async () => {
const maliciousMakerAddress = maliciousWallet.address;
await erc20TokenA.setBalance.awaitTransactionSuccessAsync(
maliciousMakerAddress,
constants.INITIAL_ERC20_BALANCE,
);
await maliciousWallet.approveERC20.awaitTransactionSuccessAsync(
erc20TokenA.address,
erc20Proxy.address,
constants.INITIAL_ERC20_ALLOWANCE,
);
signedOrder = await orderFactory.newSignedOrderAsync({
makerAddress: maliciousMakerAddress,
makerFee: constants.ZERO_AMOUNT,
});
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
signedOrder.signature = `0x0${SignatureType.Wallet}`;
const expectedError = new ExchangeRevertErrors.SignatureWalletError(
orderHashHex,
signedOrder.makerAddress,
signedOrder.signature,
constants.NULL_BYTES,
);
const tx = exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
return expect(tx).to.revertWith(expectedError);
});
it('should revert if `isValidSignature` tries to update state when SignatureType=Validator', async () => {
const isApproved = true;
await exchange.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
maliciousValidator.address,
isApproved,
{ from: makerAddress },
);
signedOrder.signature = `${maliciousValidator.address}0${SignatureType.Validator}`;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const expectedError = new ExchangeRevertErrors.SignatureValidatorError(
orderHashHex,
signedOrder.makerAddress,
signedOrder.signature,
constants.NULL_BYTES,
);
const tx = exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
return expect(tx).to.revertWith(expectedError);
});
it('should not emit transfer events for transfers where from == to', async () => {
const txReceipt = await exchangeWrapper.fillOrderAsync(signedOrder, makerAddress);
const logs = txReceipt.logs;

View File

@@ -75,12 +75,15 @@ describe('LibExchangeRichErrorDecoder', () => {
const errorCode = ExchangeRevertErrors.SignatureErrorCode.Illegal;
const orderHash = orderUtils.generatePseudoRandomOrderHash();
const signer = addressUtils.generatePseudoRandomAddress();
const validator = addressUtils.generatePseudoRandomAddress();
const signature = generateRandomBytes(SIGNATURE_LENGTH);
const errorData = generateRandomBytes(ERROR_DATA_LENGTH);
createDecodeTest(ExchangeRevertErrors.SignatureError, [errorCode, orderHash, signer, signature]);
createDecodeTest(ExchangeRevertErrors.SignatureValidatorError, [orderHash, signer, signature, errorData]);
createDecodeTest(ExchangeRevertErrors.SignatureValidatorNotApprovedError, [signer, validator]);
createDecodeTest(ExchangeRevertErrors.SignatureOrderValidatorNotApprovedError, [signer, validator]);
createDecodeTest(ExchangeRevertErrors.SignatureValidatorError, [orderHash, signer, validator, signature, errorData]);
createDecodeTest(ExchangeRevertErrors.SignatureWalletError, [orderHash, signer, signature, errorData]);
createDecodeTest(ExchangeRevertErrors.SignatureOrderValidatorError, [orderHash, signer, signature, errorData]);
createDecodeTest(ExchangeRevertErrors.SignatureOrderValidatorError, [orderHash, signer, validator, signature, errorData]);
createDecodeTest(ExchangeRevertErrors.SignatureWalletOrderValidatorError, [
orderHash,
signer,

View File

@@ -13,41 +13,37 @@ import { assetDataUtils, ExchangeRevertErrors, orderHashUtils, signatureUtils }
import { SignatureType, SignedOrder } from '@0x/types';
import { BigNumber, providerUtils, StringRevertError } from '@0x/utils';
import * as chai from 'chai';
import * as crypto from 'crypto';
import { LogWithDecodedArgs, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import ethUtil = require('ethereumjs-util');
import {
artifacts,
TestRevertReceiverContract,
TestSignatureValidatorContract,
TestSignatureValidatorSignatureValidatorApprovalEventArgs,
TestStaticCallReceiverContract,
ValidatorContract,
WalletContract,
TestValidatorWalletContract,
} from '../src';
import { ValidatorWalletAction } from './utils';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:no-unnecessary-type-assertion
describe.only('MixinSignatureValidator', () => {
const SIGNATURE_LENGTH = 65;
let chainId: number;
let signedOrder: SignedOrder;
let orderFactory: OrderFactory;
let signatureValidator: TestSignatureValidatorContract;
let testWallet: WalletContract;
let testValidator: ValidatorContract;
let maliciousWallet: TestStaticCallReceiverContract;
let maliciousValidator: TestStaticCallReceiverContract;
let revertingWallet: TestRevertReceiverContract;
let revertingValidator: TestRevertReceiverContract;
let externalRevertReason: string;
let validatorWallet: TestValidatorWalletContract;
let validator: TestValidatorWalletContract;
let validatorWalletRevertReason: string;
let signerAddress: string;
let signerPrivateKey: Buffer;
let notSignerAddress: string;
let notSignerPrivateKey: Buffer;
let signatureValidatorLogDecoder: LogDecoder;
before(async () => {
await blockchainLifecycle.startAsync();
@@ -67,42 +63,29 @@ describe.only('MixinSignatureValidator', () => {
txDefaults,
new BigNumber(chainId),
);
testWallet = await WalletContract.deployFrom0xArtifactAsync(
artifacts.Wallet,
provider,
txDefaults,
signerAddress,
);
testValidator = await ValidatorContract.deployFrom0xArtifactAsync(
artifacts.Validator,
provider,
txDefaults,
signerAddress,
);
maliciousWallet = maliciousValidator = await TestStaticCallReceiverContract.deployFrom0xArtifactAsync(
artifacts.TestStaticCallReceiver,
validatorWallet = await TestValidatorWalletContract.deployFrom0xArtifactAsync(
artifacts.TestValidatorWallet,
provider,
txDefaults,
);
revertingWallet = revertingValidator = await TestRevertReceiverContract.deployFrom0xArtifactAsync(
artifacts.TestRevertReceiver,
validator = await TestValidatorWalletContract.deployFrom0xArtifactAsync(
artifacts.TestValidatorWallet,
provider,
txDefaults,
);
externalRevertReason = await revertingWallet.REVERT_REASON.callAsync();
validatorWalletRevertReason = await validator.REVERT_REASON.callAsync();
signatureValidatorLogDecoder = new LogDecoder(web3Wrapper, artifacts);
const approveValidator = async (validatorAddress: string) => {
type SendApproveTx = (address: string, approved: boolean, txData: { from: string }) => Promise<any>;
const sendTx = async (fn: { awaitTransactionSuccessAsync: SendApproveTx }) => {
return fn.awaitTransactionSuccessAsync(validatorAddress, true, { from: signerAddress }) as Promise<any>;
};
await sendTx(signatureValidator.setSignatureValidatorApproval);
await sendTx(signatureValidator.setOrderValidatorApproval);
};
await approveValidator(testValidator.address);
await approveValidator(maliciousValidator.address);
await approveValidator(revertingValidator.address);
// Approve the validator.
signatureValidator.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
validatorWallet.address,
true,
{ from: signerAddress },
);
signatureValidator.setOrderValidatorApproval.awaitTransactionSuccessAsync(
validatorWallet.address,
true,
{ from: signerAddress },
);
const defaultOrderParams = {
...constants.STATIC_ORDER_PARAMS,
@@ -129,7 +112,12 @@ describe.only('MixinSignatureValidator', () => {
await blockchainLifecycle.revertAsync();
});
type ValidateCallAsync = (order: SignedOrder, signerAddress: string, ignatureHex: string) => Promise<any>;
type ValidateCallAsync = (
order: SignedOrder,
signerAddress: string,
signatureHex: string,
validatorAction?: ValidatorWalletAction,
) => Promise<any>;
const createHashSignatureTests = (validateCallAsync: ValidateCallAsync) => {
it('should revert when signature is empty', async () => {
@@ -141,7 +129,11 @@ describe.only('MixinSignatureValidator', () => {
signedOrder.makerAddress,
emptySignature,
);
const tx = validateCallAsync(signedOrder, signedOrder.makerAddress, emptySignature);
const tx = validateCallAsync(
signedOrder,
signedOrder.makerAddress,
emptySignature,
);
return expect(tx).to.revertWith(expectedError);
});
@@ -155,7 +147,11 @@ describe.only('MixinSignatureValidator', () => {
signedOrder.makerAddress,
unsupportedSignatureHex,
);
const tx = validateCallAsync(signedOrder, signedOrder.makerAddress, unsupportedSignatureHex);
const tx = validateCallAsync(
signedOrder,
signedOrder.makerAddress,
unsupportedSignatureHex,
);
return expect(tx).to.revertWith(expectedError);
});
@@ -168,13 +164,21 @@ describe.only('MixinSignatureValidator', () => {
signedOrder.makerAddress,
illegalSignatureHex,
);
const tx = validateCallAsync(signedOrder, signedOrder.makerAddress, illegalSignatureHex);
const tx = validateCallAsync(
signedOrder,
signedOrder.makerAddress,
illegalSignatureHex,
);
return expect(tx).to.revertWith(expectedError);
});
it('should return false when SignatureType=Invalid and signature has a length of zero', async () => {
const signatureHex = `0x${Buffer.from([SignatureType.Invalid]).toString('hex')}`;
const isValidSignature = await validateCallAsync(signedOrder, signedOrder.makerAddress, signatureHex);
const isValidSignature = await validateCallAsync(
signedOrder,
signedOrder.makerAddress,
signatureHex,
);
expect(isValidSignature).to.be.false();
});
@@ -190,44 +194,37 @@ describe.only('MixinSignatureValidator', () => {
signedOrder.makerAddress,
signatureHex,
);
const tx = validateCallAsync(signedOrder, signedOrder.makerAddress, signatureHex);
const tx = validateCallAsync(
signedOrder,
signedOrder.makerAddress,
signatureHex,
);
return expect(tx).to.revertWith(expectedError);
});
it('should return true when SignatureType=EIP712 and signature is valid', async () => {
// Create EIP712 signature
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey);
// Create 0x signature from EIP712 signature
const signature = Buffer.concat([
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
ethUtil.toBuffer(`0x${SignatureType.EIP712}`),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// Validate signature
const isValidSignature = await validateCallAsync(signedOrder, signerAddress, signatureHex);
const isValidSignature = await validateCallAsync(
signedOrder,
signerAddress,
signedOrder.signature,
);
expect(isValidSignature).to.be.true();
});
it('should return false when SignatureType=EIP712 and signature is invalid', async () => {
// Create EIP712 signature
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey);
// Create 0x signature from EIP712 signature
const signature = Buffer.concat([
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
ethUtil.toBuffer(`0x${SignatureType.EIP712}`),
crypto.randomBytes(SIGNATURE_LENGTH),
ethUtil.toBuffer([SignatureType.EIP712]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// Validate signature.
// This will fail because `signerAddress` signed the message, but we're passing in `notSignerAddress`
const isValidSignature = await validateCallAsync(signedOrder, notSignerAddress, signatureHex);
const isValidSignature = await validateCallAsync(
signedOrder,
notSignerAddress,
signatureHex,
);
expect(isValidSignature).to.be.false();
});
@@ -242,183 +239,211 @@ describe.only('MixinSignatureValidator', () => {
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
ethUtil.toBuffer(`0x${SignatureType.EthSign}`),
ethUtil.toBuffer([SignatureType.EthSign]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// Validate signature
const isValidSignature = await validateCallAsync(signedOrder, signerAddress, signatureHex);
const isValidSignature = await validateCallAsync(
signedOrder,
signerAddress,
signatureHex,
);
expect(isValidSignature).to.be.true();
});
it('should return false when SignatureType=EthSign and signature is invalid', async () => {
// Create EthSign signature
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const orderHashWithEthSignPrefixHex = signatureUtils.addSignedMessagePrefix(orderHashHex);
const orderHashWithEthSignPrefixBuffer = ethUtil.toBuffer(orderHashWithEthSignPrefixHex);
const ecSignature = ethUtil.ecsign(orderHashWithEthSignPrefixBuffer, signerPrivateKey);
// Create 0x signature from EthSign signature
const signature = Buffer.concat([
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
ethUtil.toBuffer(`0x${SignatureType.EthSign}`),
crypto.randomBytes(SIGNATURE_LENGTH),
ethUtil.toBuffer([SignatureType.EthSign]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// Validate signature.
// This will fail because `signerAddress` signed the message, but we're passing in `notSignerAddress`
const isValidSignature = await validateCallAsync(signedOrder, notSignerAddress, signatureHex);
const isValidSignature = await validateCallAsync(
signedOrder,
signerAddress,
signatureHex,
);
expect(isValidSignature).to.be.false();
});
it('should return true when SignatureType=Wallet and signature is valid', async () => {
// Create EIP712 signature
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey);
// Create 0x signature from EIP712 signature
const signature = Buffer.concat([
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
ethUtil.toBuffer(`0x${SignatureType.Wallet}`),
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
ethUtil.toBuffer([SignatureType.Wallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// Validate signature
const isValidSignature = await validateCallAsync(signedOrder, testWallet.address, signatureHex);
const isValidSignature = await validateCallAsync(
signedOrder,
validatorWallet.address,
signatureHex,
ValidatorWalletAction.ValidateSignature,
);
expect(isValidSignature).to.be.true();
});
it('should return false when SignatureType=Wallet and signature is invalid', async () => {
// Create EIP712 signature using a private key that does not belong to the wallet owner.
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
const notWalletOwnerPrivateKey = notSignerPrivateKey;
const ecSignature = ethUtil.ecsign(orderHashBuffer, notWalletOwnerPrivateKey);
// Create 0x signature from EIP712 signature
const signature = Buffer.concat([
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
ethUtil.toBuffer(`0x${SignatureType.Wallet}`),
crypto.randomBytes(SIGNATURE_LENGTH),
ethUtil.toBuffer([SignatureType.Wallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// Validate signature
const isValidSignature = await validateCallAsync(signedOrder, testWallet.address, signatureHex);
const isValidSignature = await validateCallAsync(
signedOrder,
validatorWallet.address,
signatureHex,
ValidatorWalletAction.ValidateSignature,
);
expect(isValidSignature).to.be.false();
});
it('should revert when `isValidSignature` attempts to update state and SignatureType=Wallet', async () => {
// Create EIP712 signature
it('should revert when validator attempts to update state and SignatureType=Wallet', async () => {
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey);
// Create 0x signature from EIP712 signature
const signature = Buffer.concat([
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
ethUtil.toBuffer(`0x${SignatureType.Wallet}`),
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
ethUtil.toBuffer([SignatureType.Wallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
const expectedError = new ExchangeRevertErrors.SignatureWalletError(
orderHashHex,
maliciousWallet.address,
validatorWallet.address,
signatureHex,
constants.NULL_BYTES,
);
const tx = validateCallAsync(signedOrder, maliciousWallet.address, signatureHex);
const tx = validateCallAsync(
signedOrder,
validatorWallet.address,
signatureHex,
ValidatorWalletAction.UpdateState,
);
return expect(tx).to.revertWith(expectedError);
});
it('should revert when `isValidSignature` reverts and SignatureType=Wallet', async () => {
// Create EIP712 signature
it('should revert when validator reverts and SignatureType=Wallet', async () => {
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey);
// Create 0x signature from EIP712 signature
const signature = Buffer.concat([
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
ethUtil.toBuffer(`0x${SignatureType.Wallet}`),
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
ethUtil.toBuffer([SignatureType.Wallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
const expectedError = new ExchangeRevertErrors.SignatureWalletError(
orderHashHex,
revertingWallet.address,
validatorWallet.address,
signatureHex,
new StringRevertError(externalRevertReason).encode(),
new StringRevertError(validatorWalletRevertReason).encode(),
);
const tx = validateCallAsync(
signedOrder,
validatorWallet.address,
signatureHex,
ValidatorWalletAction.Revert,
);
const tx = validateCallAsync(signedOrder, revertingWallet.address, signatureHex);
return expect(tx).to.revertWith(expectedError);
});
it('should return true when SignatureType=Validator, signature is valid and validator is approved', async () => {
const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`);
const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`);
const signature = Buffer.concat([validatorAddress, signatureType]);
const signature = Buffer.concat([
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
ethUtil.toBuffer(validatorWallet.address),
ethUtil.toBuffer([SignatureType.Validator]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
const isValidSignature = await validateCallAsync(signedOrder, signerAddress, signatureHex);
const isValidSignature = await validateCallAsync(
signedOrder,
signerAddress,
signatureHex,
ValidatorWalletAction.Allow,
);
expect(isValidSignature).to.be.true();
});
it('should return false when SignatureType=Validator, signature is invalid and validator is approved', async () => {
const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`);
const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`);
const signature = Buffer.concat([validatorAddress, signatureType]);
const signatureHex = ethUtil.bufferToHex(signature);
// This will return false because the validator requires `signerAddress`
// to be the signer.
const isValidSignature = await validateCallAsync(signedOrder, notSignerAddress, signatureHex);
const signature = Buffer.concat([
crypto.randomBytes(SIGNATURE_LENGTH),
ethUtil.toBuffer(validatorWallet.address),
ethUtil.toBuffer([SignatureType.Validator]),
]);
const isValidSignature = await validateCallAsync(
signedOrder,
signerAddress,
ethUtil.bufferToHex(signature),
ValidatorWalletAction.ValidateSignature,
);
expect(isValidSignature).to.be.false();
});
it('should revert when `isValidSignature` attempts to update state and SignatureType=Validator', async () => {
const validatorAddress = ethUtil.toBuffer(`${maliciousValidator.address}`);
const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`);
const signature = Buffer.concat([validatorAddress, signatureType]);
it('should revert when validator attempts to update state and SignatureType=Validator', async () => {
const signature = Buffer.concat([
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
ethUtil.toBuffer(validatorWallet.address),
ethUtil.toBuffer([SignatureType.Validator]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const expectedError = new ExchangeRevertErrors.SignatureValidatorError(
orderHashHex,
signedOrder.makerAddress,
signerAddress,
validatorWallet.address,
signatureHex,
constants.NULL_BYTES,
);
const tx = validateCallAsync(signedOrder, signerAddress, signatureHex);
const tx = validateCallAsync(
signedOrder,
signerAddress,
signatureHex,
ValidatorWalletAction.UpdateState,
);
return expect(tx).to.revertWith(expectedError);
});
it('should revert when `isValidSignature` reverts and SignatureType=Validator', async () => {
const validatorAddress = ethUtil.toBuffer(`${revertingValidator.address}`);
it('should revert when validator reverts and SignatureType=Validator', async () => {
const validatorAddress = ethUtil.toBuffer(`${validatorWallet.address}`);
const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`);
const signature = Buffer.concat([validatorAddress, signatureType]);
const signatureHex = ethUtil.bufferToHex(signature);
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const expectedError = new ExchangeRevertErrors.SignatureValidatorError(
orderHashHex,
signedOrder.makerAddress,
signerAddress,
validatorWallet.address,
signatureHex,
new StringRevertError(externalRevertReason).encode(),
new StringRevertError(validatorWalletRevertReason).encode(),
);
const tx = validateCallAsync(
signedOrder,
signerAddress,
signatureHex,
ValidatorWalletAction.Revert,
);
const tx = validateCallAsync(signedOrder, signerAddress, signatureHex);
return expect(tx).to.revertWith(expectedError);
});
it('should return false when SignatureType=Validator, signature is valid and validator is not approved', async () => {
it('should revert when SignatureType=Validator, signature is valid and validator is not approved', async () => {
// Set approval of signature validator to false
await signatureValidator.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
testValidator.address,
validatorWallet.address,
false,
{ from: signerAddress },
);
// Validate signature
const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`);
const validatorAddress = ethUtil.toBuffer(`${validatorWallet.address}`);
const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`);
const signature = Buffer.concat([validatorAddress, signatureType]);
const signatureHex = ethUtil.bufferToHex(signature);
const isValidSignature = await validateCallAsync(signedOrder, signerAddress, signatureHex);
expect(isValidSignature).to.be.false();
const tx = validateCallAsync(
signedOrder,
signerAddress,
signatureHex,
ValidatorWalletAction.ValidateSignature,
);
const expectedError = new ExchangeRevertErrors.SignatureValidatorNotApprovedError(
signerAddress,
validatorWallet.address,
);
return expect(tx).to.revertWith(expectedError);
});
it('should return true when SignatureType=Presigned and signer has presigned hash', async () => {
@@ -428,21 +453,41 @@ describe.only('MixinSignatureValidator', () => {
// Validate presigned signature
const signature = ethUtil.toBuffer(`0x${SignatureType.PreSigned}`);
const signatureHex = ethUtil.bufferToHex(signature);
const isValidSignature = await validateCallAsync(signedOrder, signedOrder.makerAddress, signatureHex);
const isValidSignature = await validateCallAsync(
signedOrder,
signedOrder.makerAddress,
signatureHex,
);
expect(isValidSignature).to.be.true();
});
it('should return false when SignatureType=Presigned and signer has not presigned hash', async () => {
const signature = ethUtil.toBuffer(`0x${SignatureType.PreSigned}`);
const signatureHex = ethUtil.bufferToHex(signature);
const isValidSignature = await validateCallAsync(signedOrder, signedOrder.makerAddress, signatureHex);
const isValidSignature = await validateCallAsync(
signedOrder,
signedOrder.makerAddress,
signatureHex,
);
expect(isValidSignature).to.be.false();
});
};
describe('isValidHashSignature', () => {
const validateCallAsync = async (order: SignedOrder, signer: string, signatureHex: string) => {
const validateCallAsync = async (
order: SignedOrder,
signer: string,
signatureHex: string,
validatorAction?: ValidatorWalletAction,
) => {
const orderHashHex = orderHashUtils.getOrderHashHex(order);
if (validatorAction !== undefined) {
await validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
orderHashHex,
validatorAction,
order.makerAddress,
);
}
return signatureValidator.isValidHashSignature.callAsync(orderHashHex, signer, signatureHex);
};
@@ -459,7 +504,12 @@ describe.only('MixinSignatureValidator', () => {
signedOrder.makerAddress,
inappropriateSignatureHex,
);
const tx = validateCallAsync(signedOrder, signerAddress, inappropriateSignatureHex);
const tx = validateCallAsync(
signedOrder,
signerAddress,
inappropriateSignatureHex,
ValidatorWalletAction.Allow,
);
return expect(tx).to.revertWith(expectedError);
});
@@ -503,162 +553,212 @@ describe.only('MixinSignatureValidator', () => {
});
describe('isValidOrderSignature', () => {
const validateCallAsync = async (order: SignedOrder, signer: string, signatureHex: string) => {
const validateCallAsync = async (
order: SignedOrder,
signer: string,
signatureHex: string,
validatorAction?: ValidatorWalletAction,
) => {
const orderHashHex = orderHashUtils.getOrderHashHex(order);
if (validatorAction !== undefined) {
await validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
orderHashHex,
validatorAction,
order.makerAddress,
);
}
return signatureValidator.isValidOrderSignature.callAsync(order, signer, signatureHex);
};
beforeEach(async () => {
signedOrder = await orderFactory.newSignedOrderAsync();
// Set up allowances
});
it('should return true when SignatureType=OrderValidator, signature is valid and validator is approved', async () => {
const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`);
const signatureType = ethUtil.toBuffer(`0x${SignatureType.OrderValidator}`);
const signature = Buffer.concat([validatorAddress, signatureType]);
const signature = Buffer.concat([
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
ethUtil.toBuffer(validatorWallet.address),
ethUtil.toBuffer([SignatureType.OrderValidator]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
const isValidSignature = await validateCallAsync(signedOrder, signerAddress, signatureHex);
const isValidSignature = await validateCallAsync(
signedOrder,
signerAddress,
signatureHex,
ValidatorWalletAction.ValidateSignature,
);
expect(isValidSignature).to.be.true();
});
it('should return false when SignatureType=OrderValidator, signature is invalid and validator is approved', async () => {
const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`);
const signatureType = ethUtil.toBuffer(`0x${SignatureType.OrderValidator}`);
const signature = Buffer.concat([validatorAddress, signatureType]);
const signature = Buffer.concat([
crypto.randomBytes(SIGNATURE_LENGTH),
ethUtil.toBuffer(validatorWallet.address),
ethUtil.toBuffer([SignatureType.OrderValidator]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// This will return false because the validator requires `signerAddress`
// to be the signer.
const isValidSignature = await validateCallAsync(signedOrder, notSignerAddress, signatureHex);
const isValidSignature = await validateCallAsync(
signedOrder,
signerAddress,
signatureHex,
ValidatorWalletAction.ValidateSignature,
);
expect(isValidSignature).to.be.false();
});
it('should revert when `isValidOrderSignature` attempts to update state and SignatureType=OrderValidator', async () => {
const validatorAddress = ethUtil.toBuffer(`${maliciousValidator.address}`);
const signatureType = ethUtil.toBuffer(`0x${SignatureType.OrderValidator}`);
const signature = Buffer.concat([validatorAddress, signatureType]);
const signature = Buffer.concat([
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
ethUtil.toBuffer(validatorWallet.address),
ethUtil.toBuffer([SignatureType.OrderValidator]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const expectedError = new ExchangeRevertErrors.SignatureOrderValidatorError(
orderHashHex,
signedOrder.makerAddress,
validatorWallet.address,
signatureHex,
constants.NULL_BYTES,
);
const tx = validateCallAsync(signedOrder, signerAddress, signatureHex);
const tx = validateCallAsync(
signedOrder,
signerAddress,
signatureHex,
ValidatorWalletAction.UpdateState,
);
return expect(tx).to.revertWith(expectedError);
});
it('should revert when `isValidOrderSignature` reverts and SignatureType=OrderValidator', async () => {
const validatorAddress = ethUtil.toBuffer(`${revertingValidator.address}`);
const signatureType = ethUtil.toBuffer(`0x${SignatureType.OrderValidator}`);
const signature = Buffer.concat([validatorAddress, signatureType]);
const signature = Buffer.concat([
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
ethUtil.toBuffer(validatorWallet.address),
ethUtil.toBuffer([SignatureType.OrderValidator]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const expectedError = new ExchangeRevertErrors.SignatureOrderValidatorError(
orderHashHex,
signedOrder.makerAddress,
signerAddress,
validatorWallet.address,
signatureHex,
new StringRevertError(externalRevertReason).encode(),
new StringRevertError(validatorWalletRevertReason).encode(),
);
const tx = validateCallAsync(
signedOrder,
signerAddress,
signatureHex,
ValidatorWalletAction.Revert,
);
const tx = validateCallAsync(signedOrder, signerAddress, signatureHex);
return expect(tx).to.revertWith(expectedError);
});
it('should return false when SignatureType=OrderValidator, signature is valid and validator is not approved', async () => {
it('should throw when SignatureType=OrderValidator, signature is valid and validator is not approved', async () => {
// Set approval of signature validator to false
await signatureValidator.setOrderValidatorApproval.awaitTransactionSuccessAsync(
testValidator.address,
validatorWallet.address,
false,
{ from: signerAddress },
);
// Validate signature
const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`);
const signatureType = ethUtil.toBuffer(`0x${SignatureType.OrderValidator}`);
const signature = Buffer.concat([validatorAddress, signatureType]);
const signature = Buffer.concat([
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
ethUtil.toBuffer(validatorWallet.address),
ethUtil.toBuffer([SignatureType.OrderValidator]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
const isValidSignature = await validateCallAsync(signedOrder, signerAddress, signatureHex);
expect(isValidSignature).to.be.false();
const tx = validateCallAsync(
signedOrder,
signerAddress,
signatureHex,
ValidatorWalletAction.ValidateSignature,
);
const expectedError = new ExchangeRevertErrors.SignatureOrderValidatorNotApprovedError(
signerAddress,
validatorWallet.address,
);
return expect(tx).to.rejectedWith(expectedError);
});
it('should return true when SignatureType=WalletOrderValidator and signature is valid', async () => {
// Create EIP712 signature
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey);
// Create 0x signature from EIP712 signature
const signature = Buffer.concat([
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
ethUtil.toBuffer(`0x${SignatureType.WalletOrderValidator}`),
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
ethUtil.toBuffer(validatorWallet.address),
ethUtil.toBuffer([SignatureType.OrderValidator]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// Validate signature
const isValidSignature = await validateCallAsync(signedOrder, testWallet.address, signatureHex);
const isValidSignature = await validateCallAsync(
signedOrder,
signerAddress,
signatureHex,
ValidatorWalletAction.ValidateSignature,
);
expect(isValidSignature).to.be.true();
});
it('should return false when SignatureType=WalletOrderValidator and signature is invalid', async () => {
// Create EIP712 signature using a private key that does not belong to the wallet owner.
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
const notWalletOwnerPrivateKey = notSignerPrivateKey;
const ecSignature = ethUtil.ecsign(orderHashBuffer, notWalletOwnerPrivateKey);
// Create 0x signature from EIP712 signature
const signature = Buffer.concat([
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
ethUtil.toBuffer(`0x${SignatureType.WalletOrderValidator}`),
crypto.randomBytes(SIGNATURE_LENGTH),
ethUtil.toBuffer(validatorWallet.address),
ethUtil.toBuffer([SignatureType.OrderValidator]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// Validate signature
const isValidSignature = await validateCallAsync(signedOrder, testWallet.address, signatureHex);
const isValidSignature = await validateCallAsync(
signedOrder,
signerAddress,
signatureHex,
ValidatorWalletAction.ValidateSignature,
);
expect(isValidSignature).to.be.false();
});
it('should revert when `isValidSignature` attempts to update state and SignatureType=WalletOrderValidator', async () => {
// Create EIP712 signature
it('should revert when validator attempts to update state and SignatureType=WalletOrderValidator', async () => {
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey);
// Create 0x signature from EIP712 signature
const signature = Buffer.concat([
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
ethUtil.toBuffer(`0x${SignatureType.WalletOrderValidator}`),
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
ethUtil.toBuffer(validatorWallet.address),
ethUtil.toBuffer([SignatureType.OrderValidator]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
const expectedError = new ExchangeRevertErrors.SignatureWalletOrderValidatorError(
orderHashHex,
maliciousWallet.address,
validatorWallet.address,
signatureHex,
constants.NULL_BYTES,
);
const tx = validateCallAsync(signedOrder, maliciousWallet.address, signatureHex);
const tx = validateCallAsync(
signedOrder,
validatorWallet.address,
signatureHex,
ValidatorWalletAction.UpdateState,
);
return expect(tx).to.revertWith(expectedError);
});
it('should revert when `isValidSignature` reverts and SignatureType=WalletOrderValidator', async () => {
it('should revert when validator reverts and SignatureType=WalletOrderValidator', async () => {
// Create EIP712 signature
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey);
// Create 0x signature from EIP712 signature
const signature = Buffer.concat([
ethUtil.toBuffer(ecSignature.v),
ecSignature.r,
ecSignature.s,
ethUtil.toBuffer(`0x${SignatureType.WalletOrderValidator}`),
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
ethUtil.toBuffer(validatorWallet.address),
ethUtil.toBuffer([SignatureType.OrderValidator]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
const expectedError = new ExchangeRevertErrors.SignatureWalletOrderValidatorError(
orderHashHex,
revertingWallet.address,
validatorWallet.address,
signatureHex,
new StringRevertError(externalRevertReason).encode(),
new StringRevertError(validatorWalletRevertReason).encode(),
);
const tx = validateCallAsync(
signedOrder,
validatorWallet.address,
signatureHex,
ValidatorWalletAction.Revert,
);
const tx = validateCallAsync(signedOrder, revertingWallet.address, signatureHex);
return expect(tx).to.revertWith(expectedError);
});
@@ -668,38 +768,66 @@ describe.only('MixinSignatureValidator', () => {
describe('setSignatureValidatorApproval', () => {
it('should emit a SignatureValidatorApprovalSet with correct args when a validator is approved', async () => {
const approval = true;
const res = await signatureValidatorLogDecoder.getTxWithDecodedLogsAsync(
await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync(
testValidator.address,
approval,
{
from: signerAddress,
},
),
const res = await signatureValidator.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
validatorWallet.address,
approval,
{
from: signerAddress,
},
);
expect(res.logs.length).to.equal(1);
const log = res.logs[0] as LogWithDecodedArgs<TestSignatureValidatorSignatureValidatorApprovalEventArgs>;
const logArgs = log.args;
expect(logArgs.signerAddress).to.equal(signerAddress);
expect(logArgs.validatorAddress).to.equal(testValidator.address);
expect(logArgs.validatorAddress).to.equal(validatorWallet.address);
expect(logArgs.approved).to.equal(approval);
});
it('should emit a SignatureValidatorApprovalSet with correct args when a validator is disapproved', async () => {
const approval = false;
const res = await signatureValidatorLogDecoder.getTxWithDecodedLogsAsync(
await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync(
testValidator.address,
approval,
{
from: signerAddress,
},
),
const res = await signatureValidator.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
validatorWallet.address,
approval,
{
from: signerAddress,
},
);
expect(res.logs.length).to.equal(1);
const log = res.logs[0] as LogWithDecodedArgs<TestSignatureValidatorSignatureValidatorApprovalEventArgs>;
const logArgs = log.args;
expect(logArgs.signerAddress).to.equal(signerAddress);
expect(logArgs.validatorAddress).to.equal(testValidator.address);
expect(logArgs.validatorAddress).to.equal(validatorWallet.address);
expect(logArgs.approved).to.equal(approval);
});
it('should emit a SignatureValidatorApprovalSet with correct args when an order validator is approved', async () => {
const approval = true;
const res = await signatureValidator.setOrderValidatorApproval.awaitTransactionSuccessAsync(
validatorWallet.address,
approval,
{
from: signerAddress,
},
);
expect(res.logs.length).to.equal(1);
const log = res.logs[0] as LogWithDecodedArgs<TestSignatureValidatorSignatureValidatorApprovalEventArgs>;
const logArgs = log.args;
expect(logArgs.signerAddress).to.equal(signerAddress);
expect(logArgs.validatorAddress).to.equal(validatorWallet.address);
expect(logArgs.approved).to.equal(approval);
});
it('should emit a SignatureValidatorApprovalSet with correct args when am order validator is disapproved', async () => {
const approval = false;
const res = await signatureValidator.setOrderValidatorApproval.awaitTransactionSuccessAsync(
validatorWallet.address,
approval,
{
from: signerAddress,
},
);
expect(res.logs.length).to.equal(1);
const log = res.logs[0] as LogWithDecodedArgs<TestSignatureValidatorSignatureValidatorApprovalEventArgs>;
const logArgs = log.args;
expect(logArgs.signerAddress).to.equal(signerAddress);
expect(logArgs.validatorAddress).to.equal(validatorWallet.address);
expect(logArgs.approved).to.equal(approval);
});
});

View File

@@ -33,3 +33,12 @@ export const constants = {
ExchangeFunctionName.MarketSellOrdersNoThrow,
],
};
export enum ValidatorWalletAction {
Reject = 0,
Allow = 1,
Revert = 2,
UpdateState = 3,
ValidateSignature = 4,
NTypes = 5,
}

View File

@@ -19,11 +19,8 @@
"generated-artifacts/TestAssetProxyDispatcher.json",
"generated-artifacts/TestExchangeInternals.json",
"generated-artifacts/TestLibExchangeRichErrorDecoder.json",
"generated-artifacts/TestRevertReceiver.json",
"generated-artifacts/TestSignatureValidator.json",
"generated-artifacts/TestStaticCallReceiver.json",
"generated-artifacts/Validator.json",
"generated-artifacts/Wallet.json",
"generated-artifacts/TestValidatorWallet.json",
"generated-artifacts/Whitelist.json"
],
"exclude": ["./deploy/solc/solc_bin"]