proof-of-individuality (POI)
anti-sybil token

development road map

  • create a DApp that lets anyone register a contract, and that groups these contracts into groups on a monthly or so basis, and that recycles old contracts after they’ve been in use one cycle
  • publish the framework on so that others can see how the DApp works
  • add video-hangout address generator and give each group an address each cycle
  • add verification system so peers in groups can verify each other during the hangouts
  • start connecting other services to the POIs
  • add more features, financial incentives (deposits to prevent 10000-billion-account attacks etc), other
The POI project is openly developed. Send your code to and we'll publish it here on this page, and we'll probably give you a bit of money too. The project will grow organically as more components are developed.

Project status:

Proof of concept 80 % complete. Looking for help with webrtc stuff. Join on slack,



contract poi {
    bool debug;
    uint blockNum;

    uint groupSize;
    bytes32 entropy;
    uint public numUsers;
    mapping(address => bytes32) public userHash;
    //mapping(bytes32 => address) public userAddress;
    mapping(address => uint) public userGroup;

    // max value of a sha3 hash
    bytes32 maxHash = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
    enum Phases { Registration, Commitment, Verification } 
    event Registration(bytes32 userHash);
    event Commitment(bytes32 userHash, uint group);
    event Verification(bytes32 userHash);

    Phases phase;
    uint public genesisBlock;
    uint public registrationBlock;
    uint public commitmentBlock;
    uint public validityBlock;
    function blockNumber() constant returns(uint){ if (debug) { return blockNum; } return block.number; }
    function numGroups() constant returns(uint){ return numUsers / groupSize;}
    function poi(){
        debug = true;
        groupSize = 5;
        entropy = sha3(block.blockhash(block.number));
        genesisBlock = block.number;
        registrationBlock = genesisBlock + 7;
        commitmentBlock = registrationBlock + 3;
        validityBlock = commitmentBlock + 20;
        phase = Phases.Registration;
    function register() returns(bool success){
        if ((blockNumber() > registrationBlock) // registation period over
        || (userHash[msg.sender] != bytes32(0))) return; // already registered
        // generate a hash for the given user, using previous entropy, 
        // senders address and current blocknumber.
        bytes32 h = sha3(entropy, msg.sender, block.blockhash(block.number));
        entropy = h;
        userHash[msg.sender] = h;
        //userAddress[h] = msg.sender;
        return true;
    function commit() returns(bool success){
        if ((blockNumber() < registrationBlock) // registation period not yet over
        || (blockNumber() > commitmentBlock) // commitment period over
        || (userGroup[msg.sender] != 0)) return; // group already assigned

        phase = Phases.Commitment;
        // deterministically assign user to random group (1-indexed)
        // based on number of users, group size and user hash;
        userGroup[msg.sender] = uint(userHash[msg.sender]) / (uint(maxHash) / numGroups()) + 1;
        Commitment(userHash[msg.sender], userGroup[msg.sender]);
        return true;
    function verify(bytes32 data, uint8 v, bytes32 r, bytes32 s ) returns(bool success){
        if ((blockNumber() < commitmentBlock) // commitment period not yet over
        || (blockNumber() > validityBlock) // verification period over
        || (userGroup[msg.sender] == 0)) return;

        phase = Phases.Verification;
        // TODO :)
        address signer = ecrecover( data, v, r, s);
        // is the proof provided by a user in the same group
        if (userGroup[signer] == userGroup[msg.sender]) {
            return true;
    function _incBlock() { if (debug) blockNum++; }
    function _myAddressHelper() constant returns(address){ return msg.sender; }
    function _myGroupHelper() constant returns(uint group) {
        return userGroup[msg.sender];


The POI protocol is based on fully anonymous anti-sybil assets. The hangouts are pseudonymous and essentially anonymous, since people could even wear Guy Fawkes masks and it would still work. The contract sketch below shows how the POI protocol generates new assets every verification round.

/* component of the Proof-of-individuality (POI) system. Generates new POIs each month, anonymous and un-traceable */

contract generatePOItokens{
    address owner;
    string public name;
    string public symbol;
    uint8 public decimals;
    mapping (address => uint256) public balanceOf;

    event Transfer(address indexed from, address indexed to, uint256 value);

    function generatePOItokens(address[] verifiedUsers) {
        owner = msg.sender;
        balanceOf[owner] = verifiedUsers.length;            // Give the creator all initial tokens                    
        name = "POI";                                       // Set the name for display purposes     
        symbol = "POI";                                     // Set the symbol for display purposes    
        decimals = 0;                                       // Amount of decimals for display purposes        
      /* Send POIs to every verified address */

        for (uint i = 0; i < verifiedUsers.length; i++)
           balanceOf[owner] -= 1;                                              
           balanceOf[verifiedUsers[i]] += 1;
           Transfer(owner, verifiedUsers[i], 1);            // Notify anyone listening that this transfer took place

    function depricatePOIs() {
     if (msg.sender == owner) suicide(owner);

contract POIscheduler{

address[] verifiedUsers;
address POIaddress;

uint public genesisblock;
uint public roundLength;
uint public nextRound;

    function POIscheduler(){
    owner = msg.sender;
    genesisblock = block.number;
    roundLength = 3000; // set POI pseudonym parties to happen once a month
    nextRound = genesisblock + roundLength;
    function issuePOIs() {
        // depricate old POIs
        POIaddress = new generatePOItokens(verifiedUsers);
        /* schedule a new POI round one month from now */
        nextRound += roundLength;

    function scheduleCall() public {
     /* ethereum-alarm-clock is a DAO for excecuting scheduled calls */
    	address scheduler = 0x26416b12610d26fd31d227456e9009270574038f; // alarm service v0.7 on the morden testnet

        // the 4-byte signature of the local function we want to be called.
        bytes4 sig = bytes4(sha3("issuePOIs()"));

        // approximately 1 month from now
        uint targetBlock = nextRound;

        // the 4-byte signature of the scheduleCall function.
        bytes4 scheduleCallSig = bytes4(sha3("scheduleCall(bytes4,uint256)"));, sig, targetBlock);

    function verifyPOI (address v) constant returns (string){
	if (generatePOItokens(POIaddress).balanceOf[v]==0){
		return "account does not have a valid POI";
	return "account has a valid POI";

Anti-Sybil Fuel (ASF)

To make the hangouts more immersive and to make it possible for the group to reward each other's attention, the protocol uses a reward system that I've called anti-sybil fuel (ASF). This is a currency that people use to reward one another in the hangouts, each participant has 5000 ASF that they can give away, and everyone needs to receive at least 4000 ASF to be verified.

Anti-sybil fuel (ASF) is used to gamify the POI hangouts. Participants can 
use it to "hijack each other's attention", which makes it easier for the 
POI community to keep high standards. Each user gets 5000 "anti-sybil fuel" points, 
and then rewards the other 4 users for their attention. This makes it possible 
for 4 people to put peer-pressure on the 5th if the 5th person isn't focused 
on the joint attention test. 

contract AntiSybilFuel { 

  uint public genesisblock;
  uint public deadline;

    struct ASF {
        uint256 initial_supply;
        uint256 rewarded;
        uint256 given;

    mapping (address => ASF) public ASFbalances;
    address[] participants;

    function AntiSybilFuel(address[] hangoutGroup) {

        for (uint i = 0; i < hangoutGroup.length; i++)
            ASFbalances[hangoutGroup[i]].initial_supply += 5000;

            genesisblock = block.number;
            deadline = genesisblock + 3000; // hangouts are 15 minutes long


    function rewardASF(address _to, uint256 _value) {

    /* If the sent amount is bigger than the maximum amount one can give, send max amount */
        if (ASFbalances[msg.sender].given + _value >5000)
        _value = _value-((ASFbalances[_to].rewarded+_value)-5000);

    /* If the sent amount is bigger than the maximum reward limit, send max amount */
        if (ASFbalances[_to].rewarded + _value >5000)
        _value = _value-((ASFbalances[_to].rewarded+_value)-5000);

    /* transfer the anti sybil fuel */
        ASFbalances[msg.sender].given +=_value;
        ASFbalances[_to].rewarded +=_value;
        ASFbalances[msg.sender].initial_supply -=_value;


   /* after 15 minutes, each users that has been awarded 4000 ASF or more, and 
      given out 4000 or more, is seen as verified and given a POI token */

   /* the closeSession function can be called by anyone in the hangut once the deadline has passed */

   function closeSession(){
      if(block.number4000 && participants[i].rewarded >4000)
        address[] verifiedUsers.push(participants[i])

       */ pass verifiedUsers into a contract that generates POIs, together with verifiedUsers from all other hangouts */


       */ the POI contract will then pass the full list into the contract generatePOItokens */