Add negative assertions for endEpoch

This commit is contained in:
Michael Zhu
2020-01-08 18:09:55 -08:00
parent 616533c5a8
commit 4707a46561
3 changed files with 78 additions and 3 deletions

View File

@@ -7,7 +7,11 @@ import { filterLogsToArguments, web3Wrapper } from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
import { BlockParamLiteral, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import { validEndEpochAssertion } from '../assertions/endEpoch';
import {
endEpochTooEarlyAssertion,
endEpochUnfinalizedPoolsAssertion,
validEndEpochAssertion,
} from '../assertions/endEpoch';
import { validFinalizePoolAssertion } from '../assertions/finalizePool';
import { AssertionResult } from '../assertions/function_assertion';
import { Pseudorandom } from '../utils/pseudorandom';
@@ -43,6 +47,7 @@ export function KeeperMixin<TBase extends Constructor>(Base: TBase): TBase & Con
...this.actor.simulationActions,
validFinalizePool: this._validFinalizePool(),
validEndEpoch: this._validEndEpoch(),
invalidEndEpoch: this._invalidEndEpoch(),
};
}
@@ -118,6 +123,27 @@ export function KeeperMixin<TBase extends Constructor>(Base: TBase): TBase & Con
}
}
private async *_invalidEndEpoch(): AsyncIterableIterator<AssertionResult | void> {
const { stakingWrapper } = this.actor.deployment.staking;
while (true) {
const { simulationEnvironment } = this.actor;
const aggregatedStats = AggregatedStats.fromArray(
await stakingWrapper
.aggregatedStatsByEpoch(simulationEnvironment!.currentEpoch.minus(1))
.callAsync(),
);
const assertion = aggregatedStats.numPoolsToFinalize.isGreaterThan(0)
? endEpochUnfinalizedPoolsAssertion(
this.actor.deployment,
simulationEnvironment!,
aggregatedStats.numPoolsToFinalize,
)
: endEpochTooEarlyAssertion(this.actor.deployment);
yield assertion.executeAsync([], { from: this.actor.address });
}
}
private async _fastForwardToNextEpochAsync(): Promise<void> {
const { stakingWrapper } = this.actor.deployment.staking;

View File

@@ -4,6 +4,7 @@ import {
StakingEpochEndedEventArgs,
StakingEpochFinalizedEventArgs,
StakingEvents,
StakingRevertErrors,
} from '@0x/contracts-staking';
import { constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
@@ -117,3 +118,49 @@ export function validEndEpochAssertion(
},
});
}
/**
* Returns a FunctionAssertion for `endEpoch` which assumes it has been called while the previous
* epoch hasn't been fully finalized. Checks that the transaction reverts with PreviousEpochNotFinalizedError.
*/
export function endEpochUnfinalizedPoolsAssertion(
deployment: DeploymentManager,
simulationEnvironment: SimulationEnvironment,
numPoolsToFinalizeFromPrevEpoch: BigNumber,
): FunctionAssertion<[], void, void> {
return new FunctionAssertion(deployment.staking.stakingWrapper, 'endEpoch', {
after: async (_beforeInfo: void, result: FunctionResult) => {
// Ensure that the tx reverted.
expect(result.success).to.be.false();
// Check revert error
expect(result.data).to.equal(
new StakingRevertErrors.PreviousEpochNotFinalizedError(
simulationEnvironment.currentEpoch.minus(1),
numPoolsToFinalizeFromPrevEpoch,
),
);
},
});
}
/**
* Returns a FunctionAssertion for `endEpoch` which assumes it has been called before the full epoch
* duration has elapsed. Checks that the transaction reverts with BlockTimestampTooLowError.
*/
export function endEpochTooEarlyAssertion(deployment: DeploymentManager): FunctionAssertion<[], void, void> {
const { stakingWrapper } = deployment.staking;
return new FunctionAssertion(stakingWrapper, 'endEpoch', {
after: async (_beforeInfo: void, result: FunctionResult) => {
// Ensure that the tx reverted.
expect(result.success).to.be.false();
// Check revert error
const epochEndTime = await stakingWrapper.getCurrentEpochEarliestEndTimeInSeconds().callAsync();
const lastBlockTime = await deployment.web3Wrapper.getBlockTimestampAsync('latest');
expect(result.data).to.equal(
new StakingRevertErrors.BlockTimestampTooLowError(epochEndTime, lastBlockTime),
);
},
});
}

View File

@@ -38,8 +38,10 @@ export class StakingRewardsSimulation extends Simulation {
...stakers.map(staker => [staker.simulationActions.validWithdrawDelegatorRewards, 0.1 / stakers.length]),
// 10% chance of executing validFinalizePool for a random keeper
...keepers.map(keeper => [keeper.simulationActions.validFinalizePool, 0.1 / keepers.length]),
// 10% chance of executing validEndEpoch for a random keeper
...keepers.map(keeper => [keeper.simulationActions.validEndEpoch, 0.1 / keepers.length]),
// 7% chance of executing validEndEpoch for a random keeper
...keepers.map(keeper => [keeper.simulationActions.validEndEpoch, 0.07 / keepers.length]),
// 3% chance of executing invalidEndEpoch for a random keeper
...keepers.map(keeper => [keeper.simulationActions.invalidEndEpoch, 0.03 / keepers.length]),
// 50% chance of executing an assertion generated from the pool membership simulation
[poolMembership.generator, 0.5],
// 20% chance of executing an assertion generated from the stake management simulation