Implemented a hacky version of the fillOrder fuzz tests

This commit is contained in:
Alex Towle 2019-11-22 17:45:24 -06:00 committed by Michael Zhu
parent 1e44a9c942
commit 36df5dc721
13 changed files with 205 additions and 120 deletions

View File

@ -1,6 +1,9 @@
import { constants, getRandomInteger, hexRandom } from '@0x/contracts-test-utils';
import { Order } from '@0x/types';
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types'; import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { validFillOrderCompleteFillAssertion } from '../assertions/fillOrder';
import { AssertionResult } from '../assertions/function_assertion'; import { AssertionResult } from '../assertions/function_assertion';
import { validJoinStakingPoolAssertion } from '../assertions/joinStakingPool'; import { validJoinStakingPoolAssertion } from '../assertions/joinStakingPool';
@ -33,6 +36,7 @@ export function PoolMemberMixin<TBase extends Constructor>(Base: TBase): TBase &
this.actor.simulationActions = { this.actor.simulationActions = {
...this.actor.simulationActions, ...this.actor.simulationActions,
validJoinStakingPool: this._validJoinStakingPool(), validJoinStakingPool: this._validJoinStakingPool(),
validFillOrderCompleteFill: this._validFillOrderCompleteFill(),
}; };
} }
@ -46,7 +50,6 @@ export function PoolMemberMixin<TBase extends Constructor>(Base: TBase): TBase &
.awaitTransactionSuccessAsync({ from: this.actor.address }); .awaitTransactionSuccessAsync({ from: this.actor.address });
} }
// FIXME(jalextowle): I need to make sure that this is being sent from the actor's address
private async *_validJoinStakingPool(): AsyncIterableIterator<AssertionResult | void> { private async *_validJoinStakingPool(): AsyncIterableIterator<AssertionResult | void> {
const { stakingPools } = this.actor.simulationEnvironment!; const { stakingPools } = this.actor.simulationEnvironment!;
const assertion = validJoinStakingPoolAssertion(this.actor.deployment); const assertion = validJoinStakingPoolAssertion(this.actor.deployment);
@ -55,8 +58,39 @@ export function PoolMemberMixin<TBase extends Constructor>(Base: TBase): TBase &
if (poolId === undefined) { if (poolId === undefined) {
yield undefined; yield undefined;
} else { } else {
console.log('Attempting to join pool'); yield assertion.executeAsync({ args: [poolId], txData: { from: this.actor.address } });
yield assertion.executeAsync({ args: [poolId], txData: {} }); }
}
}
private async *_validFillOrderCompleteFill(): AsyncIterableIterator<AssertionResult | void> {
const { marketMakers } = this.actor.simulationEnvironment!;
const assertion = validFillOrderCompleteFillAssertion(this.actor.deployment);
while (true) {
const maker = _.sample(marketMakers);
if (maker === undefined) {
yield undefined;
} else {
// Configure the maker's token balances so that the order will definitely be fillable.
await Promise.all([
...this.actor.deployment.tokens.erc20.map(async token => maker.configureERC20TokenAsync(token)),
...this.actor.deployment.tokens.erc20.map(async token =>
this.actor.configureERC20TokenAsync(token),
),
this.actor.configureERC20TokenAsync(
this.actor.deployment.tokens.weth,
this.actor.deployment.staking.stakingProxy.address,
),
]);
const order = await maker.signOrderAsync({
makerAssetAmount: getRandomInteger(constants.ZERO_AMOUNT, constants.INITIAL_ERC20_BALANCE),
takerAssetAmount: getRandomInteger(constants.ZERO_AMOUNT, constants.INITIAL_ERC20_BALANCE),
});
yield assertion.executeAsync({
args: [order, order.takerAssetAmount, order.signature],
txData: { from: this.actor.address },
});
} }
} }
} }

View File

@ -80,16 +80,11 @@ export function PoolOperatorMixin<TBase extends Constructor>(Base: TBase): TBase
} }
private async *_validCreateStakingPool(): AsyncIterableIterator<AssertionResult> { private async *_validCreateStakingPool(): AsyncIterableIterator<AssertionResult> {
console.log(10);
const { stakingPools } = this.actor.simulationEnvironment!; const { stakingPools } = this.actor.simulationEnvironment!;
console.log(11);
const assertion = validCreateStakingPoolAssertion(this.actor.deployment, stakingPools); const assertion = validCreateStakingPoolAssertion(this.actor.deployment, stakingPools);
console.log(12);
while (true) { while (true) {
const operatorShare = getRandomInteger(0, constants.PPM).toNumber(); const operatorShare = getRandomInteger(0, constants.PPM).toNumber();
console.log(13);
yield assertion.executeAsync({ args: [operatorShare, false], txData: { from: this.actor.address } }); yield assertion.executeAsync({ args: [operatorShare, false], txData: { from: this.actor.address } });
console.log(14);
} }
} }

View File

@ -19,7 +19,9 @@ export function validCreateStakingPoolAssertion(
): FunctionAssertion<[number, boolean], string, string> { ): FunctionAssertion<[number, boolean], string, string> {
const { stakingWrapper } = deployment.staking; const { stakingWrapper } = deployment.staking;
return new FunctionAssertion<[number, boolean], string, string>(stakingWrapper.createStakingPool, { return new FunctionAssertion<[number, boolean], string, string>(
stakingWrapper.createStakingPool.bind(stakingWrapper),
{
// Returns the expected ID of th created pool // Returns the expected ID of th created pool
before: async () => { before: async () => {
const lastPoolId = await stakingWrapper.lastPoolId().callAsync(); const lastPoolId = await stakingWrapper.lastPoolId().callAsync();
@ -37,18 +39,12 @@ export function validCreateStakingPoolAssertion(
txData: Partial<TxData>; txData: Partial<TxData>;
}, },
) => { ) => {
console.log(100);
logUtils.log(`createStakingPool(${args.args[0]}, ${args.args[1]}) => ${expectedPoolId}`); logUtils.log(`createStakingPool(${args.args[0]}, ${args.args[1]}) => ${expectedPoolId}`);
console.log(101);
// Checks the logs for the new poolId, verifies that it is as expected // Checks the logs for the new poolId, verifies that it is as expected
console.log(result.receipt);
const log = result.receipt!.logs[0]; // tslint:disable-line:no-non-null-assertion const log = result.receipt!.logs[0]; // tslint:disable-line:no-non-null-assertion
console.log(102);
const actualPoolId = (log as any).args.poolId; const actualPoolId = (log as any).args.poolId;
console.log(103);
expect(actualPoolId).to.equal(expectedPoolId); expect(actualPoolId).to.equal(expectedPoolId);
console.log(104);
// Adds the new pool to local state // Adds the new pool to local state
pools[actualPoolId] = { pools[actualPoolId] = {
@ -56,7 +52,7 @@ export function validCreateStakingPoolAssertion(
operatorShare: args.args[0], operatorShare: args.args[0],
delegatedStake: new StoredBalance(), delegatedStake: new StoredBalance(),
}; };
console.log(105);
}, },
}); },
);
} }

View File

@ -16,7 +16,9 @@ export function validDecreaseStakingPoolOperatorShareAssertion(
): FunctionAssertion<[string, number], {}, void> { ): FunctionAssertion<[string, number], {}, void> {
const { stakingWrapper } = deployment.staking; const { stakingWrapper } = deployment.staking;
return new FunctionAssertion<[string, number], {}, void>(stakingWrapper.decreaseStakingPoolOperatorShare, { return new FunctionAssertion<[string, number], {}, void>(
stakingWrapper.decreaseStakingPoolOperatorShare.bind(stakingWrapper),
{
after: async (_beforeInfo, _result: FunctionResult, args: { args: [string, number] }) => { after: async (_beforeInfo, _result: FunctionResult, args: { args: [string, number] }) => {
const poolId = args.args[0]; const poolId = args.args[0];
const expectedOperatorShare = args.args[1]; const expectedOperatorShare = args.args[1];
@ -29,5 +31,6 @@ export function validDecreaseStakingPoolOperatorShareAssertion(
// Updates the pool in local state. // Updates the pool in local state.
pools[poolId].operatorShare = operatorShare; pools[poolId].operatorShare = operatorShare;
}, },
}); },
);
} }

View File

@ -0,0 +1,22 @@
import { constants, expect, filterLogsToArguments } from '@0x/contracts-test-utils';
import { FillResults, Order } from '@0x/types';
import { BigNumber, logUtils } from '@0x/utils';
import * as _ from 'lodash';
import { Maker } from '../actors/maker';
import { DeploymentManager } from '../deployment_manager';
import { FunctionArguments, FunctionAssertion, FunctionResult } from './function_assertion';
export function validFillOrderCompleteFillAssertion(
deployment: DeploymentManager,
): FunctionAssertion<[Order, BigNumber, string], {}, FillResults> {
const exchange = deployment.exchange;
return new FunctionAssertion<[Order, BigNumber, string], {}, FillResults>(exchange.fillOrder.bind(exchange), {
after: async (_beforeInfo, result: FunctionResult, args: FunctionArguments<[Order, BigNumber, string]>) => {
expect(result.success).to.be.true();
logUtils.log(`Order filled by ${args.txData.from}`);
},
});
}

View File

@ -3,7 +3,6 @@ import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types';
import * as _ from 'lodash'; import * as _ from 'lodash';
// tslint:disable:max-classes-per-file // tslint:disable:max-classes-per-file
export type GenericContractFunction<T> = (...args: any[]) => ContractFunctionObj<T>; export type GenericContractFunction<T> = (...args: any[]) => ContractFunctionObj<T>;
export interface FunctionArguments<TArgs extends any[]> { export interface FunctionArguments<TArgs extends any[]> {
@ -58,12 +57,12 @@ export class FunctionAssertion<TArgs extends any[], TBefore, ReturnDataType> imp
// The wrapper function that will be wrapped in assertions. // The wrapper function that will be wrapped in assertions.
public wrapperFunction: ( public wrapperFunction: (
...args: any[] // tslint:disable-line:trailing-comma ...args: TArgs // tslint:disable-line:trailing-comma
) => ContractTxFunctionObj<ReturnDataType> | ContractFunctionObj<ReturnDataType>; ) => ContractTxFunctionObj<ReturnDataType> | ContractFunctionObj<ReturnDataType>;
constructor( constructor(
wrapperFunction: ( wrapperFunction: (
...args: any[] // tslint:disable-line:trailing-comma ...args: TArgs // tslint:disable-line:trailing-comma
) => ContractTxFunctionObj<ReturnDataType> | ContractFunctionObj<ReturnDataType>, ) => ContractTxFunctionObj<ReturnDataType> | ContractFunctionObj<ReturnDataType>,
condition: Partial<Condition<TArgs, TBefore>> = {}, condition: Partial<Condition<TArgs, TBefore>> = {},
) { ) {
@ -95,18 +94,12 @@ export class FunctionAssertion<TArgs extends any[], TBefore, ReturnDataType> imp
try { try {
const functionWithArgs = this.wrapperFunction(...args.args) as ContractTxFunctionObj<ReturnDataType>; const functionWithArgs = this.wrapperFunction(...args.args) as ContractTxFunctionObj<ReturnDataType>;
callResult.data = await functionWithArgs.callAsync(args.txData); callResult.data = await functionWithArgs.callAsync(args.txData);
console.log(functionWithArgs);
callResult.receipt = callResult.receipt =
functionWithArgs.awaitTransactionSuccessAsync !== undefined functionWithArgs.awaitTransactionSuccessAsync !== undefined
? await functionWithArgs.awaitTransactionSuccessAsync(args.txData) // tslint:disable-line:await-promise ? await functionWithArgs.awaitTransactionSuccessAsync(args.txData) // tslint:disable-line:await-promise
: undefined; : undefined;
// tslint:enable:await-promise // tslint:enable:await-promise
} catch (error) { } catch (error) {
console.log('got here');
console.log(error);
callResult.data = error; callResult.data = error;
callResult.success = false; callResult.success = false;
callResult.receipt = undefined; callResult.receipt = undefined;

View File

@ -7,7 +7,9 @@ import { DeploymentManager } from '../deployment_manager';
import { FunctionArguments, FunctionAssertion, FunctionResult } from './function_assertion'; import { FunctionArguments, FunctionAssertion, FunctionResult } from './function_assertion';
export function validJoinStakingPoolAssertion(deployment: DeploymentManager): FunctionAssertion<[string], {}, void> { export function validJoinStakingPoolAssertion(deployment: DeploymentManager): FunctionAssertion<[string], {}, void> {
return new FunctionAssertion<[string], {}, void>(deployment.staking.stakingWrapper.joinStakingPoolAsMaker, { const { stakingWrapper } = deployment.staking;
return new FunctionAssertion<[string], {}, void>(stakingWrapper.joinStakingPoolAsMaker.bind(stakingWrapper), {
after: async (_beforeInfo, _result: FunctionResult, args: FunctionArguments<[string]>) => { after: async (_beforeInfo, _result: FunctionResult, args: FunctionArguments<[string]>) => {
const poolId = args.args[0]; const poolId = args.args[0];
@ -32,11 +34,11 @@ export function validJoinStakingPoolAssertion(deployment: DeploymentManager): Fu
); );
expect(logArgs).to.be.deep.eq([ expect(logArgs).to.be.deep.eq([
{ {
maker: args.txData.from, makerAddress: args.txData.from,
poolId, poolId,
}, },
]); ]);
const joinedPoolId = deployment.staking.stakingWrapper.poolIdByMaker(args.txData.from); const joinedPoolId = await deployment.staking.stakingWrapper.poolIdByMaker(args.txData.from).callAsync();
expect(joinedPoolId).to.be.eq(poolId); expect(joinedPoolId).to.be.eq(poolId);
console.log(`Pool ${poolId} joined by ${args.txData.from}`); /* tslint:disable-line:no-console */ console.log(`Pool ${poolId} joined by ${args.txData.from}`); /* tslint:disable-line:no-console */

View File

@ -85,7 +85,9 @@ export function validMoveStakeAssertion(
): FunctionAssertion<[StakeInfo, StakeInfo, BigNumber], {}, void> { ): FunctionAssertion<[StakeInfo, StakeInfo, BigNumber], {}, void> {
const { stakingWrapper } = deployment.staking; const { stakingWrapper } = deployment.staking;
return new FunctionAssertion<[StakeInfo, StakeInfo, BigNumber], {}, void>(stakingWrapper.moveStake, { return new FunctionAssertion<[StakeInfo, StakeInfo, BigNumber], {}, void>(
stakingWrapper.moveStake.bind(stakingWrapper),
{
after: async ( after: async (
_beforeInfo: {}, _beforeInfo: {},
_result: FunctionResult, _result: FunctionResult,
@ -120,7 +122,9 @@ export function validMoveStakeAssertion(
const globalUndelegatedStake = await stakingWrapper const globalUndelegatedStake = await stakingWrapper
.getGlobalStakeByStatus(StakeStatus.Undelegated) .getGlobalStakeByStatus(StakeStatus.Undelegated)
.callAsync(); .callAsync();
const globalDelegatedStake = await stakingWrapper.getGlobalStakeByStatus(StakeStatus.Delegated).callAsync(); const globalDelegatedStake = await stakingWrapper
.getGlobalStakeByStatus(StakeStatus.Delegated)
.callAsync();
expect(globalUndelegatedStake).to.deep.equal(globalStake[StakeStatus.Undelegated]); expect(globalUndelegatedStake).to.deep.equal(globalStake[StakeStatus.Undelegated]);
expect(globalDelegatedStake).to.deep.equal(globalStake[StakeStatus.Delegated]); expect(globalDelegatedStake).to.deep.equal(globalStake[StakeStatus.Delegated]);
@ -134,5 +138,6 @@ export function validMoveStakeAssertion(
expect(totalStakeDelegated).to.deep.equal(pools[poolId].delegatedStake); expect(totalStakeDelegated).to.deep.equal(pools[poolId].delegatedStake);
} }
}, },
}); },
);
} }

View File

@ -33,7 +33,7 @@ export function validStakeAssertion(
): FunctionAssertion<[BigNumber], LocalBalanceStore, void> { ): FunctionAssertion<[BigNumber], LocalBalanceStore, void> {
const { stakingWrapper, zrxVault } = deployment.staking; const { stakingWrapper, zrxVault } = deployment.staking;
return new FunctionAssertion(stakingWrapper.stake, { return new FunctionAssertion(stakingWrapper.stake.bind(stakingWrapper), {
before: async (args: FunctionArguments<[BigNumber]>) => { before: async (args: FunctionArguments<[BigNumber]>) => {
const [amount] = args.args; const [amount] = args.args;

View File

@ -33,7 +33,7 @@ export function validUnstakeAssertion(
): FunctionAssertion<[BigNumber], LocalBalanceStore, void> { ): FunctionAssertion<[BigNumber], LocalBalanceStore, void> {
const { stakingWrapper, zrxVault } = deployment.staking; const { stakingWrapper, zrxVault } = deployment.staking;
return new FunctionAssertion(stakingWrapper.unstake, { return new FunctionAssertion(stakingWrapper.unstake.bind(stakingWrapper), {
before: async (args: FunctionArguments<[BigNumber]>) => { before: async (args: FunctionArguments<[BigNumber]>) => {
const [amount] = args.args; const [amount] = args.args;

View File

@ -1,6 +1,7 @@
import { GlobalStakeByStatus, StakeStatus, StakingPoolById, StoredBalance } from '@0x/contracts-staking'; import { GlobalStakeByStatus, StakeStatus, StakingPoolById, StoredBalance } from '@0x/contracts-staking';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { Maker } from './actors/maker';
import { AssertionResult } from './assertions/function_assertion'; import { AssertionResult } from './assertions/function_assertion';
import { BlockchainBalanceStore } from './balances/blockchain_balance_store'; import { BlockchainBalanceStore } from './balances/blockchain_balance_store';
import { DeploymentManager } from './deployment_manager'; import { DeploymentManager } from './deployment_manager';
@ -14,7 +15,11 @@ export class SimulationEnvironment {
}; };
public stakingPools: StakingPoolById = {}; public stakingPools: StakingPoolById = {};
public constructor(public readonly deployment: DeploymentManager, public balanceStore: BlockchainBalanceStore) {} public constructor(
public readonly deployment: DeploymentManager,
public balanceStore: BlockchainBalanceStore,
public marketMakers: Maker[] = [],
) {}
} }
export abstract class Simulation { export abstract class Simulation {

View File

@ -8,7 +8,7 @@ import { FunctionArguments, FunctionAssertion, FunctionResult } from '../asserti
const { ZERO_AMOUNT, MAX_UINT256 } = constants; const { ZERO_AMOUNT, MAX_UINT256 } = constants;
blockchainTests.resets.only('FunctionAssertion Unit Tests', env => { blockchainTests.resets('FunctionAssertion Unit Tests', env => {
let exampleContract: TestFrameworkContract; let exampleContract: TestFrameworkContract;
before(async () => { before(async () => {

View File

@ -1,6 +1,7 @@
import { blockchainTests } from '@0x/contracts-test-utils'; import { blockchainTests, constants } from '@0x/contracts-test-utils';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { Maker } from '../framework/actors/maker';
import { PoolMember } from '../framework/actors/pool_member'; import { PoolMember } from '../framework/actors/pool_member';
import { PoolOperator } from '../framework/actors/pool_operator'; import { PoolOperator } from '../framework/actors/pool_operator';
import { AssertionResult } from '../framework/assertions/function_assertion'; import { AssertionResult } from '../framework/assertions/function_assertion';
@ -27,6 +28,7 @@ class PoolMembershipSimulation extends Simulation {
const actions = [ const actions = [
operator.simulationActions.validCreateStakingPool, operator.simulationActions.validCreateStakingPool,
member.simulationActions.validJoinStakingPool, member.simulationActions.validJoinStakingPool,
member.simulationActions.validFillOrderCompleteFill,
]; ];
while (true) { while (true) {
@ -36,17 +38,45 @@ class PoolMembershipSimulation extends Simulation {
} }
} }
blockchainTests('pool membership fuzz test', env => { blockchainTests.skip('pool membership fuzz test', env => {
it('fuzz', async () => { let deployment: DeploymentManager;
const deployment = await DeploymentManager.deployAsync(env, { let maker: Maker;
numErc20TokensToDeploy: 0,
before(async () => {
deployment = await DeploymentManager.deployAsync(env, {
numErc20TokensToDeploy: 2,
numErc721TokensToDeploy: 0, numErc721TokensToDeploy: 0,
numErc1155TokensToDeploy: 0, numErc1155TokensToDeploy: 0,
}); });
const makerToken = deployment.tokens.erc20[0];
const takerToken = deployment.tokens.erc20[1];
const orderConfig = {
feeRecipientAddress: constants.NULL_ADDRESS,
makerAssetData: deployment.assetDataEncoder.ERC20Token(makerToken.address).getABIEncodedTransactionData(),
takerAssetData: deployment.assetDataEncoder.ERC20Token(takerToken.address).getABIEncodedTransactionData(),
makerFeeAssetData: deployment.assetDataEncoder
.ERC20Token(makerToken.address)
.getABIEncodedTransactionData(),
takerFeeAssetData: deployment.assetDataEncoder
.ERC20Token(takerToken.address)
.getABIEncodedTransactionData(),
makerFee: constants.ZERO_AMOUNT,
takerFee: constants.ZERO_AMOUNT,
};
maker = new Maker({
name: 'maker',
deployment,
orderConfig,
});
});
it('fuzz', async () => {
const balanceStore = new BlockchainBalanceStore({}, {}); const balanceStore = new BlockchainBalanceStore({}, {});
const simulationEnv = new SimulationEnvironment(deployment, balanceStore); const simulationEnv = new SimulationEnvironment(deployment, balanceStore, [maker]);
const simulation = new PoolMembershipSimulation(simulationEnv); const simulation = new PoolMembershipSimulation(simulationEnv);
return simulation.fuzzAsync(); return simulation.fuzzAsync();
}); });