Skip to content

Commit

Permalink
Add one-way lock to extension
Browse files Browse the repository at this point in the history
  • Loading branch information
kronosapiens committed Oct 20, 2022
1 parent edb1a01 commit fca1adf
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 14 deletions.
26 changes: 21 additions & 5 deletions contracts/extensions/ReputationBootstrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ contract ReputationBootstrapper is ColonyExtensionMeta {

address public token;
bool public giveTokens;
bool public isLocked;

uint256 public decayPeriod;
uint256 public decayNumerator;
Expand All @@ -58,7 +59,17 @@ contract ReputationBootstrapper is ColonyExtensionMeta {
// Modifiers

modifier onlyRoot() {
require(colony.hasUserRole(msgSender(), 1, ColonyDataTypes.ColonyRole.Root), "reputation-bootsrapper-caller-not-root");
require(colony.hasUserRole(msgSender(), 1, ColonyDataTypes.ColonyRole.Root), "reputation-bootstrapper-caller-not-root");
_;
}

modifier unlocked() {
require(!isLocked, "reputation-bootstrapper-locked");
_;
}

modifier locked() {
require(isLocked, "reputation-bootstrapper-unlocked");
_;
}

Expand Down Expand Up @@ -94,6 +105,7 @@ contract ReputationBootstrapper is ColonyExtensionMeta {
/// @notice Called when deprecating (or undeprecating) the extension
function deprecate(bool _deprecated) public override auth {
deprecated = _deprecated;
isLocked = true;
}

/// @notice Called when uninstalling the extension
Expand All @@ -106,12 +118,16 @@ contract ReputationBootstrapper is ColonyExtensionMeta {

// Public

function setGiveTokens(bool _giveTokens) public onlyRoot {
function lockExtension() public onlyRoot unlocked {
isLocked = true;
}

function setGiveTokens(bool _giveTokens) public onlyRoot unlocked {
giveTokens = _giveTokens;
}

function setGrants(bytes32[] memory _hashedSecrets, uint256[] memory _amounts) public onlyRoot notDeprecated {
require(_hashedSecrets.length == _amounts.length, "reputation-bootsrapper-invalid-arguments");
function setGrants(bytes32[] memory _hashedSecrets, uint256[] memory _amounts) public onlyRoot unlocked {
require(_hashedSecrets.length == _amounts.length, "reputation-bootstrapper-invalid-arguments");

for (uint256 i; i < _hashedSecrets.length; i++) {
require(_amounts[i] <= INT128_MAX, "reputation-bootstrapper-invalid-amount");
Expand All @@ -121,7 +137,7 @@ contract ReputationBootstrapper is ColonyExtensionMeta {
}
}

function claimGrant(uint256 _secret) public {
function claimGrant(uint256 _secret) public locked {
bytes32 hashedSecret = keccak256(abi.encodePacked(_secret));
uint256 grantAmount = grants[hashedSecret].amount;
uint256 tokenAmount = grants[hashedSecret].amount;
Expand Down
3 changes: 2 additions & 1 deletion test-gas-costs/gasCosts.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ contract("All", function (accounts) {
await colony.setRootRole(reputationBootstrapper.address, true);

await reputationBootstrapper.setGrants([soliditySha3(1)], [WAD], { from: MANAGER });

await reputationBootstrapper.lockExtension({ from: MANAGER });
await reputationBootstrapper.claimGrant(1, { from: WORKER });
});

Expand All @@ -387,6 +387,7 @@ contract("All", function (accounts) {
await colony.setRootRole(reputationBootstrapper.address, true);

await reputationBootstrapper.setGrants([soliditySha3(1)], [WAD], { from: MANAGER });
await reputationBootstrapper.lockExtension({ from: MANAGER });

// Reputation decays by half in 90 days
await forwardTime(SECONDS_PER_DAY * 90, this);
Expand Down
35 changes: 27 additions & 8 deletions test/extensions/reputation-bootstrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,27 +98,33 @@ contract("Reputation Bootstrapper", (accounts) => {
});

describe("managing the extension", async () => {
it("can setup repuation amounts", async () => {
it("can setup reputation amounts", async () => {
await reputationBootstrapper.setGrants([soliditySha3(PIN1), soliditySha3(PIN2)], [WAD, WAD.muln(2)]);

const grant = await reputationBootstrapper.grants(soliditySha3(PIN1));
expect(grant.amount).to.eq.BN(WAD);
});

it("cannot setup reputation amounts if not root", async () => {
await checkErrorRevert(reputationBootstrapper.setGrants([], [], { from: USER1 }), "reputation-bootsrapper-caller-not-root");
await checkErrorRevert(reputationBootstrapper.setGrants([], [], { from: USER1 }), "reputation-bootstrapper-caller-not-root");
});

it("cannot setup repuation amounts with mismatched arguments", async () => {
await checkErrorRevert(reputationBootstrapper.setGrants([], [WAD]), "reputation-bootsrapper-invalid-arguments");
it("cannot setup reputation amounts with mismatched arguments", async () => {
await checkErrorRevert(reputationBootstrapper.setGrants([], [WAD]), "reputation-bootstrapper-invalid-arguments");
});

it("cannot setup repuation amounts with invalid values", async () => {
it("cannot setup reputation amounts with invalid values", async () => {
await checkErrorRevert(reputationBootstrapper.setGrants([soliditySha3(PIN1)], [INT128_MAX.addn(1)]), "reputation-bootstrapper-invalid-amount");
});

it("can claim repuation amounts", async () => {
it("cannot setup reputation amounts when locked", async () => {
await reputationBootstrapper.lockExtension();
await checkErrorRevert(reputationBootstrapper.setGrants([soliditySha3(PIN1)], [INT128_MAX.addn(1)]), "reputation-bootstrapper-locked");
});

it("can claim reputation amounts", async () => {
await reputationBootstrapper.setGrants([soliditySha3(PIN1), soliditySha3(PIN2)], [WAD, WAD.muln(2)]);
await reputationBootstrapper.lockExtension();

await reputationBootstrapper.claimGrant(PIN1, { from: USER1 });

Expand All @@ -137,10 +143,10 @@ contract("Reputation Bootstrapper", (accounts) => {

it("can claim reputation amounts with a decay", async () => {
await reputationBootstrapper.setGrants([soliditySha3(PIN1), soliditySha3(PIN2)], [WAD, WAD.muln(2)]);
await reputationBootstrapper.lockExtension();

// Reputation decays by half in 90 days
await forwardTime(SECONDS_PER_DAY * 90, this);

await reputationBootstrapper.claimGrant(PIN1, { from: USER1 });

const inactiveCycleAddress = await colonyNetwork.getReputationMiningCycle(false);
Expand All @@ -150,19 +156,26 @@ contract("Reputation Bootstrapper", (accounts) => {
expect(updateLog.amount).to.eq.BN(WAD.divn(2).subn(406575)); // Numerical approximation
});

it("can claim repuation amounts and tokens, if set", async () => {
it("can claim reputation amounts and tokens, if set", async () => {
await token.mint(reputationBootstrapper.address, WAD.muln(10));
await reputationBootstrapper.setGiveTokens(true);

await reputationBootstrapper.setGrants([soliditySha3(PIN1), soliditySha3(PIN2)], [WAD, WAD.muln(2)]);
await reputationBootstrapper.lockExtension();

await reputationBootstrapper.claimGrant(PIN1, { from: USER1 });

const balance = await token.balanceOf(USER1);
expect(balance).to.eq.BN(WAD);
});

it("cannot set giveTokens once locked", async () => {
await reputationBootstrapper.lockExtension();
await checkErrorRevert(reputationBootstrapper.setGiveTokens(false), "reputation-bootstrapper-locked");
});

it("cannot claim a nonexistent amount", async () => {
await reputationBootstrapper.lockExtension();
await checkErrorRevert(reputationBootstrapper.claimGrant(PIN1, { from: USER1 }), "reputation-bootstrapper-nothing-to-claim");
});

Expand All @@ -171,12 +184,14 @@ contract("Reputation Bootstrapper", (accounts) => {
await reputationBootstrapper.setGiveTokens(true);

await reputationBootstrapper.setGrants([soliditySha3(PIN1)], [WAD]);
await reputationBootstrapper.lockExtension();

await checkErrorRevert(reputationBootstrapper.claimGrant(PIN1, { from: USER1 }), "ds-token-insufficient-balance");
});

it("can claim reputation via metatransactions", async () => {
await reputationBootstrapper.setGrants([soliditySha3(PIN1)], [WAD]);
await reputationBootstrapper.lockExtension();

const txData = await reputationBootstrapper.contract.methods.claimGrant(PIN1).encodeABI();
const { r, s, v } = await getMetaTransactionParameters(txData, USER0, reputationBootstrapper.address);
Expand All @@ -192,5 +207,9 @@ contract("Reputation Bootstrapper", (accounts) => {
expect(updateLog.amount).to.eq.BN(WAD);
expect(updateLog.skillId).to.eq.BN(domain1.skillId);
});

it("cannot claim reputation amounts when unlocked", async () => {
await checkErrorRevert(reputationBootstrapper.claimGrant(PIN1, { from: USER1 }), "reputation-bootstrapper-unlocked");
});
});
});

0 comments on commit fca1adf

Please sign in to comment.