Add a minimum delay on all admin update operations (#4557)

Co-authored-by: Francisco <fg@frang.io>
This commit is contained in:
Hadrien Croubois
2023-09-04 18:47:51 +02:00
committed by GitHub
parent 630844ef50
commit 9d2adccf87
2 changed files with 110 additions and 42 deletions

View File

@ -22,6 +22,8 @@ const classId = web3.utils.toBN(1);
const executeDelay = web3.utils.toBN(10);
const grantDelay = web3.utils.toBN(10);
const MINSETBACK = time.duration.days(5);
const formatAccess = access => [access[0], access[1].toString()];
contract('AccessManager', function (accounts) {
@ -37,6 +39,10 @@ contract('AccessManager', function (accounts) {
await this.manager.$_grantGroup(GROUPS.SOME, member, 0, 0);
});
it('default minsetback is 1 day', async function () {
expect(await this.manager.minSetback()).to.be.bignumber.equal(MINSETBACK);
});
it('groups are correctly initialized', async function () {
// group admin
expect(await this.manager.getGroupAdmin(GROUPS.ADMIN)).to.be.bignumber.equal(GROUPS.ADMIN);
@ -161,6 +167,7 @@ contract('AccessManager', function (accounts) {
describe('with a grant delay', function () {
beforeEach(async function () {
await this.manager.$_setGrantDelay(GROUPS.SOME, grantDelay);
await time.increase(MINSETBACK);
});
it('granted group is not active immediatly', async function () {
@ -350,6 +357,7 @@ contract('AccessManager', function (accounts) {
const oldDelay = web3.utils.toBN(10);
const newDelay = web3.utils.toBN(100);
// group is already granted (with no delay) in the initial setup. this update takes time.
await this.manager.$_grantGroup(GROUPS.SOME, member, 0, oldDelay);
const accessBefore = await this.manager.getAccess(GROUPS.SOME, member);
@ -380,6 +388,7 @@ contract('AccessManager', function (accounts) {
const oldDelay = web3.utils.toBN(100);
const newDelay = web3.utils.toBN(10);
// group is already granted (with no delay) in the initial setup. this update takes time.
await this.manager.$_grantGroup(GROUPS.SOME, member, 0, oldDelay);
const accessBefore = await this.manager.getAccess(GROUPS.SOME, member);
@ -391,27 +400,37 @@ contract('AccessManager', function (accounts) {
from: manager,
});
const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
const setback = oldDelay.sub(newDelay);
expectEvent(receipt, 'GroupGranted', {
groupId: GROUPS.SOME,
account: member,
since: timestamp.add(oldDelay).sub(newDelay),
since: timestamp.add(setback),
delay: newDelay,
});
// delayed effect
// no immediate effect
const accessAfter = await this.manager.getAccess(GROUPS.SOME, member);
expect(accessAfter[1]).to.be.bignumber.equal(oldDelay); // currentDelay
expect(accessAfter[2]).to.be.bignumber.equal(newDelay); // pendingDelay
expect(accessAfter[3]).to.be.bignumber.equal(timestamp.add(oldDelay).sub(newDelay)); // effect
expect(accessAfter[3]).to.be.bignumber.equal(timestamp.add(setback)); // effect
// delayed effect
await time.increase(setback);
const accessAfterSetback = await this.manager.getAccess(GROUPS.SOME, member);
expect(accessAfterSetback[1]).to.be.bignumber.equal(newDelay); // currentDelay
expect(accessAfterSetback[2]).to.be.bignumber.equal('0'); // pendingDelay
expect(accessAfterSetback[3]).to.be.bignumber.equal('0'); // effect
});
it('can set a user execution delay during the grant delay', async function () {
await this.manager.$_grantGroup(GROUPS.SOME, other, 10, 0);
// here: "other" is pending to get the group, but doesn't yet have it.
const { receipt } = await this.manager.grantGroup(GROUPS.SOME, other, executeDelay, { from: manager });
const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
// increasing the execution delay from 0 to executeDelay is immediate
expectEvent(receipt, 'GroupGranted', {
groupId: GROUPS.SOME,
account: other,
@ -425,38 +444,75 @@ contract('AccessManager', function (accounts) {
it('increasing the delay has immediate effect', async function () {
const oldDelay = web3.utils.toBN(10);
const newDelay = web3.utils.toBN(100);
await this.manager.$_setGrantDelay(GROUPS.SOME, oldDelay);
await time.increase(MINSETBACK);
expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(oldDelay);
const { receipt } = await this.manager.setGrantDelay(GROUPS.SOME, newDelay, { from: admin });
const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
const setback = web3.utils.BN.max(MINSETBACK, oldDelay.sub(newDelay));
expectEvent(receipt, 'GroupGrantDelayChanged', { groupId: GROUPS.SOME, delay: newDelay, since: timestamp });
expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(newDelay);
});
it('increasing the delay has delay effect', async function () {
const oldDelay = web3.utils.toBN(100);
const newDelay = web3.utils.toBN(10);
await this.manager.$_setGrantDelay(GROUPS.SOME, oldDelay);
expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(oldDelay);
const { receipt } = await this.manager.setGrantDelay(GROUPS.SOME, newDelay, { from: admin });
const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
expect(setback).to.be.bignumber.equal(MINSETBACK);
expectEvent(receipt, 'GroupGrantDelayChanged', {
groupId: GROUPS.SOME,
delay: newDelay,
since: timestamp.add(oldDelay).sub(newDelay),
since: timestamp.add(setback),
});
expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(oldDelay);
await time.increase(setback);
expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(newDelay);
});
await time.increase(oldDelay.sub(newDelay));
it('increasing the delay has delay effect #1', async function () {
const oldDelay = web3.utils.toBN(100);
const newDelay = web3.utils.toBN(10);
await this.manager.$_setGrantDelay(GROUPS.SOME, oldDelay);
await time.increase(MINSETBACK);
expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(oldDelay);
const { receipt } = await this.manager.setGrantDelay(GROUPS.SOME, newDelay, { from: admin });
const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
const setback = web3.utils.BN.max(MINSETBACK, oldDelay.sub(newDelay));
expect(setback).to.be.bignumber.equal(MINSETBACK);
expectEvent(receipt, 'GroupGrantDelayChanged', {
groupId: GROUPS.SOME,
delay: newDelay,
since: timestamp.add(setback),
});
expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(oldDelay);
await time.increase(setback);
expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(newDelay);
});
it('increasing the delay has delay effect #2', async function () {
const oldDelay = time.duration.days(30); // more than the minsetback
const newDelay = web3.utils.toBN(10);
await this.manager.$_setGrantDelay(GROUPS.SOME, oldDelay);
await time.increase(MINSETBACK);
expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(oldDelay);
const { receipt } = await this.manager.setGrantDelay(GROUPS.SOME, newDelay, { from: admin });
const timestamp = await clockFromReceipt.timestamp(receipt).then(web3.utils.toBN);
const setback = web3.utils.BN.max(MINSETBACK, oldDelay.sub(newDelay));
expect(setback).to.be.bignumber.gt(MINSETBACK);
expectEvent(receipt, 'GroupGrantDelayChanged', {
groupId: GROUPS.SOME,
delay: newDelay,
since: timestamp.add(setback),
});
expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(oldDelay);
await time.increase(setback);
expect(await this.manager.getGroupGrantDelay(GROUPS.SOME)).to.be.bignumber.equal(newDelay);
});
@ -1050,8 +1106,8 @@ contract('AccessManager', function (accounts) {
// TODO: test all admin functions
describe('class delays', function () {
const otherClassId = '2';
const delay = '1000';
const otherClassId = web3.utils.toBN(2);
const delay = web3.utils.toBN(1000);
beforeEach('set contract class', async function () {
this.target = await AccessManagedTarget.new(this.manager.address);
@ -1065,9 +1121,7 @@ contract('AccessManager', function (accounts) {
await this.call();
});
// TODO: here we need to check increase and decrease.
// - Increasing should have immediate effect
// - Decreasing should take time.
// TODO: here we need to check increase and decrease. Both should have be affected by the minsetback.
describe('with delay', function () {
beforeEach('set admin delay', async function () {
this.tx = await this.manager.setClassAdminDelay(classId, delay, { from: admin });
@ -1077,28 +1131,38 @@ contract('AccessManager', function (accounts) {
});
it('emits event and sets delay', async function () {
const since = await clockFromReceipt.timestamp(this.tx.receipt).then(web3.utils.toBN);
expectEvent(this.tx.receipt, 'ClassAdminDelayUpdated', { classId, delay, since });
const timepoint = await clockFromReceipt.timestamp(this.tx.receipt).then(web3.utils.toBN);
expectEvent(this.tx.receipt, 'ClassAdminDelayUpdated', { classId, delay, since: timepoint.add(MINSETBACK) });
// wait for delay to become active
expect(await this.manager.getClassAdminDelay(classId)).to.be.bignumber.equal('0');
await time.increase(MINSETBACK);
expect(await this.manager.getClassAdminDelay(classId)).to.be.bignumber.equal(delay);
});
it('without prior scheduling: reverts', async function () {
await expectRevertCustomError(this.call(), 'AccessManagerNotScheduled', [this.opId]);
});
describe('with prior scheduling', async function () {
beforeEach('schedule', async function () {
await this.manager.schedule(this.manager.address, this.data, 0, { from: admin });
describe('after setback', function () {
beforeEach('wait', async function () {
await time.increase(MINSETBACK);
});
it('without delay: reverts', async function () {
await expectRevertCustomError(this.call(), 'AccessManagerNotReady', [this.opId]);
it('without prior scheduling: reverts', async function () {
await expectRevertCustomError(this.call(), 'AccessManagerNotScheduled', [this.opId]);
});
it('with delay: succeeds', async function () {
await time.increase(delay);
await this.call();
describe('with prior scheduling', async function () {
beforeEach('schedule', async function () {
await this.manager.schedule(this.manager.address, this.data, 0, { from: admin });
});
it('without delay: reverts', async function () {
await expectRevertCustomError(this.call(), 'AccessManagerNotReady', [this.opId]);
});
it('with delay: succeeds', async function () {
await time.increase(delay);
await this.call();
});
});
});
});