Skip to content

Commit

Permalink
make the automatic disavowal actually apply as disavowal. Add a funct…
Browse files Browse the repository at this point in the history
…ion to markets to do disavowal of crowdsourcers
  • Loading branch information
nuevoalex committed Jan 6, 2018
1 parent 2856280 commit af3d021
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 29 deletions.
2 changes: 1 addition & 1 deletion source/contracts/reporting/BaseReportingParticipant.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ contract BaseReportingParticipant is Controlled, IReportingParticipant {
}

function isDisavowed() public view returns (bool) {
return market == IMarket(0);
return market == IMarket(0) || !market.isContainerForReportingParticipant(this);
}

function getPayoutNumerator(uint8 _outcome) public view returns (uint256) {
Expand Down
14 changes: 11 additions & 3 deletions source/contracts/reporting/Market.sol
Original file line number Diff line number Diff line change
Expand Up @@ -271,16 +271,24 @@ contract Market is DelegationTarget, Extractable, ITyped, Initializable, Ownable
// reset state back to Initial Reporter
feeWindow = IFeeWindow(0);
IInitialReporter _initialParticipant = getInitialReporter();
for (uint8 i = 1; i < participants.length; ++i) {
IDisputeCrowdsourcer(participants[i]).disavow();
}
delete participants;
participants.push(_initialParticipant);
_initialParticipant.resetReportTimestamp();
crowdsourcers = MapFactory(controller.lookup("MapFactory")).createMap(controller, this);
return true;
}

function disavowCrowdsourcers() public onlyInGoodTimes returns (bool) {
IMarket _forkingMarket = getForkingMarket();
require(_forkingMarket != IMarket(0));
require(_forkingMarket != this);
IInitialReporter _initialParticipant = getInitialReporter();
delete participants;
participants.push(_initialParticipant);
crowdsourcers = MapFactory(controller.lookup("MapFactory")).createMap(controller, this);
return true;
}

function withdrawInEmergency() public onlyInBadTimes onlyOwner returns (bool) {
IReputationToken _reputationToken = getReputationToken();
uint256 _repBalance = _reputationToken.balanceOf(this);
Expand Down
40 changes: 24 additions & 16 deletions tests/reporting/test_fee_distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,11 @@ def test_initial_report_and_participation_fee_collection(localFixture, universe,
with EtherDelta(expectedFees, tester.a0, localFixture.chain, "Redeeming didn't increase ETH correctly"):
assert categoricalInitialReport.redeem(tester.a0)

def test_failed_crowdsourcer_fees(localFixture, universe, market, cash, reputationToken):
@mark.parametrize('finalize', [
True,
False,
])
def test_failed_crowdsourcer_fees(finalize, localFixture, universe, market, cash, reputationToken):
feeWindow = localFixture.applySignature('FeeWindow', market.getFeeWindow())

# generate some fees
Expand All @@ -92,31 +96,35 @@ def test_failed_crowdsourcer_fees(localFixture, universe, market, cash, reputati
generateFees(localFixture, universe, market)

# We'll have testers contribute to a dispute but not reach the target
amount = market.getTotalStake() - 1
amount = market.getTotalStake()

with TokenDelta(reputationToken, -amount, tester.a1, "Disputing did not reduce REP balance correctly"):
assert market.contribute([0, market.getNumTicks()], False, amount, sender=tester.k1, startgas=long(6.7 * 10**6))
with TokenDelta(reputationToken, -amount + 1, tester.a1, "Disputing did not reduce REP balance correctly"):
assert market.contribute([1, market.getNumTicks()-1], False, amount - 1, sender=tester.k1, startgas=long(6.7 * 10**6))

with TokenDelta(reputationToken, -amount, tester.a2, "Disputing did not reduce REP balance correctly"):
assert market.contribute([0, market.getNumTicks()], False, amount, sender=tester.k2, startgas=long(6.7 * 10**6))
with TokenDelta(reputationToken, -amount + 1, tester.a2, "Disputing did not reduce REP balance correctly"):
assert market.contribute([1, market.getNumTicks()-1], False, amount - 1, sender=tester.k2, startgas=long(6.7 * 10**6))

assert market.getFeeWindow() == feeWindow.address

payoutDistributionHash = market.derivePayoutDistributionHash([0, market.getNumTicks()], False)
payoutDistributionHash = market.derivePayoutDistributionHash([1, market.getNumTicks()-1], False)
failedCrowdsourcer = localFixture.applySignature("DisputeCrowdsourcer", market.getCrowdsourcer(payoutDistributionHash))

# Fast forward time until the fee window is over and we can redeem to recieve the REP back and fees
localFixture.contracts["Time"].setTimestamp(feeWindow.getEndTime() + 1)
assert market.finalize()

# The dispute crowdsourcer contributor locked in REP for 2 rounds, as did the Initial Reporter
expectedTotalFees = getExpectedFees(localFixture, cash, failedCrowdsourcer, 1)

with TokenDelta(reputationToken, amount, tester.a1, "Redeeming did not refund REP"):
if finalize:
# Fast forward time until the fee window is over and we can redeem to recieve the REP back and fees
localFixture.contracts["Time"].setTimestamp(feeWindow.getEndTime() + 1)
expectedTotalFees = getExpectedFees(localFixture, cash, failedCrowdsourcer, 1)
else:
# Continue to the next round which will disavow failed crowdsourcers and let us redeem once the window is over
market.contribute([0, market.getNumTicks()], False, amount * 2, startgas=long(6.7 * 10**6))
assert market.getFeeWindow() != feeWindow.address
localFixture.contracts["Time"].setTimestamp(feeWindow.getEndTime() + 1)
expectedTotalFees = getExpectedFees(localFixture, cash, failedCrowdsourcer, 1)

with TokenDelta(reputationToken, amount - 1, tester.a1, "Redeeming did not refund REP"):
with EtherDelta(expectedTotalFees / 2, tester.a1, localFixture.chain, "Redeeming didn't increase ETH correctly"):
assert failedCrowdsourcer.redeem(tester.a1)

with TokenDelta(reputationToken, amount, tester.a2, "Redeeming did not refund REP"):
with TokenDelta(reputationToken, amount - 1, tester.a2, "Redeeming did not refund REP"):
with EtherDelta(cash.balanceOf(failedCrowdsourcer.address), tester.a2, localFixture.chain, "Redeeming didn't increase ETH correctly"):
assert failedCrowdsourcer.redeem(tester.a2)

Expand Down
25 changes: 16 additions & 9 deletions tests/reporting/test_reporting.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,13 @@ def test_roundsOfReporting(rounds, localFixture, market, universe):
feeWindow = market.getFeeWindow()
assert feeWindow == universe.getCurrentFeeWindow()

@mark.parametrize('finalizeByMigration', [
True,
False
@mark.parametrize('finalizeByMigration, manuallyDisavow', [
(True, True),
(False, True),
(True, False),
(False, False),
])
def test_forking(finalizeByMigration, localFixture, universe, market, categoricalMarket):
def test_forking(finalizeByMigration, manuallyDisavow, localFixture, universe, market, categoricalMarket):
# Let's go into the one dispute round for the categorical market
proceedToNextRound(localFixture, categoricalMarket)
proceedToNextRound(localFixture, categoricalMarket)
Expand All @@ -165,24 +167,29 @@ def test_forking(finalizeByMigration, localFixture, universe, market, categorica
numTicks = market.getNumTicks()
childUniverse = universe.createChildUniverse([numTicks/ 4, numTicks * 3 / 4], False)

# confirm that before the fork is finalized we can redeem stake in other markets crowdsourcers, which are disavowable
categoricalDisputeCrowdsourcer = localFixture.applySignature("DisputeCrowdsourcer", categoricalMarket.getReportingParticipant(1))

if manuallyDisavow:
assert categoricalMarket.disavowCrowdsourcers()
# We can redeem before the fork finalizes since disavowal has occured
assert categoricalDisputeCrowdsourcer.redeem(tester.a0)

# finalize the fork
finalizeFork(localFixture, market, universe, finalizeByMigration)

# get the reporting participants for the categorical market before they may be disavowed
categoricalInitialReport = localFixture.applySignature("InitialReporter", categoricalMarket.getReportingParticipant(0))
categoricalDisputeCrowdsourcer = localFixture.applySignature("DisputeCrowdsourcer", categoricalMarket.getReportingParticipant(1))

# The categorical market can be migrated to the winning universe
assert categoricalMarket.migrateThroughOneFork()

# This disavows the dispute crowdsourcer
# The dispute crowdsourcer has been disavowed
newUniverse = localFixture.applySignature("Universe", categoricalMarket.getUniverse())
assert newUniverse.address != universe.address
assert categoricalDisputeCrowdsourcer.isDisavowed()
assert not universe.isContainerForReportingParticipant(categoricalDisputeCrowdsourcer.address)
assert not newUniverse.isContainerForReportingParticipant(categoricalDisputeCrowdsourcer.address)

# The initial report is still present however
categoricalInitialReport = localFixture.applySignature("InitialReporter", categoricalMarket.getReportingParticipant(0))
assert categoricalMarket.getReportingParticipant(0) == categoricalInitialReport.address
assert not categoricalInitialReport.isDisavowed()
assert not universe.isContainerForReportingParticipant(categoricalInitialReport.address)
Expand Down

0 comments on commit af3d021

Please sign in to comment.