Merge pull request #2279 from 0xProject/fix/3.0-audit/staking/assert-valid-params-in-MixinParams

Assert storage params when calling `MixinParams.setParams()`.
This commit is contained in:
Lawrence Forman 2019-10-23 05:11:10 -04:00 committed by GitHub
commit f192648c76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 96 additions and 21 deletions

View File

@ -13,6 +13,10 @@
{
"note": "Removed protocol fee != 0 assertion.",
"pr": 2278
},
{
"note": "Call `StakingProxy.assertValidStorageParams()` in `MixinParams.setParams()`",
"pr": 2279
}
]
},

View File

@ -157,8 +157,8 @@ contract StakingProxy is
// Asserts that a stake weight is <= 100%.
// Asserts that pools allow >= 1 maker.
// Asserts that all addresses are initialized.
function _assertValidStorageParams()
internal
function assertValidStorageParams()
public
view
{
// Epoch length must be between 5 and 30 days long
@ -216,6 +216,6 @@ contract StakingProxy is
}
// Assert initialized storage values are valid
_assertValidStorageParams();
assertValidStorageParams();
}
}

View File

@ -55,4 +55,13 @@ contract IStakingProxy {
external
view
returns (IStructs.ReadOnlyState memory);
/// @dev Asserts that an epoch is between 5 and 30 days long.
// Asserts that 0 < cobb douglas alpha value <= 1.
// Asserts that a stake weight is <= 100%.
// Asserts that pools allow >= 1 maker.
// Asserts that all addresses are initialized.
function assertValidStorageParams()
external
view;
}

View File

@ -22,6 +22,7 @@ import "@0x/contracts-utils/contracts/src/LibRichErrors.sol";
import "../immutable/MixinStorage.sol";
import "../immutable/MixinConstants.sol";
import "../interfaces/IStakingEvents.sol";
import "../interfaces/IStakingProxy.sol";
import "../libs/LibStakingRichErrors.sol";
@ -53,6 +54,10 @@ contract MixinParams is
_cobbDouglasAlphaNumerator,
_cobbDouglasAlphaDenominator
);
// Let the staking proxy enforce that these parameters are within
// acceptable ranges.
IStakingProxy(address(this)).assertValidStorageParams();
}
/// @dev Retrieves all configurable parameter values.

View File

@ -49,7 +49,7 @@ contract TestAssertStorageParams is
minimumPoolStake = params.minimumPoolStake;
cobbDouglasAlphaNumerator = params.cobbDouglasAlphaNumerator;
cobbDouglasAlphaDenominator = params.cobbDouglasAlphaDenominator;
_assertValidStorageParams();
assertValidStorageParams();
}
function _attachStakingContract(address)

View File

@ -0,0 +1,47 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "../src/sys/MixinParams.sol";
contract TestMixinParams is
MixinParams
{
bool public shouldFailAssertValidStorageParams;
/// @dev Set `shouldFailAssertValidStorageParams`
function setShouldFailAssertValidStorageParams(bool shouldFail)
external
{
shouldFailAssertValidStorageParams = shouldFail;
}
/// @dev `IStakingProxy.assertValidStorageParams()` that reverts if
/// `shouldFailAssertValidStorageParams` is true.
function assertValidStorageParams()
public
view
{
if (shouldFailAssertValidStorageParams) {
revert("ASSERT_VALID_STORAGE_PARAMS_FAILED");
}
}
}

View File

@ -35,8 +35,8 @@ contract TestStakingProxy is
)
{}
function _assertValidStorageParams()
internal
function assertValidStorageParams()
public
view
{
require(

View File

@ -37,7 +37,7 @@
},
"config": {
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./generated-artifacts/@(IStaking|IStakingEvents|IStakingProxy|IStorage|IStorageInit|IStructs|IZrxVault|LibCobbDouglas|LibFixedMath|LibFixedMathRichErrors|LibProxy|LibSafeDowncast|LibStakingRichErrors|MixinAbstract|MixinConstants|MixinCumulativeRewards|MixinDeploymentConstants|MixinExchangeFees|MixinExchangeManager|MixinFinalizer|MixinParams|MixinScheduler|MixinStake|MixinStakeBalances|MixinStakeStorage|MixinStakingPool|MixinStakingPoolRewards|MixinStorage|ReadOnlyProxy|Staking|StakingProxy|TestAssertStorageParams|TestCobbDouglas|TestCumulativeRewardTracking|TestDelegatorRewards|TestExchangeManager|TestFinalizer|TestInitTarget|TestLibFixedMath|TestLibProxy|TestLibProxyReceiver|TestLibSafeDowncast|TestMixinStake|TestMixinStakeStorage|TestProtocolFees|TestStaking|TestStakingNoWETH|TestStakingProxy|TestStorageLayoutAndConstants|ZrxVault|ZrxVaultBackstop).json"
"abis": "./generated-artifacts/@(IStaking|IStakingEvents|IStakingProxy|IStorage|IStorageInit|IStructs|IZrxVault|LibCobbDouglas|LibFixedMath|LibFixedMathRichErrors|LibProxy|LibSafeDowncast|LibStakingRichErrors|MixinAbstract|MixinConstants|MixinCumulativeRewards|MixinDeploymentConstants|MixinExchangeFees|MixinExchangeManager|MixinFinalizer|MixinParams|MixinScheduler|MixinStake|MixinStakeBalances|MixinStakeStorage|MixinStakingPool|MixinStakingPoolRewards|MixinStorage|ReadOnlyProxy|Staking|StakingProxy|TestAssertStorageParams|TestCobbDouglas|TestCumulativeRewardTracking|TestDelegatorRewards|TestExchangeManager|TestFinalizer|TestInitTarget|TestLibFixedMath|TestLibProxy|TestLibProxyReceiver|TestLibSafeDowncast|TestMixinParams|TestMixinStake|TestMixinStakeStorage|TestProtocolFees|TestStaking|TestStakingNoWETH|TestStakingProxy|TestStorageLayoutAndConstants|ZrxVault|ZrxVaultBackstop).json"
},
"repository": {
"type": "git",

View File

@ -47,6 +47,7 @@ import * as TestLibFixedMath from '../generated-artifacts/TestLibFixedMath.json'
import * as TestLibProxy from '../generated-artifacts/TestLibProxy.json';
import * as TestLibProxyReceiver from '../generated-artifacts/TestLibProxyReceiver.json';
import * as TestLibSafeDowncast from '../generated-artifacts/TestLibSafeDowncast.json';
import * as TestMixinParams from '../generated-artifacts/TestMixinParams.json';
import * as TestMixinStake from '../generated-artifacts/TestMixinStake.json';
import * as TestMixinStakeStorage from '../generated-artifacts/TestMixinStakeStorage.json';
import * as TestProtocolFees from '../generated-artifacts/TestProtocolFees.json';
@ -101,6 +102,7 @@ export const artifacts = {
TestLibProxy: TestLibProxy as ContractArtifact,
TestLibProxyReceiver: TestLibProxyReceiver as ContractArtifact,
TestLibSafeDowncast: TestLibSafeDowncast as ContractArtifact,
TestMixinParams: TestMixinParams as ContractArtifact,
TestMixinStake: TestMixinStake as ContractArtifact,
TestMixinStakeStorage: TestMixinStakeStorage as ContractArtifact,
TestProtocolFees: TestProtocolFees as ContractArtifact,

View File

@ -45,6 +45,7 @@ export * from '../generated-wrappers/test_lib_fixed_math';
export * from '../generated-wrappers/test_lib_proxy';
export * from '../generated-wrappers/test_lib_proxy_receiver';
export * from '../generated-wrappers/test_lib_safe_downcast';
export * from '../generated-wrappers/test_mixin_params';
export * from '../generated-wrappers/test_mixin_stake';
export * from '../generated-wrappers/test_mixin_stake_storage';
export * from '../generated-wrappers/test_protocol_fees';

View File

@ -228,7 +228,7 @@ blockchainTests('Migration tests', env => {
const expectedError = new StakingRevertErrors.InvalidParamValueError(
StakingRevertErrors.InvalidParamValueErrorCodes.InvalidEpochDuration,
);
expect(tx).to.revertWith(expectedError);
return expect(tx).to.revertWith(expectedError);
});
it('reverts if epoch duration is > 30 days', async () => {
const tx = proxyContract.setAndAssertParams.awaitTransactionSuccessAsync({
@ -238,21 +238,21 @@ blockchainTests('Migration tests', env => {
const expectedError = new StakingRevertErrors.InvalidParamValueError(
StakingRevertErrors.InvalidParamValueErrorCodes.InvalidEpochDuration,
);
expect(tx).to.revertWith(expectedError);
return expect(tx).to.revertWith(expectedError);
});
it('succeeds if epoch duration is 5 days', async () => {
const tx = proxyContract.setAndAssertParams.awaitTransactionSuccessAsync({
...stakingConstants.DEFAULT_PARAMS,
epochDurationInSeconds: fiveDays,
});
expect(tx).to.be.fulfilled('');
return expect(tx).to.be.fulfilled('');
});
it('succeeds if epoch duration is 30 days', async () => {
const tx = proxyContract.setAndAssertParams.awaitTransactionSuccessAsync({
...stakingConstants.DEFAULT_PARAMS,
epochDurationInSeconds: thirtyDays,
});
expect(tx).to.be.fulfilled('');
return expect(tx).to.be.fulfilled('');
});
it('reverts if alpha denominator is 0', async () => {
const tx = proxyContract.setAndAssertParams.awaitTransactionSuccessAsync({
@ -262,7 +262,7 @@ blockchainTests('Migration tests', env => {
const expectedError = new StakingRevertErrors.InvalidParamValueError(
StakingRevertErrors.InvalidParamValueErrorCodes.InvalidCobbDouglasAlpha,
);
expect(tx).to.revertWith(expectedError);
return expect(tx).to.revertWith(expectedError);
});
it('reverts if alpha > 1', async () => {
const tx = proxyContract.setAndAssertParams.awaitTransactionSuccessAsync({
@ -273,7 +273,7 @@ blockchainTests('Migration tests', env => {
const expectedError = new StakingRevertErrors.InvalidParamValueError(
StakingRevertErrors.InvalidParamValueErrorCodes.InvalidCobbDouglasAlpha,
);
expect(tx).to.revertWith(expectedError);
return expect(tx).to.revertWith(expectedError);
});
it('succeeds if alpha == 1', async () => {
const tx = proxyContract.setAndAssertParams.awaitTransactionSuccessAsync({
@ -281,7 +281,7 @@ blockchainTests('Migration tests', env => {
cobbDouglasAlphaNumerator: new BigNumber(1),
cobbDouglasAlphaDenominator: new BigNumber(1),
});
expect(tx).to.be.fulfilled('');
return expect(tx).to.be.fulfilled('');
});
it('succeeds if alpha == 0', async () => {
const tx = proxyContract.setAndAssertParams.awaitTransactionSuccessAsync({
@ -289,7 +289,7 @@ blockchainTests('Migration tests', env => {
cobbDouglasAlphaNumerator: constants.ZERO_AMOUNT,
cobbDouglasAlphaDenominator: new BigNumber(1),
});
expect(tx).to.be.fulfilled('');
return expect(tx).to.be.fulfilled('');
});
it('reverts if delegation weight is > 100%', async () => {
const tx = proxyContract.setAndAssertParams.awaitTransactionSuccessAsync({
@ -299,14 +299,14 @@ blockchainTests('Migration tests', env => {
const expectedError = new StakingRevertErrors.InvalidParamValueError(
StakingRevertErrors.InvalidParamValueErrorCodes.InvalidRewardDelegatedStakeWeight,
);
expect(tx).to.revertWith(expectedError);
return expect(tx).to.revertWith(expectedError);
});
it('succeeds if delegation weight is 100%', async () => {
const tx = proxyContract.setAndAssertParams.awaitTransactionSuccessAsync({
...stakingConstants.DEFAULT_PARAMS,
rewardDelegatedStakeWeight: new BigNumber(stakingConstants.PPM),
});
expect(tx).to.be.fulfilled('');
return expect(tx).to.be.fulfilled('');
});
});
});

View File

@ -3,20 +3,20 @@ import { AuthorizableRevertErrors, BigNumber } from '@0x/utils';
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
import { artifacts, IStakingEventsParamsSetEventArgs, MixinParamsContract } from '../../src/';
import { artifacts, IStakingEventsParamsSetEventArgs, TestMixinParamsContract } from '../../src/';
import { constants as stakingConstants } from '../utils/constants';
import { StakingParams } from '../utils/types';
blockchainTests('Configurable Parameters unit tests', env => {
let testContract: MixinParamsContract;
let testContract: TestMixinParamsContract;
let authorizedAddress: string;
let notAuthorizedAddress: string;
before(async () => {
[authorizedAddress, notAuthorizedAddress] = await env.getAccountAddressesAsync();
testContract = await MixinParamsContract.deployFrom0xArtifactAsync(
artifacts.MixinParams,
testContract = await TestMixinParamsContract.deployFrom0xArtifactAsync(
artifacts.TestMixinParams,
env.provider,
env.txDefaults,
artifacts,
@ -66,6 +66,12 @@ blockchainTests('Configurable Parameters unit tests', env => {
return expect(tx).to.revertWith(expectedError);
});
it('throws if `assertValidStorageParams()` throws`', async () => {
await testContract.setShouldFailAssertValidStorageParams.awaitTransactionSuccessAsync(true);
const tx = setParamsAndAssertAsync({});
return expect(tx).to.revertWith('ASSERT_VALID_STORAGE_PARAMS_FAILED');
});
it('works if called by owner', async () => {
return setParamsAndAssertAsync({});
});

View File

@ -45,6 +45,7 @@
"generated-artifacts/TestLibProxy.json",
"generated-artifacts/TestLibProxyReceiver.json",
"generated-artifacts/TestLibSafeDowncast.json",
"generated-artifacts/TestMixinParams.json",
"generated-artifacts/TestMixinStake.json",
"generated-artifacts/TestMixinStakeStorage.json",
"generated-artifacts/TestProtocolFees.json",