├── README.md ├── poi.sol ├── subcontracts ├── depositGovernance.sol ├── generatePOIs.sol ├── hangout.sol ├── poi.sol └── registration.sol └── whitepaper.pdf /README.md: -------------------------------------------------------------------------------- 1 | #Proof of Individuality 2 | 3 | POIs (proof-of-individuality) are smart-assets that are hosted on the Ethereum network. They solve [one of the hard problems in crypto](https://www.reddit.com/r/CryptoUBI/comments/2v2gi6/proof_of_identityproof_of_person_the_elephant_in/) - how do you prove that a person only has one account within the system? 4 | 5 | Join the POI project on Slack, 6 | http://poiproject.herokuapp.com 7 | 8 | ##How 9 | 10 | Through person-to-person verification. Users are grouped together by random in groups of 5 or so, and every group does a video hangout at the exact same time, that lasts around 10 minutes or so. Users check so that the others in their group aren't doing another hangout at the same time. They then sign each other's POIs and verify them. Once the hangouts are finished and all POIs have been verified, everyone will know that each POI represents a unique human being. 11 | 12 | ### Overview of the subcontracts 13 | 14 | 15 | **depositGovernance.sol** 16 | 17 | Manages the anti-spam deposits, and also includes a system to vote on the size of the anti-spam deposit. 18 | A new contract is created each round, and the old one suicides. 19 | 20 | **generatePOIs.sol** 21 | 22 | Issues undivisible POI tokens to all verified users. A new contract is created after each round, and the old one suicides. 23 | 24 | **hangout.sol** 25 | 26 | Manages the verification within the hangouts. Includes the ASF system that 'gamifies the hangouts' by using a point system 27 | instead of one-vote-per-person, allowing users in a hangout to direct and steer each others attention more. 28 | A new contract is created for each hangout. 29 | 30 | **registration.sol** 31 | 32 | Manages the registration of users each month, assing them into groups by random, and boots up hangout contracts. 33 | New contract is created each round. 34 | 35 | **poi.sol** 36 | 37 | Main contract. Boots up the other contracts, manages scheduling, and integrates some function call 38 |

39 | 40 |

41 | ### FAQ 42 | 43 | **Could you expand a bit on how the anti-spam mechanism works?** 44 | 45 | The randomization makes it hard to gain control of entire groups, but controlling 2x or more the total number of users is a way to bypass that. Anti-spam deposits make it very expensive to perform this attack. 46 | -------------------------------------------------------------------------------- /poi.sol: -------------------------------------------------------------------------------- 1 | contract poi { 2 | 3 | address public POIs; 4 | address public registrationContract; 5 | address public depositContract; 6 | 7 | address scheduler; 8 | 9 | uint nextRound; 10 | uint roundLength; 11 | 12 | uint depositSize; 13 | uint groupSize; 14 | 15 | function poi (){ 16 | roundLength = 2 days; 17 | depositSize = 1 ether; 18 | groupSize = 5; 19 | scheduler = 0x26416b12610d26fd31d227456e9009270574038f; 20 | } 21 | 22 | function andSoItBegins() { 23 | if(nextRound != 0) throw; 24 | nextRound = block.number; 25 | newRound(); 26 | } 27 | 28 | function newRound() { 29 | if(block.number < nextRound) throw; 30 | registrationContract = new registration(roundLength, groupSize, depositSize); 31 | registration(registrationContract).startRound.value(200000000000000000)(); 32 | depositContract = new depositGovernance(depositSize, registrationContract); 33 | nextRound += roundLength; 34 | scheduleRound(); 35 | } 36 | 37 | function scheduleRound() internal { 38 | bytes4 sig = bytes4(sha3("newRound()")); 39 | bytes4 scheduleCallSig = bytes4(sha3("scheduleCall(bytes4,uint256)")); 40 | scheduler.call.value(50000000000000000)(scheduleCallSig, sig, nextRound); 41 | } 42 | 43 | function issuePOIs(address[] verifiedUsers) external { 44 | if(msg.sender != registrationContract) throw; 45 | generatePOIs(POIs).killContract(); 46 | POIs = new generatePOIs(verifiedUsers); 47 | endRound(); 48 | } 49 | 50 | function endRound () internal { 51 | registration(registrationContract).killContract(); 52 | depositGovernance(depositContract).processProposals(); 53 | } 54 | 55 | function newDepositSize(uint newDepositSize) external { 56 | if(msg.sender != depositContract) throw; 57 | depositSize = newDepositSize; 58 | } 59 | 60 | function verifyPOI (address v) public returns (bool success){ 61 | if (generatePOIs(POIs).POIs(v) == 1) return true; 62 | } 63 | 64 | } 65 | 66 | 67 | contract registration { 68 | 69 | address scheduler; 70 | address mainContract; 71 | address depositContract; 72 | 73 | uint randomHour; 74 | uint public deadLine; 75 | uint hangoutCountdown; 76 | uint issuePOIsCountdown; 77 | 78 | uint depositSize; 79 | uint groupSize; 80 | 81 | mapping(address => bool) public registered; 82 | address[] registeredUsers; 83 | uint256[] randomizedTemplate; 84 | 85 | mapping(address => uint) userGroup; 86 | uint groupCount; 87 | 88 | address[][] hangoutGroups; 89 | mapping(uint => bytes32) public hangoutAddress; 90 | mapping (address => bool) hangoutInSession; 91 | 92 | address[] verifiedUsers; 93 | 94 | function registration(uint roundLength, uint groupSize, uint depositSize){ 95 | mainContract = msg.sender; 96 | groupSize = groupSize; 97 | depositSize = depositSize; 98 | randomHour = uint8(sha3(this))%24 + 1; 99 | deadLine = block.number + roundLength - randomHour - 1 hours; 100 | hangoutCountdown = block.number + roundLength - randomHour; 101 | issuePOIsCountdown = block.number + roundLength - randomHour + 45 minutes; 102 | scheduler = 0x26416b12610d26fd31d227456e9009270574038f; 103 | } 104 | 105 | function startRound() external { 106 | if(msg.sender != mainContract) throw; 107 | scheduleShuffling(); 108 | scheduleHangouts(); 109 | scheduleIssuePOIs(); 110 | } 111 | 112 | function scheduleShuffling() internal { 113 | bytes4 sig = bytes4(sha3("generateGroups()")); 114 | bytes4 scheduleCallSig = bytes4(sha3("scheduleCall(bytes4,uint256)")); 115 | scheduler.call.value(50000000000000000)(scheduleCallSig, sig, deadLine); 116 | } 117 | 118 | function scheduleHangouts() internal { 119 | bytes4 sig = bytes4(sha3("startHangouts()")); 120 | bytes4 scheduleCallSig = bytes4(sha3("scheduleCall(bytes4,uint256)")); 121 | scheduler.call.value(50000000000000000)(scheduleCallSig, sig, hangoutCountdown); 122 | } 123 | 124 | function scheduleIssuePOIs() internal { 125 | bytes4 sig = bytes4(sha3("issuePOIs()")); 126 | bytes4 scheduleCallSig = bytes4(sha3("scheduleCall(bytes4,uint256)")); 127 | scheduler.call.value(50000000000000000)(scheduleCallSig, sig, issuePOIsCountdown); 128 | } 129 | 130 | function register() returns (bool success) { 131 | if(block.number > deadLine) throw; 132 | if(msg.value < depositSize) throw; 133 | if(registered[msg.sender] == true) throw; 134 | registered[msg.sender] = true; 135 | registeredUsers.push(msg.sender); 136 | depositGovernance(depositContract).registrationDeposit.value(msg.value)(msg.sender); 137 | return true; 138 | } 139 | 140 | function getRandomNumber(uint seed) internal returns (uint) { 141 | return (uint(sha3(block.blockhash(block.number-1), seed))%100); 142 | } 143 | 144 | function generateGroups() { 145 | if(block.number < deadLine) throw; 146 | 147 | uint8[2*20] memory unshuffled; 148 | 149 | for (uint8 i=0; i < registeredUsers.length; i++) { 150 | unshuffled[i] = i; 151 | } 152 | 153 | uint listIndex; 154 | 155 | for (i=0; i < registeredUsers.length; i++) { 156 | listIndex = getRandomNumber(i) % (registeredUsers.length - i); 157 | randomizedTemplate.push(unshuffled[listIndex]); 158 | unshuffled[listIndex] = unshuffled[registeredUsers.length - i - 1]; 159 | } 160 | 161 | uint counter; 162 | groupCount = 0; 163 | 164 | for(i = 0; i < randomizedTemplate.length; i++){ 165 | if(counter == groupSize){ groupCount++; counter = 0;} 166 | userGroup[registeredUsers[randomizedTemplate[i]]] = groupCount; 167 | hangoutGroups[groupCount].push(registeredUsers[randomizedTemplate[i]]); 168 | counter++; 169 | } 170 | for(i = 0; i < groupCount; i++){ 171 | hangoutAddress[i]= sha3(hangoutGroups[i]); 172 | } 173 | } 174 | 175 | function getHangoutAddress() public returns(bytes32){ 176 | if(hangoutAddress[userGroup[msg.sender]] == 0) throw; 177 | bytes32 hangoutURL = hangoutAddress[userGroup[msg.sender]]; 178 | return hangoutURL; 179 | } 180 | 181 | function startHangouts() { 182 | if(block.number < hangoutCountdown) throw; 183 | for (uint i = 0; i < groupCount; i++) { 184 | address b = new hangout(hangoutGroups[groupCount]); 185 | hangoutInSession[b] = true; 186 | } 187 | } 188 | 189 | function submitVerifiedUsers(address[] verified) { 190 | if(hangoutInSession[msg.sender] != true) throw; 191 | if(block.number > issuePOIsCountdown) throw; 192 | 193 | for (uint i = 0; i < verified.length; i++) { 194 | verifiedUsers.push(verified[i]); 195 | } 196 | hangoutInSession[msg.sender] == false; 197 | } 198 | 199 | function issuePOIs(){ 200 | if(block.number < issuePOIsCountdown) throw; 201 | poi(mainContract).issuePOIs(verifiedUsers); 202 | } 203 | 204 | function killContract(){ 205 | if(msg.sender != mainContract) throw; 206 | suicide(mainContract); 207 | } 208 | } 209 | 210 | 211 | contract hangout { 212 | 213 | address registrationContract; 214 | 215 | uint public deadline; 216 | 217 | mapping(address => uint256) positiveRewards; 218 | mapping(address => uint256) negativeRewards; 219 | mapping(address => uint256) recievedPoints; 220 | 221 | address[] participants; 222 | 223 | address[] verifiedUsers; 224 | 225 | 226 | function hangout(address[] hangoutGroup) { 227 | registrationContract = msg.sender; 228 | for (uint i = 0; i < hangoutGroup.length; i++) { 229 | participants.push(hangoutGroup[i]); 230 | } 231 | deadline = block.number + 15 minutes; 232 | } 233 | 234 | function positiveReward(address _to, uint256 _value) { 235 | uint giveLimit; 236 | uint recieveLimit; 237 | if (positiveRewards[msg.sender] + _value > 5000) { 238 | giveLimit = 5000 - positiveRewards[msg.sender]; 239 | } 240 | if (recievedPoints[_to] + _value > 5000) { 241 | recieveLimit = 5000 - recievedPoints[_to]; 242 | } 243 | if(giveLimit < recieveLimit) _value = giveLimit; 244 | else _value = recieveLimit; 245 | 246 | positiveRewards[msg.sender] +=_value; 247 | recievedPoints[_to] +=_value; 248 | } 249 | 250 | function negativeReward(address _to, uint256 _value) { 251 | if (negativeRewards[msg.sender] + _value > 2000) { // If the sent amount is bigger than the maximum amount 252 | _value = 2000 - negativeRewards[msg.sender]; // one can give, send max amount 253 | } 254 | /* transfer the anti sybil fuel */ 255 | negativeRewards[msg.sender] +=_value; 256 | recievedPoints[_to] +=_value; 257 | } 258 | 259 | function closeSession(){ 260 | if(block.number < deadline) throw; 261 | for(uint i = 0; i < participants.length; i++){ 262 | if(recievedPoints[participants[i]] > 4000) verifiedUsers.push(participants[i]); 263 | } 264 | registration(registrationContract).submitVerifiedUsers(verifiedUsers); 265 | 266 | suicide(registrationContract); 267 | } 268 | 269 | } 270 | 271 | contract generatePOIs { 272 | 273 | address mainContract; 274 | 275 | mapping (address => uint) public POIs; 276 | 277 | function generatePOIs(address[] verifiedUsers) { 278 | mainContract = msg.sender; 279 | for (uint i = 0; i < verifiedUsers.length; i++) { 280 | POIs[verifiedUsers[i]] += 1; 281 | } 282 | } 283 | 284 | function verifyPOI(address POIholder) external returns (bool success) { 285 | if (msg.sender != mainContract) throw; 286 | if(POIs[POIholder] == 1) return true; 287 | } 288 | 289 | function killContract(){ 290 | if(msg.sender != mainContract) throw; 291 | suicide(mainContract); 292 | } 293 | 294 | } 295 | 296 | 297 | contract depositGovernance { 298 | 299 | address registrationContract; 300 | address poiContract; 301 | 302 | 303 | address[] participants; 304 | mapping(address => bool) participantIndex; 305 | 306 | mapping(address => uint256) public AutoVote; 307 | mapping(address => uint256) public Votes; 308 | 309 | struct proposeNewDeposit { 310 | uint256 depositSize; 311 | uint256 votesInFavour; // in ether 312 | uint256 votesAgainst; // in ether 313 | } 314 | 315 | proposeNewDeposit[] public proposals; 316 | 317 | function depositGovernance(uint currentDepositSize, address registrationContract){ 318 | poiContract = msg.sender; 319 | registrationContract = registrationContract; 320 | proposals.push(proposeNewDeposit({ 321 | depositSize: currentDepositSize, 322 | votesInFavour: 0, 323 | votesAgainst: 0 324 | })); 325 | } 326 | 327 | 328 | function registrationDeposit(address registrant) external { 329 | if(msg.sender != registrationContract) throw; 330 | if(participantIndex[registrant] == false) participants.push(msg.sender); participantIndex[msg.sender] = true; 331 | 332 | /* the registrant automatically uses their anti-spam deposits to vote for the current depositSize */ 333 | /* they can move their auto-vote to vote for other proposals if they wish, by using voteOnProposal() */ 334 | AutoVote[registrant] += msg.value; 335 | proposals[0].votesInFavour += msg.value; 336 | } 337 | 338 | function NewProposal(uint256 depositSize) public { 339 | uint availableEther = AutoVote[msg.sender] + msg.value; 340 | if(availableEther < depositSize) throw; 341 | if(participantIndex[msg.sender] == false) participants.push(msg.sender); participantIndex[msg.sender] = true; 342 | proposals.push(proposeNewDeposit({ 343 | depositSize: depositSize, 344 | votesInFavour: depositSize, 345 | votesAgainst: 0 346 | })); 347 | AutoVote[msg.sender] -= (availableEther - msg.value); 348 | proposals[0].votesInFavour -= (availableEther - msg.value); 349 | Votes[msg.sender] += depositSize; 350 | } 351 | 352 | 353 | 354 | function voteOnProposal(uint proposalIndex, bool opinion, uint amount) public { 355 | 356 | uint availableEther = AutoVote[msg.sender] + msg.value; 357 | if(availableEther < amount) amount = availableEther; 358 | if(participantIndex[msg.sender] == false) participants.push(msg.sender); participantIndex[msg.sender] = true; 359 | 360 | if(opinion == true) 361 | proposals[proposalIndex].votesInFavour += amount; 362 | else 363 | proposals[proposalIndex].votesAgainst += amount; 364 | 365 | AutoVote[msg.sender] -= (amount - msg.value); 366 | proposals[0].votesInFavour -= (amount - msg.value); 367 | Votes[msg.sender] += amount; 368 | } 369 | 370 | function processProposals() external { // invoked at the end of each round 371 | if(msg.sender != poiContract) throw; 372 | uint iterateToHighest; 373 | 374 | for (uint i = 0; i < proposals.length; i++){ 375 | if(proposals[i].votesInFavour > proposals[i].votesAgainst && proposals[iterateToHighest].votesInFavour < proposals[i].votesInFavour) 376 | iterateToHighest = i; 377 | } 378 | 379 | uint newDepositSize = proposals[iterateToHighest].depositSize; 380 | 381 | poi(poiContract).newDepositSize(newDepositSize); 382 | 383 | /* then return deposits */ 384 | 385 | for (uint k = 1; k < participants.length; k++){ 386 | uint totalDeposit = AutoVote[participants[k]] + Votes[participants[k]]; 387 | participants[k].send(totalDeposit); 388 | } 389 | 390 | /* then suicide contract */ 391 | suicide(poiContract); 392 | } 393 | 394 | 395 | } 396 | -------------------------------------------------------------------------------- /subcontracts/depositGovernance.sol: -------------------------------------------------------------------------------- 1 | /* 2 | All deposits are managed by one contract. Upon registration, the anti-spam deposit is automatically used to vote for the current 3 | depositSize. This auto-vote can then be changed and used to vote for another proposal, or to create a new proposal. At the end of 4 | each round, proccessProposals() is called and the proposal that has the highest number of up-votes, measured in ether, becomes 5 | the new depositSize. proposals[0] will probably win most of the time, unless there is an active community effort to change the 6 | depositSize 7 | */ 8 | 9 | contract depositGovernance { 10 | 11 | address registrationContract; 12 | address poiContract; 13 | 14 | 15 | address[] participants; 16 | mapping(address => bool) participantIndex; 17 | 18 | mapping(address => uint256) public AutoVote; 19 | mapping(address => uint256) public Votes; 20 | 21 | struct proposeNewDeposit { 22 | uint256 depositSize; 23 | uint256 votesInFavour; // in ether 24 | uint256 votesAgainst; // in ether 25 | } 26 | 27 | proposeNewDeposit[] public proposals; 28 | 29 | function depositGovernance(uint currentDepositSize, address registrationContract){ 30 | poiContract = msg.sender; 31 | registrationContract = registrationContract; 32 | proposals.push(proposeNewDeposit({ 33 | depositSize: currentDepositSize, 34 | votesInFavour: 0, 35 | votesAgainst: 0 36 | })); 37 | } 38 | 39 | 40 | function registrationDeposit(address registrant) external { 41 | if(msg.sender != registrationContract) throw; 42 | if(participantIndex[registrant] == false) participants.push(msg.sender); participantIndex[msg.sender] = true; 43 | 44 | /* the registrant automatically uses their anti-spam deposits to vote for the current depositSize */ 45 | /* they can move their auto-vote to vote for other proposals if they wish, by using voteOnProposal() */ 46 | AutoVote[registrant] += msg.value; 47 | proposals[0].votesInFavour += msg.value; 48 | } 49 | 50 | function NewProposal(uint256 depositSize) public { 51 | uint availableEther = AutoVote[msg.sender] + msg.value; 52 | if(availableEther < depositSize) throw; 53 | if(participantIndex[msg.sender] == false) participants.push(msg.sender); participantIndex[msg.sender] = true; 54 | proposals.push(proposeNewDeposit({ 55 | depositSize: depositSize, 56 | votesInFavour: depositSize, 57 | votesAgainst: 0 58 | })); 59 | AutoVote[msg.sender] -= (availableEther - msg.value); 60 | proposals[0].votesInFavour -= (availableEther - msg.value); 61 | Votes[msg.sender] += depositSize; 62 | } 63 | 64 | 65 | 66 | function voteOnProposal(uint proposalIndex, bool opinion, uint amount) public { 67 | 68 | uint availableEther = AutoVote[msg.sender] + msg.value; 69 | if(availableEther < amount) amount = availableEther; 70 | if(participantIndex[msg.sender] == false) participants.push(msg.sender); participantIndex[msg.sender] = true; 71 | 72 | if(opinion == true) 73 | proposals[proposalIndex].votesInFavour += amount; 74 | else 75 | proposals[proposalIndex].votesAgainst += amount; 76 | 77 | AutoVote[msg.sender] -= (amount - msg.value); 78 | proposals[0].votesInFavour -= (amount - msg.value); 79 | Votes[msg.sender] += amount; 80 | } 81 | 82 | function processProposals() external { // invoked at the end of each round 83 | if(msg.sender != poiContract) throw; 84 | uint iterateToHighest; 85 | 86 | for (uint i = 0; i < proposals.length; i++){ 87 | if(proposals[i].votesInFavour > proposals[i].votesAgainst && proposals[iterateToHighest].votesInFavour < proposals[i].votesInFavour) 88 | iterateToHighest = i; 89 | } 90 | 91 | uint newDepositSize = proposals[iterateToHighest].depositSize; 92 | 93 | poi(poiContract).newDepositSize(newDepositSize); 94 | 95 | /* then return deposits */ 96 | 97 | for (uint k = 1; k < participants.length; k++){ 98 | uint totalDeposit = AutoVote[participants[k]] + Votes[participants[k]]; 99 | participants[k].send(totalDeposit); 100 | } 101 | 102 | /* then suicide contract */ 103 | suicide(poiContract); 104 | } 105 | 106 | 107 | } 108 | -------------------------------------------------------------------------------- /subcontracts/generatePOIs.sol: -------------------------------------------------------------------------------- 1 | contract generatePOIs { 2 | 3 | address mainContract; 4 | 5 | mapping (address => uint) public POIs; 6 | 7 | function generatePOIs(address[] verifiedUsers) { 8 | mainContract = msg.sender; 9 | for (uint i = 0; i < verifiedUsers.length; i++) { 10 | POIs[verifiedUsers[i]] += 1; 11 | } 12 | } 13 | 14 | function verifyPOI(address POIholder) external returns (bool success) { 15 | if (msg.sender != mainContract) throw; 16 | if(POIs[POIholder] == 1) return true; 17 | } 18 | 19 | function killContract(){ 20 | if(msg.sender != mainContract) throw; 21 | suicide(mainContract); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /subcontracts/hangout.sol: -------------------------------------------------------------------------------- 1 | contract hangout { 2 | 3 | /* 4 | Anti-sybil fuel (ASF) is used to gamify the POI hangouts. Participants can 5 | use it to "guide each other's attention", and also to downvote multi-group scammers. 6 | Hangout-attendees get to deal out +5000 "anti-sybil fuel" (ASF), and -2000 ASF, 7 | and need to receive +4000 to be verified. The ASF points have been balanced so that 8 | if 2 multi-group scammers are grouped togeher, the 3 other hangout-attendees have 9 | the power to down-vote those two with 3x-2000 points, preventing them from being verified. 10 | It has also been balanced so that in a group where only 2 people show up for some reason, 11 | those 2 are not penalized and can verify one another. 12 | */ 13 | 14 | address registrationContract; 15 | 16 | uint public genesisblock; 17 | uint public deadline; 18 | 19 | mapping(address => uint256) positiveRewards; // hangout-attendees get to deal out +5000 anti-sybil fuel (ASF), 20 | mapping(address => uint256) negativeRewards; // and -2000 ASF, 21 | mapping(address => uint256) recievedPoints; // and need to receive +4000 to be verified 22 | 23 | address[] participants; 24 | 25 | address[] verifiedUsers; 26 | 27 | 28 | function hangout(address[] hangoutGroup) { 29 | for (uint i = 0; i < hangoutGroup.length; i++) { 30 | participants.push(hangoutGroup[i]); 31 | } 32 | genesisblock = block.number; 33 | deadline = genesisblock + 15 minutes; // hangouts are 15 minutes long 34 | registrationContract = msg.sender; 35 | } 36 | 37 | function positiveReward(address _to, uint256 _value) { 38 | uint giveLimit; 39 | uint recieveLimit; 40 | if (positiveRewards[msg.sender] + _value > 5000) { // If the sent amount is bigger than the maximum amount 41 | giveLimit = 5000 - positiveRewards[msg.sender]; // one can give, send max amount 42 | } 43 | if (recievedPoints[_to] + _value > 5000) { // If the sent amount is bigger than the maximum 44 | recieveLimit = 5000 - recievedPoints[_to]; // reward limit, send max amount 45 | } 46 | if(giveLimit < recieveLimit) _value = giveLimit; 47 | else _value = recieveLimit; 48 | 49 | /* transfer the anti sybil fuel */ 50 | positiveRewards[msg.sender] +=_value; 51 | recievedPoints[_to] +=_value; 52 | } 53 | 54 | function negativeReward(address _to, uint256 _value) { 55 | if (negativeRewards[msg.sender] + _value > 2000) { // If the sent amount is bigger than the maximum amount 56 | _value = 2000 - negativeRewards[msg.sender]; // one can give, send max amount 57 | } 58 | /* transfer the anti sybil fuel */ 59 | negativeRewards[msg.sender] +=_value; 60 | recievedPoints[_to] +=_value; 61 | } 62 | 63 | 64 | /* after 15 minutes, each users that has been awarded 4000 ASF or more is seen as verified and given a POI token */ 65 | 66 | /* the closeSession function can be called by anyone in the hangut once the deadline has passed */ 67 | 68 | function closeSession(){ 69 | if(block.number < deadline) throw; 70 | 71 | for(uint i = 0; i < participants.length; i++){ 72 | if(recievedPoints[participants[i]] > 4000) 73 | verifiedUsers.push(participants[i]); 74 | } 75 | /* pass verifiedUsers into a contract that generates POIs, together with verifiedUsers from all other hangouts */ 76 | 77 | registration(registrationContract).submitVerifiedUsers(verifiedUsers); 78 | 79 | /* the POI contract will then pass the full list into the contract generatePOItokens */ 80 | 81 | /* the anti-sybil fuel is then destroyed */ 82 | 83 | suicide(registrationContract); 84 | 85 | } 86 | -------------------------------------------------------------------------------- /subcontracts/poi.sol: -------------------------------------------------------------------------------- 1 | ontract poi { 2 | 3 | address public POIs; 4 | address public registrationContract; 5 | address public depositContract; 6 | 7 | address scheduler; // address to the alarm contract, see http://ethereum-alarm-clock.com 8 | 9 | uint genesisblock; 10 | uint nextRound; 11 | uint roundLength; 12 | 13 | uint depositSize; 14 | uint groupSize; 15 | 16 | function poi (){ 17 | genesisblock = block.number; 18 | roundLength = 2 days; 19 | depositSize = 1 ether; 20 | groupSize = 5; 21 | nextRound = genesisblock; 22 | scheduler = 0x26416b12610d26fd31d227456e9009270574038f; // alarm service on morden testnet 23 | newRound(); 24 | } 25 | 26 | function scheduleCall() internal { 27 | bytes4 sig = bytes4(sha3("newRound()")); 28 | bytes4 scheduleCallSig = bytes4(sha3("scheduleCall(bytes4,uint256)")); 29 | scheduler.call.value(50000000000000000)(scheduleCallSig, sig, nextRound); 30 | } 31 | 32 | function newRound() { 33 | if(block.number < nextRound) throw; 34 | registrationContract = new registration(roundLength, groupSize, depositSize); 35 | registrationContract.send(200000000000000000); 36 | depositContract = new depositGovernance(depositSize, registrationContract); 37 | nextRound += roundLength; 38 | scheduleCall(); 39 | } 40 | 41 | function issuePOIs(address[] verifiedUsers) external { 42 | if(msg.sender != registrationContract) throw; 43 | POIs = new generatePOIs(verifiedUsers); 44 | endRound(); 45 | } 46 | 47 | function endRound(){ 48 | registration(registrationContract).killContract(); 49 | depositGovernance(depositContract).processProposals(); 50 | } 51 | 52 | function newDepositSize(uint newDepositSize) external { 53 | if(msg.sender != depositContract) throw; 54 | depositSize = newDepositSize; 55 | } 56 | 57 | function verifyPOI (address v) public returns (bool success){ 58 | if (generatePOIs(POIs).POIs(v) == 1) return true; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /subcontracts/registration.sol: -------------------------------------------------------------------------------- 1 | contract registration { 2 | 3 | address scheduler; 4 | address poiContract; 5 | 6 | uint randomHour; // alternate the hour of the day that the global event occurs on 7 | uint public deadLine; 8 | uint hangoutCountdown; 9 | uint issuePOIsCountdown; 10 | 11 | uint groupSize; 12 | 13 | /* these are used for the registration, randomization process and to assing users into groups */ 14 | address[] registeredUsers; 15 | uint256[] randomizedTemplate; 16 | mapping(address => bool) public registered; 17 | 18 | mapping(address => uint) public userGroup; 19 | uint groupCount; 20 | 21 | /* these are used for booting up the hangout sessions */ 22 | address[][] hangoutGroups; 23 | mapping(uint => bytes32) public hangoutAddressRegistry; 24 | mapping (address => bool) hangoutInSession; 25 | 26 | /* when you issue POIs, you pass along a list of verified users */ 27 | address[] verifiedUsers; 28 | 29 | uint depositSize; 30 | address depositContract; 31 | 32 | 33 | function registration(uint roundLength, uint groupSize, uint depositSize){ 34 | groupSize = groupSize; 35 | depositSize = depositSize; 36 | randomHour = uint8(sha3(this))%24 + 1; // generate random number between 1 and 24 37 | deadLine = block.number + roundLength - randomHour - 1 hours; // leave enough time for the randomization algorithm to add users to groups 38 | hangoutCountdown = block.number + roundLength - randomHour; // allow hangouts to begin at the randomHour clock stroke 39 | issuePOIsCountdown = block.number + roundLength - randomHour + 45 minutes; // leave 30 minutes for all verified users to be submitted 40 | poiContract = msg.sender; 41 | scheduler = 0x26416b12610d26fd31d227456e9009270574038f; // alarm service on morden testnet 42 | scheduleShuffling(); 43 | scheduleHangouts(); 44 | scheduleIssuePOIs(); 45 | } 46 | 47 | 48 | function register() returns (bool success) { 49 | if(block.number > deadLine) throw; 50 | if(msg.value < depositSize) throw; 51 | if(registered[msg.sender] == true) throw; 52 | registered[msg.sender] = true; 53 | registeredUsers.push(msg.sender); 54 | depositGovernance(depositContract).registrationDeposit.value(msg.value)(msg.sender); 55 | return true; 56 | } 57 | 58 | function scheduleShuffling() internal { 59 | bytes4 sig = bytes4(sha3("generateGroups()")); 60 | bytes4 scheduleCallSig = bytes4(sha3("scheduleCall(bytes4,uint256)")); 61 | scheduler.call.value(50000000000000000)(scheduleCallSig, sig, deadLine); 62 | } 63 | 64 | function scheduleHangouts() internal { 65 | bytes4 sig = bytes4(sha3("bootUpHangouts()")); 66 | bytes4 scheduleCallSig = bytes4(sha3("scheduleCall(bytes4,uint256)")); 67 | scheduler.call.value(50000000000000000)(scheduleCallSig, sig, hangoutCountdown); 68 | } 69 | 70 | function scheduleIssuePOIs() internal { 71 | bytes4 sig = bytes4(sha3("issuePOIs()")); 72 | bytes4 scheduleCallSig = bytes4(sha3("scheduleCall(bytes4,uint256)")); 73 | scheduler.call.value(50000000000000000)(scheduleCallSig, sig, issuePOIsCountdown); 74 | } 75 | 76 | 77 | function getRandomNumber(uint seed) internal returns (uint) { 78 | return (uint(sha3(block.blockhash(block.number-1), seed))%100); 79 | } 80 | 81 | function generateGroups() { 82 | if(block.number < deadLine) throw; 83 | 84 | /* ether-poker's algorithm for shuffling a deck of cards is used to shuffle the list of registered users */ 85 | 86 | uint8[2*20] memory unshuffled; 87 | 88 | for (uint8 i=0; i < registeredUsers.length; i++) { 89 | unshuffled[i] = i; 90 | } 91 | 92 | uint listIndex; 93 | 94 | for (i=0; i < registeredUsers.length; i++) { 95 | listIndex = getRandomNumber(i) % (registeredUsers.length - i); 96 | randomizedTemplate.push(unshuffled[listIndex]); 97 | unshuffled[listIndex] = unshuffled[registeredUsers.length - i - 1]; 98 | } 99 | 100 | /* the randomized list is then used to assign users into groups */ 101 | 102 | uint groupCount; 103 | uint counter; 104 | 105 | for(i = 0; i < randomizedTemplate.length; i++){ 106 | if(counter == groupSize){ groupCount++; counter = 0;} 107 | userGroup[registeredUsers[randomizedTemplate[i]]] = groupCount; 108 | hangoutGroups[groupCount].push(registeredUsers[randomizedTemplate[i]]); 109 | counter++; 110 | } 111 | 112 | /* hangout addresses are generated and mapped to hangout groups */ 113 | 114 | for(i = 0; i < groupCount; i++){ 115 | hangoutAddressRegistry[i]= sha3(hangoutGroups[i]); 116 | } 117 | } 118 | 119 | function getHangoutAddress() returns(bytes32){ 120 | if(hangoutAddressRegistry[userGroup[msg.sender]] == 0) throw; 121 | // maybe use http://appear.in for first version 122 | // hangoutURL = "http://appear.in" + hangoutAddressRegistry[userGroup[msg.sender]] 123 | bytes32 hangoutURL = hangoutAddressRegistry[userGroup[msg.sender]]; 124 | return hangoutURL; 125 | } 126 | 127 | 128 | function bootUpHangouts() { 129 | if(block.number < hangoutCountdown) throw; 130 | for (uint i = 0; i < groupCount; i++) 131 | address b = new hangout(hangoutGroups[groupCount]); 132 | hangoutInSession[b] = true; 133 | } 134 | 135 | 136 | function submitVerifiedUsers(address[] verified) { 137 | if(hangoutInSession[msg.sender] != true) throw; // can only be invoked by hangout contract 138 | if(block.number > issuePOIsCountdown) throw; // deadLine has passed and POIs have already started being issued 139 | 140 | for (uint i = 0; i < verified.length; i++) 141 | verifiedUsers.push(verified[i]); 142 | 143 | hangoutInSession[msg.sender] == false; 144 | } 145 | 146 | function issuePOIs(){ 147 | if(block.number < issuePOIsCountdown) throw; // hangouts are still in session 148 | poi(poiContract).issuePOIs(verifiedUsers); 149 | 150 | } 151 | 152 | function killContract(){ 153 | if(msg.sender != poiContract) throw; 154 | suicide(poiContract); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /whitepaper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proofofindividuality/poi/dfef1ca9f9a9c14248f5538471897d4f974b404e/whitepaper.pdf --------------------------------------------------------------------------------