Skip to content

Instantly share code, notes, and snippets.

@resilience-me
Last active November 10, 2018 18:02
Show Gist options
  • Save resilience-me/22abe73a80fdc2a11555eef91b85d01f to your computer and use it in GitHub Desktop.
Save resilience-me/22abe73a80fdc2a11555eef91b85d01f to your computer and use it in GitHub Desktop.
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