Skip to content

Instantly share code, notes, and snippets.

@RossPfeiffer
Created August 14, 2020 00:50
Show Gist options
  • Save RossPfeiffer/817817fda46e314e2a7ebd6badbdc184 to your computer and use it in GitHub Desktop.
Save RossPfeiffer/817817fda46e314e2a7ebd6badbdc184 to your computer and use it in GitHub Desktop.
pragma solidity ^ 0.6.8;
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 txCoverageFee;
uint serviceFee;
bool subjective;
mapping(address => mapping(address => bool)) attacks;
mapping(address => bool) damaged;
bool rejected;
uint numberOfOptions;
string[] resolvedStrings;
uint[] resolvedUints;
bool[] resolvedBools;
address[] resolvedAddresses;
mapping(address => bool) punished;
}
//oracle configs
uint constant ROUNDTABLE_SEATS = 0;
uint constant RESPONSE_TIME_WINDOW = 1;
uint constant DELEGATE_REWARDSHARE = 2;
uint constant ELECTORATE_REWARDSHARE = 3;
uint constant FREEZE_TIMEOUT = 4;
uint constant TX_FEE_PER = 5;
uint constant SERVICE_FEE = 6;
uint constant CONFIGS = 7;
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)) paADDRts;
mapping(address => uint) earningsPerShare;
//Tx Coverage fee
uint earningsPerWatcher;
uint totalWatchers;
mapping(address => uint256) watcherPaADDRts;
//lazy UI data
mapping(address => address[]) public ADDRrBacking;
mapping(address => mapping(address => bool)) public alreadyBacking;
ResolveToken resolveToken;
constructor(address _resolve) public{
resolveToken = ResolveToken(_resolve);
}
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] - paADDRts[pool][account];
uint newMoney = shares[pool][account] * owedPerShare;
paADDRts[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 - watcherPaADDRts[watcher];
watcherPaADDRts[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<1/*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]){
ADDRrBacking[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 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.subjective = subjective;
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 <= ticket.timeRequested + oracleConfigurations[RESPONSE_TIME_WINDOW] ){
ticket.poll.committed[sender] = true;
ticket.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 + oracleConfigurations[RESPONSE_TIME_WINDOW] && now <= ticket.timeRequested + oracleConfigurations[RESPONSE_TIME_WINDOW]*2 ){
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 SubjectiveStance(address sender, uint ticketID, address defender, bool stance);
function subjectiveStance(uint[] memory tickets, address[] memory defenders, bool[] memory stances) public{
address sender = msg.sender;
RequestTicket storage ticket;
for(uint R; R<tickets.length; R+=1 ){
ticket = requestTickets[ tickets[R] ];
if( timeSeated[sender] <= ticket.timeRequested && timeSeated[defenders[R]] <= ticket.timeRequested && now > ticket.timeRequested + oracleConfigurations[RESPONSE_TIME_WINDOW]*2 && now <= ticket.timeRequested + oracleConfigurations[RESPONSE_TIME_WINDOW]*3 ){
ticket.attacks[sender][defenders[R]] = stances[R];
emit SubjectiveStance(sender, tickets[R], defenders[R], stances[R]);
}
}
}
event Attack(address offense, address defense, uint damage);
function calculateDamage( uint ticketID ) internal returns(uint combatWeight, uint[] memory damage){
RequestTicket storage ticket = requestTickets[ticketID];
address offensiveWatcher;
address defender;
uint Y;
uint X;
damage = new uint[](hotSeats);
if(ticket.subjective){
for(X = 0; X < hotSeats; X+=1){
offensiveWatcher = chairsCandidate[X];
if( !frausted(offensiveWatcher) && timeSeated[offensiveWatcher] <= ticket.timeRequested ){
combatWeight += totalShares[offensiveWatcher];
for(Y = 0; Y < hotSeats; Y+=1){
if( !frausted(offensiveWatcher) ){
defender = chairsCandidate[Y];
if(ticket.attacks[offensiveWatcher][defender])
damage[Y] += totalShares[offensiveWatcher];
emit Attack(offensiveWatcher, defender, totalShares[offensiveWatcher]);
}
}
}
}
}
}
event FinalizedRequestOptions(string[] stringOptions, uint[] uintOptions, bool[] boolOptions, address[] addressOptions, address[] watchers);
function finalizeRequest(uint ticketID) public{
// if responsew 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){
address watcher;
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);
uint[] memory UINTs = new uint[](6);//0= weight of votes, 1=top Option, 2= number of options, 3=top Option, 4 =total eligible weight, 5 = combat weight
uint opt;
uint[] memory damage;
(UINTs[5]/*combatWeight*/, damage) = calculateDamage(ticketID);
for(uint chair = 0; chair < hotSeats; chair+=1){
watcher = chairsCandidate[chair];
if(damage[chair]>UINTs[5]/*combatWeight*//2){
if( !frausted(watcher) && timeSeated[watcher] <= ticket.timeRequested ){
UINTs[4]/*total Eligible Weight*/ += totalShares[watcher];
if(ticket.poll.voted[watcher]){
UINTs[0]/*weight of votes*/ += totalShares[watcher];
//check to see if chosen option already is accounted for, if so, add weight to it.
for(opt = 0; opt<UINTs[2]/*option count*/; 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[UINTs[3]/*top option*/] && !ticket.subjective){
UINTs[3]/*top option*/ = opt;
}
break;
}
}
//add new unique option
if(opt == UINTs[2]/*option count*/){
if(ticket.poll.dataType == 0){
stringOptions[UINTs[2]/*option count*/] = ticket.poll.stringVotes[watcher];
}else if(ticket.poll.dataType == 1){
uintOptions[UINTs[2]/*option count*/] = ticket.poll.uintVotes[watcher];
}else if(ticket.poll.dataType == 2){
boolOptions[UINTs[2]/*option count*/] = ticket.poll.boolVotes[watcher];
}else if(ticket.poll.dataType == 3){
addressOptions[UINTs[2]/*option count*/] = ticket.poll.addressVotes[watcher];
}
optionWeights[UINTs[2]/*option count*/] = totalShares[watcher];
UINTs[2]/*option count*/+=1;
}
}else if(ticket.poll.rejected[watcher]){
UINTs[1]/*weight of rejections*/ += totalShares[watcher];
}
}
}else{
ticket.damaged[watcher] = true;
}
}
if( (UINTs[4]/*total Eligible Weight*/ == (UINTs[1]/*weight of rejections*/ + UINTs[0]/*weight of votes*/) && !ticket.subjective) || (now > ticket.timeRequested + oracleConfigurations[RESPONSE_TIME_WINDOW]*(ticket.subjective?3:2) ) ){
bool rejected;
if( UINTs[1]/*weight of rejections*/ > optionWeights[UINTs[3]/*top option*/] ){
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(ticket.subjective){
ticket.numberOfOptions = UINTs[2]/*option count*/;
if(dataType == 0){
ticket.resolvedStrings = stringOptions;
}else if(dataType == 1){
ticket.resolvedUints = uintOptions;
}else if(dataType == 2){
ticket.resolvedBools = boolOptions;
}else if(dataType == 3){
ticket.resolvedAddresses = addressOptions;
}
}else{
ticket.numberOfOptions = 1;
if(dataType == 0){
ticket.resolvedStrings = [stringOptions[UINTs[3]/*top option*/]];
}else if(dataType == 1){
ticket.resolvedUints = [uintOptions[UINTs[3]/*top option*/]];
}else if(dataType == 2){
ticket.resolvedBools = [boolOptions[UINTs[3]/*top option*/]];
}else if(dataType == 3){
ticket.resolvedAddresses = [addressOptions[UINTs[3]/*top option*/]];
}
}
}
ticket.finalized = true;
}else{
revert();
}
}
}
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
RequestTicket storage ticket;
for(uint i; i<watchers.length; i+=1){
ticket = requestTickets[ tickets[i] ];
if( candidatesChair[watchers[i]] < hotSeats &&
!ticket.poll.committed[ watchers[i] ] &&
now > ticket.timeRequested + oracleConfigurations[RESPONSE_TIME_WINDOW] &&
now <= ticket.timeRequested + oracleConfigurations[RESPONSE_TIME_WINDOW] + oracleConfigurations[RESPONSE_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
RequestTicket storage ticket;
for(uint i; i<watchers.length; i+=1){
ticket = requestTickets[ tickets[i] ];
if( ticket.poll.committed[ watchers[i] ] &&
ticket.poll.revealed[ watchers[i] ] &&
now > requestTickets[ tickets[i] ].timeRequested + oracleConfigurations[RESPONSE_TIME_WINDOW] + oracleConfigurations[RESPONSE_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
address watcher;
RequestTicket storage ticket;
for(uint i; i<watchers.length; i+=1){
ticket = requestTickets[ tickets[i] ];
watcher = watchers[i];
if( ticket.finalized &&
(
(ticket.poll.dataType == 0 && !compareStrings(ticket.resolvedStrings[0], ticket.poll.stringVotes[ watcher ] ))||
(ticket.poll.dataType == 1 && ticket.resolvedUints[0] != ticket.poll.uintVotes[ watcher ] )||
(ticket.poll.dataType == 2 && ticket.resolvedBools[0] != ticket.poll.boolVotes[ watcher ] )||
(ticket.poll.dataType == 3 && ticket.resolvedAddresses[0] != ticket.poll.addressVotes[ watcher ] )||
(!ticket.rejected && ticket.poll.rejected[ watcher ] )||/*if they reject something the majority didn't reject*/
(ticket.subjective && ticket.damaged[ watcher ] )/*if their subjective contribution is garbage*/
)
){
if(punish(tickets[i] , watcher)){
emit FreezeWrongWatchers(tickets[i] , 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;
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 viewRequestTicket(uint ticketID) public view returns(
address sender,
string memory query,
uint timeRequested,
bool finalized,
uint txCoverageFeePaid,
uint serviceFeePaid,
bool rejected,
string[] memory resolvedStrings,
uint[] memory resolvedUints,
bool[] memory resolvedBools,
address[] memory resolvedAddresses,
uint8 dataType){
RequestTicket storage T;
T = requestTickets[ticketID];
sender = T.sender;
query = T.query;
timeRequested = T.timeRequested;
finalized = T.finalized;
txCoverageFeePaid = T.txCoverageFee;
serviceFeePaid = T.serviceFee;
rejected = T.rejected;
resolvedStrings = T.resolvedStrings;
resolvedUints = T.resolvedUints;
resolvedBools = T.resolvedBools;
resolvedAddresses = T.resolvedAddresses;
dataType = T.poll.dataType;
}
function viewCandidates(address ADDR)public view returns(uint[] memory dividends, uint[] memory seat, bool[] memory isFrozen, bool[] memory isPaused){
uint L = ADDRrBacking[ADDR].length;
dividends = new uint[](L);
seat = new uint[](L);
isFrozen = new bool[](L);
isPaused = new bool[](L);
uint owedPerShare;// = earningsPerShare[pool] - paADDRts[pool][account];
uint newMoney;// = shares[pool][account] * owedPerShare;
address candidate;
for(uint c;c<L;c+=1){
candidate = ADDRrBacking[ADDR][c];
owedPerShare = earningsPerShare[candidate] - paADDRts[candidate][ADDR];
newMoney = shares[candidate][ADDR] * 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 - watcherPaADDRts[account];
}
_txCoverageFeeEarnings = txCoverageFeeEarnings;
}
}
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