Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Added PauserRole. (#1283)
  • Loading branch information
nventuro authored Sep 6, 2018
commit 4d4a0044b7d5dc09f8c07a97e3cd88a482aefeb4
35 changes: 35 additions & 0 deletions contracts/access/rbac/PauserRole.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
pragma solidity ^0.4.24;

import "./Roles.sol";


contract PauserRole {
using Roles for Roles.Role;

Roles.Role private pausers;

constructor() public {
pausers.add(msg.sender);
}

modifier onlyPauser() {
require(isPauser(msg.sender));
_;
}

function isPauser(address _account) public view returns (bool) {
return pausers.has(_account);
}

function addPauser(address _account) public onlyPauser {
pausers.add(_account);
}

function renouncePauser() public {
pausers.remove(msg.sender);
}

function _removePauser(address _account) internal {
pausers.remove(_account);
}
}
1 change: 1 addition & 0 deletions contracts/bounties/BreakInvariantBounty.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.4.24;
import "../payment/PullPayment.sol";
import "../ownership/Ownable.sol";


/**
* @title BreakInvariantBounty
* @dev This bounty will pay out to a researcher if they break invariant logic of the contract.
Expand Down
9 changes: 4 additions & 5 deletions contracts/lifecycle/Pausable.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
pragma solidity ^0.4.24;


import "../ownership/Ownable.sol";
import "../access/rbac/PauserRole.sol";


/**
* @title Pausable
* @dev Base contract which allows children to implement an emergency stop mechanism.
*/
contract Pausable is Ownable {
contract Pausable is PauserRole {
event Paused();
event Unpaused();

Expand All @@ -34,15 +33,15 @@ contract Pausable is Ownable {
/**
* @dev called by the owner to pause, triggers stopped state
*/
function pause() public onlyOwner whenNotPaused {
function pause() public onlyPauser whenNotPaused {
paused = true;
emit Paused();
}

/**
* @dev called by the owner to unpause, returns to normal state
*/
function unpause() public onlyOwner whenPaused {
function unpause() public onlyPauser whenPaused {
paused = false;
emit Unpaused();
}
Expand Down
115 changes: 76 additions & 39 deletions test/lifecycle/Pausable.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,58 +8,95 @@ require('chai')
.use(require('chai-bignumber')(BigNumber))
.should();

contract('Pausable', function () {
contract('Pausable', function ([_, pauser, anyone]) {
beforeEach(async function () {
this.Pausable = await PausableMock.new();
this.Pausable = await PausableMock.new({ from: pauser });
});

it('can perform normal process in non-pause', async function () {
(await this.Pausable.count()).should.be.bignumber.equal(0);
context('when unapused', function () {
beforeEach(async function () {
(await this.Pausable.paused()).should.equal(false);
});

await this.Pausable.normalProcess();
(await this.Pausable.count()).should.be.bignumber.equal(1);
});
it('can perform normal process in non-pause', async function () {
(await this.Pausable.count()).should.be.bignumber.equal(0);

it('can not perform normal process in pause', async function () {
await this.Pausable.pause();
(await this.Pausable.count()).should.be.bignumber.equal(0);
await this.Pausable.normalProcess({ from: anyone });
(await this.Pausable.count()).should.be.bignumber.equal(1);
});

await assertRevert(this.Pausable.normalProcess());
(await this.Pausable.count()).should.be.bignumber.equal(0);
});
it('cannot take drastic measure in non-pause', async function () {
await assertRevert(this.Pausable.drasticMeasure({ from: anyone }));
(await this.Pausable.drasticMeasureTaken()).should.equal(false);
});

it('can not take drastic measure in non-pause', async function () {
await assertRevert(this.Pausable.drasticMeasure());
(await this.Pausable.drasticMeasureTaken()).should.equal(false);
});
describe('pausing', function () {
it('is pausable by the pauser', async function () {
await this.Pausable.pause({ from: pauser });
(await this.Pausable.paused()).should.equal(true);
});

it('can take a drastic measure in a pause', async function () {
await this.Pausable.pause();
await this.Pausable.drasticMeasure();
(await this.Pausable.drasticMeasureTaken()).should.equal(true);
});
it('reverts when pausing from non-pauser', async function () {
await assertRevert(this.Pausable.pause({ from: anyone }));
});

it('should resume allowing normal process after pause is over', async function () {
await this.Pausable.pause();
await this.Pausable.unpause();
await this.Pausable.normalProcess();
(await this.Pausable.count()).should.be.bignumber.equal(1);
});
context('when paused', function () {
beforeEach(async function () {
({ logs: this.logs } = await this.Pausable.pause({ from: pauser }));
});

it('should prevent drastic measure after pause is over', async function () {
await this.Pausable.pause();
await this.Pausable.unpause();
it('emits a Paused event', function () {
expectEvent.inLogs(this.logs, 'Paused');
});

await assertRevert(this.Pausable.drasticMeasure());
it('cannot perform normal process in pause', async function () {
await assertRevert(this.Pausable.normalProcess({ from: anyone }));
});

(await this.Pausable.drasticMeasureTaken()).should.equal(false);
});
it('can take a drastic measure in a pause', async function () {
await this.Pausable.drasticMeasure({ from: anyone });
(await this.Pausable.drasticMeasureTaken()).should.equal(true);
});

it('reverts when re-pausing', async function () {
await assertRevert(this.Pausable.pause({ from: pauser }));
});

describe('unpausing', function () {
it('is unpausable by the pauser', async function () {
await this.Pausable.unpause({ from: pauser });
(await this.Pausable.paused()).should.equal(false);
});

it('reverts when unpausing from non-pauser', async function () {
await assertRevert(this.Pausable.unpause({ from: anyone }));
});

context('when paused', function () {
beforeEach(async function () {
({ logs: this.logs } = await this.Pausable.unpause({ from: pauser }));
});

it('emits an Unpaused event', function () {
expectEvent.inLogs(this.logs, 'Unpaused');
});

it('should resume allowing normal process', async function () {
(await this.Pausable.count()).should.be.bignumber.equal(0);
await this.Pausable.normalProcess({ from: anyone });
(await this.Pausable.count()).should.be.bignumber.equal(1);
});

it('should log Pause and Unpause events appropriately', async function () {
const setPauseLogs = (await this.Pausable.pause()).logs;
expectEvent.inLogs(setPauseLogs, 'Paused');
it('should prevent drastic measure', async function () {
await assertRevert(this.Pausable.drasticMeasure({ from: anyone }));
});

const setUnPauseLogs = (await this.Pausable.unpause()).logs;
expectEvent.inLogs(setUnPauseLogs, 'Unpaused');
it('reverts when re-unpausing', async function () {
await assertRevert(this.Pausable.unpause({ from: pauser }));
});
});
});
});
});
});
});
Loading