Skip to content

Instantly share code, notes, and snippets.

@gnprice
Last active March 24, 2019 00:22
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save gnprice/9e242e5d4d40070cf5a6a13002e478ce to your computer and use it in GitHub Desktop.
pragma solidity ^0.5.0;
contract LowestUnique {
uint256 ante = 0.01 ether;
enum Stage { INIT, IN_PROGRESS, END }
Stage private stage;
uint private commitDeadline;
uint private revealDeadline;
uint256 private pot;
address[] private players;
mapping(address => uint256) private blindedInputs;
mapping(address => uint256) private revealedInputs;
event CommitInput(address indexed user, uint256 blindedInput);
event BogusReveal(address indexed user);
event RevealInput(address indexed user, uint256 input);
constructor() public {
// called on contract deploy
}
function start() external {
if (stage != Stage.INIT) {
revert();
}
commitDeadline = block.number + 100;
revealDeadline = commitDeadline + 100;
stage = Stage.IN_PROGRESS;
}
function commitInput(uint256 input) external payable {
if (stage != Stage.IN_PROGRESS || commitDeadline < block.number) {
revert();
}
if (msg.value < ante) {
revert();
}
pot += msg.value;
players.push(msg.sender);
blindedInputs[msg.sender] = input;
emit CommitInput(msg.sender, input);
}
function revealInput(uint256 number, uint256 nonce) external {
if (stage != Stage.IN_PROGRESS
|| block.number <= commitDeadline
|| revealDeadline < block.number) {
revert();
}
uint256 blindedInput = blindedInputs[msg.sender];
if (blindedInput != keccak256(number, nonce)
|| number == 0) {
emit BogusReveal(msg.sender);
return;
}
revealedInputs[msg.sender] = number;
emit RevealInput(msg.sender, number);
}
function computeWinner() private returns (bool has_winner, address winner) {
uint256 numDistinct = 0;
uint256[] memory distinctValues = new uint256[](players.length);
mapping(uint256 => uint) storage counts;
for (uint i = 0; i < players.length; i++) {
uint256 val = revealedInputs[players[i]];
if (val == 0)
continue;
counts[val]++;
if (counts[val] == 1) {
distinctValues[numDistinct++] = val;
}
}
uint256 numUnique = 0;
uint256[] memory uniqueValues = new uint256[](numDistinct);
for (uint i = 0; i < distinctValues.length; i++) {
uint256 val = distinctValues[i];
if (counts[val] == 1) {
uniqueValues[numUnique++] = val;
}
}
if (uniqueValues.length == 0) {
return (false, 0);
}
uint256 minValue = uniqueValues[0];
for (uint i = 1; i < uniqueValues.length; i++) {
uint256 val = uniqueValues[i];
if (val < minValue) {
minValue = val;
}
}
for (uint i = 0; i < players.length; i++) {
if (revealedInputs[players[i]] == minValue) {
return (true, players[i]);
}
}
}
function settle() external {
if (stage != Stage.IN_PROGRESS || block.number <= revealDeadline) {
revert();
}
(bool hasWinner, address winner) = computeWinner();
if (!hasWinner) {
// Everybody lost. :-P Just burn/bury the pot.
} else {
winner.transfer(pot);
}
stage = Stage.END;
}
function getInput() public view returns (uint256) {
return revealedInputs[msg.sender];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment