@0x/contracts-exchange: Consolidate signature types.

`@0x/contracts-exchange`: Fighting with linearization issues.
This commit is contained in:
Lawrence Forman
2019-06-25 18:11:16 -04:00
committed by Amir Bandeali
parent cf6144599d
commit eb9b2f355e
11 changed files with 395 additions and 719 deletions

View File

@@ -31,10 +31,8 @@
"src/interfaces/IExchange.sol", "src/interfaces/IExchange.sol",
"src/interfaces/IExchangeCore.sol", "src/interfaces/IExchangeCore.sol",
"src/interfaces/IMatchOrders.sol", "src/interfaces/IMatchOrders.sol",
"src/interfaces/IOrderValidator.sol",
"src/interfaces/ISignatureValidator.sol", "src/interfaces/ISignatureValidator.sol",
"src/interfaces/ITransactions.sol", "src/interfaces/ITransactions.sol",
"src/interfaces/IValidator.sol",
"src/interfaces/IWallet.sol", "src/interfaces/IWallet.sol",
"src/interfaces/IEIP1271Wallet.sol", "src/interfaces/IEIP1271Wallet.sol",
"src/interfaces/IWrapperFunctions.sol", "src/interfaces/IWrapperFunctions.sol",

View File

@@ -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);
}

View File

@@ -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/LibFillResults.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "./interfaces/IAssetProxyDispatcher.sol";
import "./interfaces/IExchangeCore.sol"; import "./interfaces/IExchangeCore.sol";
import "./interfaces/ISignatureValidator.sol";
import "./MixinAssetProxyDispatcher.sol"; import "./MixinAssetProxyDispatcher.sol";
import "./MixinExchangeRichErrors.sol"; import "./MixinExchangeRichErrors.sol";
import "./MixinSignatureValidator.sol"; import "./MixinCommon.sol";
import "./MixinAssetProxyDispatcher.sol";
import "./MixinTransactions.sol";
contract MixinExchangeCore is contract MixinExchangeCore is
@@ -39,8 +35,10 @@ contract MixinExchangeCore is
LibExchangeSelectors, LibExchangeSelectors,
LibMath, LibMath,
LibFillResults, LibFillResults,
MixinAssetProxyDispatcher, LibOrder,
MixinSignatureValidator ReentrancyGuard,
MixinCommon,
MixinAssetProxyDispatcher
{ {
using LibBytes for bytes; using LibBytes for bytes;

View File

@@ -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( function SignatureValidatorError(
bytes32 hash, bytes32 hash,
address signerAddress, 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( function OrderStatusError(
bytes32 orderHash, bytes32 orderHash,
LibOrder.OrderStatus orderStatus LibOrder.OrderStatus orderStatus

View File

@@ -22,24 +22,23 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibEIP1271.sol"; import "@0x/contracts-utils/contracts/src/LibEIP1271.sol";
import "@0x/contracts-utils/contracts/src/ReentrancyGuard.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/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
import "./interfaces/IWallet.sol"; import "./interfaces/IWallet.sol";
import "./interfaces/IEIP1271Wallet.sol"; import "./interfaces/IEIP1271Wallet.sol";
import "./interfaces/IValidator.sol";
import "./interfaces/IOrderValidator.sol";
import "./interfaces/ISignatureValidator.sol"; import "./interfaces/ISignatureValidator.sol";
import "./MixinTransactions.sol";
import "./MixinExchangeRichErrors.sol"; import "./MixinExchangeRichErrors.sol";
import "./MixinCommon.sol";
contract MixinSignatureValidator is contract MixinSignatureValidator is
MixinExchangeRichErrors,
ReentrancyGuard,
LibOrder,
LibEIP1271,
ISignatureValidator, ISignatureValidator,
MixinTransactions LibOrder,
LibZeroExTransaction,
LibEIP1271,
ReentrancyGuard,
MixinCommon,
MixinExchangeRichErrors
{ {
using LibBytes for bytes; using LibBytes for bytes;
@@ -49,9 +48,6 @@ contract MixinSignatureValidator is
// Mapping of signer => validator => approved // Mapping of signer => validator => approved
mapping (address => mapping (address => bool)) public allowedValidators; 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. /// @dev Approves a hash on-chain.
/// After presigning a hash, the preSign signature type will become valid for that hash and signer. /// After presigning a hash, the preSign signature type will become valid for that hash and signer.
/// @param hash Any 32-byte hash. /// @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. /// @dev Verifies that a signature for an order is valid.
/// @param order The order. /// @param order The order.
/// @param signerAddress Address that should have signed the given order. /// @param signerAddress Address that should have signed the given order.
/// @param signature Proof that the order has been signed by signer. /// @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( function isValidOrderSignature(
Order memory order, Order memory order,
address signerAddress, address signerAddress,
@@ -126,13 +102,13 @@ contract MixinSignatureValidator is
); );
} }
/// @dev Verifies that a hash has been signed by the given signer. /// @dev Verifies that a signature for a transaction is valid.
/// @param hash Any 32-byte hash. /// @param transaction The transaction.
/// @param signerAddress Address that should have signed the given hash. /// @param signerAddress Address that should have signed the given order.
/// @param signature Proof that the hash has been signed by signer. /// @param signature Proof that the order has been signed by signer.
/// @return True if the signature is valid for the given hash and signer. /// @return isValid true if the signature is valid for the given transaction and signer.
function isValidHashSignature( function isValidTransactionSignature(
bytes32 hash, ZeroExTransaction memory transaction,
address signerAddress, address signerAddress,
bytes memory signature bytes memory signature
) )
@@ -140,42 +116,24 @@ contract MixinSignatureValidator is
view view
returns (bool isValid) returns (bool isValid)
{ {
SignatureType signatureType = _readValidSignatureType( bytes32 transactionHash = getTransactionHash(transaction);
hash, return _isValidTransactionWithHashSignature(
signerAddress, transaction,
signature transactionHash,
);
// 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,
signerAddress, signerAddress,
signature signature
); );
} }
/// @dev Checks if a signature is of a type that should be verified for /// @dev Checks if a signature is of a type that should be verified for
/// every subsequent fill. /// every action.
/// @param orderHash The hash of the order. /// @param hash The hash of the order/transaction.
/// @param signerAddress The address of the signer. /// @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 /// @return needsRegularValidation True if the signature should be validated
/// for every operation. /// for every action.
function doesSignatureRequireRegularValidation( function doesSignatureRequireRegularValidation(
bytes32 orderHash, bytes32 hash,
address signerAddress, address signerAddress,
bytes memory signature bytes memory signature
) )
@@ -184,16 +142,14 @@ contract MixinSignatureValidator is
returns (bool needsRegularValidation) returns (bool needsRegularValidation)
{ {
SignatureType signatureType = _readValidSignatureType( SignatureType signatureType = _readValidSignatureType(
orderHash, hash,
signerAddress, signerAddress,
signature signature
); );
// Only signature types that take a full order should be validated
// regularly.
return return
signatureType == SignatureType.OrderValidator || signatureType == SignatureType.Wallet ||
signatureType == SignatureType.OrderWallet || signatureType == SignatureType.Validator ||
signatureType == SignatureType.EIP1271OrderWallet; signatureType == SignatureType.EIP1271Wallet;
} }
/// @dev Verifies that an order, with provided order hash, has been signed /// @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 orderHash The hash of the order.
/// @param signerAddress Address that should have signed the.Signat given hash. /// @param signerAddress Address that should have signed the.Signat given hash.
/// @param signature Proof that the hash has been signed by signer. /// @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( function _isValidOrderWithHashSignature(
Order memory order, Order memory order,
bytes32 orderHash, bytes32 orderHash,
@@ -218,42 +174,169 @@ contract MixinSignatureValidator is
signerAddress, signerAddress,
signature signature
); );
if (signatureType == SignatureType.OrderValidator) { if (signatureType == SignatureType.Validator) {
// The entire order is verified by validator contract. // The entire order is verified by a validator contract.
isValid = _validateOrderWithValidator( isValid = _validateBytesWithValidator(
order, abi.encode(order, orderHash),
orderHash, orderHash,
signerAddress, signerAddress,
signature signature
); );
return isValid; } else if (signatureType == SignatureType.EIP1271Wallet) {
} else if (signatureType == SignatureType.OrderWallet) {
// The entire order is verified by a wallet contract. // The entire order is verified by a wallet contract.
isValid = _validateOrderWithWallet( isValid = _validateBytesWithWallet(
order, abi.encode(order, orderHash),
orderHash, orderHash,
signerAddress, signerAddress,
signature signature
); );
return isValid; } else {
} else if (signatureType == SignatureType.EIP1271OrderWallet) { // Otherwise, it's one of the hash-only signature types.
// The entire order is verified by a wallet contract. isValid = _validateHashSignatureTypes(
isValid = _validateOrderWithEIP1271Wallet(
order,
orderHash,
signerAddress,
signature
);
return isValid;
}
// Otherwise, it's one of the hash-compatible signature types.
return _validateHashSignatureTypes(
signatureType, signatureType,
orderHash, orderHash,
signerAddress, signerAddress,
signature signature
); );
} }
}
/// @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. /// Reads the `SignatureType` from the end of a signature and validates it.
function _readValidSignatureType( function _readValidSignatureType(
@@ -304,12 +387,12 @@ contract MixinSignatureValidator is
return SignatureType(signatureTypeRaw); 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 hash Any 32 byte hash.
/// @param walletAddress Address that should have signed the given hash /// @param walletAddress Address that should have signed the given hash
/// and defines its own signature verification method. /// and defines its own signature verification method.
/// @param signature Proof that the hash has been signed by signer. /// @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( function _validateHashWithWallet(
bytes32 hash, bytes32 hash,
address walletAddress, address walletAddress,
@@ -319,6 +402,11 @@ contract MixinSignatureValidator is
view view
returns (bool isValid) 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; uint256 signatureLength = signature.length;
// HACK(dorothy-zbornak): Temporarily shave the signature type // HACK(dorothy-zbornak): Temporarily shave the signature type
// from the signature for the encode call then restore // 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. /// @dev Verifies arbitrary data and a signature via an EIP1271 Wallet
/// @param hash Any 32 byte hash. /// contract, where the wallet address is also the signer address.
/// @param walletAddress Address that should have signed the given hash /// @param data Arbitrary signed data.
/// and defines its own signature verification method. /// @param hash The hash associated with the data.
/// @param signature Proof that the hash has been signed by signer. /// @param walletAddress Contract that will verify the data and signature.
/// @return True if the signature is validated by the Walidator. /// @param signature Proof that the data has been signed by signer.
function _validateHashWithEIP1271Wallet( /// @return isValid True if the signature is validated by the Wallet.
function _validateBytesWithWallet(
bytes memory data,
bytes32 hash, bytes32 hash,
address walletAddress, address walletAddress,
bytes memory signature bytes memory signature
@@ -366,6 +456,11 @@ contract MixinSignatureValidator is
view view
returns (bool isValid) 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; uint256 signatureLength = signature.length;
// HACK(dorothy-zbornak): Temporarily shave the signature type // HACK(dorothy-zbornak): Temporarily shave the signature type
// from the signature for the encode call then restore // from the signature for the encode call then restore
@@ -374,8 +469,6 @@ contract MixinSignatureValidator is
mstore(signature, sub(signatureLength, 1)) mstore(signature, sub(signatureLength, 1))
} }
// Encode the call data. // Encode the call data.
bytes memory data = new bytes(32);
data.writeBytes32(0, hash);
bytes memory callData = abi.encodeWithSelector( bytes memory callData = abi.encodeWithSelector(
IEIP1271Wallet(walletAddress).isValidSignature.selector, IEIP1271Wallet(walletAddress).isValidSignature.selector,
data, data,
@@ -400,13 +493,15 @@ contract MixinSignatureValidator is
)); ));
} }
/// @dev Verifies signature using logic defined by Validator contract. /// @dev Verifies arbitrary data and a signature via an EIP1271 contract
/// If used with an order, the maker of the order can still be an EOA. /// whose address is encoded in the signature.
/// @param hash Any 32 byte hash. /// @param data Arbitrary signed data.
/// @param hash The hash associated with the data.
/// @param signerAddress Address that should have signed the given hash. /// @param signerAddress Address that should have signed the given hash.
/// @param signature Proof that the hash has been signed by signer. /// @param signature Proof that the data has been signed by signer.
/// @return True if the signature is validated by the Validator. /// @return isValid True if the signature is validated by the validator contract.
function _validateHashWithValidator( function _validateBytesWithValidator(
bytes memory data,
bytes32 hash, bytes32 hash,
address signerAddress, address signerAddress,
bytes memory signature bytes memory signature
@@ -415,12 +510,11 @@ contract MixinSignatureValidator is
view view
returns (bool isValid) 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: // A signature using this type should be encoded as:
// | Offset | Length | Contents | // | Offset | Length | Contents |
// | 0x00 | x | Signature to validate | // | 0x00 | x | Signature to validate |
// | 0x00 + x | 20 | Address of validator contract | // | 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; uint256 signatureLength = signature.length;
// Read the validator address from the signature. // Read the validator address from the signature.
@@ -440,9 +534,8 @@ contract MixinSignatureValidator is
} }
// Encode the call data. // Encode the call data.
bytes memory callData = abi.encodeWithSelector( bytes memory callData = abi.encodeWithSelector(
IValidator(validatorAddress).isValidSignature.selector, IEIP1271Wallet(validatorAddress).isValidSignature.selector,
hash, data,
signerAddress,
signature signature
); );
// Restore the full signature. // Restore the full signature.
@@ -451,9 +544,9 @@ contract MixinSignatureValidator is
} }
// Static call the verification function. // Static call the verification function.
(bool didSucceed, bytes memory returnData) = validatorAddress.staticcall(callData); (bool didSucceed, bytes memory returnData) = validatorAddress.staticcall(callData);
// Return data should be a single bool. // Return data should be the `EIP1271_MAGIC_VALUE`.
if (didSucceed && returnData.length == 32) { if (didSucceed && returnData.length <= 32) {
return returnData.readUint256(0) == 1; return returnData.readBytes4(0) == EIP1271_MAGIC_VALUE;
} }
// Static call to verifier failed. // Static call to verifier failed.
_rrevert(SignatureValidatorError( _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];
}
} }

View File

@@ -20,16 +20,16 @@ pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
import "./interfaces/ISignatureValidator.sol";
import "./interfaces/ITransactions.sol"; import "./interfaces/ITransactions.sol";
import "./MixinExchangeRichErrors.sol"; import "./MixinExchangeRichErrors.sol";
import "./MixinSignatureValidator.sol";
contract MixinTransactions is contract MixinTransactions is
MixinExchangeRichErrors, ITransactions,
LibZeroExTransaction, LibZeroExTransaction,
ISignatureValidator, MixinExchangeRichErrors,
ITransactions MixinSignatureValidator
{ {
// Mapping of transaction hash => executed // Mapping of transaction hash => executed
// This prevents transactions from being executed more than once. // This prevents transactions from being executed more than once.
@@ -113,7 +113,8 @@ contract MixinTransactions is
address signerAddress = transaction.signerAddress; address signerAddress = transaction.signerAddress;
if (signerAddress != msg.sender) { if (signerAddress != msg.sender) {
// Validate signature // Validate signature
if (!isValidHashSignature( if (!_isValidTransactionWithHashSignature(
transaction,
transactionHash, transactionHash,
signerAddress, signerAddress,
signature)) { signature)) {

View File

@@ -37,10 +37,6 @@ contract IExchangeRichErrors {
bytes4 internal constant SIGNATURE_VALIDATOR_NOT_APPROVED_ERROR_SELECTOR = bytes4 internal constant SIGNATURE_VALIDATOR_NOT_APPROVED_ERROR_SELECTOR =
0xa15c0d06; 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(keccak256("SignatureValidatorError(bytes32,address,address,bytes,bytes)"))
bytes4 internal constant SIGNATURE_VALIDATOR_ERROR_SELECTOR = bytes4 internal constant SIGNATURE_VALIDATOR_ERROR_SELECTOR =
0xa23838b8; 0xa23838b8;
@@ -49,14 +45,6 @@ contract IExchangeRichErrors {
bytes4 internal constant SIGNATURE_WALLET_ERROR_SELECTOR = bytes4 internal constant SIGNATURE_WALLET_ERROR_SELECTOR =
0x1b8388f7; 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(keccak256("OrderStatusError(bytes32,uint8)"))
bytes4 internal constant ORDER_STATUS_ERROR_SELECTOR = bytes4 internal constant ORDER_STATUS_ERROR_SELECTOR =
0xfdb6ca8d; 0xfdb6ca8d;

View File

@@ -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);
}

View File

@@ -20,6 +20,7 @@ pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol";
contract ISignatureValidator { contract ISignatureValidator {
@@ -33,11 +34,8 @@ contract ISignatureValidator {
Wallet, // 0x04 Wallet, // 0x04
Validator, // 0x05 Validator, // 0x05
PreSigned, // 0x06 PreSigned, // 0x06
OrderValidator, // 0x07 EIP1271Wallet, // 0x07
OrderWallet, // 0x08 NSignatureTypes // 0x08, number of signature types. Always leave at end.
EIP1271Wallet, // 0x09
EIP1271OrderWallet, // 0x0A
NSignatureTypes // 0x0B, number of signature types. Always leave at end.
} }
event SignatureValidatorApproval( event SignatureValidatorApproval(
@@ -71,13 +69,13 @@ contract ISignatureValidator {
) )
external; external;
/// @dev Verifies that a signature for a hash is valid. /// @dev Verifies that a signature for an order is valid.
/// @param hash Message hash that is signed. /// @param order The order.
/// @param signerAddress Address of signer. /// @param signerAddress Address that should have signed the given order.
/// @param signature Proof of signing. /// @param signature Proof that the order has been signed by signer.
/// @return Validity of order signature. /// @return isValid true if the signature is valid for the given order and signer.
function isValidHashSignature( function isValidOrderSignature(
bytes32 hash, LibOrder.Order memory order,
address signerAddress, address signerAddress,
bytes memory signature bytes memory signature
) )
@@ -85,13 +83,13 @@ contract ISignatureValidator {
view view
returns (bool isValid); returns (bool isValid);
/// @dev Verifies that a signature for an order is valid. /// @dev Verifies that a signature for a transaction is valid.
/// @param order The order. /// @param transaction The transaction.
/// @param signerAddress Address of signer. /// @param signerAddress Address that should have signed the given order.
/// @param signature Proof of signing. /// @param signature Proof that the order has been signed by signer.
/// @return Validity of order signature. /// @return isValid true if the signature is valid for the given transaction and signer.
function isValidOrderSignature( function isValidTransactionSignature(
LibOrder.Order memory order, LibZeroExTransaction.ZeroExTransaction memory transaction,
address signerAddress, address signerAddress,
bytes memory signature bytes memory signature
) )

View File

@@ -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);
}

View File

@@ -1,6 +1,6 @@
/* /*
Copyright 2018 ZeroEx Intl. Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with 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; pragma experimental ABIEncoderV2;
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; 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-erc20/contracts/src/interfaces/IERC20Token.sol";
import "@0x/contracts-utils/contracts/src/LibBytes.sol"; import "@0x/contracts-utils/contracts/src/LibBytes.sol";
import "@0x/contracts-utils/contracts/src/LibEIP1271.sol"; import "@0x/contracts-utils/contracts/src/LibEIP1271.sol";
@@ -30,6 +31,11 @@ interface ISimplifiedExchange {
external external
view view
returns (bytes32 orderHash); 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; using LibBytes for bytes;
// Revert reason for `Revert` `ValidatorAction`. /// @dev Revert reason for `Revert` `ValidatorAction`.
string constant public REVERT_REASON = "you shall not pass"; 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 { enum ValidatorAction {
// Return false (default) // Return failure (default)
Reject, Reject,
// Return true // Return success
Accept, Accept,
// Revert // Revert
Revert, Revert,
// Update state // Update state
UpdateState, UpdateState,
// Validate signature // Ensure the signature hash matches what was prepared
ValidateSignature, MatchSignatureHash,
NTypes NTypes
} }
@@ -62,8 +78,10 @@ contract TestValidatorWallet is
uint256 internal _state = 1; uint256 internal _state = 1;
/// @dev What action to execute when a hash is validated . /// @dev What action to execute when a hash is validated .
mapping (bytes32 => ValidatorAction) internal _hashActions; mapping (bytes32 => ValidatorAction) internal _hashActions;
/// @dev Allowed signers for hash signature types. /// @dev The data type of a hash.
mapping (bytes32 => address) internal _validSignerForHash; mapping (bytes32 => DataType) internal _hashDataTypes;
/// @dev keccak256 of the expected signature data for a hash.
mapping (bytes32 => DataType) internal _hashSignatureHashes;
constructor(address exchange) public { constructor(address exchange) public {
_exchange = ISimplifiedExchange(exchange); _exchange = ISimplifiedExchange(exchange);
@@ -83,32 +101,34 @@ contract TestValidatorWallet is
IERC20Token(token).approve(spender, value); 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 hash The hash.
/// @param action action to take. /// @param dataType The data type associated with the hash.
/// @param allowedSigner Signer that must be recovered with /// @param action Action to take.
/// `ValidateSignature` action type and `Wallet` or /// @param signatureHash keccak256 of the expected signature data.
/// `OrderWallet` signature types. function prepare(
function setValidateAction(
bytes32 hash, bytes32 hash,
DataType dataType,
ValidatorAction action, ValidatorAction action,
address allowedSigner bytes32 signatureHash
) )
external external
{ {
if (uint8(action) >= uint8(ValidatorAction.NTypes)) { if (uint8(dataType) >= uint8(DataType.NTypes)) {
revert("UNSUPPORTED_VALIDATE_ACTION"); revert("UNSUPPORTED_DATA_TYPE");
} }
if (uint8(action) >= uint8(ValidatorAction.NTypes)) {
revert("UNSUPPORTED_VALIDATOR_ACTION");
}
_hashDataTypes[hash] = dataType;
_hashActions[hash] = action; _hashActions[hash] = action;
_validSignerForHash[hash] = allowedSigner; _hashSignatureHashes[hash] = signatureHash;
} }
/// @dev Validates a hash with the following signature types: /// @dev Validates data signed by either `EIP1271Wallet` or `Validator` signature types.
/// `EIP1271Wallet` and `EIP1271WalletOrder`signature types. /// @param data Abi-encoded data (Order or ZeroExTransaction) and a hash.
/// 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`. /// @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( function isValidSignature(
bytes memory data, bytes memory data,
bytes memory signature bytes memory signature
@@ -116,35 +136,30 @@ contract TestValidatorWallet is
public public
returns (bytes4 magicValue) returns (bytes4 magicValue)
{ {
bytes32 hash = _getOrderHashFromEIP1271Data(data); bytes32 hash = _decodeAndValidateHashFromEncodedData(data);
ValidatorAction action = _hashActions[hash]; ValidatorAction action = _hashActions[hash];
// solhint-disable-next-line no-empty-blocks
if (action == ValidatorAction.Reject) { if (action == ValidatorAction.Reject) {
// NOOP. magicValue = 0x0;
} else if (action == ValidatorAction.Accept) { } else if (action == ValidatorAction.Accept) {
magicValue = EIP1271_MAGIC_VALUE; magicValue = EIP1271_MAGIC_VALUE;
} else if (action == ValidatorAction.Revert) { } else if (action == ValidatorAction.Revert) {
revert(REVERT_REASON); revert(REVERT_REASON);
} else if (action == ValidatorAction.UpdateState) { } else if (action == ValidatorAction.UpdateState) {
_updateState(); _updateState();
} else { // action == ValidatorAction.ValidateSignature } else {
if (data.length != 32) { assert(action == ValidatorAction.MatchSignatureHash);
// `data` is an abi-encoded Order. bytes32 expectedSignatureHash = _hashSignatureHashes[hash];
LibOrder.Order memory order = _getOrderFromEIP1271Data(data); if (keccak256(signature) == expectedSignatureHash) {
if (order.makerAddress == address(this)) {
magicValue = EIP1271_MAGIC_VALUE;
}
} else if (_validSignerForHash[hash] == address(this)) {
magicValue = EIP1271_MAGIC_VALUE; 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 hash Message hash that is signed.
/// @param signerAddress Address that should have signed the given hash. /// @param signerAddress Address that should have signed the given hash.
/// @param signature Proof of signing. /// @param signature Proof of signing.
/// @return Validity of order signature. /// @return isValid `true` if the signature check succeeds.
function isValidSignature( function isValidSignature(
bytes32 hash, bytes32 hash,
address signerAddress, address signerAddress,
@@ -160,129 +175,61 @@ contract TestValidatorWallet is
isValid = true; isValid = true;
} else if (action == ValidatorAction.Revert) { } else if (action == ValidatorAction.Revert) {
revert(REVERT_REASON); revert(REVERT_REASON);
} else if (action == ValidatorAction.ValidateSignature) { } else if (action == ValidatorAction.UpdateState) {
isValid = _isSignedBy(hash, signature, signerAddress);
} else { // action == ValidatorAction.UpdateState
_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 { } else {
// OrderValidator type. assert(action == ValidatorAction.MatchSignatureHash);
isValid = _isSignedBy(orderHash, signature, order.makerAddress); 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() function _updateState()
private private
{ {
_state++; _state++;
} }
/// @dev Verifies the signer of a hash is correct. function _decodeAndValidateHashFromEncodedData(bytes memory data)
function _isSignedBy(
bytes32 hash,
bytes memory signature,
address signerAddress
)
private
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;
}
function _getOrderHashFromEIP1271Data(bytes memory data)
private private
view
returns (bytes32 hash) returns (bytes32 hash)
{ {
if (data.length == 32) { // HACK(dorothy-zbornak): First we want the hash, which is the second
// `data` is an order hash. // encoded parameter. We will initially treat all fields as inline
hash = data.readBytes32(0); // `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"
);
} else { } else {
// `data` is an abi-encoded Order. assert(dataType == DataType.ZeroExTransaction);
LibOrder.Order memory order = _getOrderFromEIP1271Data(data); // Decode the first parameter as a `ZeroExTransaction` type.
// Use the Exchange contract to convert it into a hash. LibZeroExTransaction.ZeroExTransaction memory transaction =
hash = _exchange.getOrderHash(order); 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));
}
} }