-
Notifications
You must be signed in to change notification settings - Fork 112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Stagger allowed miner responses during a dispute #842
Conversation
48e70bc
to
9910e0c
Compare
ab699cc
to
5548d2c
Compare
Further comments as this hopefully nears being done:
I have added a I have added a helper function |
56ac63b
to
c7c4635
Compare
d4dc043
to
57e78d9
Compare
57e78d9
to
ffe55ba
Compare
909e18d
to
7c59ab2
Compare
551dc1a
to
46a7a3b
Compare
46a7a3b
to
8c4214e
Compare
|
||
contract ReputationMiningCycleBinarySearch is ReputationMiningCycleCommon { | ||
function respondToBinarySearchForChallenge( | ||
uint256 round, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we not using the underscore convention in these contracts? We use them in the interface contract...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's barely used in the interface contract either for what it's worth, but I guess it's best to be consistent across all contracts.
return false; | ||
} | ||
|
||
uint256 delta = now - since; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading through this and I'm still getting confused around which way the window works. One one level it seems like it should be >=
, i.e. more restrictive when the delta is large, but we have the opposite. Can we rename delta
to responseTimeRemaining
or something? since
could also be clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rearranged this a bit, that hopefully makes it clearer to the reader.
@@ -367,30 +377,44 @@ class ReputationMinerClient { | |||
|
|||
// If we're here, we do have an opponent. | |||
// Has our opponent timed out? | |||
// TODO: Remove these magic numbers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the client not have a constants.js
file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does not. We could add one, ideally though we'd want these values being picked up off the contracts, which I think is what I had in mind when I wrote this comment.
await forwardTime(MINING_CYCLE_DURATION, this); | ||
|
||
let nSubmissionsForHash = await repCycle.getNSubmissionsForHash("0x12345678", 10, "0x00"); | ||
expect(nSubmissionsForHash).to.eq.BN(0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to.be.zero
const noChallengeCompleted = noEventSeen(repCycleEthers, "ChallengeCompleted"); | ||
const noHashInvalidated = noEventSeen(repCycleEthers, "HashInvalidated"); | ||
|
||
await noChallengeCompleted; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remind me why we don't just await
these promises directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it's testing the auto functionality. If we awaited directly where they're created, the actions to trigger them have not yet taken place on chain so the test would time out. If we moved the creation to where they're awaited, then the tests become spotty, as the thing the promises are waiting for might have happened before they've been set up.
|
||
await goodClient.submitRootHash(); | ||
let reward = await repCycle.getDisputeRewardSize(); | ||
expect(reward).to.eq.BN("0"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to.be.zero
e6539f3
to
e5dee55
Compare
// require user made a submission | ||
if (reputationHashSubmissions[msg.sender].proposedNewRootHash == bytes32(0x00)) { | ||
return false; | ||
} | ||
uint256 target = delta * Y; | ||
uint256 windowOpenFor = now - _responseWindowOpened; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to make the most of this variable I'd be happy using it up in the conditional as
if (windowOpenFor <= SUBMITTER_ONLY_WINDOW_DURATION) {
Even if there's only one
How did we ever write bulletproof tests without makeTxAtTimestamp?
This brings them in line with most of the rest of the codebase i.e. prefixed with _
e5dee55
to
9bee220
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! This has been in the works a while.
First off, this is (notionally) based on #812 hence draft status for now.
We obviously hope that multiple people will run reputation mining clients. For submissions, we have a window over which it gets easier and easier to submit new hashes, with the difficulty being randomised per user, and with it being easier to submit if you have more staked. We did this because we wanted rewards to be 'fairly' distributed and for miners to not be wasting gas, making submissions that wouldn't succeed because all the slots filled up before their transaction was mined (which wasn't the case when they broadcast it). This PR adds similar functionality to dispute resolutions.
In this implementation, there is a window during a dispute resolution where only those that have submitted a hash are able to respond to a challenge, and the point in this window where a submitter is able to respond is randomised per-submitter for each stage. This additional window lasts 10 minutes, so in the event of a 'full' mining cycle submission, each submitter can expect to get (waves hands vaguely) a minute after they are eligible to respond to a dispute before someone else becomes eligible, which seems adequate. This increases the potential severity of a DDOS attack, because each dispute round can take twice as long to resolve if bad submissions are defended judiciously, but the well-behaving miners shouldn't step on each others toes during submissions, which is well worth the cost.
There's one subtlety in the implementation worth drawing attention to; in the case of all but one of these windows, the window is based on the last response timestamp of the submission being defended. The one case that's not true is the
invalidateHash
call where an entry is being invalidated (i.e. not the case where a bye is awarded), where the window is based on the last response timestamp of the submission being invalided.