Skip to content

Commit

Permalink
Introduce paid flag to grants, add deprecation behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
kronosapiens committed Oct 29, 2022
1 parent b6f7b25 commit 5b12c64
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 92 deletions.
63 changes: 27 additions & 36 deletions contracts/extensions/ReputationBootstrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ contract ReputationBootstrapper is ColonyExtensionMeta {

// Events

event GrantSet(bytes32 hashedSecret, uint256 reputationAmount);
event GrantSet(bool paid, bytes32 hashedSecret, uint256 reputationAmount);
event GrantClaimed(address recipient, uint256 reputationAmount, uint256 tokenAmount);

// Data structures
Expand All @@ -48,14 +48,13 @@ contract ReputationBootstrapper is ColonyExtensionMeta {
// Storage

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

uint256 public decayPeriod;
uint256 public decayNumerator;
uint256 public decayDenominator;

mapping (bytes32 => Grant) public grants;
uint256 totalPaidGrants;
mapping (bool => mapping (bytes32 => Grant)) public grants;
mapping (bytes32 => mapping (bytes32 => uint256)) public committedSecrets;

// Modifiers
Expand All @@ -65,16 +64,6 @@ contract ReputationBootstrapper is ColonyExtensionMeta {
_;
}

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

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

// Overrides

/// @notice Returns the identifier of the extension
Expand Down Expand Up @@ -107,7 +96,6 @@ 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 @@ -120,41 +108,44 @@ contract ReputationBootstrapper is ColonyExtensionMeta {

// Public

/// @notice Lock the extension, allowing grants to the claimed and preventing new grants
function lockExtension() public onlyRoot unlocked {
isLocked = true;
}

/// @notice Configure whether or not reputation claims come with tokens
/// @param _giveTokens A boolean setting the functionality to true or false
function setGiveTokens(bool _giveTokens) public onlyRoot unlocked {
giveTokens = _giveTokens;
}

/// @notice Set an arbitrary number of grants
/// @param _paid An array of booleans indicated pair or unpaid
/// @param _hashedSecrets An array of (hashed) secrets
/// @param _amounts An array of reputation amounts claimable by the secret
function setGrants(bytes32[] memory _hashedSecrets, uint256[] memory _amounts) public onlyRoot unlocked {
function setGrants(bool[] memory _paid, bytes32[] memory _hashedSecrets, uint256[] memory _amounts) public onlyRoot notDeprecated {
require(_paid.length == _hashedSecrets.length, "reputation-bootstrapper-invalid-arguments");
require(_hashedSecrets.length == _amounts.length, "reputation-bootstrapper-invalid-arguments");
uint256 balance = ERC20(token).balanceOf(address(this));

for (uint256 i; i < _hashedSecrets.length; i++) {
require(_amounts[i] <= INT128_MAX, "reputation-bootstrapper-invalid-amount");
grants[_hashedSecrets[i]] = Grant(_amounts[i], block.timestamp);

emit GrantSet(_hashedSecrets[i], _amounts[i]);
if (_paid[i]) {
uint256 priorGrant = grants[_paid[i]][_hashedSecrets[i]].amount;
if (priorGrant < _amounts[i]) {
totalPaidGrants += _amounts[i] - priorGrant;
} else {
totalPaidGrants -= priorGrant - _amounts[i];
}
require(totalPaidGrants <= balance, "reputation-bootstrapper-insufficient-balance");
}

grants[_paid[i]][_hashedSecrets[i]] = Grant(_amounts[i], block.timestamp);

emit GrantSet(_paid[i], _hashedSecrets[i], _amounts[i]);
}
}

/// @notice Commit the secret, beginning the security delay window
/// @param _committedSecret A sha256 hash of (userAddress, secret)
function commitSecret(bytes32 _committedSecret) public locked {
function commitSecret(bytes32 _committedSecret) public notDeprecated {
bytes32 addressHash = keccak256(abi.encodePacked(msgSender(), _committedSecret));
committedSecrets[addressHash][_committedSecret] = block.timestamp;
}

/// @notice Claim the grant, after committing the secret and having the security delay elapse
/// @param _secret The secret corresponding to a reputation grant
function claimGrant(uint256 _secret) public locked {
function claimGrant(bool _paid, uint256 _secret) public notDeprecated {
bytes32 committedSecret = keccak256(abi.encodePacked(msgSender(), _secret));
bytes32 addressHash = keccak256(abi.encodePacked(msgSender(), committedSecret));
uint256 commitTimestamp = committedSecrets[addressHash][committedSecret];
Expand All @@ -165,13 +156,13 @@ contract ReputationBootstrapper is ColonyExtensionMeta {
);

bytes32 hashedSecret = keccak256(abi.encodePacked(_secret));
uint256 grantAmount = grants[hashedSecret].amount;
uint256 tokenAmount = grants[hashedSecret].amount;
uint256 grantTimestamp = grants[hashedSecret].timestamp + SECURITY_DELAY; // Don't decay during delay window
uint256 grantAmount = grants[_paid][hashedSecret].amount;
uint256 tokenAmount = _paid ? grants[_paid][hashedSecret].amount : 0;
uint256 grantTimestamp = grants[_paid][hashedSecret].timestamp + SECURITY_DELAY; // Don't decay during delay window

require(grantAmount > 0, "reputation-bootstrapper-nothing-to-claim");

delete grants[hashedSecret];
delete grants[_paid][hashedSecret];

uint256 decayEpochs = (block.timestamp - grantTimestamp) / decayPeriod;
uint256 adjustedNumerator = decayNumerator;
Expand All @@ -192,7 +183,7 @@ contract ReputationBootstrapper is ColonyExtensionMeta {

colony.emitDomainReputationReward(1, msgSender(), int256(grantAmount));

if (giveTokens) {
if (tokenAmount > 0) {
require(ERC20(token).transfer(msgSender(), tokenAmount), "reputation-bootstrapper-transfer-failed");
}

Expand Down
14 changes: 6 additions & 8 deletions test-gas-costs/gasCosts.js
Original file line number Diff line number Diff line change
Expand Up @@ -378,13 +378,12 @@ contract("All", function (accounts) {
await colony.setRootRole(reputationBootstrapper.address, true);

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

await reputationBootstrapper.commitSecret(soliditySha3(WORKER, secret));
await reputationBootstrapper.commitSecret(soliditySha3(WORKER, secret), { from: WORKER });
await forwardTime(SECONDS_PER_HOUR, this);

await reputationBootstrapper.claimGrant(secret, { from: WORKER });
await reputationBootstrapper.claimGrant(false, secret, { from: WORKER });
});

it("when bootstrapping reputation with decay", async function () {
Expand All @@ -393,16 +392,15 @@ contract("All", function (accounts) {
await colony.setRootRole(reputationBootstrapper.address, true);

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

await reputationBootstrapper.commitSecret(soliditySha3(WORKER, secret));
await reputationBootstrapper.commitSecret(soliditySha3(WORKER, secret), { from: WORKER });
await forwardTime(SECONDS_PER_HOUR, this);

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

await reputationBootstrapper.claimGrant(secret, { from: WORKER });
await reputationBootstrapper.claimGrant(false, secret, { from: WORKER });
});
});
});
4 changes: 2 additions & 2 deletions test-smoke/colony-storage-consistent.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ contract("Contract Storage", (accounts) => {
console.log("miningCycleStateHash:", miningCycleStateHash);
console.log("tokenLockingStateHash:", tokenLockingStateHash);

expect(colonyNetworkStateHash).to.equal("0x8cf2a9c61511e2ed0426ec96967ddd39c54f4dfae7656aeaf51aad372a94bee7");
expect(colonyStateHash).to.equal("0x245ab8afdffe1bc3c51b33eaddb1d23e6836edc2c634da97459bdec1a47dbb01");
expect(colonyNetworkStateHash).to.equal("0x25e8213be3dc0269def8b888999814007d5c2b7c2b8a6600d425e0453076e920");
expect(colonyStateHash).to.equal("0x97b9839b665bdb38f9875182479694f0783529c36793ff18a58390efdb42c513");
expect(metaColonyStateHash).to.equal("0xff23657f917385e6a94f328907443fef625f08b8b3224e065a53b690f91be0bb");
expect(miningCycleStateHash).to.equal("0x264d4a83e21fef92f687f9fabacae9370966b0b30ebc15307653c4c3d33a0035");
expect(tokenLockingStateHash).to.equal("0x983a56a52582ce548e98659e15a9baa5387886fcb0ac1185dbd746dfabf00338");
Expand Down
Loading

0 comments on commit 5b12c64

Please sign in to comment.