Skip to content

Reputation System

Timothy Wang edited this page May 10, 2018 · 9 revisions

Reputation System

Global 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.

Project-specific Reputation System

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 Mechanism

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.

Preliminary Implementation

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]);
    }
  }

}

Reference:

[1] Blockchain Based Reputation Systems - Doug King | March 2016 Presentation Night

[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

[6] Backfeed: Spreading Consensus

[7] Fabriq: Reputation Management System - London 2015

Clone this wiki locally