@0x/contracts-exchange: Rename WalletOrderValidator to OrderWallet signature type

`@0x/contracts-exchange`: Rename `SignatureWalletOrderValidatorError` to `SignatureOrderWalletError`
`@0x/contracts-exchange`: Add `IEIP1271Wallet` interface
`@0x/contracts-exchange`: Add `EIP1271Wallet` and `EIP1271OrderWallet` to `SignatureType`
`@0x/contracts-exchange`: Always check `OrderValidator`, `OrderWallet`, `EIP1271OrderWallet` signature types on every fill
`@0x/contracts-exchange`: Add tests for EIP1271 signature types.
`@0x/contracts-exchange`: Update `LibExchangeRichErrorDecoder` for new/renamed Error types.
This commit is contained in:
Lawrence Forman
2019-06-21 21:26:50 -04:00
committed by Amir Bandeali
parent bd5babf65d
commit 33df11b755
14 changed files with 570 additions and 95 deletions

View File

@@ -82,6 +82,26 @@
"note": "Always check `OrderValidator` and `WalletOrderValidator` signature types on every fill",
"pr": "TODO"
},
{
"note": "Rename `WalletOrderValidator` to `OrderWallet` signature type",
"pr": "TODO"
},
{
"note": "Rename `SignatureWalletOrderValidatorError` to `SignatureOrderWalletError`",
"pr": "TODO"
},
{
"note": "Add `IEIP1271Wallet` interface",
"pr": "TODO"
},
{
"note": "Add `EIP1271Wallet` and `EIP1271OrderWallet` to `SignatureType`",
"pr": "TODO"
},
{
"note": "Always check `OrderValidator`, `OrderWallet`, `EIP1271OrderWallet` signature types on every fill",
"pr": "TODO"
},
{
"note": "Add `validatorAddress` field to `SignatureValidatorError` and `SignatureOrderValidatorError` rich reverts",
"pr": "TODO"

View File

@@ -36,6 +36,7 @@
"src/interfaces/ITransactions.sol",
"src/interfaces/IValidator.sol",
"src/interfaces/IWallet.sol",
"src/interfaces/IEIP1271Wallet.sol",
"src/interfaces/IWrapperFunctions.sol",
"test/ReentrantERC20Token.sol",
"test/TestAssetProxyDispatcher.sol",

View File

@@ -138,7 +138,7 @@ contract MixinExchangeRichErrors is
);
}
function SignatureWalletOrderValidatorError(
function SignatureOrderWalletError(
bytes32 orderHash,
address wallet,
bytes memory signature,
@@ -149,7 +149,7 @@ contract MixinExchangeRichErrors is
returns (bytes memory)
{
return abi.encodeWithSelector(
SIGNATURE_WALLET_ORDER_VALIDATOR_ERROR_SELECTOR,
SIGNATURE_ORDER_WALLET_ERROR_SELECTOR,
orderHash,
wallet,
signature,

View File

@@ -24,6 +24,7 @@ import "@0x/contracts-utils/contracts/src/ReentrancyGuard.sol";
import "@0x/contracts-utils/contracts/src/RichErrors.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "./interfaces/IWallet.sol";
import "./interfaces/IEIP1271Wallet.sol";
import "./interfaces/IValidator.sol";
import "./interfaces/IOrderValidator.sol";
import "./interfaces/ISignatureValidator.sol";
@@ -40,6 +41,9 @@ contract MixinSignatureValidator is
{
using LibBytes for bytes;
// Magic bytes returned by EIP1271 wallets on success.
bytes4 constant public EIP1271_MAGIC_VALUE = 0x20c13b0b;
// Mapping of hash => signer => signed
mapping (bytes32 => mapping (address => bool)) public preSigned;
@@ -144,8 +148,11 @@ contract MixinSignatureValidator is
);
// Only hash-compatible signature types can be handled by this
// function.
if (signatureType == SignatureType.OrderValidator ||
signatureType == SignatureType.WalletOrderValidator) {
if (
signatureType == SignatureType.OrderValidator ||
signatureType == SignatureType.OrderWallet ||
signatureType == SignatureType.EIP1271OrderWallet
) {
_rrevert(SignatureError(
SignatureErrorCodes.INAPPROPRIATE_SIGNATURE_TYPE,
hash,
@@ -186,7 +193,8 @@ contract MixinSignatureValidator is
// regularly.
return
signatureType == SignatureType.OrderValidator ||
signatureType == SignatureType.WalletOrderValidator;
signatureType == SignatureType.OrderWallet ||
signatureType == SignatureType.EIP1271OrderWallet;
}
/// @dev Verifies that an order, with provided order hash, has been signed
@@ -220,7 +228,7 @@ contract MixinSignatureValidator is
signature
);
return isValid;
} else if (signatureType == SignatureType.WalletOrderValidator) {
} else if (signatureType == SignatureType.OrderWallet) {
// The entire order is verified by a wallet contract.
isValid = _validateOrderWithWallet(
order,
@@ -229,6 +237,15 @@ contract MixinSignatureValidator is
signature
);
return isValid;
} else if (signatureType == SignatureType.EIP1271OrderWallet) {
// The entire order is verified by a wallet contract.
isValid = _validateOrderWithEIP1271Wallet(
order,
orderHash,
signerAddress,
signature
);
return isValid;
}
// Otherwise, it's one of the hash-compatible signature types.
return _validateHashSignatureTypes(
@@ -333,6 +350,53 @@ contract MixinSignatureValidator is
));
}
/// @dev Verifies signature using logic defined by an EIP1271 Wallet contract.
/// @param hash Any 32 byte hash.
/// @param walletAddress Address that should have signed the given hash
/// and defines its own signature verification method.
/// @param signature Proof that the hash has been signed by signer.
/// @return True if the signature is validated by the Walidator.
function _validateHashWithEIP1271Wallet(
bytes32 hash,
address walletAddress,
bytes memory signature
)
private
view
returns (bool isValid)
{
uint256 signatureLength = signature.length;
// Shave the signature type off the signature.
assembly {
mstore(signature, sub(signatureLength, 1))
}
// Encode the call data.
bytes memory data = new bytes(32);
data.writeBytes32(0, hash);
bytes memory callData = abi.encodeWithSelector(
IEIP1271Wallet(walletAddress).isValidSignature.selector,
data,
signature
);
// Restore the full signature.
assembly {
mstore(signature, signatureLength)
}
// Static call the verification function.
(bool didSucceed, bytes memory returnData) = walletAddress.staticcall(callData);
// Return data should be the `EIP1271_MAGIC_VALUE`.
if (didSucceed && returnData.length == 32) {
return bytes4(returnData.readBytes32(0)) == EIP1271_MAGIC_VALUE;
}
// Static call to verifier failed.
_rrevert(SignatureWalletError(
hash,
walletAddress,
signature,
returnData
));
}
/// @dev Verifies signature using logic defined by Validator contract.
/// If used with an order, the maker of the order can still be an EOA.
/// @param hash Any 32 byte hash.
@@ -436,7 +500,55 @@ contract MixinSignatureValidator is
return returnData.readUint256(0) == 1;
}
// Static call to verifier failed.
_rrevert(SignatureWalletOrderValidatorError(
_rrevert(SignatureOrderWalletError(
orderHash,
walletAddress,
signature,
returnData
));
}
/// @dev Verifies order AND signature via an EIP1271 Wallet contract.
/// @param order The order.
/// @param orderHash The order hash.
/// @param walletAddress Address that should have signed the given hash
/// and defines its own order/signature verification method.
/// @param signature Proof that the order has been signed by signer.
/// @return True if order and signature are validated by the Wallet.
function _validateOrderWithEIP1271Wallet(
Order memory order,
bytes32 orderHash,
address walletAddress,
bytes memory signature
)
private
view
returns (bool isValid)
{
uint256 signatureLength = signature.length;
// Shave the signature type off the signature.
assembly {
mstore(signature, sub(signatureLength, 1))
}
// Encode the call data.
bytes memory data = abi.encode(order);
bytes memory callData = abi.encodeWithSelector(
IEIP1271Wallet(walletAddress).isValidSignature.selector,
data,
signature
);
// Restore the full signature.
assembly {
mstore(signature, signatureLength)
}
// Static call the verification function.
(bool didSucceed, bytes memory returnData) = walletAddress.staticcall(callData);
// Return data should be the `EIP1271_MAGIC_VALUE`.
if (didSucceed && returnData.length == 32) {
return bytes4(returnData.readBytes32(0)) == EIP1271_MAGIC_VALUE;
}
// Static call to verifier failed.
_rrevert(SignatureOrderWalletError(
orderHash,
walletAddress,
signature,
@@ -509,7 +621,7 @@ contract MixinSignatureValidator is
}
/// Validates a hash-compatible signature type
/// (anything but `OrderValidator` and `WalletOrderValidator`).
/// (anything but `OrderValidator` and `OrderWallet`).
function _validateHashSignatureTypes(
SignatureType signatureType,
bytes32 hash,
@@ -603,6 +715,15 @@ contract MixinSignatureValidator is
);
return isValid;
// Signature verified by an EIP1271 wallet contract.
// If used with an order, the maker of the order is the wallet contract.
} else if (signatureType == SignatureType.EIP1271Wallet) {
isValid = _validateHashWithEIP1271Wallet(
hash,
signerAddress,
signature
);
return isValid;
}
// Otherwise, signatureType == SignatureType.PreSigned
assert(signatureType == SignatureType.PreSigned);

View File

@@ -0,0 +1,38 @@
/*
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.9;
contract IEIP1271Wallet {
// Magic bytes returned by EIP1271 wallets on success.
bytes4 constant public EIP1271_MAGIC_VALUE = 0x20c13b0b;
/// @dev Verifies that a signature is valid.
/// @param data Arbitrary data.
/// @param signature Signature of `data`.
/// @return magicValue .
function isValidSignature(
bytes calldata data,
bytes calldata signature
)
external
view
returns (bytes4 magicValue);
}

View File

@@ -91,4 +91,12 @@ contract IExchangeCore {
public
view
returns (LibOrder.OrderInfo memory orderInfo);
/// @dev Calculates Keccak-256 hash of the order.
/// @param order The order structure.
/// @return Keccak-256 EIP712 hash of the order.
function getOrderHash(LibOrder.Order memory order)
public
view
returns (bytes32 orderHash);
}

View File

@@ -53,9 +53,9 @@ contract IExchangeRichErrors {
bytes4 internal constant SIGNATURE_ORDER_VALIDATOR_ERROR_SELECTOR =
0xf45375b7;
// bytes4(keccak256("SignatureWalletOrderValidatorError(bytes32,address,bytes,bytes)"))
bytes4 internal constant SIGNATURE_WALLET_ORDER_VALIDATOR_ERROR_SELECTOR =
0xa85f3360;
// bytes4(keccak256("SignatureOrderWalletError(bytes32,address,bytes,bytes)"))
bytes4 internal constant SIGNATURE_ORDER_WALLET_ERROR_SELECTOR =
0x37fa88c3;
// bytes4(keccak256("OrderStatusError(bytes32,uint8)"))
bytes4 internal constant ORDER_STATUS_ERROR_SELECTOR =

View File

@@ -26,16 +26,18 @@ contract ISignatureValidator {
// Allowed signature types.
enum SignatureType {
Illegal, // 0x00, default value
Invalid, // 0x01
EIP712, // 0x02
EthSign, // 0x03
Wallet, // 0x04
Validator, // 0x05
PreSigned, // 0x06
OrderValidator, // 0x07
WalletOrderValidator, // 0x08
NSignatureTypes // 0x09, number of signature types. Always leave at end.
Illegal, // 0x00, default value
Invalid, // 0x01
EIP712, // 0x02
EthSign, // 0x03
Wallet, // 0x04
Validator, // 0x05
PreSigned, // 0x06
OrderValidator, // 0x07
OrderWallet, // 0x08
EIP1271Wallet, // 0x09
EIP1271OrderWallet, // 0x0A
NSignatureTypes // 0x0B, number of signature types. Always leave at end.
}
event SignatureValidatorApproval(

View File

@@ -154,13 +154,13 @@ contract LibExchangeRichErrorDecoder is
errorData = _readErrorParameterAsBytes(encoded, 4);
}
/// @dev Decompose an ABI-encoded SignatureWalletOrderValidatorError.
/// @dev Decompose an ABI-encoded SignatureOrderWalletError.
/// @param encoded ABI-encoded revert error.
/// @return errorCode The error code.
/// @return signerAddress The expected signer of the hash.
/// @return signature The full signature bytes.
/// @return errorData The revert data thrown by the validator contract.
function decodeSignatureWalletOrderValidatorError(bytes memory encoded)
function decodeSignatureOrderWalletError(bytes memory encoded)
public
pure
returns (
@@ -170,7 +170,7 @@ contract LibExchangeRichErrorDecoder is
bytes memory errorData
)
{
_assertSelectorBytes(encoded, SIGNATURE_WALLET_ORDER_VALIDATOR_ERROR_SELECTOR);
_assertSelectorBytes(encoded, SIGNATURE_ORDER_WALLET_ERROR_SELECTOR);
hash = _readErrorParameterAsBytes32(encoded, 0);
signerAddress = _readErrorParameterAsAddress(encoded, 1);
signature = _readErrorParameterAsBytes(encoded, 2);

View File

@@ -20,12 +20,14 @@ pragma solidity ^0.5.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibEIP712ExchangeDomain.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "../src/MixinSignatureValidator.sol";
import "../src/MixinTransactions.sol";
contract TestSignatureValidator is
LibEIP712ExchangeDomain,
LibOrder,
MixinSignatureValidator
{

View File

@@ -23,11 +23,22 @@ 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";
interface ISimplifiedExchange {
function getOrderHash(LibOrder.Order calldata order)
external
view
returns (bytes32 orderHash);
}
// solhint-disable no-unused-vars
contract TestValidatorWallet {
using LibBytes for bytes;
// Revert reason for `Revert` `ValidatorAction`.
string constant public REVERT_REASON = "you shall not pass";
// Magic bytes returned by EIP1271 wallets on success.
bytes4 constant public EIP1271_MAGIC_VALUE = 0x20c13b0b;
enum ValidatorAction {
// Return false (default)
@@ -42,12 +53,18 @@ contract TestValidatorWallet {
ValidateSignature,
NTypes
}
/// @dev The Exchange contract.
ISimplifiedExchange internal _EXCHANGE;
/// @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 Allowed signers for hash signature types.
mapping (bytes32=>address) internal _validSignerForHash;
constructor(address exchange) public {
_EXCHANGE = ISimplifiedExchange(exchange);
}
/// @dev Set the action to take when validating a hash.
/// @param hash The hash.
@@ -66,7 +83,75 @@ contract TestValidatorWallet {
revert('UNSUPPORTED_VALIDATE_ACTION');
}
_hashActions[hash] = action;
_hashWalletSigners[hash] = allowedSigner;
_validSignerForHash[hash] = allowedSigner;
}
/// @dev Validates a hash with the following signature types:
/// `EIP1271Wallet` and `EIP1271WalletOrder`signature types.
/// The length of `data` will determine which signature type is in use.
/// @param data Arbitrary data. Either an Order hash or abi-encoded Order.
/// @param signature Signature for `data`.
/// @return magicValue Returns `EIP1271_MAGIC_VALUE` if the signature check succeeds.
function isValidSignature(
bytes memory data,
bytes memory signature
)
public
returns (bytes4 magicValue)
{
bytes32 hash = _getOrderHashFromEIP1271Data(data);
ValidatorAction action = _hashActions[hash];
if (action == ValidatorAction.Reject) {
// NOOP.
} else if (action == ValidatorAction.Accept) {
magicValue = EIP1271_MAGIC_VALUE;
} else if (action == ValidatorAction.Revert) {
revert(REVERT_REASON);
} else if (action == ValidatorAction.UpdateState) {
_updateState();
} else { // action == ValidatorAction.ValidateSignature
if (data.length != 32) {
// `data` is an abi-encoded Order.
LibOrder.Order memory order = _getOrderFromEIP1271Data(data);
if (order.makerAddress == address(this)) {
magicValue = EIP1271_MAGIC_VALUE;
}
} else if (_validSignerForHash[hash] == address(this)) {
magicValue = EIP1271_MAGIC_VALUE;
}
}
}
function _getOrderHashFromEIP1271Data(bytes memory data)
private
returns (bytes32 hash)
{
if (data.length == 32) {
// `data` is an order hash.
hash = data.readBytes32(0);
} else {
// `data` is an abi-encoded Order.
LibOrder.Order memory order = _getOrderFromEIP1271Data(data);
// Use the Exchange contract to convert it into a hash.
hash = _EXCHANGE.getOrderHash(order);
}
}
function _getOrderFromEIP1271Data(bytes memory data)
private
returns (LibOrder.Order memory order)
{
require(data.length > 32, "INVALID_EIP1271_ORDER_DATA_LENGTH");
assembly {
// Skip past the length to find the first parameter.
let argsStart := add(data, 32)
order := add(argsStart, mload(argsStart))
// Destructively point the asset data fields to absolute locations.
for {let o := 0x140} lt(o, 0x1C0) {o := add(o, 0x20)} {
let arg := add(order, o)
mstore(arg, add(argsStart, add(mload(arg), 0x20)))
}
}
}
/// @dev Validates a hash with the `Validator` signature type.
@@ -91,7 +176,7 @@ contract TestValidatorWallet {
revert(REVERT_REASON);
} else if (action == ValidatorAction.ValidateSignature) {
isValid = _isSignedBy(hash, signature, signerAddress);
} else { // if (action == ValidatorAction.UpdateState) {
} else { // action == ValidatorAction.UpdateState
_updateState();
}
}
@@ -115,14 +200,14 @@ contract TestValidatorWallet {
} else if (action == ValidatorAction.Revert) {
revert(REVERT_REASON);
} else if (action == ValidatorAction.ValidateSignature) {
isValid = _isSignedBy(hash, signature, _hashWalletSigners[hash]);
} else { // if (action == ValidatorAction.UpdateState) {
isValid = _validSignerForHash[hash] == address(this);
} else { // action == ValidatorAction.UpdateState
_updateState();
}
}
/// @dev Validates a hash with the `OrderValidator` and
/// `WalletOrderValidator` signature types.
/// @dev Validates a hash with the `OrderValidator` and `OrderWallet`
/// signature types.
/// @param order The order.
/// @param orderHash The order hash.
/// @param signature Proof of signing.
@@ -143,12 +228,14 @@ contract TestValidatorWallet {
} else if (action == ValidatorAction.Revert) {
revert(REVERT_REASON);
} else if (action == ValidatorAction.ValidateSignature) {
if (order.makerAddress == address(this)) {
isValid = true;
if (signature.length == 0) {
// OrderWallet type.
isValid = order.makerAddress == address(this);
} else {
// OrderValidator type.
isValid = _isSignedBy(orderHash, signature, order.makerAddress);
}
} else { // if (action == ValidatorAction.UpdateState) {
} else { // action == ValidatorAction.UpdateState
_updateState();
}
}

View File

@@ -35,7 +35,6 @@ import { RevertReason, SignatureType, SignedOrder } from '@0x/types';
import { BigNumber, providerUtils, StringRevertError } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as chai from 'chai';
import * as crypto from 'crypto';
import { LogWithDecodedArgs } from 'ethereum-types';
import ethUtil = require('ethereumjs-util');
import * as _ from 'lodash';
@@ -144,6 +143,7 @@ describe('Exchange core', () => {
artifacts.TestValidatorWallet,
provider,
txDefaults,
exchange.address,
);
reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync(
artifacts.ReentrantERC20Token,
@@ -314,9 +314,9 @@ describe('Exchange core', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should revert if `WalletOrderValidator` signature type rejects during a second fill', async () => {
it('should revert if `OrderWallet` signature type rejects during a second fill', async () => {
const signature = Buffer.concat([
ethUtil.toBuffer([SignatureType.WalletOrderValidator]),
ethUtil.toBuffer([SignatureType.OrderWallet]),
]);
signedOrder.makerAddress = validatorWallet.address;
signedOrder.signature = ethUtil.bufferToHex(signature);
@@ -352,6 +352,45 @@ describe('Exchange core', () => {
);
return expect(tx).to.revertWith(expectedError);
});
it('should revert if `EIP1271OrderWallet` signature type rejects during a second fill', async () => {
const signature = Buffer.concat([
ethUtil.toBuffer([SignatureType.EIP1271OrderWallet]),
]);
signedOrder.makerAddress = validatorWallet.address;
signedOrder.signature = ethUtil.bufferToHex(signature);
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
// Allow the signature check for the first fill.
await validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
orderHashHex,
ValidatorWalletAction.Accept,
signedOrder.makerAddress,
);
const fillAmount = signedOrder.takerAssetAmount.div(10);
await exchangeWrapper.fillOrderAsync(
signedOrder,
takerAddress,
{ takerAssetFillAmount: fillAmount },
);
// Reject the signature check for the second fill.
await validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
orderHashHex,
ValidatorWalletAction.Reject,
signedOrder.makerAddress,
);
const tx = exchangeWrapper.fillOrderAsync(
signedOrder,
takerAddress,
{ takerAssetFillAmount: fillAmount },
);
const expectedError = new ExchangeRevertErrors.SignatureError(
ExchangeRevertErrors.SignatureErrorCode.BadSignature,
orderHashHex,
signedOrder.makerAddress,
signedOrder.signature,
);
return expect(tx).to.revertWith(expectedError);
});
});
it('should throw if fully filled', async () => {

View File

@@ -84,12 +84,7 @@ describe('LibExchangeRichErrorDecoder', () => {
createDecodeTest(ExchangeRevertErrors.SignatureValidatorError, [orderHash, signer, validator, signature, errorData]);
createDecodeTest(ExchangeRevertErrors.SignatureWalletError, [orderHash, signer, signature, errorData]);
createDecodeTest(ExchangeRevertErrors.SignatureOrderValidatorError, [orderHash, signer, validator, signature, errorData]);
createDecodeTest(ExchangeRevertErrors.SignatureWalletOrderValidatorError, [
orderHash,
signer,
signature,
errorData,
]);
createDecodeTest(ExchangeRevertErrors.SignatureOrderWalletError, [orderHash, signer, signature, errorData]);
})();
(() => {

View File

@@ -14,7 +14,7 @@ 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 { LogWithDecodedArgs } from 'ethereum-types';
import ethUtil = require('ethereumjs-util');
import {
@@ -38,7 +38,6 @@ describe('MixinSignatureValidator', () => {
let orderFactory: OrderFactory;
let signatureValidator: TestSignatureValidatorContract;
let validatorWallet: TestValidatorWalletContract;
let validator: TestValidatorWalletContract;
let validatorWalletRevertReason: string;
let signerAddress: string;
let signerPrivateKey: Buffer;
@@ -67,25 +66,24 @@ describe('MixinSignatureValidator', () => {
artifacts.TestValidatorWallet,
provider,
txDefaults,
signatureValidator.address,
);
validator = await TestValidatorWalletContract.deployFrom0xArtifactAsync(
artifacts.TestValidatorWallet,
provider,
txDefaults,
);
validatorWalletRevertReason = await validator.REVERT_REASON.callAsync();
validatorWalletRevertReason = await validatorWallet.REVERT_REASON.callAsync();
// Approve the validator.
signatureValidator.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
validatorWallet.address,
true,
{ from: signerAddress },
);
signatureValidator.setOrderValidatorApproval.awaitTransactionSuccessAsync(
validatorWallet.address,
true,
{ from: signerAddress },
);
// Approve the validator for both signers.
await Promise.all([signerAddress, notSignerAddress].map(async (addr: string) => {
const tx1 = signatureValidator.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
validatorWallet.address,
true,
{ from: addr },
);
const tx2 = signatureValidator.setOrderValidatorApproval.awaitTransactionSuccessAsync(
validatorWallet.address,
true,
{ from: addr },
);
return Promise.all([tx1, tx2]);
}));
const defaultOrderParams = {
...constants.STATIC_ORDER_PARAMS,
@@ -95,6 +93,8 @@ describe('MixinSignatureValidator', () => {
takerAssetData: assetDataUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
makerFee: constants.ZERO_AMOUNT,
takerFee: constants.ZERO_AMOUNT,
domain: {
verifyingContractAddress: signatureValidator.address,
chainId,
@@ -270,8 +270,8 @@ describe('MixinSignatureValidator', () => {
});
it('should return true when SignatureType=Wallet and signature is valid', async () => {
signedOrder.makerAddress = validatorWallet.address;
const signature = Buffer.concat([
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
ethUtil.toBuffer([SignatureType.Wallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
@@ -286,15 +286,15 @@ describe('MixinSignatureValidator', () => {
});
it('should return false when SignatureType=Wallet and signature is invalid', async () => {
signedOrder.makerAddress = validatorWallet.address;
const signature = Buffer.concat([
crypto.randomBytes(SIGNATURE_LENGTH),
ethUtil.toBuffer([SignatureType.Wallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// Validate signature
const isValidSignature = await validateCallAsync(
signedOrder,
validatorWallet.address,
notSignerAddress,
signatureHex,
ValidatorWalletAction.ValidateSignature,
);
@@ -302,9 +302,9 @@ describe('MixinSignatureValidator', () => {
});
it('should revert when validator attempts to update state and SignatureType=Wallet', async () => {
signedOrder.makerAddress = validatorWallet.address;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const signature = Buffer.concat([
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
ethUtil.toBuffer([SignatureType.Wallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
@@ -324,9 +324,9 @@ describe('MixinSignatureValidator', () => {
});
it('should revert when validator reverts and SignatureType=Wallet', async () => {
signedOrder.makerAddress = validatorWallet.address;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const signature = Buffer.concat([
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
ethUtil.toBuffer([SignatureType.Wallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
@@ -356,7 +356,7 @@ describe('MixinSignatureValidator', () => {
signedOrder,
signerAddress,
signatureHex,
ValidatorWalletAction.Accept,
ValidatorWalletAction.ValidateSignature,
);
expect(isValidSignature).to.be.true();
});
@@ -401,9 +401,11 @@ describe('MixinSignatureValidator', () => {
});
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 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(
@@ -429,10 +431,12 @@ describe('MixinSignatureValidator', () => {
false,
{ from: signerAddress },
);
const signature = Buffer.concat([
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
ethUtil.toBuffer(validatorWallet.address),
ethUtil.toBuffer([SignatureType.Validator]),
]);
// Validate signature
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 tx = validateCallAsync(
signedOrder,
@@ -447,6 +451,82 @@ describe('MixinSignatureValidator', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should return true when SignatureType=EIP1271Wallet and signature is valid', async () => {
signedOrder.makerAddress = validatorWallet.address;
const signature = Buffer.concat([
ethUtil.toBuffer([SignatureType.EIP1271Wallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// Validate signature
const isValidSignature = await validateCallAsync(
signedOrder,
validatorWallet.address,
signatureHex,
ValidatorWalletAction.ValidateSignature,
);
expect(isValidSignature).to.be.true();
});
it('should return false when SignatureType=EIP1271Wallet and signature is invalid', async () => {
signedOrder.makerAddress = validatorWallet.address;
const signature = Buffer.concat([
ethUtil.toBuffer([SignatureType.EIP1271Wallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// Validate signature
const isValidSignature = await validateCallAsync(
signedOrder,
validatorWallet.address,
signatureHex,
ValidatorWalletAction.Reject,
);
expect(isValidSignature).to.be.false();
});
it('should revert when validator attempts to update state and SignatureType=EIP1271Wallet', async () => {
signedOrder.makerAddress = validatorWallet.address;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const signature = Buffer.concat([
ethUtil.toBuffer([SignatureType.EIP1271Wallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
const expectedError = new ExchangeRevertErrors.SignatureWalletError(
orderHashHex,
validatorWallet.address,
signatureHex,
constants.NULL_BYTES,
);
const tx = validateCallAsync(
signedOrder,
validatorWallet.address,
signatureHex,
ValidatorWalletAction.UpdateState,
);
return expect(tx).to.revertWith(expectedError);
});
it('should revert when validator reverts and SignatureType=EIP1271Wallet', async () => {
signedOrder.makerAddress = validatorWallet.address;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const signature = Buffer.concat([
ethUtil.toBuffer([SignatureType.EIP1271Wallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
const expectedError = new ExchangeRevertErrors.SignatureWalletError(
orderHashHex,
validatorWallet.address,
signatureHex,
new StringRevertError(validatorWalletRevertReason).encode(),
);
const tx = validateCallAsync(
signedOrder,
validatorWallet.address,
signatureHex,
ValidatorWalletAction.Revert,
);
return expect(tx).to.revertWith(expectedError);
});
it('should return true when SignatureType=Presigned and signer has presigned hash', async () => {
// Presign hash
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
@@ -477,7 +557,7 @@ describe('MixinSignatureValidator', () => {
describe('isValidHashSignature', () => {
const validateCallAsync = async (
order: SignedOrder,
signer: string,
validSigner: string,
signatureHex: string,
validatorAction?: ValidatorWalletAction,
) => {
@@ -486,10 +566,14 @@ describe('MixinSignatureValidator', () => {
await validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
orderHashHex,
validatorAction,
order.makerAddress,
validSigner,
);
}
return signatureValidator.isValidHashSignature.callAsync(orderHashHex, signer, signatureHex);
return signatureValidator.isValidHashSignature.callAsync(
orderHashHex,
order.makerAddress,
signatureHex,
);
};
beforeEach(async () => {
@@ -556,7 +640,7 @@ describe('MixinSignatureValidator', () => {
describe('isValidOrderSignature', () => {
const validateCallAsync = async (
order: SignedOrder,
signer: string,
validSigner: string,
signatureHex: string,
validatorAction?: ValidatorWalletAction,
) => {
@@ -565,15 +649,18 @@ describe('MixinSignatureValidator', () => {
await validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
orderHashHex,
validatorAction,
order.makerAddress,
validSigner,
);
}
return signatureValidator.isValidOrderSignature.callAsync(order, signer, signatureHex);
return signatureValidator.isValidOrderSignature.callAsync(
order,
order.makerAddress,
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 () => {
@@ -682,10 +769,10 @@ describe('MixinSignatureValidator', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should return true when SignatureType=WalletOrderValidator and signature is valid', async () => {
it('should return true when SignatureType=OrderWallet and signature is valid', async () => {
signedOrder.makerAddress = validatorWallet.address;
const signature = Buffer.concat([
ethUtil.toBuffer([SignatureType.WalletOrderValidator]),
ethUtil.toBuffer([SignatureType.OrderWallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// Validate signature
@@ -698,11 +785,10 @@ describe('MixinSignatureValidator', () => {
expect(isValidSignature).to.be.true();
});
it('should return false when SignatureType=WalletOrderValidator and signature is invalid', async () => {
signedOrder.makerAddress = notSignerAddress;
it('should return false when SignatureType=OrderWallet and signature is invalid', async () => {
signedOrder.makerAddress = validatorWallet.address;
const signature = Buffer.concat([
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
ethUtil.toBuffer([SignatureType.WalletOrderValidator]),
ethUtil.toBuffer([SignatureType.OrderWallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// Validate signature
@@ -710,19 +796,19 @@ describe('MixinSignatureValidator', () => {
signedOrder,
validatorWallet.address,
signatureHex,
ValidatorWalletAction.ValidateSignature,
ValidatorWalletAction.Reject,
);
expect(isValidSignature).to.be.false();
});
it('should revert when validator attempts to update state and SignatureType=WalletOrderValidator', async () => {
it('should revert when validator attempts to update state and SignatureType=OrderWallet', async () => {
signedOrder.makerAddress = validatorWallet.address;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const signature = Buffer.concat([
ethUtil.toBuffer([SignatureType.WalletOrderValidator]),
ethUtil.toBuffer([SignatureType.OrderWallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
const expectedError = new ExchangeRevertErrors.SignatureWalletOrderValidatorError(
const expectedError = new ExchangeRevertErrors.SignatureOrderWalletError(
orderHashHex,
validatorWallet.address,
signatureHex,
@@ -737,14 +823,90 @@ describe('MixinSignatureValidator', () => {
return expect(tx).to.revertWith(expectedError);
});
it('should revert when validator reverts and SignatureType=WalletOrderValidator', async () => {
it('should revert when validator reverts and SignatureType=OrderWallet', async () => {
signedOrder.makerAddress = validatorWallet.address;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const signature = Buffer.concat([
ethUtil.toBuffer([SignatureType.WalletOrderValidator]),
ethUtil.toBuffer([SignatureType.OrderWallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
const expectedError = new ExchangeRevertErrors.SignatureWalletOrderValidatorError(
const expectedError = new ExchangeRevertErrors.SignatureOrderWalletError(
orderHashHex,
validatorWallet.address,
signatureHex,
new StringRevertError(validatorWalletRevertReason).encode(),
);
const tx = validateCallAsync(
signedOrder,
validatorWallet.address,
signatureHex,
ValidatorWalletAction.Revert,
);
return expect(tx).to.revertWith(expectedError);
});
it('should return true when SignatureType=EIP1271OrderWallet and signature is valid', async () => {
signedOrder.makerAddress = validatorWallet.address;
const signature = Buffer.concat([
ethUtil.toBuffer([SignatureType.EIP1271OrderWallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// Validate signature
const isValidSignature = await validateCallAsync(
signedOrder,
validatorWallet.address,
signatureHex,
ValidatorWalletAction.ValidateSignature,
);
expect(isValidSignature).to.be.true();
});
it('should return false when SignatureType=EIP1271OrderWallet and signature is invalid', async () => {
signedOrder.makerAddress = validatorWallet.address;
const signature = Buffer.concat([
ethUtil.toBuffer([SignatureType.EIP1271OrderWallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
// Validate signature
const isValidSignature = await validateCallAsync(
signedOrder,
validatorWallet.address,
signatureHex,
ValidatorWalletAction.Reject,
);
expect(isValidSignature).to.be.false();
});
it('should revert when validator attempts to update state and SignatureType=EIP1271OrderWallet', async () => {
signedOrder.makerAddress = validatorWallet.address;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const signature = Buffer.concat([
ethUtil.toBuffer([SignatureType.EIP1271OrderWallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
const expectedError = new ExchangeRevertErrors.SignatureOrderWalletError(
orderHashHex,
validatorWallet.address,
signatureHex,
constants.NULL_BYTES,
);
const tx = validateCallAsync(
signedOrder,
validatorWallet.address,
signatureHex,
ValidatorWalletAction.UpdateState,
);
return expect(tx).to.revertWith(expectedError);
});
it('should revert when validator reverts and SignatureType=EIP1271OrderWallet', async () => {
signedOrder.makerAddress = validatorWallet.address;
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const signature = Buffer.concat([
ethUtil.toBuffer([SignatureType.EIP1271OrderWallet]),
]);
const signatureHex = ethUtil.bufferToHex(signature);
const expectedError = new ExchangeRevertErrors.SignatureOrderWalletError(
orderHashHex,
validatorWallet.address,
signatureHex,