-
Notifications
You must be signed in to change notification settings - Fork 0
Reputation System
For each regulator, we have a reputation vector of arbitrary dimensions. Each dimension is called reputation context and represents one aspect of audit, such as Financial Audit, Smart Contract Audit, Compliance audit, etc.
Similar to the reputation vector described in Global Reputation System, except that the project-specific reputation vector can only be updated by the investors of that project
Reputation update is a per-project operation. Investors of a project are only responsible to update the corresponding project-specific reputation system, and the global reputation system is passively updated by continuously aggregating project-specific reputation vectors.
Reputation update can only be initiated at most once for each milestone of a project, between the starting time and the deadline of the milestone, by any address.
Once the reputation update is initiated, a proxy voting is started, and investors of a project delegates their votes to regulators.
We convert project-specific tokens to votes by using the formula
votes = tokens * avg_price_per_tokens
where avg_price_per_tokens is the average ICO price in unit of ethers of the project. Essentially, we convert tokens to ethers, a standard unit, which is useful for updating our global reputation system.
Next step, for each reputation context, investors delegates their votes to regulators. Votes for each reputation context are independent, that is, investors have the same number of votes for each reputation context.
For instance, an investor has 100 votes, and we have three reputation context -- context_a, context_b, context_c, three regulators regulator_1, regulator_2, regulator_3. The following table shows a potential distribution of votes among regulators per reputation context.
| reputation context | regulator_1 | regulator_2 | regulator_3 |
|---|---|---|---|
| context_a | 100 | 0 | 0 |
| context_b | 50 | 0 | 50 |
| context_c | 10 | 50 | 40 |
Let denotes the above proxy voting matrix per investor for investor k:
Then, the i-th column is the proxy voting reputation vector per investor of i-th regulator.
Summing up all investor's proxy voting matrix per investor, we get the proxy voting matrix P
Once the proxy voting finishes, the project-specific reputation vectors, denoted as , are updated:
where 0.9 is the discount factor
Also, the global reputations vectors are updated using the following formula
where is the global reputation vector of i-th regulator.
contract ReputationSystem {
struct Context {
uint lastUpdated;
uint votes;
mapping(bytes32 => uint) pendingVotes;
uint totalPendingVotes;
}
struct PollRequest {
uint minStartTime;
uint maxEndTime;
// Let price = basic unit of a Token / Wei
// then price can possibly be a floating number, which solidity
// does not support
// Define pseudoPrice = price if price >= 1, and set priceGteOne = true
// otherwise pseudoPrice = 1/price, and set priceGteOne = false
// priceGteOne == true => basic unit of a Token / Wei
// priceGteOne == false => Wei / basic unit of a Token
uint pseudoPrice;
bool priceGteOne;
}
struct Reputation {
// member address => context => votes
mapping(address => mapping(bytes32 =>Context)) repVec;
}
// reputation system id => reputation struct
mapping(bytes32 => Reputation) reputations;
uint pollNonce = 0;
mapping(bytes32 => uint) pollIdToNonce;
mapping(uint => bytes32) nounceToPollId;
mapping(bytes32 => PollRequest) pollRequests;
CarbonVoteX public carbon;
/*
Register a poll request for a milestone for a project
Any address can then start a proxy voting with CarbonVoteX with [pollId], providing the initiation time is
within [minStartTime, maxStartTime]
Only accessible to specific addresses
Typically, this function is called by a milestone controller when a project deploys milestones
@param pollId the id of the poll to request, generated using keccak256(projectNameHash, miletoneNameHash)
@param minStartTime the minimum starting time (unix timestamp) to start a poll (by calling carabon.register())
@param maxStartTime the maximum starting time (unix timestamp) to start a poll
*/
function registerPollRequest(bytes32 pollId, uint minStartTime, uint maxStartTime) public {
require(pollRequests[pollId].minStartTime == 0 && pollRequests[pollId].maxEndTime == 0);
pollRequests[pollId].minStartTime = minStartTime;
pollRequests[pollId].maxEndTime = maxEndTime;
}
/*
Modify a poll request for a milestone for a project
Any address can then start a proxy voting with CarbonVoteX with [pollId], providing the initiation time is
within [minStartTime, maxStartTime]
Only accessible to specific addresses
Typically, this function is called by a milestone controller when a milestone's starting time and/or deadline is modified
@param pollId the id of the poll to request, generated using keccak256(projectNameHash, miletoneNameHash)
@param minStartTime the minimum starting time (unix timestamp) to start a poll (by calling carabon.register())
@param maxStartTime the maximum starting time (unix timestamp) to start a poll
*/
function modifyPollRequest(bytes32 pollId, uint minStartTime, uint maxEndTime) public {
require(pollRequests[pollId].minStartTime =! 0 && pollRequests[pollId].maxEndTime =! 0);
pollRequests[pollId].minStartTime = minStartTime;
pollRequests[pollId].maxEndTime = maxEndTime;
}
/*
Before starting a poll, we verify the current time complies with the info providied in the
corresponding poll request
@param pollId the id of the poll to validate, generated using keccak256(projectNameHash, miletoneNameHash)
*/
function validatePollRequest(bytes32 pollId) private returns (bool) {
// note that if pollId does not exist in pollRequests => maxEndTime < now => no need to check
// if pollId exists
return pollRequests[pollId].minStartTime <= now && now < pollRequests[pollId].maxEndTime;
}
/*
Start a poll by any address
@param pollId the id of the poll to start, generated using keccak256(projectNameHash, miletoneNameHash)
*/
function startPoll(bytes32 pollId) public {
require(validatePollRequest(pollId));
// 20 blocks is roughly 5 min
// for testing only
uint startBlock = block.number + 20;
uint endBlock = startBlock + 20;
carbon.register(pollId, startBlock, endBlock, pollRequests[pollId].tokenAddr);
pollNonce += 1;
pollIdToNonce[pollId] = pollNounce;
}
/*
Delegate votes to a principal in a poll, called by proxies
@param id the id of the reputation system
id cannot be the global reputation system's id
@param member the address of a member
@param context the context of the reputation vector
@param pollId the id of the poll, from which we delegate votes
@param votesInWei the number of Wei to delegate
*/
function delegate(bytes32 id, address member, bytes32 context, bytes32 pollId, uint votesInWei) public {
// First convert votes in wei to votes in project's token's basic unit
uint votes = convertVotes(votesInWei, pollRequests[pollId].pseudoPrice, pollRequests[pollId].priceGteOne);
// This basically checks if the member has enough votes
carbon.vote(pollId, keccak256(id, member, context), votes);
// record voting results, here we use votes in wei as our standard unit across projects
// update project-specific repuation
reputations[id].repVec[member][context].pendingVotes[pollId] += votesInWei;
reputations[id].repVec[member][context].totalPendingVotes += votesInWei;
// update global reputation
reputations[globalId].repVec[member][context].pendingVotes[pollId] += votesInWei;
reputations[globalId].repVec[member][context].totalPendingVotes += votesInWei;
}
/*
Return the number of effetive votes
@param id the id of the reputation system
@param member the address of a member
@param context the context of the reputation vector
*/
function getVotes(bytes32 id, address member, bytes32 context) public {
return requireUpdate(id, member, context) ? 0 : reputations[id].repVec[member][context].votes;
}
/*
Check if a context of a reputation vector needs update
@param id the id of the reputation system
@param member the address of a member
@param context the context of the reputation vector
*/
function requireUpdate(bytes32 id, address member, bytes32 context) {
Context storage context = reputations[id].repVec[member][context];
return context.pendingVotes != 0;
}
/*
Update a context of a reputation vector
Principals have to update their reputation vectors if somebody has delegated
votes to them
*/
function updateRepVecContext(bytes32 id, address member, bytes32 context) {
Context storage context = reputations[id].repVec[member][context];
uint lastUpdated = context.lastUpdated;
if (pvCount > 100 + lastUpdated) {
// we only update the most recent 100 proxy votings
// this avoids potential out-of-gas errors
lastUpdated = pvCount - 100;
}
// update votes from the oldest proxy voting to the latest proxy voting
for (int i = lastUpdated + 1; i <= pvCount; i++) {
bytes32 pollId = nounceToPollId[i];
if (carbon.expired(pollId)) {
// the poll has finished, now we update the rep vec
context.totalPendingVotes = 0;
context.pendingVotes[pollId] = 0;
context.votes = (context.votes*9 + context.pendingVotes[pollId])/10;
context.lastUpdated = i;
}
}
}
function batchUpdateRepVecContext(bytes32 id, address member, bytes32[] contexts) {
for(uint i=0; i<contexts.length; i++) {
update(id, member, contexts[i]);
}
}
}[1] Blockchain Based Reputation Systems - Doug King | March 2016 Presentation Night
- video: https://www.youtube.com/watch?v=fj8nP2Bci-4
- slide: https://docs.google.com/presentation/d/1O5PdYj1ZKMqZBIKPTVlYxpPC0Nvz9gAnfJ3uaIrjK3Q/edit?usp=sharing
[2] Randy Farmer and Bryce Glass. 2010. Building Web Reputation Systems (1st ed.). Yahoo! Press, USA
[3] Building Web Reputation Systems Google Talk
[4] Decentralized Reputation System for Transaction Networks
[5] Colony: A platform for open organizations
- website: https://colony.io/
[6] Backfeed: Spreading Consensus
- website: http://backfeed.cc/
[7] Fabriq: Reputation Management System - London 2015