SLCR Voting in Solidity ~Introduction for TCR~
PLCR Voting has been a good reference for voting system especially for rational TCR (Token Curated Registry) implementations and it seems de-facto standard now. In this article I talked about little changes to the PLCR (Partial-Lock-Commit-Reveal) voting system with the idea I call SLCR (Simultaneously-Lock-Commit-Reveal) voting by bringing two phased locking for tokens (staking and locking) for the voting system for TCR applications.
What is TCR ?
There has been a lot of hypes around TCR for a while and there are not so many TCR tangible production implementations in the world except AdChain, FOAM and the most famous one TCR Party until now.
This paper explains well in the first place for everyone to understand the concept, the required parameters and some threats we might have to tackle with in the near future, but let’s go over basic stuff here before going to SLCR (Simultaneously-Lock-Commit-Reveal) voting system that I considered to improve from PLCR (Partial-Lock-Commit-Reveal). [1]
As explained in the paper above, Token-curated registries are decentralized curated lists with intrinsic economic incentives for token holders to curate the list’s contents judiciously.
Shopping lists, lists of “good” colleges, lists of America’s most wanted criminals, and many more. Most lists can be abstractly classified as either whitelists or blacklists, and in both cases the contents of a list uniformly satisfy some criteria (things I need to cook, colleges whose graduates on average exit debt within 10 years, individuals with FBI bounties over $100,000)
It’s beneficial for consumers that only the legitimate listees are kept in the decentralized place by token holders who are incentivized to maintain the list with intrinsic tokens.
Let me illustrate:
You are an owner of one of decent Italian restaurants nearby but need more attentions from people in your area. People (consumers) want to enjoy night-out with family but your restaurant hasn’t been rated good or not found even by them. You (candidates) can submit an application for TCR listing place that has been maintained by operators (token holders) who are responsible to keep the quality of that restaurant list and to lure consumer’s attentions by using the listing own economic token in the market. If your application was accepted by token holders and as long as they do not challenge your listing to push you out, your restaurant would be kept in the listing to get regular crowds and new people.
Here’s a consumer/candidates/token holders balancing triangle with favorable incentive design as explained in the article. Bilateral incentivized relations between consumers over token holders and token holders over candidates and candidates over consumers are also important.
Voting system has come !!
It’s said that the idea of PLCR (Partial-Lock-Commit-Reveal) was mentioned in this article firstly for the Colony project. PLCR voting is an efficient system for token-weighted voting which enables a user to participate in multiple polls simultaneously with their tokens while preventing the double-voting of tokens within polls. [2]
To prevent bandwagoning and groupthink, the article introduced a commit-reveal mechanism to meet these three requirements by hiding ongoing outcome of the vote until the vote has ended.
- Diversity of opinion: Each person should have private information even if it’s just an eccentric interpretation of the known facts.
- Independence: People’s opinions aren’t determined by the opinions of those around them.
- Decentralization: People are able to specialize and draw on local knowledge.
Therefore it’s important sealing votes in an envelope until it’s revealed, to hide who voted and how many tokens are voted For and Against to which party to prevent voters from those effects (bandwagoning and groupthink). The first step in the commit-reveal mechanism is to seal voter’s option (For or Against) with its salt number into the hash value, then as the second step the vote is unsealed and the vote option can be added to the total tally by inspecting the hash value if it matches in the vote contract.
As this voting system wasn’t invented for TCR specific but the Colony project, the PLCR allows voters to use the “same locked tokens” in the vote contract for committing multiple polls simultaneously under the partial-lock condition. Because the tokens locked in the vote contract seem to sound the voting rights for each poll and it was not designed for the losing party leading to a loss of her staked tokens after the poll ended. The amount of tokens just affect the voting power during polls but voted tokens are not taken from individual voters.
Please refer to the ConsenSys reference and its implementation. [3]
partial-lock: although committed tokens are locked, you are able to and are highly encouraged to use those same “locked” tokens to commit votes in multiple polls at the same time
I was thinking to lock those tokens that have been locked (in other word “staked”) in the vote contract on each poll simultaneously as well, which means a voter’s able to vote her tokens exactly the same number of the staked tokens in the vote contract in total.
Let me illustrate more clearly:
When 10 Token are deposited to the voting contract and Alice voted 6 of 10 tokens from her stake and it’s under the commitment state, we thought only 4 tokens must be usable for a different poll. Why ? Because if Alice could use the deposited 10 Tokens for every poll then the liquidity from her staked token would be excess. For instance, especially for a big token holder like Alice, when she had 10,000 tokens in case of TCR application and staked it and at the same time had chances for 50 polls because she wanted to challenge those candidates for some reasons, she would have covert and enormous 500,000 voting powers at commitment stage in total. When her votes are brought under the light (Reveal period) then people will notice she voted a huge amount of tokens and might have influenced some applications at the same time to get control and give impression at some points. This phenomenon seems exponential for token holders who have more bigger amount of tokens. So a motivation here is to avoid partial-lock behavior that might cause excess liquidity for multiple polls in the commitment stage and lock tokens for every poll from Alice’s staked tokens in the vote contract. This looks more flawless for TCR application. And once those commitments are revealed her voting power would be regained.
Let me continue: This is a snippet from the PLCR Voting article. [4]
As an illustration: a user loads 10 tokens into the PLCR Voting contract. The user then commits 10 tokens in poll A and six tokens in poll B. After revealing in poll A, six tokens remain locked in poll B but the user can withdraw four tokens.
In this implementation the user can commit the 2 polls with the same staked tokens under the vote contract simultaneously. What if she loses in both 2 polls, being at the losing party ? She could gamble her staked tokens as much as possible for challenges in TCR application and be looking for a chance to win in one of them.
In a naive non-PLCR polling system, a user might lock tokens in a smart contract describing a single poll. This is not an ideal solution because it prevents the user from participating in multiple polls simultaneously with the same tokens. If the user’s tokens are locked in some smart contract, another smart contract cannot `transferFrom` the user to lock them itself.
This is fine, we should not prevent the user from participating in multiple polls simultaneously, but with not the same tokens that have been locked in the vote contract, especially for TCR application.
Here’s an idea SLCR (Simultaneously-Lock-Commit-Reveal) voting system that is more efficient for token-weighted voting which enables a user to participate in multiple polls simultaneously with their tokens while preventing the double-voting of tokens within poll and preventing the double-using of the staked tokens across polls. “Simultaneous” means the layered locks of staking of tokens from a user to the voting contract firstly and locking the token amount for each poll and the total amount of tokens of the user does not exceed the staked token in the voting contract even during the commitment phase secondary. There are two phases for this amendment, staking and locking.
- Token staking: Until the poll end, Alice should not be able to transfer her tokens to Bob or somebody else. Required amount of tokens should be staked in the voting contract and the tokens can be withdrawn as long as they are not used for votes after reveal phase.
- Poll locking: Total amount of tokens should match the sum of tokens used in Alice’s polls in total. Alice should not be able to double-use the staked tokens across polls. After reveal phase, she can withdraw those tokens from the voting contract to the token contract mapping to hers.
This is my repository. Please note that this is still in progress and has not completed yet. [5]
Improvements
Sorry it’s been long but there are not so many changes from PLCR (Partial-Lock-Commit-Releav). One aspect of this writing was just to help my understanding.
As the double-linked list for polls is supposed to be sorted in the ConsenSys implementation, the getLockedTokens function was simple enough to get the last node’s token number. Let’s have a look of this implementation.
/**
* @dev Gets top element of sorted poll-linked-list
* @param _voter Address of user to check against
* @return Integer identifier to poll with meximum number of tokens committed to it
*/
function getLastNode(address _voter) constant public returns (uint256 pollID) {
return dllMap[_voter].getPrev(0);
}/**
* @dev Gets the numTokens property of getLastNode
* @param _voter Address of user to check against
* @return Maximum number of tokens committed in poll specified
*/
function getLockedTokens(address _voter) constant public returns (uint256 numTokens) {
return getNumTokens(_voter, getLastNode(_voter));
}
However in SLCR (Simultaneously-Lock-Commit-Reveal) we have to count all used tokens in each poll. Here’s an idea of my implementation. We may not need the sorting capability in the double-linked list for nodes but let’s keep it for now.
/** Proposal
* @dev Gets the numTokens property of all polls
* @param _voter Address of user to check against
* @return Total number of tokens committed in polls
*/
function getLockedTokens(address _voter) constant public returns (uint256 numTokens) {
// Get the last node in the list and the number of tokens in that node
uint256 nodeID = getLastNode(_voter);
uint256 tokensInNode = getNumTokens(_voter, nodeID); uint256 totalTokens; // Interate backwards through the list until reaching the root node
while(nodeID != 0) {
// Get the number of tokens in the current node
tokensInNode = getNumTokens(_voter, nodeID);
totalTokens = totalTokens.add(tokensInNode);
nodeID = dllMap[_voter].getPrev(nodeID); // Get the previous ddl node bacward
} // If a list is emply, zero value will be returned
return totalTokens;
}
Or we don’t need to do that even. We just need to modify voteTokenBalance for Alice by subtracting when her vote is committed and adding back when her vote is revealed and rescued as below. As her vote is committed, her staked tokens reduce and she’s not able to use the same tokens for another vote.
These functions need to be updated about the voteTokenBalance accordingly. Feel free to post a comment, PR and join the discussion to improve the code in github.
Additionally we would want to improve the registry contract in TCR as well without touch & remove action, etc in the next article. TCR and voting systems should be more realistic and rational more than ever, it’s not a hype.
Conclusion
I introduced an idea SLCR (Simultaneously-Lock-Commit-Reveal) voting system that is more efficient for token-weighted voting which enables a user to participate in multiple polls simultaneously with their tokens while preventing the double-voting of tokens within poll and preventing the double-using of the staked tokens across polls.