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 * as _ from 'lodash';
import { validFillOrderCompleteFillAssertion } from '../assertions/fillOrder';
import { AssertionResult } from '../assertions/function_assertion';
import { validJoinStakingPoolAssertion } from '../assertions/joinStakingPool';
@ -33,6 +36,7 @@ export function PoolMemberMixin<TBase extends Constructor>(Base: TBase): TBase &
this.actor.simulationActions = {
...this.actor.simulationActions,
validJoinStakingPool: this._validJoinStakingPool(),
validFillOrderCompleteFill: this._validFillOrderCompleteFill(),
};
}
@ -46,7 +50,6 @@ export function PoolMemberMixin<TBase extends Constructor>(Base: TBase): TBase &
.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> {
const { stakingPools } = this.actor.simulationEnvironment!;
const assertion = validJoinStakingPoolAssertion(this.actor.deployment);
@ -55,8 +58,39 @@ export function PoolMemberMixin<TBase extends Constructor>(Base: TBase): TBase &
if (poolId === undefined) {
yield undefined;
} else {
console.log('Attempting to join pool');
yield assertion.executeAsync({ args: [poolId], txData: {} });
yield assertion.executeAsync({ args: [poolId], txData: { from: this.actor.address } });
}
}
}
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> {
console.log(10);
const { stakingPools } = this.actor.simulationEnvironment!;
console.log(11);
const assertion = validCreateStakingPoolAssertion(this.actor.deployment, stakingPools);
console.log(12);
while (true) {
const operatorShare = getRandomInteger(0, constants.PPM).toNumber();
console.log(13);
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> {
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
before: async () => {
const lastPoolId = await stakingWrapper.lastPoolId().callAsync();
@ -37,18 +39,12 @@ export function validCreateStakingPoolAssertion(
txData: Partial<TxData>;
},
) => {
console.log(100);
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
console.log(result.receipt);
const log = result.receipt!.logs[0]; // tslint:disable-line:no-non-null-assertion
console.log(102);
const actualPoolId = (log as any).args.poolId;
console.log(103);
expect(actualPoolId).to.equal(expectedPoolId);
console.log(104);
// Adds the new pool to local state
pools[actualPoolId] = {
@ -56,7 +52,7 @@ export function validCreateStakingPoolAssertion(
operatorShare: args.args[0],
delegatedStake: new StoredBalance(),
};
console.log(105);
},
});
},
);
}

View File

@ -16,7 +16,9 @@ export function validDecreaseStakingPoolOperatorShareAssertion(
): FunctionAssertion<[string, number], {}, void> {
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] }) => {
const poolId = args.args[0];
const expectedOperatorShare = args.args[1];
@ -29,5 +31,6 @@ export function validDecreaseStakingPoolOperatorShareAssertion(
// Updates the pool in local state.
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';
// tslint:disable:max-classes-per-file
export type GenericContractFunction<T> = (...args: any[]) => ContractFunctionObj<T>;
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.
public wrapperFunction: (
...args: any[] // tslint:disable-line:trailing-comma
...args: TArgs // tslint:disable-line:trailing-comma
) => ContractTxFunctionObj<ReturnDataType> | ContractFunctionObj<ReturnDataType>;
constructor(
wrapperFunction: (
...args: any[] // tslint:disable-line:trailing-comma
...args: TArgs // tslint:disable-line:trailing-comma
) => ContractTxFunctionObj<ReturnDataType> | ContractFunctionObj<ReturnDataType>,
condition: Partial<Condition<TArgs, TBefore>> = {},
) {
@ -95,18 +94,12 @@ export class FunctionAssertion<TArgs extends any[], TBefore, ReturnDataType> imp
try {
const functionWithArgs = this.wrapperFunction(...args.args) as ContractTxFunctionObj<ReturnDataType>;
callResult.data = await functionWithArgs.callAsync(args.txData);
console.log(functionWithArgs);
callResult.receipt =
functionWithArgs.awaitTransactionSuccessAsync !== undefined
? await functionWithArgs.awaitTransactionSuccessAsync(args.txData) // tslint:disable-line:await-promise
: undefined;
// tslint:enable:await-promise
} catch (error) {
console.log('got here');
console.log(error);
callResult.data = error;
callResult.success = false;
callResult.receipt = undefined;

View File

@ -7,7 +7,9 @@ import { DeploymentManager } from '../deployment_manager';
import { FunctionArguments, FunctionAssertion, FunctionResult } from './function_assertion';
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]>) => {
const poolId = args.args[0];
@ -32,11 +34,11 @@ export function validJoinStakingPoolAssertion(deployment: DeploymentManager): Fu
);
expect(logArgs).to.be.deep.eq([
{
maker: args.txData.from,
makerAddress: args.txData.from,
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);
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> {
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 (
_beforeInfo: {},
_result: FunctionResult,
@ -120,7 +122,9 @@ export function validMoveStakeAssertion(
const globalUndelegatedStake = await stakingWrapper
.getGlobalStakeByStatus(StakeStatus.Undelegated)
.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(globalDelegatedStake).to.deep.equal(globalStake[StakeStatus.Delegated]);
@ -134,5 +138,6 @@ export function validMoveStakeAssertion(
expect(totalStakeDelegated).to.deep.equal(pools[poolId].delegatedStake);
}
},
});
},
);
}

View File

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

View File

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

View File

@ -1,6 +1,7 @@
import { GlobalStakeByStatus, StakeStatus, StakingPoolById, StoredBalance } from '@0x/contracts-staking';
import * as _ from 'lodash';
import { Maker } from './actors/maker';
import { AssertionResult } from './assertions/function_assertion';
import { BlockchainBalanceStore } from './balances/blockchain_balance_store';
import { DeploymentManager } from './deployment_manager';
@ -14,7 +15,11 @@ export class SimulationEnvironment {
};
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 {

View File

@ -8,7 +8,7 @@ import { FunctionArguments, FunctionAssertion, FunctionResult } from '../asserti
const { ZERO_AMOUNT, MAX_UINT256 } = constants;
blockchainTests.resets.only('FunctionAssertion Unit Tests', env => {
blockchainTests.resets('FunctionAssertion Unit Tests', env => {
let exampleContract: TestFrameworkContract;
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 { Maker } from '../framework/actors/maker';
import { PoolMember } from '../framework/actors/pool_member';
import { PoolOperator } from '../framework/actors/pool_operator';
import { AssertionResult } from '../framework/assertions/function_assertion';
@ -27,6 +28,7 @@ class PoolMembershipSimulation extends Simulation {
const actions = [
operator.simulationActions.validCreateStakingPool,
member.simulationActions.validJoinStakingPool,
member.simulationActions.validFillOrderCompleteFill,
];
while (true) {
@ -36,17 +38,45 @@ class PoolMembershipSimulation extends Simulation {
}
}
blockchainTests('pool membership fuzz test', env => {
it('fuzz', async () => {
const deployment = await DeploymentManager.deployAsync(env, {
numErc20TokensToDeploy: 0,
blockchainTests.skip('pool membership fuzz test', env => {
let deployment: DeploymentManager;
let maker: Maker;
before(async () => {
deployment = await DeploymentManager.deployAsync(env, {
numErc20TokensToDeploy: 2,
numErc721TokensToDeploy: 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 simulationEnv = new SimulationEnvironment(deployment, balanceStore);
const simulationEnv = new SimulationEnvironment(deployment, balanceStore, [maker]);
const simulation = new PoolMembershipSimulation(simulationEnv);
return simulation.fuzzAsync();
});