732 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			732 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { ExchangeContract } from '@0x/contracts-exchange';
 | |
| import {
 | |
|     blockchainTests,
 | |
|     constants,
 | |
|     expect,
 | |
|     orderHashUtils,
 | |
|     signingUtils,
 | |
|     transactionHashUtils,
 | |
| } from '@0x/contracts-test-utils';
 | |
| import { Order, SignatureType, ZeroExTransaction } from '@0x/types';
 | |
| import { hexUtils, logUtils } from '@0x/utils';
 | |
| import * as ethUtil from 'ethereumjs-util';
 | |
| import * as _ from 'lodash';
 | |
| 
 | |
| import { artifacts } from '../artifacts';
 | |
| import { AssertionResult } from '../framework/assertions/function_assertion';
 | |
| import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
 | |
| import { DeploymentManager } from '../framework/deployment_manager';
 | |
| import { Simulation, SimulationEnvironment } from '../framework/simulation';
 | |
| import { Pseudorandom } from '../framework/utils/pseudorandom';
 | |
| import { TestSignatureValidationWalletContract } from '../wrappers';
 | |
| 
 | |
| // tslint:disable: max-classes-per-file no-non-null-assertion no-unnecessary-type-assertion
 | |
| const tests = process.env.FUZZ_TEST === 'exchange/signature_validation' ? blockchainTests : blockchainTests.skip;
 | |
| 
 | |
| tests('Exchange signature validation fuzz tests', env => {
 | |
|     const ALL_SIGNATURE_TYPES = [
 | |
|         SignatureType.Illegal,
 | |
|         SignatureType.Invalid,
 | |
|         SignatureType.EthSign,
 | |
|         SignatureType.EIP712,
 | |
|         SignatureType.Wallet,
 | |
|         SignatureType.Validator,
 | |
|         SignatureType.PreSigned,
 | |
|         SignatureType.EIP1271Wallet,
 | |
|     ];
 | |
|     const ALL_WORKING_SIGNATURE_TYPES = [
 | |
|         SignatureType.EthSign,
 | |
|         SignatureType.EIP712,
 | |
|         SignatureType.Wallet,
 | |
|         SignatureType.Validator,
 | |
|         SignatureType.PreSigned,
 | |
|         SignatureType.EIP1271Wallet,
 | |
|     ];
 | |
|     const HASH_COMPATIBLE_SIGNATURE_TYPES = [
 | |
|         SignatureType.EthSign,
 | |
|         SignatureType.EIP712,
 | |
|         SignatureType.Wallet,
 | |
|         SignatureType.PreSigned,
 | |
|     ];
 | |
|     const STATIC_SIGNATURE_TYPES = [SignatureType.EthSign, SignatureType.EIP712, SignatureType.PreSigned];
 | |
|     const ALWAYS_FAILING_SIGNATURE_TYPES = [SignatureType.Illegal, SignatureType.Invalid];
 | |
|     const WALLET_SIGNATURE_TYPES = [SignatureType.Wallet, SignatureType.EIP1271Wallet];
 | |
|     const STRICT_LENGTH_SIGNATURE_TYPES = [SignatureType.EthSign, SignatureType.EIP712];
 | |
|     const CALLBACK_SIGNATURE_TYPES = [SignatureType.Wallet, SignatureType.EIP1271Wallet, SignatureType.Validator];
 | |
| 
 | |
|     let walletContractAddress: string;
 | |
|     let notWalletContractAddress: string;
 | |
|     let deployment: DeploymentManager;
 | |
|     let exchange: ExchangeContract;
 | |
|     let accounts: string[];
 | |
|     let privateKeys: { [address: string]: Buffer };
 | |
|     let chainId: number;
 | |
| 
 | |
|     interface SignatureTestParams {
 | |
|         signatureType: SignatureType;
 | |
|         signer: string;
 | |
|         signature: string;
 | |
|         hash: string;
 | |
|         signerKey?: Buffer;
 | |
|         validator?: string;
 | |
|         payload?: string;
 | |
|         order?: Order;
 | |
|         transaction?: ZeroExTransaction;
 | |
|     }
 | |
| 
 | |
|     before(async () => {
 | |
|         chainId = await env.web3Wrapper.getChainIdAsync();
 | |
|         accounts = await env.getAccountAddressesAsync();
 | |
|         privateKeys = _.zipObject(accounts, accounts.map((a, i) => constants.TESTRPC_PRIVATE_KEYS[i]));
 | |
|         deployment = await DeploymentManager.deployAsync(env, {
 | |
|             numErc20TokensToDeploy: 0,
 | |
|             numErc721TokensToDeploy: 0,
 | |
|             numErc1155TokensToDeploy: 0,
 | |
|         });
 | |
|         exchange = deployment.exchange;
 | |
|         walletContractAddress = (await TestSignatureValidationWalletContract.deployFrom0xArtifactAsync(
 | |
|             artifacts.TestSignatureValidationWallet,
 | |
|             env.provider,
 | |
|             env.txDefaults,
 | |
|             {},
 | |
|         )).address;
 | |
|         // This just has to be a contract address that doesn't implement the
 | |
|         // wallet spec.
 | |
|         notWalletContractAddress = exchange.address;
 | |
|     });
 | |
| 
 | |
|     function randomPayload(): string {
 | |
|         return Pseudorandom.hex(Pseudorandom.integer(0, 66).toNumber());
 | |
|     }
 | |
| 
 | |
|     async function presignHashAsync(signer: string, hash: string): Promise<void> {
 | |
|         await exchange.preSign(hash).awaitTransactionSuccessAsync({ from: signer });
 | |
|     }
 | |
| 
 | |
|     async function approveValidatorAsync(signer: string, validator: string, approved: boolean = true): Promise<void> {
 | |
|         await exchange
 | |
|             .setSignatureValidatorApproval(validator, approved)
 | |
|             .awaitTransactionSuccessAsync({ from: signer });
 | |
|     }
 | |
| 
 | |
|     function createSignature(params: {
 | |
|         signatureType: SignatureType;
 | |
|         hash?: string;
 | |
|         signerKey?: Buffer;
 | |
|         validator?: string;
 | |
|         payload?: string;
 | |
|     }): string {
 | |
|         const payload = params.payload || constants.NULL_BYTES;
 | |
|         const signatureByte = hexUtils.leftPad(params.signatureType, 1);
 | |
|         switch (params.signatureType) {
 | |
|             default:
 | |
|             case SignatureType.Illegal:
 | |
|             case SignatureType.Invalid:
 | |
|             case SignatureType.PreSigned:
 | |
|                 return hexUtils.concat(payload, signatureByte);
 | |
|             case SignatureType.EIP712:
 | |
|             case SignatureType.EthSign:
 | |
|                 return hexUtils.concat(
 | |
|                     payload,
 | |
|                     ethUtil.bufferToHex(
 | |
|                         signingUtils.signMessage(
 | |
|                             ethUtil.toBuffer(params.hash),
 | |
|                             params.signerKey!,
 | |
|                             params.signatureType,
 | |
|                         ),
 | |
|                     ),
 | |
|                 );
 | |
|             case SignatureType.Wallet:
 | |
|             case SignatureType.EIP1271Wallet:
 | |
|                 return hexUtils.concat(payload, params.signatureType);
 | |
|             case SignatureType.Validator:
 | |
|                 return hexUtils.concat(payload, params.validator!, params.signatureType);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async function mangleSignatureParamsAsync(params: SignatureTestParams): Promise<SignatureTestParams> {
 | |
|         const mangled = { ...params };
 | |
|         const MANGLE_MODES = [
 | |
|             'TRUNCATE_SIGNATURE',
 | |
|             'RETYPE_SIGNATURE',
 | |
|             'RANDOM_HASH',
 | |
|             'RANDOM_ORDER',
 | |
|             'RANDOM_TRANSACTION',
 | |
|             'RANDOM_SIGNER',
 | |
|         ];
 | |
|         const invalidModes = [];
 | |
|         if (!STRICT_LENGTH_SIGNATURE_TYPES.includes(mangled.signatureType)) {
 | |
|             invalidModes.push('TRUNCATE_SIGNATURE');
 | |
|         }
 | |
|         if (CALLBACK_SIGNATURE_TYPES.includes(mangled.signatureType)) {
 | |
|             invalidModes.push('RANDOM_HASH');
 | |
|         }
 | |
|         if (params.transaction === undefined) {
 | |
|             invalidModes.push('RANDOM_TRANSACTION');
 | |
|         }
 | |
|         if (params.order === undefined) {
 | |
|             invalidModes.push('RANDOM_ORDER');
 | |
|         }
 | |
|         if (params.order !== undefined || params.hash !== undefined) {
 | |
|             invalidModes.push('RANDOM_HASH');
 | |
|         }
 | |
|         const mode = Pseudorandom.sample(_.without(MANGLE_MODES, ...invalidModes))!;
 | |
|         switch (mode) {
 | |
|             case 'TRUNCATE_SIGNATURE':
 | |
|                 while (hexUtils.slice(mangled.signature, -1) === hexUtils.leftPad(mangled.signatureType, 1)) {
 | |
|                     mangled.signature = hexUtils.slice(mangled.signature, 0, -1);
 | |
|                 }
 | |
|                 break;
 | |
|             case 'RETYPE_SIGNATURE':
 | |
|                 mangled.signatureType = WALLET_SIGNATURE_TYPES.includes(mangled.signatureType)
 | |
|                     ? Pseudorandom.sample(_.without(ALL_SIGNATURE_TYPES, ...WALLET_SIGNATURE_TYPES))!
 | |
|                     : Pseudorandom.sample(_.without(ALL_SIGNATURE_TYPES, mangled.signatureType))!;
 | |
|                 mangled.signature = hexUtils.concat(hexUtils.slice(mangled.signature, 0, -1), mangled.signatureType);
 | |
|                 break;
 | |
|             case 'RANDOM_SIGNER':
 | |
|                 mangled.signer = Pseudorandom.hex(constants.ADDRESS_LENGTH);
 | |
|                 if (mangled.order) {
 | |
|                     mangled.order.makerAddress = mangled.signer;
 | |
|                 }
 | |
|                 if (mangled.transaction) {
 | |
|                     mangled.transaction.signerAddress = mangled.signer;
 | |
|                 }
 | |
|                 break;
 | |
|             case 'RANDOM_HASH':
 | |
|                 mangled.hash = Pseudorandom.hex();
 | |
|                 break;
 | |
|             case 'RANDOM_ORDER':
 | |
|                 mangled.order = randomOrder({
 | |
|                     exchangeAddress: mangled.order!.exchangeAddress,
 | |
|                     chainId: mangled.order!.chainId,
 | |
|                 });
 | |
|                 mangled.hash = orderHashUtils.getOrderHashHex(mangled.order);
 | |
|                 break;
 | |
|             case 'RANDOM_TRANSACTION':
 | |
|                 mangled.transaction = randomTransaction({
 | |
|                     domain: mangled.transaction!.domain,
 | |
|                 });
 | |
|                 mangled.hash = await transactionHashUtils.getTransactionHashHex(mangled.transaction);
 | |
|                 break;
 | |
|             default:
 | |
|                 throw new Error(`Unhandled mangle mode: ${mode}`);
 | |
|         }
 | |
|         return mangled;
 | |
|     }
 | |
| 
 | |
|     function createHashTestParams(fields: Partial<SignatureTestParams> = {}): SignatureTestParams {
 | |
|         const signatureType =
 | |
|             fields.signatureType === undefined
 | |
|                 ? Pseudorandom.sample(HASH_COMPATIBLE_SIGNATURE_TYPES)!
 | |
|                 : fields.signatureType;
 | |
|         const signer =
 | |
|             fields.signer ||
 | |
|             (WALLET_SIGNATURE_TYPES.includes(signatureType) ? walletContractAddress : Pseudorandom.sample(accounts)!);
 | |
|         const validator =
 | |
|             fields.validator || (signatureType === SignatureType.Validator ? walletContractAddress : undefined);
 | |
|         const signerKey = fields.signerKey || privateKeys[signer];
 | |
|         const hash = fields.hash || Pseudorandom.hex();
 | |
|         const payload =
 | |
|             fields.payload ||
 | |
|             (STRICT_LENGTH_SIGNATURE_TYPES.includes(signatureType) ? constants.NULL_BYTES : randomPayload());
 | |
|         const signature = createSignature({ signatureType, hash, signerKey, payload, validator });
 | |
|         return {
 | |
|             hash,
 | |
|             payload,
 | |
|             signature,
 | |
|             signatureType,
 | |
|             signer,
 | |
|             signerKey,
 | |
|             validator,
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     async function assertValidHashSignatureAsync(params: {
 | |
|         hash: string;
 | |
|         signer: string;
 | |
|         signature: string;
 | |
|         isValid: boolean;
 | |
|     }): Promise<void> {
 | |
|         try {
 | |
|             let result;
 | |
|             try {
 | |
|                 result = await exchange.isValidHashSignature(params.hash, params.signer, params.signature).callAsync();
 | |
|             } catch (err) {
 | |
|                 if (params.isValid) {
 | |
|                     throw err;
 | |
|                 }
 | |
|                 return;
 | |
|             }
 | |
|             expect(result).to.eq(!!params.isValid);
 | |
|         } catch (err) {
 | |
|             logUtils.warn(params);
 | |
|             throw err;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async function* validTestHashSignature(): AsyncIterableIterator<void> {
 | |
|         while (true) {
 | |
|             const { hash, signature, signatureType, signer } = createHashTestParams();
 | |
|             yield (async () => {
 | |
|                 if (signatureType === SignatureType.PreSigned) {
 | |
|                     await presignHashAsync(signer, hash);
 | |
|                 }
 | |
|                 await assertValidHashSignatureAsync({
 | |
|                     hash,
 | |
|                     signer,
 | |
|                     signature,
 | |
|                     isValid: true,
 | |
|                 });
 | |
|             })();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async function* invalidTestHashStaticSignature(): AsyncIterableIterator<void> {
 | |
|         while (true) {
 | |
|             const randomSignerKey = ethUtil.toBuffer(Pseudorandom.hex());
 | |
|             const signer = Pseudorandom.sample([notWalletContractAddress, walletContractAddress, ...accounts])!;
 | |
|             const { hash, signature } = createHashTestParams({
 | |
|                 signatureType: Pseudorandom.sample([...STATIC_SIGNATURE_TYPES, ...ALWAYS_FAILING_SIGNATURE_TYPES])!,
 | |
|                 signer,
 | |
|                 // Always sign with a random key.
 | |
|                 signerKey: randomSignerKey,
 | |
|             });
 | |
|             yield assertValidHashSignatureAsync({
 | |
|                 hash,
 | |
|                 signer,
 | |
|                 signature,
 | |
|                 isValid: false,
 | |
|             });
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async function* invalidTestHashWalletSignature(): AsyncIterableIterator<void> {
 | |
|         while (true) {
 | |
|             const signer = Pseudorandom.sample([notWalletContractAddress, ...accounts])!;
 | |
|             const { hash, signature } = createHashTestParams({
 | |
|                 signatureType: SignatureType.Wallet,
 | |
|                 signer,
 | |
|             });
 | |
|             yield assertValidHashSignatureAsync({
 | |
|                 hash,
 | |
|                 signer,
 | |
|                 signature,
 | |
|                 isValid: false,
 | |
|             });
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async function* invalidTestHashValidatorSignature(): AsyncIterableIterator<void> {
 | |
|         while (true) {
 | |
|             const isNotApproved = Pseudorandom.sample([true, false])!;
 | |
|             const signer = Pseudorandom.sample([...accounts])!;
 | |
|             const validator = isNotApproved
 | |
|                 ? walletContractAddress
 | |
|                 : Pseudorandom.sample([
 | |
|                       // All validator signatures are invalid for the hash test, so passing a valid
 | |
|                       // wallet contract should still fail.
 | |
|                       walletContractAddress,
 | |
|                       notWalletContractAddress,
 | |
|                       ...accounts,
 | |
|                   ])!;
 | |
|             const { hash, signature } = createHashTestParams({
 | |
|                 signatureType: SignatureType.Validator,
 | |
|                 validator,
 | |
|             });
 | |
|             yield (async () => {
 | |
|                 if (!isNotApproved) {
 | |
|                     await approveValidatorAsync(signer, validator);
 | |
|                 }
 | |
|                 await assertValidHashSignatureAsync({
 | |
|                     hash,
 | |
|                     signer,
 | |
|                     signature,
 | |
|                     isValid: false,
 | |
|                 });
 | |
|             })();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async function* invalidTestHashMangledSignature(): AsyncIterableIterator<void> {
 | |
|         while (true) {
 | |
|             const params = createHashTestParams({ signatureType: Pseudorandom.sample(ALL_SIGNATURE_TYPES)! });
 | |
|             const mangled = await mangleSignatureParamsAsync(params);
 | |
|             yield (async () => {
 | |
|                 await assertValidHashSignatureAsync({
 | |
|                     hash: mangled.hash,
 | |
|                     signer: mangled.signer,
 | |
|                     signature: mangled.signature,
 | |
|                     isValid: false,
 | |
|                 });
 | |
|             })();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     function randomOrder(fields: Partial<Order> = {}): Order {
 | |
|         return {
 | |
|             chainId,
 | |
|             exchangeAddress: exchange.address,
 | |
|             expirationTimeSeconds: Pseudorandom.integer(1, 2 ** 32),
 | |
|             salt: Pseudorandom.integer(0, constants.MAX_UINT256),
 | |
|             makerAssetData: Pseudorandom.hex(36),
 | |
|             takerAssetData: Pseudorandom.hex(36),
 | |
|             makerFeeAssetData: Pseudorandom.hex(36),
 | |
|             takerFeeAssetData: Pseudorandom.hex(36),
 | |
|             makerAssetAmount: Pseudorandom.integer(0, 100e18),
 | |
|             takerAssetAmount: Pseudorandom.integer(0, 100e18),
 | |
|             makerFee: Pseudorandom.integer(0, 100e18),
 | |
|             takerFee: Pseudorandom.integer(0, 100e18),
 | |
|             feeRecipientAddress: Pseudorandom.hex(constants.ADDRESS_LENGTH),
 | |
|             makerAddress: Pseudorandom.hex(constants.ADDRESS_LENGTH),
 | |
|             takerAddress: Pseudorandom.hex(constants.ADDRESS_LENGTH),
 | |
|             senderAddress: Pseudorandom.hex(constants.ADDRESS_LENGTH),
 | |
|             ...fields,
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     async function createOrderTestParamsAsync(
 | |
|         fields: Partial<SignatureTestParams> = {},
 | |
|     ): Promise<SignatureTestParams & { order: Order }> {
 | |
|         const signatureType =
 | |
|             fields.signatureType === undefined
 | |
|                 ? Pseudorandom.sample(ALL_WORKING_SIGNATURE_TYPES)!
 | |
|                 : fields.signatureType;
 | |
|         const signer =
 | |
|             fields.signer ||
 | |
|             (WALLET_SIGNATURE_TYPES.includes(signatureType) ? walletContractAddress : Pseudorandom.sample(accounts)!);
 | |
|         const validator =
 | |
|             fields.validator || (signatureType === SignatureType.Validator ? walletContractAddress : undefined);
 | |
|         const signerKey = fields.signerKey || privateKeys[signer];
 | |
|         const order = fields.order || randomOrder({ makerAddress: signer });
 | |
|         const hash = fields.hash || orderHashUtils.getOrderHashHex(order);
 | |
|         const payload =
 | |
|             fields.payload ||
 | |
|             (STRICT_LENGTH_SIGNATURE_TYPES.includes(signatureType) ? constants.NULL_BYTES : randomPayload());
 | |
|         const signature = createSignature({ signatureType, hash, signerKey, payload, validator });
 | |
|         return {
 | |
|             hash,
 | |
|             order,
 | |
|             payload,
 | |
|             signature,
 | |
|             signatureType,
 | |
|             signer,
 | |
|             signerKey,
 | |
|             validator,
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     async function assertValidOrderSignatureAsync(params: {
 | |
|         order: Order;
 | |
|         signature: string;
 | |
|         isValid: boolean;
 | |
|     }): Promise<void> {
 | |
|         try {
 | |
|             let result;
 | |
|             try {
 | |
|                 result = await exchange.isValidOrderSignature(params.order, params.signature).callAsync();
 | |
|             } catch (err) {
 | |
|                 if (params.isValid) {
 | |
|                     throw err;
 | |
|                 }
 | |
|                 return;
 | |
|             }
 | |
|             expect(result).to.eq(!!params.isValid);
 | |
|         } catch (err) {
 | |
|             logUtils.warn(params);
 | |
|             throw err;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async function* validTestOrderSignature(): AsyncIterableIterator<void> {
 | |
|         while (true) {
 | |
|             const { hash, order, signature, signatureType, signer, validator } = await createOrderTestParamsAsync();
 | |
|             yield (async () => {
 | |
|                 if (signatureType === SignatureType.PreSigned) {
 | |
|                     await presignHashAsync(signer, hash);
 | |
|                 } else if (signatureType === SignatureType.Validator) {
 | |
|                     await approveValidatorAsync(signer, validator!);
 | |
|                 }
 | |
|                 await assertValidOrderSignatureAsync({
 | |
|                     order,
 | |
|                     signature,
 | |
|                     isValid: true,
 | |
|                 });
 | |
|             })();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async function* invalidTestOrderStaticSignature(): AsyncIterableIterator<void> {
 | |
|         while (true) {
 | |
|             const randomSignerKey = ethUtil.toBuffer(Pseudorandom.hex());
 | |
|             const signer = Pseudorandom.sample([notWalletContractAddress, walletContractAddress, ...accounts])!;
 | |
|             const { order, signature } = await createOrderTestParamsAsync({
 | |
|                 signatureType: Pseudorandom.sample([...STATIC_SIGNATURE_TYPES, ...ALWAYS_FAILING_SIGNATURE_TYPES])!,
 | |
|                 signer,
 | |
|                 // Always sign with a random key.
 | |
|                 signerKey: randomSignerKey,
 | |
|             });
 | |
|             yield assertValidOrderSignatureAsync({
 | |
|                 order,
 | |
|                 signature,
 | |
|                 isValid: false,
 | |
|             });
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async function* invalidTestOrderWalletSignature(): AsyncIterableIterator<void> {
 | |
|         while (true) {
 | |
|             const signer = Pseudorandom.sample([notWalletContractAddress, ...accounts])!;
 | |
|             const { order, signature } = await createOrderTestParamsAsync({
 | |
|                 signatureType: Pseudorandom.sample(WALLET_SIGNATURE_TYPES)!,
 | |
|                 signer,
 | |
|             });
 | |
|             yield assertValidOrderSignatureAsync({
 | |
|                 order,
 | |
|                 signature,
 | |
|                 isValid: false,
 | |
|             });
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async function* invalidTestOrderValidatorSignature(): AsyncIterableIterator<void> {
 | |
|         while (true) {
 | |
|             const isNotApproved = Pseudorandom.sample([true, false])!;
 | |
|             const signer = Pseudorandom.sample([...accounts])!;
 | |
|             const validator = isNotApproved
 | |
|                 ? walletContractAddress
 | |
|                 : Pseudorandom.sample([notWalletContractAddress, ...accounts])!;
 | |
|             const { order, signature } = await createOrderTestParamsAsync({
 | |
|                 signatureType: SignatureType.Validator,
 | |
|                 validator,
 | |
|             });
 | |
|             yield (async () => {
 | |
|                 if (!isNotApproved) {
 | |
|                     await approveValidatorAsync(signer, validator);
 | |
|                 }
 | |
|                 await assertValidOrderSignatureAsync({
 | |
|                     order,
 | |
|                     signature,
 | |
|                     isValid: false,
 | |
|                 });
 | |
|             })();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async function* invalidTestOrderMangledSignature(): AsyncIterableIterator<void> {
 | |
|         while (true) {
 | |
|             const params = await createOrderTestParamsAsync({
 | |
|                 signatureType: Pseudorandom.sample(ALL_SIGNATURE_TYPES)!,
 | |
|             });
 | |
|             const mangled = await mangleSignatureParamsAsync(params);
 | |
|             yield (async () => {
 | |
|                 await assertValidOrderSignatureAsync({
 | |
|                     order: mangled.order!,
 | |
|                     signature: mangled.signature,
 | |
|                     isValid: false,
 | |
|                 });
 | |
|             })();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     function randomTransaction(fields: Partial<ZeroExTransaction> = {}): ZeroExTransaction {
 | |
|         return {
 | |
|             domain: {
 | |
|                 chainId,
 | |
|                 verifyingContract: exchange.address,
 | |
|                 name: '0x Protocol',
 | |
|                 version: '3.0.0',
 | |
|             },
 | |
|             gasPrice: Pseudorandom.integer(1e9, 100e9),
 | |
|             expirationTimeSeconds: Pseudorandom.integer(1, 2 ** 32),
 | |
|             salt: Pseudorandom.integer(0, constants.MAX_UINT256),
 | |
|             signerAddress: Pseudorandom.hex(constants.ADDRESS_LENGTH),
 | |
|             data: Pseudorandom.hex(Pseudorandom.integer(4, 128).toNumber()),
 | |
|             ...fields,
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     async function createTransactionTestParamsAsync(
 | |
|         fields: Partial<SignatureTestParams> = {},
 | |
|     ): Promise<SignatureTestParams & { transaction: ZeroExTransaction }> {
 | |
|         const signatureType =
 | |
|             fields.signatureType === undefined
 | |
|                 ? Pseudorandom.sample(ALL_WORKING_SIGNATURE_TYPES)!
 | |
|                 : fields.signatureType;
 | |
|         const signer =
 | |
|             fields.signer ||
 | |
|             (WALLET_SIGNATURE_TYPES.includes(signatureType) ? walletContractAddress : Pseudorandom.sample(accounts)!);
 | |
|         const validator =
 | |
|             fields.validator || (signatureType === SignatureType.Validator ? walletContractAddress : undefined);
 | |
|         const signerKey = fields.signerKey || privateKeys[signer];
 | |
|         const transaction = fields.transaction || randomTransaction({ signerAddress: signer });
 | |
|         const hash = fields.hash || transactionHashUtils.getTransactionHashHex(transaction);
 | |
|         const payload =
 | |
|             fields.payload ||
 | |
|             (STRICT_LENGTH_SIGNATURE_TYPES.includes(signatureType) ? constants.NULL_BYTES : randomPayload());
 | |
|         const signature = createSignature({ signatureType, hash, signerKey, payload, validator });
 | |
|         return {
 | |
|             hash,
 | |
|             transaction,
 | |
|             payload,
 | |
|             signature,
 | |
|             signatureType,
 | |
|             signer,
 | |
|             signerKey,
 | |
|             validator,
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     async function assertValidTransactionSignatureAsync(params: {
 | |
|         transaction: ZeroExTransaction;
 | |
|         signature: string;
 | |
|         isValid: boolean;
 | |
|     }): Promise<void> {
 | |
|         try {
 | |
|             let result;
 | |
|             try {
 | |
|                 result = await exchange.isValidTransactionSignature(params.transaction, params.signature).callAsync();
 | |
|             } catch (err) {
 | |
|                 if (params.isValid) {
 | |
|                     throw err;
 | |
|                 }
 | |
|                 return;
 | |
|             }
 | |
|             expect(result).to.eq(!!params.isValid);
 | |
|         } catch (err) {
 | |
|             logUtils.warn(params);
 | |
|             throw err;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async function* validTestTransactionSignature(): AsyncIterableIterator<void> {
 | |
|         while (true) {
 | |
|             const {
 | |
|                 hash,
 | |
|                 transaction,
 | |
|                 signature,
 | |
|                 signatureType,
 | |
|                 signer,
 | |
|                 validator,
 | |
|             } = await createTransactionTestParamsAsync();
 | |
|             yield (async () => {
 | |
|                 if (signatureType === SignatureType.PreSigned) {
 | |
|                     await presignHashAsync(signer, hash);
 | |
|                 } else if (signatureType === SignatureType.Validator) {
 | |
|                     await approveValidatorAsync(signer, validator!);
 | |
|                 }
 | |
|                 await assertValidTransactionSignatureAsync({
 | |
|                     transaction,
 | |
|                     signature,
 | |
|                     isValid: true,
 | |
|                 });
 | |
|             })();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async function* invalidTestTransactionStaticSignature(): AsyncIterableIterator<void> {
 | |
|         while (true) {
 | |
|             const randomSignerKey = ethUtil.toBuffer(Pseudorandom.hex());
 | |
|             const signer = Pseudorandom.sample([notWalletContractAddress, walletContractAddress, ...accounts])!;
 | |
|             const { transaction, signature } = await createTransactionTestParamsAsync({
 | |
|                 signatureType: Pseudorandom.sample([...STATIC_SIGNATURE_TYPES, ...ALWAYS_FAILING_SIGNATURE_TYPES])!,
 | |
|                 signer,
 | |
|                 // Always sign with a random key.
 | |
|                 signerKey: randomSignerKey,
 | |
|             });
 | |
|             yield assertValidTransactionSignatureAsync({
 | |
|                 transaction,
 | |
|                 signature,
 | |
|                 isValid: false,
 | |
|             });
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async function* invalidTestTransactionWalletSignature(): AsyncIterableIterator<void> {
 | |
|         while (true) {
 | |
|             const signer = Pseudorandom.sample([notWalletContractAddress, ...accounts])!;
 | |
|             const { transaction, signature } = await createTransactionTestParamsAsync({
 | |
|                 signatureType: Pseudorandom.sample(WALLET_SIGNATURE_TYPES)!,
 | |
|                 signer,
 | |
|             });
 | |
|             yield assertValidTransactionSignatureAsync({
 | |
|                 transaction,
 | |
|                 signature,
 | |
|                 isValid: false,
 | |
|             });
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async function* invalidTestTransactionValidatorSignature(): AsyncIterableIterator<void> {
 | |
|         while (true) {
 | |
|             const isNotApproved = Pseudorandom.sample([true, false])!;
 | |
|             const signer = Pseudorandom.sample([...accounts])!;
 | |
|             const validator = isNotApproved
 | |
|                 ? walletContractAddress
 | |
|                 : Pseudorandom.sample([notWalletContractAddress, ...accounts])!;
 | |
|             const { transaction, signature } = await createTransactionTestParamsAsync({
 | |
|                 signatureType: SignatureType.Validator,
 | |
|                 validator,
 | |
|             });
 | |
|             yield (async () => {
 | |
|                 if (!isNotApproved) {
 | |
|                     await approveValidatorAsync(signer, validator);
 | |
|                 }
 | |
|                 await assertValidTransactionSignatureAsync({
 | |
|                     transaction,
 | |
|                     signature,
 | |
|                     isValid: false,
 | |
|                 });
 | |
|             })();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     async function* invalidTestTransactionMangledSignature(): AsyncIterableIterator<void> {
 | |
|         while (true) {
 | |
|             const params = await createTransactionTestParamsAsync({
 | |
|                 signatureType: Pseudorandom.sample(ALL_SIGNATURE_TYPES)!,
 | |
|             });
 | |
|             const mangled = await mangleSignatureParamsAsync(params);
 | |
|             yield (async () => {
 | |
|                 await assertValidTransactionSignatureAsync({
 | |
|                     transaction: mangled.transaction!,
 | |
|                     signature: mangled.signature,
 | |
|                     isValid: false,
 | |
|                 });
 | |
|             })();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     it('fuzz', async () => {
 | |
|         const FUZZ_ACTIONS = [
 | |
|             validTestHashSignature(),
 | |
|             invalidTestHashStaticSignature(),
 | |
|             invalidTestHashWalletSignature(),
 | |
|             invalidTestHashValidatorSignature(),
 | |
|             invalidTestHashMangledSignature(),
 | |
|             validTestOrderSignature(),
 | |
|             invalidTestOrderStaticSignature(),
 | |
|             invalidTestOrderWalletSignature(),
 | |
|             invalidTestOrderValidatorSignature(),
 | |
|             invalidTestOrderMangledSignature(),
 | |
|             validTestTransactionSignature(),
 | |
|             invalidTestTransactionStaticSignature(),
 | |
|             invalidTestTransactionWalletSignature(),
 | |
|             invalidTestTransactionValidatorSignature(),
 | |
|             invalidTestTransactionMangledSignature(),
 | |
|         ];
 | |
|         const simulationEnvironment = new SimulationEnvironment(deployment, new BlockchainBalanceStore({}, {}), []);
 | |
|         const simulation = new class extends Simulation {
 | |
|             // tslint:disable-next-line: prefer-function-over-method
 | |
|             protected async *_assertionGenerator(): AsyncIterableIterator<AssertionResult | void> {
 | |
|                 while (true) {
 | |
|                     const action = Pseudorandom.sample(FUZZ_ACTIONS)!;
 | |
|                     yield (await action!.next()).value;
 | |
|                 }
 | |
|             }
 | |
|         }(simulationEnvironment);
 | |
|         simulation.resets = true;
 | |
|         return simulation.fuzzAsync();
 | |
|     });
 | |
| });
 | |
| // tslint:disable-next-line max-file-line-count
 |