Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contracts first version #13

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open

Contracts first version #13

wants to merge 22 commits into from

Conversation

bingen
Copy link
Collaborator

@bingen bingen commented Jun 21, 2018

First version of contracts and tests.
Fixes #1

bingen added 10 commits June 14, 2018 18:34
As there can only be one application per registry entry, and only one
challenge per application/entry, we can use the same id for all of them.
Closes #3.
Besides, add CI.
Now Voting app can resolve challenge when vote is over without the
need for another call from Curation app.
`manifest.json` and `arapp.json`
@bingen bingen self-assigned this Jul 9, 2018
Challenge amount was always being used. Although it's usually going to
be the same (min deposit), it was not correct.
Copy link

@izqui izqui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WIP review

mapping(bytes32 => Challenge) challenges;
mapping(address => mapping(uint256 => bool)) usedLocks;

event NewApplication(bytes32 entryId, address applicant);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should index entryIds

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, there already was an issue for this, #12 , I just forgot ;-) I'm going to add it to the description so it's closed automatically.

uint64 date;
bool resolved;
uint256 amount;
uint256 lockId;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may run into issues with lockIds changing as when you remove a lock in the staking app the ids can change: https://github.com/aragon/aragon-apps/blob/b968e3ec9ab70cf6cfbfa5ebca8a58460669e7d1/future-apps/staking/contracts/Staking.sol#L173-L178

If a user has an unrelated lock with id = 0 and then creates a lock for creating a challenge with id = 1, if the lock 0 gets unlocked before the challenge is resolved, lock 1 will be lock 0 and unlocking will fail as there is no lock 1.

I think this issue may apply to PLCR voting as well. We may need to rethink how locks get stored in the Staking app.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, since lock ids are exposed to the outside world, they should be immutable.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created aragon/aragon-apps#406 for this


function _checkLock(address user, uint256 lockId, uint64 date) internal returns (uint256) {
// check lockId was not used before
require(!usedLocks[user][lockId]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though we can be fairly sure that the staking app is not malicious and it is not going to re-enter, we can have this check after the staking.getLock(msg.sender, lockId) to make sure the storage is correct after the external call.

Ideally we would do a STATICCALL but it is too cumbersome in this version of solidity.

if (application.amount < minDeposit) {
registry.remove(entryId);
staking.unlock(application.applicant, application.lockId);
staking.unlock(msg.sender, lockId);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we require a having a lock for this case if the challenge is immediately resolved?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point!

// check application doesn't have an ongoing challenge
require(!challengeExists(entryId));
// check locked tokens
uint256 amount = _checkLock(msg.sender, lockId, getTimestamp().add(applyStageLen));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This amount is never checked (it is just saved in the Challenge struct) so I assume there is no minimum amount to stake in order to challenge. Couldn't this become a spam issue in which someone can just challenge all applications without having to stake and risk virtually any money?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's checked inside _checkLock.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally missed it, 👍

assembly {
mstore(add(executionScript, 0x20), spec)
mstore(add(targetBytes, 0x20), target)
mstore(add(executionScript, 0x24), mload(add(targetBytes, 0x2C)))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not store the target address directly here?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to just mload(target), shift the required bits and mstore that. Otherwise we are reading from unknown memory and that's a little scary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get why we are reading from unknown memory, but my assembly understanding is limited yet. Anyway, you are right, this way is too messy, I did it as you say, just shifting the address, way better. Thanks!

}
}

function registerApplication(bytes32 entryId) isInitialized public {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this function can only be used for unchallenged applications, I would rename to signal that: registerUnchallengedApplication(bytes32 entryId)


// insert in Registry app
registry.add(application.data);
application.registered = true;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to remove an entry once registered and recover the deposit?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, not right now. This possibility doesn't appear in any of the documents (white paper, requirements or specs) as far as I remember, but I think it makes total sense. I'll add it, but also pinging @lkngtn so he is aware of this use case.

// Unlock challenger tokens from Staking app
reward = challenge.amount.mul(dispensationPct) / PCT_BASE;
// Redistribute tokens
staking.unlockAndMoveTokens(challenge.challenger, challenge.lockId, application.applicant, reward);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also an issue with staking, but it would be more descriptive if the function signature referenced the fact that the unlock is partial: staking.partialUnlockAndMove(...)

// amount * (voter / total) * (1 - dispensationPct)
uint256 reward = amount.mul(voterWinningStake).mul(PCT_BASE.sub(dispensationPct)) / (totalWinningStake * PCT_BASE);
// Redistribute tokens
staking.unlockAndMoveTokens(loser, loserLockId, msg.sender, reward);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be cleaner if the curation app just took all the tokens from the challenger or applicant onresolveChallenge(...). This way we don't need to modify the lock every time, being more efficient.

Also in terms of the user that lost some tokens, I think it is better to remove them from their account ASAP as there is no way they are going to recover them.

Copy link
Collaborator Author

@bingen bingen Jul 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Completely agreed. Plus:

  • It would solve the TODO 3 lines below about that truncating issue.
  • It would follow the same logic as our PLCR

@bingen
Copy link
Collaborator Author

bingen commented Jul 16, 2018

Fixes #12 (per review).

@bingen
Copy link
Collaborator Author

bingen commented Jul 16, 2018

Fixes #4
Closes #5

Registry app must now be initialized.
Locks now have start date too.
* @param _voting New Voting app
*/
function setVotingApp(IVoting _voting) authP(CHANGE_VOTING_APP_ROLE, arr(voting, _voting)) public {
_setVotingApp(_voting);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite dangerous if there are any challenges open, as they don't have a reference to the voting app, but rely on the global voting instance. I would remove the ability to hot swap the voting app after initialization.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!!

mstore(add(executionScript, 0x40), entryId)
}

uint256 voteId = voting.newVote(executionScript, "");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: currently PLCR is not executing scripts https://github.com/aragonlabs/plcr/pull/1/files#r207182982

}

// check locked tokens
uint256 amount = _checkLock(msg.sender, lockId, getTimestamp(), getTimestamp().add(applyStageLen));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The end date should be when the voting ends, but since we don't have any guarantee that resolveChallenge(...) will be called at that point, the lock shouldn't have an end date to avoid someone pulling the tokens deposited for the challenge before it can be resolved.


// Remove from Registry app
application.registered = false;
registry.remove(entryId);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have special logic for whether the application was already in the registry or the challenge is done during the applyStageLen period. This isn't causing a crash because the registry doesn't care whether an entry was present in the registry before attempting to delete it: https://github.com/aragonlabs/registry/pull/3/files#r207185327

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, actually I think that's the reason I was not doing those checks on Registry app: it was making things easier here, as the operation would become "idempotent", i.e., it doesn't matter what's the state, the result will always be the desired (in this case, having the entry out of the registry).

vote.votersRewardPool = amount - reward;

// unlock tokens from Staking app
staking.unlock(application.applicant, application.lockId);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the application enters the registry we shouldn't remove its lock


// Remove challenge, and application if needed
if (vote.result == true) {
delete(applications[entryId]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing both application and challenge from storage once resolved is going to make it real hard to get historical data from the app. This would be a major change to how data is stored here, but let's think about it.

// rewardPool * (voter / total)
uint256 reward = vote.votersRewardPool.mul(voterWinningStake) / vote.totalWinningStake;
// Redistribute tokens
staking.moveTokens(address(this), msg.sender, reward);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should emit an event as PLCR does when tokens are claimed

if (!application.registered) {
application.registered = true;
// insert in Registry app
registry.add(application.data);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every time an application is registered or removed, the curation app should emit an event. With the current state in which we remove application and challenge data right away, it would be very hard for observers of the contract what is going on

}

function _setMinDeposit(uint256 _minDeposit) internal {
minDeposit = _minDeposit;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please log when any of the params change.

import "./interfaces/IVoting.sol";


contract Curation is AragonApp {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should consider all the cases in which locks are created to the curation app that may result in tokens getting locked forever, for example, locking tokens to create an application or challenge and being frontrun in the action. We need a mechanism to unlock when the locks haven't been used.

Comment on the topic for PLCR: https://github.com/aragonlabs/plcr/pull/1/files#r206916590

Copy link
Collaborator Author

@bingen bingen Aug 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, another related example: #16
Right now nobody is winning the Vote, so tokens are being locked.

@bingen bingen added this to the sprint: 1.2 milestone Sep 10, 2018
@sohkai sohkai removed this from the sprint: 1.2 milestone Jan 2, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants