In @0x/order-utils: Add TransactionSignatureError to ExchangeRevertErrors.

In `@0x/contracts-exchange`: Add `TransactionSignatureError`, supplanting `TransactionErrorCodes.BAD_SIGNATURE`, and associated test.
This commit is contained in:
Lawrence Forman 2019-04-11 18:59:26 -04:00 committed by Amir Bandeali
parent a0223835b8
commit 3c88ede02c
6 changed files with 77 additions and 22 deletions

View File

@ -216,6 +216,23 @@ contract MixinExchangeRichErrors is
); );
} }
function TransactionSignatureError(
bytes32 transactionHash,
address signer,
bytes memory signature
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
TRANSACTION_SIGNATURE_ERROR_SELECTOR,
transactionHash,
signer,
signature
);
}
function TransactionExecutionError( function TransactionExecutionError(
bytes32 transactionHash, bytes32 transactionHash,
bytes memory errorData bytes memory errorData

View File

@ -272,24 +272,23 @@ contract MixinSignatureValidator is
assembly { assembly {
mstore(signature, sub(signatureLength, 1)) mstore(signature, sub(signatureLength, 1))
} }
// Encode the call data. // Encode the call data.
bytes memory callData = abi.encodeWithSelector( bytes memory callData = abi.encodeWithSelector(
IWallet(walletAddress).isValidSignature.selector, IWallet(walletAddress).isValidSignature.selector,
hash, hash,
signature signature
); );
// Restore the full signature.
assembly {
mstore(signature, signatureLength)
}
// Static call the verification function. // Static call the verification function.
(bool didSucceed, bytes memory returnData) = walletAddress.staticcall(callData); (bool didSucceed, bytes memory returnData) = walletAddress.staticcall(callData);
// Return data should be a single bool. // Return data should be a single bool.
if (didSucceed && returnData.length == 32) if (didSucceed && returnData.length == 32)
return returnData.readUint256(0) != 0; return returnData.readUint256(0) != 0;
// Static call to verifier failed. // Static call to verifier failed.
// Restore the full signature.
assembly {
mstore(signature, signatureLength)
}
rrevert(SignatureError( rrevert(SignatureError(
SignatureErrorCodes.WALLET_ERROR, SignatureErrorCodes.WALLET_ERROR,
hash, hash,
@ -323,7 +322,6 @@ contract MixinSignatureValidator is
assembly { assembly {
mstore(signature, sub(signatureLength, 21)) mstore(signature, sub(signatureLength, 21))
} }
// Encode the call data. // Encode the call data.
bytes memory callData = abi.encodeWithSelector( bytes memory callData = abi.encodeWithSelector(
IValidator(validatorAddress).isValidSignature.selector, IValidator(validatorAddress).isValidSignature.selector,
@ -331,17 +329,16 @@ contract MixinSignatureValidator is
signerAddress, signerAddress,
signature signature
); );
// Restore the full signature.
assembly {
mstore(signature, signatureLength)
}
// 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 a single bool.
if (didSucceed && returnData.length == 32) if (didSucceed && returnData.length == 32)
return returnData.readUint256(0) != 0; return returnData.readUint256(0) != 0;
// Static call to verifier failed. // Static call to verifier failed.
// Restore the full signature.
assembly {
mstore(signature, signatureLength)
}
rrevert(SignatureError( rrevert(SignatureError(
SignatureErrorCodes.VALIDATOR_ERROR, SignatureErrorCodes.VALIDATOR_ERROR,
hash, hash,

View File

@ -74,9 +74,10 @@ contract MixinTransactions is
transactionHash, transactionHash,
signerAddress, signerAddress,
signature)) { signature)) {
rrevert(TransactionError( rrevert(TransactionSignatureError(
TransactionErrorCodes.BAD_SIGNATURE, transactionHash,
transactionHash signerAddress,
signature
)); ));
} }

View File

@ -48,8 +48,7 @@ contract MExchangeRichErrors is
enum TransactionErrorCodes { enum TransactionErrorCodes {
NO_REENTRANCY, NO_REENTRANCY,
ALREADY_EXECUTED, ALREADY_EXECUTED
BAD_SIGNATURE
} }
// solhint-disable func-name-mixedcase // solhint-disable func-name-mixedcase
@ -189,6 +188,18 @@ contract MExchangeRichErrors is
pure pure
returns (bytes memory); returns (bytes memory);
bytes4 internal constant TRANSACTION_SIGNATURE_ERROR_SELECTOR =
bytes4(keccak256("TransactionSignatureError(bytes32,address,bytes)"));
function TransactionSignatureError(
bytes32 transactionHash,
address signer,
bytes memory signature
)
internal
pure
returns (bytes memory);
bytes4 internal constant TRANSACTION_EXECUTION_ERROR_SELECTOR = bytes4 internal constant TRANSACTION_EXECUTION_ERROR_SELECTOR =
bytes4(keccak256("TransactionExecutionError(bytes32,bytes)")); bytes4(keccak256("TransactionExecutionError(bytes32,bytes)"));

View File

@ -29,6 +29,7 @@ import {
} from '@0x/types'; } from '@0x/types';
import { BigNumber, providerUtils } from '@0x/utils'; import { BigNumber, providerUtils } from '@0x/utils';
import * as chai from 'chai'; import * as chai from 'chai';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { artifacts, ExchangeContract, ExchangeWrapper, ExchangeWrapperContract, WhitelistContract } from '../src/'; import { artifacts, ExchangeContract, ExchangeWrapper, ExchangeWrapperContract, WhitelistContract } from '../src/';
@ -136,7 +137,7 @@ describe('Exchange transactions', () => {
makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address, chainId); makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address, chainId);
takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address, chainId); takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address, chainId);
}); });
describe('executeTransaction', () => { describe.only('executeTransaction', () => {
describe('fillOrder', () => { describe('fillOrder', () => {
let takerAssetFillAmount: BigNumber; let takerAssetFillAmount: BigNumber;
beforeEach(async () => { beforeEach(async () => {
@ -153,6 +154,24 @@ describe('Exchange transactions', () => {
signedTx = takerTransactionFactory.newSignedTransaction(data); signedTx = takerTransactionFactory.newSignedTransaction(data);
}); });
it('should throw if signature is invalid', async () => {
const v = ethUtil.toBuffer(signedTx.signature.slice(0, 4));
const invalidR = ethUtil.sha3('invalidR');
const invalidS = ethUtil.sha3('invalidS');
const signatureType = ethUtil.toBuffer(`0x${signedTx.signature.slice(-2)}`);
const invalidSigBuff = Buffer.concat([v, invalidR, invalidS, signatureType]);
const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`;
signedTx.signature = invalidSigHex;
const transactionHashHex = transactionHashUtils.getTransactionHashHex(signedTx);
const expectedError = new ExchangeRevertErrors.TransactionSignatureError(
transactionHashHex,
signedTx.signerAddress,
signedTx.signature,
);
const tx = exchangeWrapper.executeTransactionAsync(signedTx, senderAddress);
return expect(tx).to.revertWith(expectedError);
});
it('should throw if not called by specified sender', async () => { it('should throw if not called by specified sender', async () => {
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const transactionHashHex = transactionHashUtils.getTransactionHashHex(signedTx); const transactionHashHex = transactionHashUtils.getTransactionHashHex(signedTx);

View File

@ -28,14 +28,13 @@ export enum AssetProxyDispatchErrorCode {
export enum TransactionErrorCode { export enum TransactionErrorCode {
NoReentrancy, NoReentrancy,
AlreadyExecuted, AlreadyExecuted,
BadSignature,
} }
export class SignatureError extends RevertError { export class SignatureError extends RevertError {
constructor(error?: SignatureErrorCode, orderHash?: string, signer?: string, signature?: string) { constructor(error?: SignatureErrorCode, hash?: string, signer?: string, signature?: string) {
super('SignatureError(uint8 error, bytes32 orderHash, address signer, bytes signature)', { super('SignatureError(uint8 error, bytes32 hash, address signer, bytes signature)', {
error, error,
orderHash, hash,
signer, signer,
signature, signature,
}); });
@ -120,6 +119,16 @@ export class TransactionError extends RevertError {
} }
} }
export class TransactionSignatureError extends RevertError {
constructor(transactionHash?: string, signer?: string, signature?: string) {
super('TransactionSignatureError(bytes32 transactionHash, address signer, bytes signature)', {
transactionHash,
signer,
signature,
});
}
}
export class TransactionExecutionError extends RevertError { export class TransactionExecutionError extends RevertError {
constructor(transactionHash?: string, errorData?: string) { constructor(transactionHash?: string, errorData?: string) {
super('TransactionExecutionError(bytes32 transactionHash, bytes errorData)', { transactionHash, errorData }); super('TransactionExecutionError(bytes32 transactionHash, bytes errorData)', { transactionHash, errorData });
@ -145,6 +154,7 @@ const types = [
AssetProxyTransferError, AssetProxyTransferError,
NegativeSpreadError, NegativeSpreadError,
TransactionError, TransactionError,
TransactionSignatureError,
TransactionExecutionError, TransactionExecutionError,
IncompleteFillError, IncompleteFillError,
]; ];