From eb9b2f355e01a2bb78a7234ecd39c8bc99e6cfd6 Mon Sep 17 00:00:00 2001 From: Lawrence Forman Date: Tue, 25 Jun 2019 18:11:16 -0400 Subject: [PATCH] `@0x/contracts-exchange`: Consolidate signature types. `@0x/contracts-exchange`: Fighting with linearization issues. --- contracts/exchange/compiler.json | 2 - .../exchange/contracts/src/MixinCommon.sol | 64 ++ .../contracts/src/MixinExchangeCore.sol | 14 +- .../contracts/src/MixinExchangeRichErrors.sol | 55 -- .../contracts/src/MixinSignatureValidator.sol | 603 ++++++------------ .../contracts/src/MixinTransactions.sol | 11 +- .../src/interfaces/IExchangeRichErrors.sol | 12 - .../src/interfaces/IOrderValidator.sol | 40 -- .../src/interfaces/ISignatureValidator.sol | 36 +- .../contracts/src/interfaces/IValidator.sol | 40 -- .../contracts/test/TestValidatorWallet.sol | 237 +++---- 11 files changed, 395 insertions(+), 719 deletions(-) create mode 100644 contracts/exchange/contracts/src/MixinCommon.sol delete mode 100644 contracts/exchange/contracts/src/interfaces/IOrderValidator.sol delete mode 100644 contracts/exchange/contracts/src/interfaces/IValidator.sol diff --git a/contracts/exchange/compiler.json b/contracts/exchange/compiler.json index 3e013232b6..e9c443e3ea 100644 --- a/contracts/exchange/compiler.json +++ b/contracts/exchange/compiler.json @@ -31,10 +31,8 @@ "src/interfaces/IExchange.sol", "src/interfaces/IExchangeCore.sol", "src/interfaces/IMatchOrders.sol", - "src/interfaces/IOrderValidator.sol", "src/interfaces/ISignatureValidator.sol", "src/interfaces/ITransactions.sol", - "src/interfaces/IValidator.sol", "src/interfaces/IWallet.sol", "src/interfaces/IEIP1271Wallet.sol", "src/interfaces/IWrapperFunctions.sol", diff --git a/contracts/exchange/contracts/src/MixinCommon.sol b/contracts/exchange/contracts/src/MixinCommon.sol new file mode 100644 index 0000000000..744c0df00b --- /dev/null +++ b/contracts/exchange/contracts/src/MixinCommon.sol @@ -0,0 +1,64 @@ +/* + + 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; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; +import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol"; + + +contract MixinCommon { + // Defined in MixinSignatureValidator + function _isValidOrderWithHashSignature( + LibOrder.Order memory order, + bytes32 orderHash, + address signerAddress, + bytes memory signature + ) + internal + view + returns (bool isValid); + + // Defined in MixinSignatureValidator + function _isValidTransactionWithHashSignature( + LibZeroExTransaction.ZeroExTransaction memory transaction, + bytes32 transactionHash, + address signerAddress, + bytes memory signature + ) + internal + view + returns (bool isValid); + + // Defined in MixinSignatureValidator + function doesSignatureRequireRegularValidation( + bytes32 hash, + address signerAddress, + bytes memory signature + ) + public + pure + returns (bool needsRegularValidation); + + // Defined in MixinTransactions + function _getCurrentContextAddress() + internal + view + returns (address); +} diff --git a/contracts/exchange/contracts/src/MixinExchangeCore.sol b/contracts/exchange/contracts/src/MixinExchangeCore.sol index 540fc87c0b..689063cbf9 100644 --- a/contracts/exchange/contracts/src/MixinExchangeCore.sol +++ b/contracts/exchange/contracts/src/MixinExchangeCore.sol @@ -24,14 +24,10 @@ import "@0x/contracts-exchange-libs/contracts/src/LibExchangeSelectors.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; -import "./interfaces/IAssetProxyDispatcher.sol"; import "./interfaces/IExchangeCore.sol"; -import "./interfaces/ISignatureValidator.sol"; import "./MixinAssetProxyDispatcher.sol"; import "./MixinExchangeRichErrors.sol"; -import "./MixinSignatureValidator.sol"; -import "./MixinAssetProxyDispatcher.sol"; -import "./MixinTransactions.sol"; +import "./MixinCommon.sol"; contract MixinExchangeCore is @@ -39,8 +35,10 @@ contract MixinExchangeCore is LibExchangeSelectors, LibMath, LibFillResults, - MixinAssetProxyDispatcher, - MixinSignatureValidator + LibOrder, + ReentrancyGuard, + MixinCommon, + MixinAssetProxyDispatcher { using LibBytes for bytes; @@ -543,7 +541,7 @@ contract MixinExchangeCore is order.feeRecipientAddress, fillResults.takerFeePaid ); - + // Transfer maker fee -> feeRecipient _dispatchTransferFrom( orderHash, diff --git a/contracts/exchange/contracts/src/MixinExchangeRichErrors.sol b/contracts/exchange/contracts/src/MixinExchangeRichErrors.sol index 3bdf737622..5d53a8ec21 100644 --- a/contracts/exchange/contracts/src/MixinExchangeRichErrors.sol +++ b/contracts/exchange/contracts/src/MixinExchangeRichErrors.sol @@ -62,21 +62,6 @@ contract MixinExchangeRichErrors is ); } - 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, @@ -117,46 +102,6 @@ contract MixinExchangeRichErrors is ); } - function SignatureOrderValidatorError( - bytes32 orderHash, - address signerAddress, - address validatorAddress, - bytes memory signature, - bytes memory errorData - ) - internal - pure - returns (bytes memory) - { - return abi.encodeWithSelector( - SIGNATURE_ORDER_VALIDATOR_ERROR_SELECTOR, - orderHash, - signerAddress, - validatorAddress, - signature, - errorData - ); - } - - function SignatureOrderWalletError( - bytes32 orderHash, - address wallet, - bytes memory signature, - bytes memory errorData - ) - internal - pure - returns (bytes memory) - { - return abi.encodeWithSelector( - SIGNATURE_ORDER_WALLET_ERROR_SELECTOR, - orderHash, - wallet, - signature, - errorData - ); - } - function OrderStatusError( bytes32 orderHash, LibOrder.OrderStatus orderStatus diff --git a/contracts/exchange/contracts/src/MixinSignatureValidator.sol b/contracts/exchange/contracts/src/MixinSignatureValidator.sol index e0a26c7d12..e57e04ded4 100644 --- a/contracts/exchange/contracts/src/MixinSignatureValidator.sol +++ b/contracts/exchange/contracts/src/MixinSignatureValidator.sol @@ -22,24 +22,23 @@ pragma experimental ABIEncoderV2; import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-utils/contracts/src/LibEIP1271.sol"; 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 "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol"; import "./interfaces/IWallet.sol"; import "./interfaces/IEIP1271Wallet.sol"; -import "./interfaces/IValidator.sol"; -import "./interfaces/IOrderValidator.sol"; import "./interfaces/ISignatureValidator.sol"; -import "./MixinTransactions.sol"; import "./MixinExchangeRichErrors.sol"; +import "./MixinCommon.sol"; contract MixinSignatureValidator is - MixinExchangeRichErrors, - ReentrancyGuard, - LibOrder, - LibEIP1271, ISignatureValidator, - MixinTransactions + LibOrder, + LibZeroExTransaction, + LibEIP1271, + ReentrancyGuard, + MixinCommon, + MixinExchangeRichErrors { using LibBytes for bytes; @@ -49,9 +48,6 @@ contract MixinSignatureValidator is // Mapping of signer => validator => approved mapping (address => mapping (address => bool)) public allowedValidators; - // Mapping of signer => order validator => approved - mapping (address => mapping (address => bool)) public allowedOrderValidators; - /// @dev Approves a hash on-chain. /// After presigning a hash, the preSign signature type will become valid for that hash and signer. /// @param hash Any 32-byte hash. @@ -83,31 +79,11 @@ contract MixinSignatureValidator is ); } - /// @dev Approves/unnapproves an OrderValidator contract to verify signatures on signer's behalf - /// using the `OrderValidator` signature type. - /// @param validatorAddress Address of Validator contract. - /// @param approval Approval or disapproval of Validator contract. - function setOrderValidatorApproval( - address validatorAddress, - bool approval - ) - external - nonReentrant - { - address signerAddress = _getCurrentContextAddress(); - allowedOrderValidators[signerAddress][validatorAddress] = approval; - emit SignatureValidatorApproval( - signerAddress, - validatorAddress, - approval - ); - } - /// @dev Verifies that a signature for an order is valid. /// @param order The order. /// @param signerAddress Address that should have signed the given order. /// @param signature Proof that the order has been signed by signer. - /// @return True if the signature is valid for the given order and signer. + /// @return isValid true if the signature is valid for the given order and signer. function isValidOrderSignature( Order memory order, address signerAddress, @@ -126,13 +102,13 @@ contract MixinSignatureValidator is ); } - /// @dev Verifies that a hash has been signed by the given signer. - /// @param hash Any 32-byte hash. - /// @param signerAddress Address that should have signed the given hash. - /// @param signature Proof that the hash has been signed by signer. - /// @return True if the signature is valid for the given hash and signer. - function isValidHashSignature( - bytes32 hash, + /// @dev Verifies that a signature for a transaction is valid. + /// @param transaction The transaction. + /// @param signerAddress Address that should have signed the given order. + /// @param signature Proof that the order has been signed by signer. + /// @return isValid true if the signature is valid for the given transaction and signer. + function isValidTransactionSignature( + ZeroExTransaction memory transaction, address signerAddress, bytes memory signature ) @@ -140,42 +116,24 @@ contract MixinSignatureValidator is view returns (bool isValid) { - SignatureType signatureType = _readValidSignatureType( - hash, - signerAddress, - signature - ); - // Only hash-compatible signature types can be handled by this - // function. - if ( - signatureType == SignatureType.OrderValidator || - signatureType == SignatureType.OrderWallet || - signatureType == SignatureType.EIP1271OrderWallet - ) { - _rrevert(SignatureError( - SignatureErrorCodes.INAPPROPRIATE_SIGNATURE_TYPE, - hash, - signerAddress, - signature - )); - } - return _validateHashSignatureTypes( - signatureType, - hash, + bytes32 transactionHash = getTransactionHash(transaction); + return _isValidTransactionWithHashSignature( + transaction, + transactionHash, signerAddress, signature ); } /// @dev Checks if a signature is of a type that should be verified for - /// every subsequent fill. - /// @param orderHash The hash of the order. + /// every action. + /// @param hash The hash of the order/transaction. /// @param signerAddress The address of the signer. - /// @param signature The signature for `orderHash`. + /// @param signature The signature for `hash`. /// @return needsRegularValidation True if the signature should be validated - /// for every operation. + /// for every action. function doesSignatureRequireRegularValidation( - bytes32 orderHash, + bytes32 hash, address signerAddress, bytes memory signature ) @@ -184,16 +142,14 @@ contract MixinSignatureValidator is returns (bool needsRegularValidation) { SignatureType signatureType = _readValidSignatureType( - orderHash, + hash, signerAddress, signature ); - // Only signature types that take a full order should be validated - // regularly. return - signatureType == SignatureType.OrderValidator || - signatureType == SignatureType.OrderWallet || - signatureType == SignatureType.EIP1271OrderWallet; + signatureType == SignatureType.Wallet || + signatureType == SignatureType.Validator || + signatureType == SignatureType.EIP1271Wallet; } /// @dev Verifies that an order, with provided order hash, has been signed @@ -202,7 +158,7 @@ contract MixinSignatureValidator is /// @param orderHash The hash of the order. /// @param signerAddress Address that should have signed the.Signat given hash. /// @param signature Proof that the hash has been signed by signer. - /// @return isValid True if the signature is valid for the given hash and signer. + /// @return isValid True if the signature is valid for the given order and signer. function _isValidOrderWithHashSignature( Order memory order, bytes32 orderHash, @@ -218,41 +174,168 @@ contract MixinSignatureValidator is signerAddress, signature ); - if (signatureType == SignatureType.OrderValidator) { - // The entire order is verified by validator contract. - isValid = _validateOrderWithValidator( - order, + if (signatureType == SignatureType.Validator) { + // The entire order is verified by a validator contract. + isValid = _validateBytesWithValidator( + abi.encode(order, orderHash), orderHash, signerAddress, signature ); - return isValid; - } else if (signatureType == SignatureType.OrderWallet) { + } else if (signatureType == SignatureType.EIP1271Wallet) { // The entire order is verified by a wallet contract. - isValid = _validateOrderWithWallet( - order, + isValid = _validateBytesWithWallet( + abi.encode(order, orderHash), orderHash, signerAddress, signature ); - return isValid; - } else if (signatureType == SignatureType.EIP1271OrderWallet) { - // The entire order is verified by a wallet contract. - isValid = _validateOrderWithEIP1271Wallet( - order, + } else { + // Otherwise, it's one of the hash-only signature types. + isValid = _validateHashSignatureTypes( + signatureType, orderHash, signerAddress, signature ); - return isValid; } - // Otherwise, it's one of the hash-compatible signature types. - return _validateHashSignatureTypes( - signatureType, - orderHash, + } + + /// @dev Verifies that a transaction, with provided order hash, has been signed + /// by the given signer. + /// @param transaction The transaction. + /// @param transactionHash The hash of the transaction. + /// @param signerAddress Address that should have signed the.Signat given hash. + /// @param signature Proof that the hash has been signed by signer. + /// @return isValid True if the signature is valid for the given transaction and signer. + function _isValidTransactionWithHashSignature( + ZeroExTransaction memory transaction, + bytes32 transactionHash, + address signerAddress, + bytes memory signature + ) + internal + view + returns (bool isValid) + { + SignatureType signatureType = _readValidSignatureType( + transactionHash, signerAddress, signature ); + if (signatureType == SignatureType.Validator) { + // The entire transaction is verified by a validator contract. + isValid = _validateBytesWithValidator( + abi.encode(transaction, transactionHash), + transactionHash, + signerAddress, + signature + ); + } else if (signatureType == SignatureType.EIP1271Wallet) { + // The entire transaction is verified by a wallet contract. + isValid = _validateBytesWithWallet( + abi.encode(transaction, transactionHash), + transactionHash, + signerAddress, + signature + ); + } else { + // Otherwise, it's one of the hash-only signature types. + isValid = _validateHashSignatureTypes( + signatureType, + transactionHash, + signerAddress, + signature + ); + } + } + + /// Validates a hash-only signature type + /// (anything but `Validator` and `EIP1271Wallet`). + function _validateHashSignatureTypes( + SignatureType signatureType, + bytes32 hash, + address signerAddress, + bytes memory signature + ) + private + view + returns (bool isValid) + { + // Always invalid signature. + // Like Illegal, this is always implicitly available and therefore + // offered explicitly. It can be implicitly created by providing + // a correctly formatted but incorrect signature. + if (signatureType == SignatureType.Invalid) { + if (signature.length != 1) { + _rrevert(SignatureError( + SignatureErrorCodes.INVALID_LENGTH, + hash, + signerAddress, + signature + )); + } + isValid = false; + + // Signature using EIP712 + } else if (signatureType == SignatureType.EIP712) { + if (signature.length != 66) { + _rrevert(SignatureError( + SignatureErrorCodes.INVALID_LENGTH, + hash, + signerAddress, + signature + )); + } + uint8 v = uint8(signature[0]); + bytes32 r = signature.readBytes32(1); + bytes32 s = signature.readBytes32(33); + address recovered = ecrecover( + hash, + v, + r, + s + ); + isValid = signerAddress == recovered; + + // Signed using web3.eth_sign + } else if (signatureType == SignatureType.EthSign) { + if (signature.length != 66) { + _rrevert(SignatureError( + SignatureErrorCodes.INVALID_LENGTH, + hash, + signerAddress, + signature + )); + } + uint8 v = uint8(signature[0]); + bytes32 r = signature.readBytes32(1); + bytes32 s = signature.readBytes32(33); + address recovered = ecrecover( + keccak256(abi.encodePacked( + "\x19Ethereum Signed Message:\n32", + hash + )), + v, + r, + s + ); + isValid = signerAddress == recovered; + + // Signature verified by wallet contract. + } else if (signatureType == SignatureType.Wallet) { + isValid = _validateHashWithWallet( + hash, + signerAddress, + signature + ); + + // Otherwise, signatureType == SignatureType.PreSigned + } else { + assert(signatureType == SignatureType.PreSigned); + // Signer signed hash previously using the preSign function. + isValid = preSigned[hash][signerAddress]; + } } /// Reads the `SignatureType` from the end of a signature and validates it. @@ -304,12 +387,12 @@ contract MixinSignatureValidator is return SignatureType(signatureTypeRaw); } - /// @dev Verifies signature using logic defined by Wallet contract. + /// @dev Verifies a hash and signature using logic defined by 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. + /// @return isValid True if the signature is validated by the Wallet. function _validateHashWithWallet( bytes32 hash, address walletAddress, @@ -319,6 +402,11 @@ contract MixinSignatureValidator is view returns (bool isValid) { + // A signature using this type should be encoded as: + // | Offset | Length | Contents | + // | 0x00 | x | Signature to validate | + // | 0x00 + x | 1 | Signature type is always "\x04" | + uint256 signatureLength = signature.length; // HACK(dorothy-zbornak): Temporarily shave the signature type // from the signature for the encode call then restore @@ -351,13 +439,15 @@ 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( + /// @dev Verifies arbitrary data and a signature via an EIP1271 Wallet + /// contract, where the wallet address is also the signer address. + /// @param data Arbitrary signed data. + /// @param hash The hash associated with the data. + /// @param walletAddress Contract that will verify the data and signature. + /// @param signature Proof that the data has been signed by signer. + /// @return isValid True if the signature is validated by the Wallet. + function _validateBytesWithWallet( + bytes memory data, bytes32 hash, address walletAddress, bytes memory signature @@ -366,6 +456,11 @@ contract MixinSignatureValidator is view returns (bool isValid) { + // A signature using this type should be encoded as: + // | Offset | Length | Contents | + // | 0x00 | x | Signature to validate | + // | 0x00 + x | 1 | Signature type is always "\x07" | + uint256 signatureLength = signature.length; // HACK(dorothy-zbornak): Temporarily shave the signature type // from the signature for the encode call then restore @@ -374,8 +469,6 @@ contract MixinSignatureValidator is 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, @@ -400,13 +493,15 @@ contract MixinSignatureValidator is )); } - /// @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. + /// @dev Verifies arbitrary data and a signature via an EIP1271 contract + /// whose address is encoded in the signature. + /// @param data Arbitrary signed data. + /// @param hash The hash associated with the data. /// @param signerAddress Address that should have signed the given hash. - /// @param signature Proof that the hash has been signed by signer. - /// @return True if the signature is validated by the Validator. - function _validateHashWithValidator( + /// @param signature Proof that the data has been signed by signer. + /// @return isValid True if the signature is validated by the validator contract. + function _validateBytesWithValidator( + bytes memory data, bytes32 hash, address signerAddress, bytes memory signature @@ -415,12 +510,11 @@ contract MixinSignatureValidator is view returns (bool isValid) { - // If used with an order, the maker of the order can still be an EOA. // A signature using this type should be encoded as: // | Offset | Length | Contents | // | 0x00 | x | Signature to validate | // | 0x00 + x | 20 | Address of validator contract | - // | 0x14 + x | 1 | Signature type is always "\x06" | + // | 0x14 + x | 1 | Signature type is always "\x05" | uint256 signatureLength = signature.length; // Read the validator address from the signature. @@ -440,9 +534,8 @@ contract MixinSignatureValidator is } // Encode the call data. bytes memory callData = abi.encodeWithSelector( - IValidator(validatorAddress).isValidSignature.selector, - hash, - signerAddress, + IEIP1271Wallet(validatorAddress).isValidSignature.selector, + data, signature ); // Restore the full signature. @@ -451,9 +544,9 @@ contract MixinSignatureValidator is } // Static call the verification function. (bool didSucceed, bytes memory returnData) = validatorAddress.staticcall(callData); - // Return data should be a single bool. - if (didSucceed && returnData.length == 32) { - return returnData.readUint256(0) == 1; + // Return data should be the `EIP1271_MAGIC_VALUE`. + if (didSucceed && returnData.length <= 32) { + return returnData.readBytes4(0) == EIP1271_MAGIC_VALUE; } // Static call to verifier failed. _rrevert(SignatureValidatorError( @@ -465,280 +558,4 @@ contract MixinSignatureValidator is )); } - /// @dev Verifies order AND signature via a 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 _validateOrderWithWallet( - Order memory order, - bytes32 orderHash, - address walletAddress, - bytes memory signature - ) - private - view - returns (bool isValid) - { - uint256 signatureLength = signature.length; - // HACK(dorothy-zbornak): Temporarily shave the signature type - // from the signature for the encode call then restore - // it immediately after because we want to keep signatures intact. - assembly { - mstore(signature, sub(signatureLength, 1)) - } - // Encode the call data. - bytes memory callData = abi.encodeWithSelector( - IWallet(walletAddress).isValidOrderSignature.selector, - order, - orderHash, - 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 a single bool. - if (didSucceed && returnData.length == 32) { - return returnData.readUint256(0) == 1; - } - // Static call to verifier failed. - _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; - // HACK(dorothy-zbornak): Temporarily shave the signature type - // from the signature for the encode call then restore - // it immediately after because we want to keep signatures intact. - 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 returnData.readBytes4(0) == EIP1271_MAGIC_VALUE; - } - // Static call to verifier failed. - _rrevert(SignatureOrderWalletError( - orderHash, - walletAddress, - signature, - returnData - )); - } - - /// @dev Verifies order AND signature via Validator contract. - /// If used with an order, the maker of the order can still be an EOA. - /// @param order The order. - /// @param orderHash The order hash. - /// @param signerAddress Address that should have signed the given hash. - /// @param signature Proof that the hash has been signed by signer. - /// @return True if order and signature are validated by the Validator. - function _validateOrderWithValidator( - Order memory order, - bytes32 orderHash, - address signerAddress, - bytes memory signature - ) - private - view - returns (bool isValid) - { - // A signature using this type should be encoded as: - // | Offset | Length | Contents | - // | 0x00 | x | Signature to validate | - // | 0x00 + x | 20 | Address of validator contract | - // | 0x14 + x | 1 | Signature type is always "\x07" | - - uint256 signatureLength = signature.length; - // Read the validator address from the signature. - address validatorAddress = signature.readAddress(signatureLength - 21); - // Ensure signer has approved validator. - if (!allowedOrderValidators[signerAddress][validatorAddress]) { - _rrevert(SignatureOrderValidatorNotApprovedError( - signerAddress, - validatorAddress - )); - } - // HACK(dorothy-zbornak): Temporarily shave the validator address - // and signature type from the signature for the encode call then restore - // it immediately after because we want to keep signatures intact. - assembly { - mstore(signature, sub(signatureLength, 21)) - } - // Encode the call data. - bytes memory callData = abi.encodeWithSelector( - IOrderValidator(validatorAddress).isValidOrderSignature.selector, - order, - orderHash, - signature - ); - // Restore the full signature. - assembly { - mstore(signature, signatureLength) - } - // Static call the verification function. - (bool didSucceed, bytes memory returnData) = validatorAddress.staticcall(callData); - // Return data should be a single bool. - if (didSucceed && returnData.length == 32) { - return returnData.readUint256(0) == 1; - } - // Static call to verifier failed. - _rrevert(SignatureOrderValidatorError( - orderHash, - signerAddress, - validatorAddress, - signature, - returnData - )); - } - - /// Validates a hash-compatible signature type - /// (anything but `OrderValidator` and `OrderWallet`). - function _validateHashSignatureTypes( - SignatureType signatureType, - bytes32 hash, - address signerAddress, - bytes memory signature - ) - private - view - returns (bool isValid) - { - // Always invalid signature. - // Like Illegal, this is always implicitly available and therefore - // offered explicitly. It can be implicitly created by providing - // a correctly formatted but incorrect signature. - if (signatureType == SignatureType.Invalid) { - if (signature.length != 1) { - _rrevert(SignatureError( - SignatureErrorCodes.INVALID_LENGTH, - hash, - signerAddress, - signature - )); - } - isValid = false; - return isValid; - - // Signature using EIP712 - } else if (signatureType == SignatureType.EIP712) { - if (signature.length != 66) { - _rrevert(SignatureError( - SignatureErrorCodes.INVALID_LENGTH, - hash, - signerAddress, - signature - )); - } - uint8 v = uint8(signature[0]); - bytes32 r = signature.readBytes32(1); - bytes32 s = signature.readBytes32(33); - address recovered = ecrecover( - hash, - v, - r, - s - ); - isValid = signerAddress == recovered; - return isValid; - - // Signed using web3.eth_sign - } else if (signatureType == SignatureType.EthSign) { - if (signature.length != 66) { - _rrevert(SignatureError( - SignatureErrorCodes.INVALID_LENGTH, - hash, - signerAddress, - signature - )); - } - uint8 v = uint8(signature[0]); - bytes32 r = signature.readBytes32(1); - bytes32 s = signature.readBytes32(33); - address recovered = ecrecover( - keccak256(abi.encodePacked( - "\x19Ethereum Signed Message:\n32", - hash - )), - v, - r, - s - ); - isValid = signerAddress == recovered; - return isValid; - - // Signature verified by wallet contract. - // If used with an order, the maker of the order is the wallet contract. - } else if (signatureType == SignatureType.Wallet) { - isValid = _validateHashWithWallet( - hash, - signerAddress, - signature - ); - return isValid; - - // Signature verified by validator contract. - // If used with an order, the maker of the order can still be an EOA. - } else if (signatureType == SignatureType.Validator) { - isValid = _validateHashWithValidator( - hash, - signerAddress, - signature - ); - 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); - // Signer signed hash previously using the preSign function. - return preSigned[hash][signerAddress]; - } } diff --git a/contracts/exchange/contracts/src/MixinTransactions.sol b/contracts/exchange/contracts/src/MixinTransactions.sol index 732a743d78..301a6dd682 100644 --- a/contracts/exchange/contracts/src/MixinTransactions.sol +++ b/contracts/exchange/contracts/src/MixinTransactions.sol @@ -20,16 +20,16 @@ pragma solidity ^0.5.9; pragma experimental ABIEncoderV2; import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol"; -import "./interfaces/ISignatureValidator.sol"; import "./interfaces/ITransactions.sol"; import "./MixinExchangeRichErrors.sol"; +import "./MixinSignatureValidator.sol"; contract MixinTransactions is - MixinExchangeRichErrors, + ITransactions, LibZeroExTransaction, - ISignatureValidator, - ITransactions + MixinExchangeRichErrors, + MixinSignatureValidator { // Mapping of transaction hash => executed // This prevents transactions from being executed more than once. @@ -113,7 +113,8 @@ contract MixinTransactions is address signerAddress = transaction.signerAddress; if (signerAddress != msg.sender) { // Validate signature - if (!isValidHashSignature( + if (!_isValidTransactionWithHashSignature( + transaction, transactionHash, signerAddress, signature)) { diff --git a/contracts/exchange/contracts/src/interfaces/IExchangeRichErrors.sol b/contracts/exchange/contracts/src/interfaces/IExchangeRichErrors.sol index 39d7b36175..46da6ecd9a 100644 --- a/contracts/exchange/contracts/src/interfaces/IExchangeRichErrors.sol +++ b/contracts/exchange/contracts/src/interfaces/IExchangeRichErrors.sol @@ -37,10 +37,6 @@ contract IExchangeRichErrors { 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 = 0xa23838b8; @@ -49,14 +45,6 @@ contract IExchangeRichErrors { bytes4 internal constant SIGNATURE_WALLET_ERROR_SELECTOR = 0x1b8388f7; - // bytes4(keccak256("SignatureOrderValidatorError(bytes32,address,address,bytes,bytes)")) - bytes4 internal constant SIGNATURE_ORDER_VALIDATOR_ERROR_SELECTOR = - 0xf45375b7; - - // 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 = 0xfdb6ca8d; diff --git a/contracts/exchange/contracts/src/interfaces/IOrderValidator.sol b/contracts/exchange/contracts/src/interfaces/IOrderValidator.sol deleted file mode 100644 index 60640772f8..0000000000 --- a/contracts/exchange/contracts/src/interfaces/IOrderValidator.sol +++ /dev/null @@ -1,40 +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.9; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; - - -contract IOrderValidator { - - /// @dev Verifies that an order AND a signature is valid. - /// @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); -} diff --git a/contracts/exchange/contracts/src/interfaces/ISignatureValidator.sol b/contracts/exchange/contracts/src/interfaces/ISignatureValidator.sol index e07a5656be..51cefaf6a7 100644 --- a/contracts/exchange/contracts/src/interfaces/ISignatureValidator.sol +++ b/contracts/exchange/contracts/src/interfaces/ISignatureValidator.sol @@ -20,6 +20,7 @@ pragma solidity ^0.5.9; pragma experimental ABIEncoderV2; import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; +import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol"; contract ISignatureValidator { @@ -33,11 +34,8 @@ contract ISignatureValidator { Wallet, // 0x04 Validator, // 0x05 PreSigned, // 0x06 - OrderValidator, // 0x07 - OrderWallet, // 0x08 - EIP1271Wallet, // 0x09 - EIP1271OrderWallet, // 0x0A - NSignatureTypes // 0x0B, number of signature types. Always leave at end. + EIP1271Wallet, // 0x07 + NSignatureTypes // 0x08, number of signature types. Always leave at end. } event SignatureValidatorApproval( @@ -71,13 +69,13 @@ contract ISignatureValidator { ) external; - /// @dev Verifies that a signature for a hash is valid. - /// @param hash Message hash that is signed. - /// @param signerAddress Address of signer. - /// @param signature Proof of signing. - /// @return Validity of order signature. - function isValidHashSignature( - bytes32 hash, + /// @dev Verifies that a signature for an order is valid. + /// @param order The order. + /// @param signerAddress Address that should have signed the given order. + /// @param signature Proof that the order has been signed by signer. + /// @return isValid true if the signature is valid for the given order and signer. + function isValidOrderSignature( + LibOrder.Order memory order, address signerAddress, bytes memory signature ) @@ -85,13 +83,13 @@ contract ISignatureValidator { view returns (bool isValid); - /// @dev Verifies that a signature for an order is valid. - /// @param order The order. - /// @param signerAddress Address of signer. - /// @param signature Proof of signing. - /// @return Validity of order signature. - function isValidOrderSignature( - LibOrder.Order memory order, + /// @dev Verifies that a signature for a transaction is valid. + /// @param transaction The transaction. + /// @param signerAddress Address that should have signed the given order. + /// @param signature Proof that the order has been signed by signer. + /// @return isValid true if the signature is valid for the given transaction and signer. + function isValidTransactionSignature( + LibZeroExTransaction.ZeroExTransaction memory transaction, address signerAddress, bytes memory signature ) diff --git a/contracts/exchange/contracts/src/interfaces/IValidator.sol b/contracts/exchange/contracts/src/interfaces/IValidator.sol deleted file mode 100644 index eb9a173c3c..0000000000 --- a/contracts/exchange/contracts/src/interfaces/IValidator.sol +++ /dev/null @@ -1,40 +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.9; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; - - -contract IValidator { - - /// @dev Verifies that a signature is valid. - /// @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 - view - returns (bool isValid); -} diff --git a/contracts/exchange/contracts/test/TestValidatorWallet.sol b/contracts/exchange/contracts/test/TestValidatorWallet.sol index 022661b6f0..8a5d2fda8c 100644 --- a/contracts/exchange/contracts/test/TestValidatorWallet.sol +++ b/contracts/exchange/contracts/test/TestValidatorWallet.sol @@ -1,6 +1,6 @@ /* - Copyright 2018 ZeroEx Intl. + Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ pragma solidity ^0.5.5; pragma experimental ABIEncoderV2; import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; +import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol"; import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-utils/contracts/src/LibEIP1271.sol"; @@ -30,6 +31,11 @@ interface ISimplifiedExchange { external view returns (bytes32 orderHash); + + function getTransactionHash(LibZeroExTransaction.ZeroExTransaction calldata transaction) + external + view + returns (bytes32 transactionHash); } @@ -39,20 +45,30 @@ contract TestValidatorWallet is { using LibBytes for bytes; - // Revert reason for `Revert` `ValidatorAction`. + /// @dev Revert reason for `Revert` `ValidatorAction`. string constant public REVERT_REASON = "you shall not pass"; + enum DataType { + // No data type; only expecting a hash (default) + None, + // An Order + Order, + // A ZeroExTransaction + ZeroExTransaction, + NTypes + } + enum ValidatorAction { - // Return false (default) + // Return failure (default) Reject, - // Return true + // Return success Accept, // Revert Revert, // Update state UpdateState, - // Validate signature - ValidateSignature, + // Ensure the signature hash matches what was prepared + MatchSignatureHash, NTypes } @@ -62,8 +78,10 @@ contract TestValidatorWallet is uint256 internal _state = 1; /// @dev What action to execute when a hash is validated . mapping (bytes32 => ValidatorAction) internal _hashActions; - /// @dev Allowed signers for hash signature types. - mapping (bytes32 => address) internal _validSignerForHash; + /// @dev The data type of a hash. + mapping (bytes32 => DataType) internal _hashDataTypes; + /// @dev keccak256 of the expected signature data for a hash. + mapping (bytes32 => DataType) internal _hashSignatureHashes; constructor(address exchange) public { _exchange = ISimplifiedExchange(exchange); @@ -83,32 +101,34 @@ contract TestValidatorWallet is IERC20Token(token).approve(spender, value); } - /// @dev Set the action to take when validating a hash. + /// @dev Prepares this contract to validate a signature. /// @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( + /// @param dataType The data type associated with the hash. + /// @param action Action to take. + /// @param signatureHash keccak256 of the expected signature data. + function prepare( bytes32 hash, + DataType dataType, ValidatorAction action, - address allowedSigner + bytes32 signatureHash ) external { - if (uint8(action) >= uint8(ValidatorAction.NTypes)) { - revert("UNSUPPORTED_VALIDATE_ACTION"); + if (uint8(dataType) >= uint8(DataType.NTypes)) { + revert("UNSUPPORTED_DATA_TYPE"); } + if (uint8(action) >= uint8(ValidatorAction.NTypes)) { + revert("UNSUPPORTED_VALIDATOR_ACTION"); + } + _hashDataTypes[hash] = dataType; _hashActions[hash] = action; - _validSignerForHash[hash] = allowedSigner; + _hashSignatureHashes[hash] = signatureHash; } - /// @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. + /// @dev Validates data signed by either `EIP1271Wallet` or `Validator` signature types. + /// @param data Abi-encoded data (Order or ZeroExTransaction) and a hash. /// @param signature Signature for `data`. - /// @return magicValue Returns `EIP1271_MAGIC_VALUE` if the signature check succeeds. + /// @return magicValue `EIP1271_MAGIC_VALUE` if the signature check succeeds. function isValidSignature( bytes memory data, bytes memory signature @@ -116,35 +136,30 @@ contract TestValidatorWallet is public returns (bytes4 magicValue) { - bytes32 hash = _getOrderHashFromEIP1271Data(data); + bytes32 hash = _decodeAndValidateHashFromEncodedData(data); ValidatorAction action = _hashActions[hash]; - // solhint-disable-next-line no-empty-blocks if (action == ValidatorAction.Reject) { - // NOOP. + magicValue = 0x0; } 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)) { + } else { + assert(action == ValidatorAction.MatchSignatureHash); + bytes32 expectedSignatureHash = _hashSignatureHashes[hash]; + if (keccak256(signature) == expectedSignatureHash) { magicValue = EIP1271_MAGIC_VALUE; } } } - /// @dev Validates a hash with the `Validator` signature type. + /// @dev Validates a hash with the `Wallet` 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. + /// @return isValid `true` if the signature check succeeds. function isValidSignature( bytes32 hash, address signerAddress, @@ -160,129 +175,61 @@ contract TestValidatorWallet is isValid = true; } else if (action == ValidatorAction.Revert) { revert(REVERT_REASON); - } else if (action == ValidatorAction.ValidateSignature) { - isValid = _isSignedBy(hash, signature, signerAddress); - } else { // action == ValidatorAction.UpdateState + } 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.Accept) { - isValid = true; - } else if (action == ValidatorAction.Revert) { - revert(REVERT_REASON); - } else if (action == ValidatorAction.ValidateSignature) { - isValid = _validSignerForHash[hash] == address(this); - } else { // action == ValidatorAction.UpdateState - _updateState(); - } - } - - /// @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. - /// @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.Accept) { - isValid = true; - } else if (action == ValidatorAction.Revert) { - revert(REVERT_REASON); - } else if (action == ValidatorAction.ValidateSignature) { - if (signature.length == 0) { - // OrderWallet type. - isValid = order.makerAddress == address(this); - } else { - // OrderValidator type. - isValid = _isSignedBy(orderHash, signature, order.makerAddress); + } else { + assert(action == ValidatorAction.MatchSignatureHash); + bytes32 expectedSignatureHash = _hashSignatureHashes[hash]; + if (keccak256(signature) == expectedSignatureHash) { + isValid = true; } - } else { // action == ValidatorAction.UpdateState - _updateState(); } } - /// @dev Increments state variable. + /// @dev Increments state variable to trigger a state change. function _updateState() private { _state++; } - /// @dev Verifies the signer of a hash is correct. - function _isSignedBy( - bytes32 hash, - bytes memory signature, - address signerAddress - ) - private - pure - returns (bool isSignedBy) + function _decodeAndValidateHashFromEncodedData(bytes memory data) + private + view + returns (bytes32 hash) { - 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 - ) + // HACK(dorothy-zbornak): First we want the hash, which is the second + // encoded parameter. We will initially treat all fields as inline + // `bytes32`s and ignore the first one to extract it. + (,hash) = abi.decode(data, (bytes32, bytes32)); + // Now we can figure out what the data type is from a previous call to + // `prepare()`. + DataType dataType = _hashDataTypes[hash]; + require( + dataType != DataType.None, + "EXPECTED_NO_DATA_TYPE" + ); + if (dataType == DataType.Order) { + // Decode the first parameter as an `Order` type. + LibOrder.Order memory order = abi.decode(data, (LibOrder.Order)); + // Use the Exchange to calculate the hash of the order and assert + // that it matches the one we extracted previously. + require( + _exchange.getOrderHash(order) == hash, + "UNEXPECTED_ORDER_HASH" ); - recovered = ecrecover(ethSignHash, v, r, s); - } - isSignedBy = recovered == signerAddress; - } - - 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); + assert(dataType == DataType.ZeroExTransaction); + // Decode the first parameter as a `ZeroExTransaction` type. + LibZeroExTransaction.ZeroExTransaction memory transaction = + abi.decode(data, (LibZeroExTransaction.ZeroExTransaction)); + // Use the Exchange to calculate the hash of the transaction and assert + // that it matches the one we extracted previously. + require( + _exchange.getTransactionHash(transaction) == hash, + "UNEXPECTED_TRANSACTION_HASH" + ); } + return hash; } - - function _getOrderFromEIP1271Data(bytes memory data) - private - returns (LibOrder.Order memory order) - { - require(data.length > 32, "INVALID_EIP1271_ORDER_DATA_LENGTH"); - return abi.decode(data, (LibOrder.Order)); - } - }