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(
bytes32 transactionHash,
bytes memory errorData

View File

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

View File

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

View File

@ -48,8 +48,7 @@ contract MExchangeRichErrors is
enum TransactionErrorCodes {
NO_REENTRANCY,
ALREADY_EXECUTED,
BAD_SIGNATURE
ALREADY_EXECUTED
}
// solhint-disable func-name-mixedcase
@ -189,6 +188,18 @@ contract MExchangeRichErrors is
pure
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(keccak256("TransactionExecutionError(bytes32,bytes)"));

View File

@ -29,6 +29,7 @@ import {
} from '@0x/types';
import { BigNumber, providerUtils } from '@0x/utils';
import * as chai from 'chai';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
import { artifacts, ExchangeContract, ExchangeWrapper, ExchangeWrapperContract, WhitelistContract } from '../src/';
@ -136,7 +137,7 @@ describe('Exchange transactions', () => {
makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address, chainId);
takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address, chainId);
});
describe('executeTransaction', () => {
describe.only('executeTransaction', () => {
describe('fillOrder', () => {
let takerAssetFillAmount: BigNumber;
beforeEach(async () => {
@ -153,6 +154,24 @@ describe('Exchange transactions', () => {
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 () => {
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
const transactionHashHex = transactionHashUtils.getTransactionHashHex(signedTx);

View File

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