@0x/contracts-exchange
: Consolidate signature types.
`@0x/contracts-exchange`: Fighting with linearization issues.
This commit is contained in:
parent
cf6144599d
commit
eb9b2f355e
@ -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",
|
||||
|
64
contracts/exchange/contracts/src/MixinCommon.sol
Normal file
64
contracts/exchange/contracts/src/MixinCommon.sol
Normal 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);
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,42 +174,169 @@ 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,
|
||||
orderHash,
|
||||
signerAddress,
|
||||
signature
|
||||
);
|
||||
return isValid;
|
||||
}
|
||||
// Otherwise, it's one of the hash-compatible signature types.
|
||||
return _validateHashSignatureTypes(
|
||||
} else {
|
||||
// Otherwise, it's one of the hash-only signature types.
|
||||
isValid = _validateHashSignatureTypes(
|
||||
signatureType,
|
||||
orderHash,
|
||||
signerAddress,
|
||||
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.
|
||||
function _readValidSignatureType(
|
||||
@ -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];
|
||||
}
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
||||
)
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
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)
|
||||
{
|
||||
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)
|
||||
function _decodeAndValidateHashFromEncodedData(bytes memory data)
|
||||
private
|
||||
view
|
||||
returns (bytes32 hash)
|
||||
{
|
||||
if (data.length == 32) {
|
||||
// `data` is an order hash.
|
||||
hash = data.readBytes32(0);
|
||||
// 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"
|
||||
);
|
||||
} 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user