Last active
November 10, 2018 18:02
-
-
Save resilience-me/22abe73a80fdc2a11555eef91b85d01f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
contract MazeHashFunction { | |
function generateRandomNumber(uint _treasureMap) view internal returns (uint) { | |
bytes memory randomNumber = new bytes(32); | |
bytes memory treasureMapInBytes = toBytes(_treasureMap); | |
uint8 nextByteInTreasureMap = uint8(treasureMapInBytes[31]); | |
uint8 pointerToNextPosition = nextByteInTreasureMap; | |
for(uint i = 31; i >0; i--) { | |
uint nextHashInLabyrinth = uint(blockhash(block.number - 1 - pointerToNextPosition)); | |
bytes memory blockHashToBytes = toBytes(nextHashInLabyrinth); | |
uint8 byteFromBlockhash = uint8(blockHashToBytes[i]); | |
nextByteInTreasureMap = uint8(treasureMapInBytes[i]); | |
uint8 nextRandomNumber = nextByteInTreasureMap ^ byteFromBlockhash; | |
randomNumber[i] = bytes1(nextRandomNumber); | |
pointerToNextPosition = nextRandomNumber; | |
} | |
return toUint(randomNumber); | |
} | |
function toBytes(uint256 x) pure internal returns (bytes b) { | |
b = new bytes(32); | |
assembly { mstore(add(b, 32), x) } | |
} | |
function toUint(bytes x) pure internal returns (uint b) { | |
assembly { | |
b := mload(add(x, 0x20)) | |
} | |
} | |
} | |
contract PseudonymPairs is MazeHashFunction{ | |
function bootstrapNetwork(address _nym) { | |
require(now < genesisTimestamp); | |
//require(msg.sender == ); | |
sortMe(_nym); | |
} | |
enum State { interlude, pseudonymEvent, eventFinished, endOfPeriod } | |
uint[4] timeperiods; | |
uint randomHour; // The exact hour of the event cycles by random over 24 hours | |
uint genesisTimestamp = 1545523200; // Saturday 22 December 2018 00:00:00, pseudonym events | |
// scheduled to random hour between 00:00 and 23:59 on saturdays | |
uint entropy; | |
uint eventCounter; | |
function setRandomHour() internal { | |
entropy = generateRandomNumber(entropy); | |
uint stepIn24HourCycle = eventCounter % 24; | |
uint randomNumber = entropy % (24 - stepIn24HourCycle); | |
uint randomClockstroke = stepIn24HourCycle + randomNumber; | |
shuffleUtility[3].shufflingIndex[randomClockstroke] = shuffleUtility[3].shufflingIndex[stepIn24HourCycle]; // Update the index that is used for cycling through random hours | |
randomHour = randomClockstroke; | |
timeperiods[2] = 28 days - randomHour; | |
timeperiods[1] = 28 days - randomHour - 20 minutes; | |
} | |
modifier atTime(State _inState, State _nextState) { | |
require(now > getTime(_inState)); | |
require(now < getTime(_nextState)); | |
_; | |
} | |
function getTime(State _state) view returns (uint) { | |
return genesisTimestamp + (timeperiods[3] * eventCounter) + timeperiods[uint(_state)]; | |
} | |
modifier incrementScheduler { | |
if(now > getTime(State.endOfPeriod)) { | |
eventCounter++; | |
setRandomHour(); | |
} | |
_; | |
} | |
constructor() { | |
genesisTimestamp = now; | |
timeperiods[3] = 28 days; // 13 events per year, 364 day year. The pseudonym event is on a saturday, always | |
timeperiods[2] = 28 days - randomHour; // This varies with randomHour, adjusted every new pseudonym month | |
timeperiods[1] = 28 days - randomHour - 20 minutes; // The pseudonym event lasts 20 minutes | |
timeperiods[0] = 0; | |
} | |
// The shuffle algorithm is similar to Algorithm P (Shuffle) from Knuth, 1969, in turn similar to the Fisher–Yates shuffle | |
// from 1938. It gets the position of an object from shufflingIndex, using a random number generator, and then updates the | |
// index so that the same position cannot be given to another object, and does so by switching places between the object | |
// that was picked, and the first object in the list, exchange shufflingIndex[randomNumber] and shufflingIndex[counter]. | |
// The algorithm can be applied for different types of use cases, including shuffling a population that grows from 0 to n | |
// people, "invasive shuffling". In the Pseudonym Pairs dApp, I use it for shuffling the population as a whole (using "invasive | |
// shuffling"), assigning people who opt-in to pairs that make up the "virtual borders" (using the method in the pickFromHat(), | |
// see below), scheduling the pseudonym event for a random hour each month, cycling through 24 hours (same method as pickFromHat()), | |
// letting people re-assign from pairs that were broken up (same as pickFromHat()), and issuing border tokens to a random person for | |
// each border token that is sold (also same as pickFromHat()). | |
struct ShuffleAlgorithm { | |
mapping(uint => uint) shufflingIndex; | |
uint counter; | |
} | |
ShuffleAlgorithm[4] shuffleUtility; | |
mapping(address => uint) pseudonymID; | |
mapping(uint => address) pseudonymIndex; | |
mapping(address => uint) immigrantID; | |
mapping(uint => address) immigrantIndex; | |
function sortMe(address _nym) internal atTime(State.interlude, State.pseudonymEvent) { | |
uint8 idx; | |
// Map even and uneven numbers in separate indices, each integer representing a pair with two people | |
// this lets people who opt-in "hitch-hike" on the shuffling of the entire population, so that optIn() | |
// can be invoked continuously | |
if(totalSorted % 2 == 1) { | |
shuffleUtility[0].counter++; | |
idx = 0; | |
} | |
else { | |
shuffleUtility[1].counter++; | |
idx = 1; | |
} | |
uint totalSorted = shuffleUtility[0].counter + shuffleUtility[1].counter; | |
pseudonymID[_nym] = totalSorted; | |
pseudonymIndex[totalSorted] = _nym; | |
entropy = generateRandomNumber(entropy); | |
uint pos = shuffleUtility[idx].counter; | |
uint randomNumber = 1 + (entropy % (pos - 1)); | |
shuffleUtility[idx].shufflingIndex[pos] = randomNumber; | |
shuffleUtility[idx].shufflingIndex[randomNumber] = pos; | |
} | |
function getPair(address _nym) view atTime(State.pseudonymEvent, State.endOfPeriod) returns (uint) { | |
uint ID = pseudonymID[_nym]; | |
uint8 idx; | |
uint nominator; | |
if(ID % 2 == 1) { | |
nominator += 1; | |
idx = 0; | |
} | |
else idx = 1; | |
uint pos = nominator / 2; | |
uint pairID = shuffleUtility[idx].shufflingIndex[pos]; | |
return pairID; | |
} | |
function optIn() atTime(State.interlude, State.pseudonymEvent) { | |
uint totalPairs = shuffleUtility[1].counter; | |
shuffleUtility[2].counter++; | |
uint totalImmigrants = shuffleUtility[2].counter; | |
require(totalPairs >= 1); | |
require(totalPairs > (totalImmigrants - 1)); | |
require(immigrantID[msg.sender] == 0); | |
//require(borderToken.balanceOf[msg.sender] >= 1); | |
//borderToken.balanceOf[msg.sender]--; | |
immigrantID[msg.sender] = totalImmigrants; | |
immigrantIndex[totalImmigrants] = msg.sender; | |
entropy = generateRandomNumber(entropy); | |
uint randomNumber = 1 + (entropy % (totalPairs - totalImmigrants)); | |
uint randomPerson = totalImmigrants + randomNumber; // Mapped to evenNumbers, hitch-hikes on a registered person, and the shuffling on their end | |
// Prune the list with randomPerson, adding the person from the beginning to its position | |
if(shuffleUtility[2].shufflingIndex[randomPerson] == 0) shuffleUtility[2].shufflingIndex[randomPerson] = randomPerson; | |
if(shuffleUtility[2].shufflingIndex[totalImmigrants] == 0) shuffleUtility[2].shufflingIndex[totalImmigrants] = totalImmigrants; | |
shuffleUtility[2].shufflingIndex[randomPerson] = shuffleUtility[2].shufflingIndex[totalImmigrants]; | |
// Assign the person to randomPerson | |
shuffleUtility[2].shufflingIndex[totalImmigrants] = shuffleUtility[2].shufflingIndex[randomPerson]; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment