Permalink
Find file
6ed2226 Dec 21, 2016
@joeykrug @tinybike
executable file 277 lines (261 sloc) 13.7 KB
# This software (Augur) allows buying && selling event outcomes in ethereum
# Copyright (C) 2015 Forecast Foundation OU
# This program is free software; you can redistribute it &&/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is free software: you can redistribute it &&/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Any questions please contact joey@augur.net
import branches as BRANCHES
import expiringEvents as EXPEVENTS
import cash as CASH
import events as EVENTS
import markets as MARKETS
import reporting as REPORTING
import backstops as BACKSTOPS
import consensusData as CONSENSUS
import penalizationCatchup as CATCHUP
import reportingThreshold as THRESHOLD
inset('refund.se')
inset('logReturn.se')
event submittedReportHash(sender: indexed, branch: indexed, event: indexed, reportHash, encryptedReport, encryptedSalt, ethics, timestamp)
event submittedReport(sender: indexed, branch: indexed, event: indexed, salt, report, ethics, timestamp)
event makeReports_logReturn(returnValue)
macro MAX_THRESHOLD: 10^54
def makeHash(salt, report, eventID, sender):
hashInfo = array(4)
hashInfo[0] = sender
hashInfo[1] = salt
hashInfo[2] = report
hashInfo[3] = eventID
reportHash = sha3(hashInfo, chars=32*len(hashInfo))
return(reportHash)
# Allows a user to submit the hash of their report / commit
# Error: 0: not caught up on rep redistributions
# Error -1: invalid event
# Error -2: not in first half of period [commit part]
# Error -3: not eligible to report on this event
def submitReportHash(event, reportHash, encryptedReport, encryptedSalt, ethics):
branch = EVENTS.getBranch(event)
votePeriod = BRANCHES.getVotePeriod(branch)
# makes sure a user is up to date on penalizations
lastPeriodPenalized = CONSENSUS.getPenalizedUpTo(branch, msg.sender)
lastPeriod = BRANCHES.getVotePeriod(branch)-1
delta = lastPeriod - lastPeriodPenalized
if(delta > 1):
if(CATCHUP.penalizationCatchup(branch, msg.sender)!=1):
return(0)
if(!CONSENSUS.getRepRedistributionDone(branch, msg.sender)):
return(0)
# if first report of period, num events not set
if(EXPEVENTS.getNumEventsToReportOn(branch, votePeriod)==0):
EXPEVENTS.setNumEventsToReportOn(branch)
# set amount of fees to be distributed in this period from the branch
BRANCHES.setInitialBalance(branch, votePeriod, CASH.balance(branch))
eventIndex = EXPEVENTS.getEventIndex(votePeriod, event)
eventID = EXPEVENTS.getEvent(branch, votePeriod, eventIndex)
if(eventIndex==0 && (eventID==0 || event!=eventID)):
return(-1)
reportingThreshold = 0
# check if this is a backstop event
if(EXPEVENTS.getRequired(event) || EVENTS.getReportingThreshold(event)):
reportingThreshold = MAX_THRESHOLD
else:
if(EXPEVENTS.getLesserReportNum(branch, votePeriod, event)==0):
THRESHOLD.calculateReportTargetForEvent(branch, event, votePeriod, msg.sender)
reportingThreshold = THRESHOLD.calculateReportingThreshold(branch, event, votePeriod, msg.sender)
# do abs then mult by 2 to ensure pos. and between 0 and 1
shaHash = sha3(msg.sender + event)
# max value 10^54: 2^256 / 10^54 = 115792089237316195423570.985...
shaHash = abs(shaHash) / 115792089237316195423571
if(shaHash > reportingThreshold):
return(-3)
periodLength = BRANCHES.getPeriodLength(branch)
residual = block.timestamp % periodLength
if(residual <= periodLength/2):
EXPEVENTS.setReportHash(branch, votePeriod, msg.sender, reportHash, event)
EXPEVENTS.setEncryptedReport(branch, votePeriod, msg.sender, encryptedReport, encryptedSalt, ethics, event)
log(type=submittedReportHash, msg.sender, branch, event, reportHash, encryptedReport, encryptedSalt, ethics, block.timestamp)
return(1)
return(-2)
# Submits / reveals a report for a period
# @return 1 if success
# Error messages
# 0: reporter doesn't exist or has <1 rep
# -1: has already reported
# -2: not in second half of period [reveal part]
# -3: hash doesn't match
# -4: bad report
# -5: invalid event
# -6: already resolved
# -7: <48 hr left in period, too late to report, able to put up readj. bonds though
# -8: fees couldn't be collected
def submitReport(event, salt, report, ethics):
branch = EVENTS.getBranch(event)
balance = REPORTING.getRepBalance(branch, msg.sender)
votePeriod = BRANCHES.getVotePeriod(branch)
if(balance<ONE):
logReturn(makeReports_logReturn, 0)
if(EXPEVENTS.getReport(branch, votePeriod, event, msg.sender)):
logReturn(makeReports_logReturn, -1)
eventIndex = EXPEVENTS.getEventIndex(votePeriod, event)
# makes sure event is in the given branch and vote period
eventID = EXPEVENTS.getEvent(branch, votePeriod, eventIndex)
if(eventIndex==0 && (eventID==0 || event!=eventID)):
logReturn(makeReports_logReturn, -5)
elif(EVENTS.getOutcome(event)!=0):
logReturn(makeReports_logReturn, -6)
periodLength = BRANCHES.getPeriodLength(branch)
# ensures user has collected fees for last reporting period
if(!CONSENSUS.getFeesCollected(branch, msg.sender, votePeriod-1)):
logReturn(makeReports_logReturn, -8)
# commented out for testing
#if(block.timestamp/periodLength!=((block.timestamp + 2*TWENTYFOURHR)/periodLength)):
# logReturn(makeReports_logReturn, -7)
residual = block.timestamp % periodLength
if(residual > periodLength/2):
realHash = EXPEVENTS.getReportHash(branch, votePeriod, msg.sender, event)
if(self.makeHash(salt, report, eventID, msg.sender)!=realHash || realHash==0):
logReturn(makeReports_logReturn, -3)
forkedOverEthicality = BACKSTOPS.getForkedOverEthicality(event)
forkedOverThisEvent = 0
if(BRANCHES.getEventForkedOver(branch) == event):
forkedOverThisEvent = 1
roundTwo = BACKSTOPS.getRoundTwo(event)
report = self.validateReport(event, branch, votePeriod, report, forkedOverEthicality, forkedOverThisEvent, roundTwo, balance)
if(report == -4):
logReturn(makeReports_logReturn, -4)
EXPEVENTS.setReport(branch, votePeriod, eventID, report, msg.sender)
# set ethics value for event
ethics = ethics
if(ethics!=ONE and ethics!=0):
ethics = ONE
# set ethicality for forked event
if(forkedOverThisEvent):
# fork remove ethicality option if was forked over ethicality, other ethicality choice is the ethicality
if(forkedOverEthicality):
oldEthical = ethic_catch(EVENTS.getEthical(event))
if(oldEthical == ONE):
ethics = 0
else:
ethics = ONE
EXPEVENTS.setEthicReport(branch, votePeriod, event, ethics, msg.sender)
ethics = (EVENTS.getForkEthicality(event)*EXPEVENTS.getRepEvent(branch, votePeriod, event) + ethics*balance) / (EXPEVENTS.getRepEvent(branch, votePeriod, event) + balance)
EVENTS.setForkEthicality(event, ethics)
# set ethicality for a regular event
else:
EXPEVENTS.setEthicReport(branch, votePeriod, event, ethics, msg.sender)
# weight by rep
if(roundTwo):
ethics = (EVENTS.getEthics(event)*EXPEVENTS.getRepEvent(branch, votePeriod, event) + ethics*balance) / (EXPEVENTS.getRepEvent(branch, votePeriod, event) + balance)
# just a simple avg.
else:
ethics = (EVENTS.getEthics(event)*EXPEVENTS.getNumReportsEvent(branch, votePeriod, event) + ethics) / (EXPEVENTS.getNumReportsEvent(branch, votePeriod, event) + 1)
EVENTS.setEthics(eventID, ethics)
EXPEVENTS.addReportToEvent(branch, votePeriod, eventID, msg.sender)
# round 2 and fork events are weighted by rep
if(roundTwo or forkedOverThisEvent):
EXPEVENTS.addRepEvent(branch, votePeriod, event, EXPEVENTS.getBeforeRep(branch, votePeriod, msg.sender))
reportsNum = EXPEVENTS.getLesserReportNum(branch, votePeriod, event)
paidBack = EXPEVENTS.getReportersPaidSoFar(branch, event)
diff = reportsNum - paidBack
# used to pay reporters after reporting for their gas costs
if(diff >= 1):
EXPEVENTS.addReportersPaidSoFar(branch, event)
CASH.subtractCash(branch, 3500000*tx.gasprice)
CASH.addCash(msg.sender, 3500000*tx.gasprice)
log(type=submittedReport, msg.sender, branch, event, salt, report, ethics, block.timestamp)
logReturn(makeReports_logReturn, 1)
logReturn(makeReports_logReturn, -2)
# validates and submits report
# weights reports by rep if round 2 and fork
# Returns -4 if report is invalid
# isn't between 1 and 2 if binary
# if same report as original outcome / ethicality if a forked event not forked over ethicality it's also invalid b/c this option was removed as a poss. answer
def validateReport(eventID, branch, votePeriod, report, forkedOverEthicality, forkedOverThisEvent, roundTwo, balance):
outcome = 0
# binary
if(EVENTS.getNumOutcomes(eventID)==2 and EVENTS.getMaxValue(eventID)==TWO && EVENTS.getMinValue(eventID)==ONE):
if(report>2*ONE or report<ONE or report==0):
return(-4)
# outcome is calculated as we go along on a report by report basis (i.e. lazily evaluating things)
elif(forkedOverThisEvent):
# in case of fork remove the original outcome as a possible response if didn't fork over ethics
if(!forkedOverEthicality && report == catch(EVENTS.getUncaughtOutcome(eventID))):
return(-4)
outcome = (EVENTS.getForkOutcome(eventID)*EXPEVENTS.getRepEvent(branch, votePeriod, eventID) + report*balance) / (EXPEVENTS.getRepEvent(branch, votePeriod, eventID) + balance)
EVENTS.setForkOutcome(eventID, outcome)
else:
if(roundTwo):
outcome = (EVENTS.getUncaughtOutcome(eventID)*EXPEVENTS.getRepEvent(branch, votePeriod, eventID) + report*balance) / (EXPEVENTS.getRepEvent(branch, votePeriod, eventID) + balance)
else:
outcome = (EVENTS.getUncaughtOutcome(eventID)*EXPEVENTS.getNumReportsEvent(branch, votePeriod, eventID) + report) / (EXPEVENTS.getNumReportsEvent(branch, votePeriod, eventID) + 1)
EVENTS.setUncaughtOutcome(eventID, outcome)
# scalar or categorical
else:
if(report > ONE):
report = ONE
elif(report<=0):
# 1 is the new 0 [1/10**18 is basically 0 but able to be differentiated from no report of 0]
report = 1
if(forkedOverThisEvent):
# in case of fork remove the original outcome as a possible response if didn't fork over ethics
if(!forkedOverEthicality && report == EVENTS.getUncaughtOutcome(eventID)):
return(-4)
# outcome (uncaught and mode) is calculated as we go along on a report by report basis (i.e. lazily evaluating things)
EXPEVENTS.addToWeightOfReport(votePeriod, eventID, report, balance)
if(EXPEVENTS.getWeightOfReport(votePeriod, eventID, report) > EXPEVENTS.getCurrentModeItems(votePeriod, eventID)):
EXPEVENTS.setCurrentMode(votePeriod, eventID, report)
EXPEVENTS.setCurrentModeItems(votePeriod, eventID, report)
outcome = EXPEVENTS.getCurrentMode(votePeriod, eventID)
EVENTS.setForkOutcome(eventID, outcome)
else:
if(roundTwo):
# outcome (uncaught and mode) is calculated as we go along on a report by report basis (i.e. lazily evaluating things)
EXPEVENTS.addToWeightOfReport(votePeriod, eventID, report, balance)
if(EXPEVENTS.getWeightOfReport(votePeriod, eventID, report) > EXPEVENTS.getCurrentModeItems(votePeriod, eventID)):
EXPEVENTS.setCurrentMode(votePeriod, eventID, report)
EXPEVENTS.setCurrentModeItems(votePeriod, eventID, report)
outcome = EXPEVENTS.getCurrentMode(votePeriod, eventID)
else:
# outcome (uncaught and mode) is calculated as we go along on a report by report basis (i.e. lazily evaluating things)
EXPEVENTS.addToWeightOfReport(votePeriod, eventID, report, 1)
if(EXPEVENTS.getWeightOfReport(votePeriod, eventID, report) > EXPEVENTS.getCurrentModeItems(votePeriod, eventID)):
EXPEVENTS.setCurrentMode(votePeriod, eventID, report)
EXPEVENTS.setCurrentModeItems(votePeriod, eventID, report)
outcome = EXPEVENTS.getCurrentMode(votePeriod, eventID)
EVENTS.setUncaughtOutcome(eventID, outcome)
return(report)
macro YES: TWO
macro NO: ONE
macro BAD: 3 * ONEHALF
macro CATCH_TOLERANCE: ONE / 10
# Bins values to 1, 1.5, 2
macro catch($x):
if($x < (BAD - CATCH_TOLERANCE)):
NO
elif($x > (BAD + CATCH_TOLERANCE)):
YES
else:
BAD
macro ethic_catch($x):
if($x < ONEHALF):
0
else:
ONE
macro abs($a):
if($a<0):
$a = -$a
$a