Created
August 8, 2020 08:00
-
-
Save RossPfeiffer/3179ecd93b8430257cafd6585508feae to your computer and use it in GitHub Desktop.
This file contains 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
pragma solidity ^ 0.6.6; | |
pragma experimental ABIEncoderV2; | |
contract Oracle{ | |
address payable ORACLE = address(0); | |
address address0 = address(0); | |
struct Poll{ | |
uint8 dataType; // string, uint, bool, address | |
//commit | |
mapping(address => bool) committed; | |
mapping(address => bytes) commitHash; | |
//reveal | |
mapping(address => bool) revealed; | |
mapping(address => bool) rejected; | |
mapping(address => bool) voted; | |
mapping(address => string) stringVotes; | |
mapping(address => uint) uintVotes; | |
mapping(address => bool) boolVotes; | |
mapping(address => address) addressVotes; | |
} | |
struct RequestTicket{ | |
uint ID; | |
address sender; | |
string query; | |
uint timeRequested; | |
bool finalized; | |
Poll poll; | |
uint additionalTime; | |
bool subjective; | |
uint txCoverageFee; | |
uint serviceFee; | |
bool needsResponse; | |
bool rejected; | |
string resolvedString; | |
uint resolvedUint; | |
bool resolvedBool; | |
address resolvedAddress; | |
mapping(address => bool) punished; | |
} | |
//oracle configs | |
uint constant ROUNDTABLE_SEATS = 0; | |
uint constant COMMIT_TIME_WINDOW = 1; | |
uint constant REVEAL_TIME_WINDOW = 2; | |
uint constant DELEGATE_REWARDSHARE = 3; | |
uint constant ELECTORATE_REWARDSHARE = 4; | |
uint constant FREEZE_TIMEOUT = 5; | |
uint constant TX_FEE_PER = 6; | |
uint constant SERVICE_FEE = 7; | |
uint constant CONFIGS = 8; | |
uint[] oracleConfigurations = new uint[](CONFIGS); | |
mapping(uint/*configID*/ => mapping(uint => uint) ) public totalVotes_forEach_configOption; | |
mapping(uint/*configID*/ => mapping(address => uint) ) public individualsSelectedOption; | |
mapping(address => uint) resolveWeight; | |
mapping(address => uint) weightLocked; | |
RequestTicket[] requestTickets; | |
//ROUND TABLE & Candidates | |
mapping(uint => address) public chairsCandidate; // only looks at the first X indexes | |
mapping(address => uint) candidatesChair; | |
mapping(address => uint) timeSeated; // watchers aren't responsible for requestTickets that came in before them | |
mapping(address => bool) frozen; | |
mapping(address => uint) timeWhenThawedOut; | |
mapping(address => bool) paused; // self pause | |
mapping(address => bool) hasChair; | |
uint chairs; | |
uint public hotSeats; | |
uint256 constant scaleFactor = 0x10000000000000000; | |
//PAYROLL | |
mapping(address => uint) earnings; | |
mapping(address => uint) totalShares; | |
mapping(address => mapping(address => uint256)) public shares; | |
mapping(address => mapping(address => uint256)) payouts; | |
mapping(address => uint) earningsPerShare; | |
//Tx Coverage fee | |
uint earningsPerWatcher; | |
uint totalWatchers; | |
mapping(address => uint256) watcherPayouts; | |
//lazy UI data | |
mapping(address => address[]) public yourBacking; | |
mapping(address => mapping(address => bool)) public alreadyBacking; | |
ResolveToken resolveToken; | |
constructor(address _resolve) public{ | |
resolveToken = ResolveToken(_resolve); | |
} | |
fallback () payable external {} | |
function addressPayable(address addr) public pure returns(address payable){ | |
return address( uint160(addr) ); | |
} | |
function addShares(address pool, address account, uint amount) internal{ | |
update(pool, addressPayable(account)); | |
totalShares[pool] += amount; | |
shares[pool][account] += amount; | |
if(pool == ORACLE){ | |
updateWatcherTxEarnings(addressPayable(account)); | |
totalWatchers += 1; | |
} | |
} | |
function removeShares(address pool, address account, uint amount) internal{ | |
update(pool, addressPayable(account)); | |
totalShares[pool] -= amount; | |
shares[pool][account] -= amount; | |
if(pool == ORACLE){ | |
updateWatcherTxEarnings(addressPayable(account)); | |
totalWatchers -= 1; | |
} | |
} | |
event Cashout(address account, uint amount); | |
event WatcherPayroll(address watcher, uint paidOut); | |
function update(address pool, address payable account) internal { | |
uint owedPerShare = earningsPerShare[pool] - payouts[pool][account]; | |
uint newMoney = shares[pool][account] * owedPerShare; | |
payouts[pool][account] = earningsPerShare[pool]; | |
if(pool == ORACLE){ | |
uint drs = oracleConfigurations[DELEGATE_REWARDSHARE]; | |
uint ers = oracleConfigurations[ELECTORATE_REWARDSHARE]; | |
uint eth4Watcher = newMoney *drs/(drs+ers); | |
earnings[account] += eth4Watcher/scaleFactor; | |
earningsPerShare[account] += (newMoney - eth4Watcher)/totalShares[account]; | |
emit Cashout(account, eth4Watcher/scaleFactor); | |
emit WatcherPayroll(account, (newMoney - eth4Watcher)/totalShares[account]); | |
}else{ | |
earnings[account] += newMoney/scaleFactor; | |
emit Cashout(account, newMoney/scaleFactor); | |
} | |
} | |
event TxCashout(address watcher, uint amount); | |
function updateWatcherTxEarnings(address payable watcher) internal { | |
uint owed = earningsPerWatcher - watcherPayouts[watcher]; | |
watcherPayouts[watcher] = earningsPerWatcher; | |
earnings[watcher] += owed; | |
emit TxCashout(watcher, owed); | |
} | |
event StakeResolves( address indexed addr, uint256 amountStaked, bytes _data ); | |
function tokenFallback(address from, uint value, bytes calldata _data) external{ | |
if( msg.sender == address(resolveToken) ){ | |
resolveWeight[from] += value; | |
//update option totals | |
uint option; | |
for(uint8 config = 0; config<CONFIGS; config+=1){ | |
option = individualsSelectedOption[config][from]; | |
totalVotes_forEach_configOption[config][option] += value; | |
assertOption(config, option); | |
} | |
emit StakeResolves(from, value, _data); | |
address backImmediately = bytesToAddress( _data ); | |
if( backImmediately != address0 ){ | |
backCandidate(backImmediately,value, false); | |
} | |
}else{ | |
revert(); | |
} | |
} | |
event UnstakeResolves(address sender, uint amount); | |
function unstakeResolves(uint amount) public{ | |
address sender = msg.sender; | |
if( amount <= ( resolveWeight[sender] - weightLocked[sender] ) ){ | |
resolveWeight[sender] -= amount; | |
for(uint config = 0; config<CONFIGS; config+=1){ | |
totalVotes_forEach_configOption[config][individualsSelectedOption[config][sender]] -= amount; | |
} | |
emit UnstakeResolves(sender, amount); | |
resolveToken.transfer(sender, amount); | |
}else{ | |
revert(); | |
} | |
} | |
event BackCandidate(address sender,address candidate, uint amount); | |
function backCandidate(address candidate, uint amount, bool _assert) public{ | |
address sender = msg.sender; | |
require(candidate!=ORACLE); | |
if( amount <= ( resolveWeight[sender] - weightLocked[sender] ) && !isWatcher(candidate) ){ | |
weightLocked[sender] += amount; | |
addShares(candidate, sender, amount); | |
//I guess candidates should be able to opt into pausing themselves instead of being cast as a delegate right away. | |
//Idk where else to write this comment | |
//If the candidate isn't seated, and freezing and pause isn't preventing them from being seated ... then assertCandidate() | |
if( candidatesChair[candidate] >= hotSeats && !frausted(candidate) && _assert){ | |
assertCandidate(candidate); | |
} | |
emit BackCandidate(sender, candidate, amount); | |
//LAZY U.I. | |
if(!alreadyBacking[sender][candidate]){ | |
yourBacking[sender].push(candidate); | |
alreadyBacking[sender][candidate] = true; | |
} | |
}else{ | |
revert(); | |
} | |
} | |
event WithdrawBacking(address sender,address candidate, uint amount); | |
function withdrawBacking(address candidate, uint amount) public{ | |
address sender = msg.sender; | |
if( amount <= shares[candidate][sender] && !frozen[candidate] && ( !(candidatesChair[candidate]<hotSeats) || paused[candidate] ) ){ | |
weightLocked[sender] -= amount; | |
removeShares(candidate, sender, amount); | |
emit WithdrawBacking(sender, candidate, amount); | |
}else{ | |
revert(); | |
} | |
} | |
function frausted(address candidate) public view returns(bool){ | |
return paused[candidate] || frozen[candidate]; | |
} | |
event AssertCandidate(address candidate, bool asserted, address replacedWatcher, uint newSeat); | |
function assertCandidate(address candidate) public returns(bool){ | |
if(candidatesChair[candidate] >= hotSeats && candidate != ORACLE && !frausted(candidate) ){ | |
address weakestLink = chairsCandidate[0]; | |
bool thereIsAFraustedDelegate; | |
uint chairNumber; | |
address comparedDelegate; | |
for(uint i = 0; i<hotSeats; i+=1){ | |
comparedDelegate = chairsCandidate[i]; | |
if(thereIsAFraustedDelegate && frausted(comparedDelegate) ){ | |
if( totalShares[comparedDelegate] < totalShares[weakestLink] ){ | |
weakestLink = comparedDelegate; | |
chairNumber = i; | |
} | |
}else{ | |
if( frausted(comparedDelegate) ){ | |
thereIsAFraustedDelegate = true; | |
weakestLink = comparedDelegate; | |
chairNumber = i; | |
}else if( totalShares[comparedDelegate] < totalShares[weakestLink] ){ | |
weakestLink = comparedDelegate; | |
chairNumber = i; | |
} | |
} | |
} | |
//check if we can swap out seated watcher & then swap 'em | |
if( totalShares[candidate] > totalShares[weakestLink] || thereIsAFraustedDelegate ){ | |
uint theChairForTheLoser; | |
if(hasChair[candidate]){ | |
theChairForTheLoser = candidatesChair[candidate]; | |
}else{ | |
hasChair[candidate] = true; | |
theChairForTheLoser = chairs; | |
chairs += 1; | |
} | |
// | |
//old candidate getting bumped | |
removeShares(ORACLE, weakestLink, totalShares[weakestLink]); | |
candidatesChair[ weakestLink ] = theChairForTheLoser; | |
chairsCandidate[theChairForTheLoser] = weakestLink; | |
//new candidate sitting at the round table | |
addShares(ORACLE, candidate, totalShares[candidate]); | |
chairsCandidate[chairNumber] = candidate; | |
candidatesChair[candidate] = chairNumber; | |
emit AssertCandidate(candidate, true, weakestLink, chairNumber); | |
return true; | |
}else{ | |
if(!hasChair[candidate]){ | |
hasChair[candidate] = true; | |
candidatesChair[candidate] = chairs; | |
chairsCandidate[chairs] = candidate; | |
emit AssertCandidate(candidate, false, address0, chairs); | |
chairs+=1; | |
return false; | |
}else{ | |
revert(); | |
} | |
} | |
}else{ | |
revert(); | |
} | |
} | |
event OptionVote(address sender, uint8 config, uint option, uint weight); | |
function optionVote(bool[] memory isModifying, uint[] memory modifiedOptions) public{ | |
address sender = msg.sender; | |
for(uint8 config = 0; config<CONFIGS; config+=1){ | |
if(isModifying[config]){ | |
uint selectedOption = individualsSelectedOption[config][sender]; | |
totalVotes_forEach_configOption[config][ selectedOption ] -= resolveWeight[sender]; | |
individualsSelectedOption[config][sender] = modifiedOptions[config]; | |
totalVotes_forEach_configOption[config][ modifiedOptions[config] ] += resolveWeight[sender]; | |
emit OptionVote(sender, config, modifiedOptions[config], resolveWeight[sender]); | |
assertOption( config, modifiedOptions[config] ); | |
} | |
} | |
} | |
event AssertOption(uint8 config, uint option); | |
function assertOption(uint8 config, uint option) public{ | |
if( totalVotes_forEach_configOption[config][option] > totalVotes_forEach_configOption[config][ oracleConfigurations[config] ] ){ | |
oracleConfigurations[config] = option; | |
emit AssertOption(config, option); | |
} | |
} | |
function isWatcher(address candidate) public view returns(bool){ | |
return candidatesChair[candidate]<hotSeats && !frausted(candidate); | |
} | |
function getFee() public view returns(uint txCoverageFee, uint serviceFee){ | |
uint activeWatchers; | |
for(uint chair = 0; chair<hotSeats; chair+=1){ | |
if( !frausted(chairsCandidate[chair]) ){ | |
activeWatchers += 1; | |
} | |
} | |
return ( TX_FEE_PER*activeWatchers, oracleConfigurations[SERVICE_FEE] ); | |
} | |
//------------------------------ Request Ticket Life Cycle | |
event FileRequestTicket(address sender, uint ticketID, string query, uint timeRequested, uint8 dataType, uint feePaid); | |
function fileRequestTicket(string memory query, uint8 returnType, bool needsResponse, uint additionalTime, bool subjective) public payable returns(uint ticketID){ | |
uint ETH = msg.value; | |
(uint txCoverageFee, uint serviceFee) = getFee(); | |
if(ETH == txCoverageFee + serviceFee ){ | |
RequestTicket memory ticket; | |
ticket.query = query; | |
ticket.timeRequested = now; | |
ticket.ID = requestTickets.length; | |
ticket.sender = msg.sender; | |
ticket.needsResponse = needsResponse; | |
ticket.subjective = subjective; | |
ticket.additionalTime = additionalTime; | |
Poll memory poll; | |
if(returnType>3) | |
returnType = 3; | |
poll.dataType = returnType; | |
ticket.poll = poll; | |
requestTickets.push(ticket); | |
ticket.txCoverageFee = txCoverageFee; | |
ticket.serviceFee = serviceFee; | |
emit FileRequestTicket(msg.sender, ticketID, query, now, returnType, ETH); | |
return ticket.ID; | |
}else{ | |
revert(); | |
} | |
} | |
event CommitVote(address sender, bytes hash); | |
function commitVote(uint[] memory tickets, bytes[] memory voteHashes) public{ | |
address sender = msg.sender; | |
//RequestTicket storage ticket; | |
for(uint R; R<tickets.length; R+=1 ){ | |
//ticket = requestTickets[ tickets[R] ]; | |
if(now <= requestTickets[ tickets[R] ].timeRequested + requestTickets[ tickets[R] ].additionalTime + oracleConfigurations[COMMIT_TIME_WINDOW] ){ | |
requestTickets[ tickets[R] ].poll.committed[sender] = true; | |
requestTickets[ tickets[R] ].poll.commitHash[sender] = voteHashes[R]; | |
emit CommitVote(sender, voteHashes[R]); | |
} | |
} | |
} | |
event RevealVote(address voter, uint ticketID, bool rejected, /*suba*/ string stringVote, uint uintVote, bool boolVote, address addressVote); | |
function revealVote(uint[] memory tickets, bool[] memory rejected, string[] memory stringVotes, uint[] memory uintVotes, bool[] memory boolVotes, address[] memory addressVotes, string[] memory passwords) public{ | |
address sender = msg.sender; | |
RequestTicket memory ticket; | |
bytes memory abiEncodePacked; | |
for(uint R; R<tickets.length; R+=1 ){ | |
ticket = requestTickets[ tickets[R] ]; | |
if(now > ticket.timeRequested + ticket.additionalTime + oracleConfigurations[COMMIT_TIME_WINDOW] && now <= ticket.timeRequested + ticket.additionalTime + oracleConfigurations[COMMIT_TIME_WINDOW] + oracleConfigurations[REVEAL_TIME_WINDOW] ){ | |
if(ticket.poll.dataType == 0){ | |
abiEncodePacked = abi.encodePacked( rejected[R], stringVotes[R], passwords[R] ); | |
}else if(ticket.poll.dataType == 1){ | |
abiEncodePacked = abi.encodePacked( rejected[R], uintVotes[R], passwords[R] ); | |
}else if(ticket.poll.dataType == 2){ | |
abiEncodePacked = abi.encodePacked( rejected[R], boolVotes[R], passwords[R] ); | |
}else if(ticket.poll.dataType == 3){ | |
abiEncodePacked = abi.encodePacked( rejected[R], addressVotes[R], passwords[R] ); | |
} | |
if( compareBytes( abiEncodePacked, requestTickets[ tickets[R] ].poll.commitHash[sender]) ){ | |
requestTickets[ tickets[R] ].poll.revealed[sender] = true; | |
if(rejected[R]){ | |
requestTickets[ tickets[R] ].poll.rejected[sender] = true; | |
}else{ | |
requestTickets[ tickets[R] ].poll.voted[sender] = true; | |
if(ticket.poll.dataType == 0){ | |
requestTickets[ tickets[R] ].poll.stringVotes[sender] = stringVotes[R]; | |
}else if(ticket.poll.dataType == 1){ | |
requestTickets[ tickets[R] ].poll.uintVotes[sender] = uintVotes[R]; | |
}else if(ticket.poll.dataType == 2){ | |
requestTickets[ tickets[R] ].poll.boolVotes[sender] = boolVotes[R]; | |
}else if(ticket.poll.dataType == 3){ | |
requestTickets[ tickets[R] ].poll.addressVotes[sender] = addressVotes[R]; | |
} | |
} | |
emit RevealVote(sender, tickets[R], rejected[R], stringVotes[R], uintVotes[R], boolVotes[R], addressVotes[R]); | |
} | |
} | |
} | |
} | |
event FinalizedRequestOptions(string[] stringOptions, uint[] uintOptions, bool[] boolOptions, address[] addressOptions, address[] watchers); | |
function finalizeRequest(uint ticketID) public{ | |
// if response time window is over or all delegates have voted, | |
// anyone can finalize the request to trigger the event | |
RequestTicket storage ticket = requestTickets[ticketID]; | |
if(!ticket.finalized){ | |
uint totalEligibleWeight; | |
address watcher; | |
//uint WEIGHTS[0]; | |
//uint WEIGHTS[1]; | |
uint[] memory WEIGHTS = new uint[](2);//0 weight of votes | 1 weight of rejections | |
string[] memory stringOptions = new string[](hotSeats); | |
uint[] memory uintOptions = new uint[](hotSeats); | |
bool[] memory boolOptions = new bool[](hotSeats); | |
address[] memory addressOptions = new address[](hotSeats); | |
uint[] memory optionWeights = new uint[](hotSeats); | |
address[] memory watchers = new address[](hotSeats);// LAZY UI data | |
//uint options; | |
uint[] memory OPT = new uint[](2);//0= number of options, 1=top Option | |
//uint weight; | |
//uint topOption; | |
uint opt; | |
for(uint chair = 0; chair < hotSeats; chair+=1){ | |
watcher = chairsCandidate[chair]; | |
watchers[chair] = watcher; | |
//weight = totalShares[watcher]; | |
if( !frausted(watcher) && timeSeated[watcher] <= ticket.timeRequested ){ | |
totalEligibleWeight += totalShares[watcher]; | |
if(ticket.poll.voted[watcher]){ | |
WEIGHTS[0] += totalShares[watcher]; | |
//check to see if chosen option already is accounted for, if so, add weight to it. | |
for(opt = 0; opt<OPT[0]; opt+=1){ | |
if( (ticket.poll.dataType == 0 && compareStrings(stringOptions[opt],ticket.poll.stringVotes[watcher]) ) || | |
(ticket.poll.dataType == 1 && uintOptions[opt] == ticket.poll.uintVotes[watcher]) || | |
(ticket.poll.dataType == 2 && boolOptions[opt] == ticket.poll.boolVotes[watcher]) || | |
(ticket.poll.dataType == 3 && addressOptions[opt] == ticket.poll.addressVotes[watcher]) | |
){ | |
optionWeights[opt] += totalShares[watcher]; | |
if(optionWeights[opt] > optionWeights[OPT[1]]){ | |
OPT[1] = opt; | |
} | |
break; | |
} | |
} | |
//add new unique option | |
if(opt == OPT[0]){ | |
if(ticket.poll.dataType == 0){ | |
stringOptions[OPT[0]] = ticket.poll.stringVotes[watcher]; | |
}else if(ticket.poll.dataType == 1){ | |
uintOptions[OPT[0]] = ticket.poll.uintVotes[watcher]; | |
}else if(ticket.poll.dataType == 2){ | |
boolOptions[OPT[0]] = ticket.poll.boolVotes[watcher]; | |
}else if(ticket.poll.dataType == 3){ | |
addressOptions[OPT[0]] = ticket.poll.addressVotes[watcher]; | |
} | |
optionWeights[OPT[0]] = totalShares[watcher]; | |
OPT[0]+=1; | |
} | |
}else if(ticket.poll.rejected[watcher]){ | |
WEIGHTS[1] += totalShares[watcher]; | |
} | |
} | |
} | |
if( totalEligibleWeight == (WEIGHTS[1] + WEIGHTS[0]) || now > ticket.timeRequested + ticket.additionalTime+oracleConfigurations[REVEAL_TIME_WINDOW]+oracleConfigurations[COMMIT_TIME_WINDOW] ){ | |
bool rejected; | |
if( WEIGHTS[1] > optionWeights[OPT[1]] ){ | |
rejected = true; | |
} | |
//dish out the rewards | |
earningsPerShare[ORACLE] += ticket.serviceFee * scaleFactor / totalShares[ORACLE]; | |
earningsPerWatcher += ticket.txCoverageFee / totalWatchers; | |
//write results in stone | |
if(rejected){ | |
ticket.rejected = true; | |
}else{ | |
uint8 dataType = ticket.poll.dataType; | |
if(dataType == 0){ | |
ticket.resolvedString = stringOptions[OPT[1]]; | |
}else if(dataType == 1){ | |
ticket.resolvedUint = uintOptions[OPT[1]]; | |
}else if(dataType == 2){ | |
ticket.resolvedBool = boolOptions[OPT[1]]; | |
}else if(dataType == 3){ | |
ticket.resolvedAddress = addressOptions[OPT[1]]; | |
} | |
} | |
ticket.finalized = true; | |
EMIT_FinalizeRequestData(ticket); | |
emit FinalizedRequestOptions( stringOptions, uintOptions, boolOptions, addressOptions, watchers); | |
//return results to requestor | |
if( isContract(ticket.sender) && ticket.needsResponse){ | |
sendOracleResponse(ticket); | |
} | |
}else{ | |
revert(); | |
} | |
} | |
} | |
event FinalizedRequestTicketData(uint ticketID, string query, uint8 dataType, bool rejected, string resolvedString, uint resolvedUint, bool resolvedBool, address resolvedAddress); | |
function EMIT_FinalizeRequestData(RequestTicket storage ticket) internal{ | |
FinalizedRequestTicketData(ticket.ID, ticket.query, ticket.poll.dataType, ticket.rejected, ticket.resolvedString, ticket.resolvedUint, ticket.resolvedBool, ticket.resolvedAddress); | |
} | |
function sendOracleResponse(RequestTicket memory ticket) internal{ | |
address(ticket.sender).call(abi.encodeWithSignature("receiveOracleResponse(bool,string,uint,bool,address)",ticket.rejected, ticket.resolvedString,ticket.resolvedUint,ticket.resolvedBool,ticket.resolvedAddress ) ); | |
} | |
function cashout(address[] memory pools) public{ | |
address payable sender = msg.sender; | |
for(uint p; p < pools.length; p+=1){ | |
update(pools[p], sender); | |
} | |
uint ETH = earnings[sender]; | |
earnings[sender] = 0; | |
sender.transfer( ETH ); | |
} | |
function runWatcherPayroll(address watcher) public{ | |
update(ORACLE, addressPayable(watcher) ); | |
updateWatcherTxEarnings(addressPayable(watcher) ); | |
} | |
function tryToPunish(uint[] memory tickets, address[] memory watchers) public{ | |
freezeNoncommits(tickets, watchers); | |
freezeUnrevealedCommits(tickets, watchers); | |
freezeWrongWatchers(tickets, watchers); | |
} | |
event FreezeNoncommits(uint ticketID, address watcher); | |
function freezeNoncommits(uint[] memory tickets, address[] memory watchers) public{ | |
// get them while they're still at the round table and we're in the reveal phase of a ticket | |
for(uint i; i<watchers.length; i+=1){ | |
if( candidatesChair[watchers[i]] < hotSeats && | |
!requestTickets[ tickets[i] ].poll.committed[ watchers[i] ] && | |
now > requestTickets[ tickets[i] ].timeRequested + requestTickets[ tickets[i] ].additionalTime + oracleConfigurations[COMMIT_TIME_WINDOW] && | |
now <= requestTickets[ tickets[i] ].timeRequested + requestTickets[ tickets[i] ].additionalTime + oracleConfigurations[COMMIT_TIME_WINDOW] + oracleConfigurations[REVEAL_TIME_WINDOW] | |
){ | |
if(punish(tickets[i] , watchers[i]) ){ | |
emit FreezeNoncommits(tickets[i] , watchers[i]); | |
} | |
} | |
} | |
} | |
event FreezeUnrevealedCommits(uint ticketID, address watcher); | |
function freezeUnrevealedCommits(uint[] memory tickets, address[] memory watchers) public{ | |
// get them if they made a commit, but did not reveal it after the reveal window is over | |
for(uint i; i<watchers.length; i+=1){ | |
if( requestTickets[ tickets[i] ].poll.committed[ watchers[i] ] && | |
!requestTickets[ tickets[i] ].poll.revealed[ watchers[i] ] && | |
now > requestTickets[ tickets[i] ].timeRequested + requestTickets[ tickets[i] ].additionalTime + oracleConfigurations[COMMIT_TIME_WINDOW] + oracleConfigurations[REVEAL_TIME_WINDOW] | |
){ | |
if(punish(tickets[i] , watchers[i]) ){ | |
emit FreezeUnrevealedCommits(tickets[i] , watchers[i]); | |
} | |
} | |
} | |
} | |
event FreezeWrongWatchers(uint ticketID, address watcher); | |
function freezeWrongWatchers(uint[] memory tickets, address[] memory watchers) public{ | |
// get them if the ticket is finalized and their vote doesn't match the resolved answer | |
uint ticket; | |
address watcher; | |
for(uint i; i<watchers.length; i+=1){ | |
ticket = tickets[i]; | |
watcher = watchers[i]; | |
if( requestTickets[ ticket ].finalized && | |
( | |
(requestTickets[ ticket ].poll.dataType == 0 && !compareStrings(requestTickets[ ticket ].resolvedString, requestTickets[ ticket ].poll.stringVotes[ watcher ] ))|| | |
(requestTickets[ ticket ].poll.dataType == 1 && requestTickets[ ticket ].resolvedUint != requestTickets[ ticket ].poll.uintVotes[ watcher ] )|| | |
(requestTickets[ ticket ].poll.dataType == 2 && requestTickets[ ticket ].resolvedBool != requestTickets[ ticket ].poll.boolVotes[ watcher ] )|| | |
(requestTickets[ ticket ].poll.dataType == 3 && requestTickets[ ticket ].resolvedAddress != requestTickets[ ticket ].poll.addressVotes[ watcher ] ) | |
) | |
){ | |
if(punish(ticket , watcher)){ | |
emit FreezeWrongWatchers(ticket , watcher); | |
} | |
} | |
} | |
} | |
event Punish(uint thatOutTime, bool freshFreeze, bool knockOff); | |
function punish(uint ticketID, address watcher) internal returns(bool punished){ | |
if(!requestTickets[ticketID].punished[watcher] && timeSeated[watcher] <= requestTickets[ ticketID ].timeRequested ){ | |
frozen[watcher] = true; | |
bool freshFreeze; | |
bool knockOff; | |
if(timeWhenThawedOut[watcher] > now){ | |
timeWhenThawedOut[watcher] += oracleConfigurations[FREEZE_TIMEOUT]; | |
}else{ | |
timeWhenThawedOut[watcher] = now + oracleConfigurations[FREEZE_TIMEOUT]; | |
freshFreeze = true; | |
if(isWatcher(watcher)){ | |
// | |
removeShares(ORACLE, watcher, totalShares[watcher]); | |
knockOff = true; | |
} | |
} | |
emit Punish(timeWhenThawedOut[watcher], freshFreeze, knockOff); | |
return true; | |
} | |
return false; | |
} | |
event Thaw(address candidate, bool seated); | |
function thaw(address candidate, bool _assert) public{ | |
if( now >= timeWhenThawedOut[candidate] ){ | |
bool seated; | |
frozen[candidate] = false; | |
if( candidatesChair[candidate] < hotSeats && !paused[candidate]){ | |
// | |
addShares(ORACLE, candidate, totalShares[candidate]); | |
seated = true; | |
}else if( _assert ){ | |
seated = assertCandidate(candidate); | |
} | |
emit Thaw(candidate, seated); | |
}else{ | |
revert(); | |
} | |
} | |
event PauseOut(address sender, bool wasWatcher); | |
function pauseOut() public{ | |
address sender = msg.sender; | |
bool wasWatcher; | |
if(isWatcher(sender)){ | |
wasWatcher = true; | |
// | |
removeShares(ORACLE, sender, totalShares[sender]); | |
} | |
paused[sender] = true; | |
emit PauseOut(sender, wasWatcher); | |
} | |
event Unpause(address sender, bool seated); | |
function unpause(bool _assert) public{ | |
address sender = msg.sender; | |
paused[sender] = false; | |
bool seated; | |
if( candidatesChair[sender] < hotSeats && !frozen[sender]){ | |
// | |
addShares(ORACLE, sender, totalShares[sender]); | |
seated = true; | |
}else if( _assert ){ | |
seated = assertCandidate(sender); | |
} | |
emit Unpause(sender, seated); | |
} | |
event UpdateRoundTable(uint newTotalHotSeats); | |
function updateRoundTable(uint seats) public{ | |
// update hotSeats up and down. | |
address candidate; | |
for(uint s; s<seats; s+=1){ | |
if( oracleConfigurations[ROUNDTABLE_SEATS] > hotSeats ){ | |
candidate = chairsCandidate[hotSeats]; | |
if(candidate == address0){ | |
break; | |
}else{ | |
// | |
addShares(ORACLE, candidate, totalShares[candidate]); | |
hotSeats += 1; | |
} | |
} | |
if( oracleConfigurations[ROUNDTABLE_SEATS] < hotSeats ){ | |
candidate = chairsCandidate[hotSeats-1]; | |
// | |
removeShares(ORACLE, candidate, totalShares[candidate]); | |
hotSeats -= 1; | |
} | |
if( oracleConfigurations[ROUNDTABLE_SEATS] == hotSeats ){break;} | |
} | |
emit UpdateRoundTable(hotSeats); | |
} | |
function viewRequestTickets(uint[] memory ticketIDs) public view returns( | |
address[] memory sender, | |
string[] memory query, | |
uint[] memory timeRequested, | |
bool[] memory finalized, | |
uint[] memory txCoverageFeePaid, | |
uint[] memory serviceFeePaid, | |
bool[] memory rejected, | |
string[] memory resolvedString, | |
uint[] memory resolvedUint, | |
bool[] memory resolvedBool, | |
address[] memory resolvedAddress, | |
uint8[] memory dataType){ | |
uint L = ticketIDs.length; | |
sender = new address[](L); | |
query = new string[](L); | |
timeRequested = new uint[](L); | |
finalized = new bool[](L); | |
txCoverageFeePaid = new uint[](L); | |
serviceFeePaid = new uint[](L); | |
rejected = new bool[](L); | |
resolvedString = new string[](L); | |
resolvedUint = new uint[](L); | |
resolvedBool = new bool[](L); | |
resolvedAddress = new address[](L); | |
dataType = new uint8[](L); | |
for(uint t; t<L;t+=1){ | |
sender[t] = requestTickets[t].sender; | |
query[t] = requestTickets[t].query; | |
timeRequested[t] = requestTickets[t].timeRequested; | |
finalized[t] = requestTickets[t].finalized; | |
txCoverageFeePaid[t] = requestTickets[t].txCoverageFee; | |
serviceFeePaid[t] = requestTickets[t].serviceFee; | |
rejected[t] = requestTickets[t].rejected; | |
resolvedString[t] = requestTickets[t].resolvedString; | |
resolvedUint[t] = requestTickets[t].resolvedUint; | |
resolvedBool[t] = requestTickets[t].resolvedBool; | |
resolvedAddress[t] = requestTickets[t].resolvedAddress; | |
dataType[t] = requestTickets[t].poll.dataType; | |
} | |
//return (ticket.sender, ticket.query,ticket.timeRequested,ticket.finalized,ticket.ETH,ticket.rejected,ticket.resolvedString,ticket.resolvedUint,ticket.resolvedBool,ticket.resolvedAddress,ticket.poll.dataType); | |
} | |
function viewYourCandidates(address you)public view returns(uint[] memory dividends, uint[] memory seat, bool[] memory isFrozen, bool[] memory isPaused){ | |
uint L = yourBacking[you].length; | |
dividends = new uint[](L); | |
seat = new uint[](L); | |
isFrozen = new bool[](L); | |
isPaused = new bool[](L); | |
uint owedPerShare;// = earningsPerShare[pool] - payouts[pool][account]; | |
uint newMoney;// = shares[pool][account] * owedPerShare; | |
address candidate; | |
for(uint c;c<L;c+=1){ | |
candidate = yourBacking[you][c]; | |
owedPerShare = earningsPerShare[candidate] - payouts[candidate][you]; | |
newMoney = shares[candidate][you] * owedPerShare; | |
dividends[c] = newMoney; | |
seat[c] = candidatesChair[candidate]; | |
isFrozen[c] = frozen[candidate]; | |
isPaused[c] = paused[candidate]; | |
} | |
} | |
function compareStrings(string memory a, string memory b) public pure returns (bool) { | |
return (keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b))) ); | |
} | |
function compareBytes(bytes memory a, bytes memory b) public pure returns (bool) { | |
return (keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b))) ); | |
} | |
function bytesToAddress(bytes memory bys) private pure returns (address addr){ | |
assembly { | |
addr := mload(add(bys,20)) | |
} | |
} | |
function accountData(address account) public view returns( | |
uint _resolveWeight, | |
uint _weightLocked, | |
uint _candidatesChair, | |
uint _timeSeated, | |
bool _frozen, | |
uint _timeWhenThawedOut, | |
bool _paused, | |
bool _hasChair, | |
uint _earnings, | |
uint _totalShares, | |
uint _txCoverageFeeEarnings | |
){ | |
_resolveWeight = resolveWeight[account]; | |
_weightLocked = weightLocked[account]; | |
_candidatesChair = candidatesChair[account]; | |
_timeSeated = timeSeated[account]; | |
_frozen = frozen[account]; | |
_timeWhenThawedOut = timeWhenThawedOut[account]; | |
_paused = paused[account]; | |
_hasChair = hasChair[account]; | |
_earnings = earnings[account]; | |
_totalShares = totalShares[account]; | |
uint txCoverageFeeEarnings; | |
if( !isWatcher(account) ){ | |
txCoverageFeeEarnings = earningsPerWatcher - watcherPayouts[account]; | |
} | |
_txCoverageFeeEarnings = txCoverageFeeEarnings; | |
} | |
function isContract(address _addr) public view returns (bool is_contract) { | |
uint length; | |
assembly { | |
//retrieve the size of the code on target address, this needs assembly | |
length := extcodesize(_addr) | |
} | |
if(length>0) { | |
return true; | |
}else { | |
return false; | |
} | |
} | |
} | |
abstract contract OracleInterfacingContract{ | |
function receiveOracleResponse(bool rejected, string calldata STRING, uint UINT, bool BOOL, address ADDRESS) external virtual returns(bool); | |
} | |
abstract contract ResolveToken{ | |
function transfer(address _to, uint256 _value) public virtual returns (bool); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment