-
Notifications
You must be signed in to change notification settings - Fork 60
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
Reintroduce timestamp checks #1724
Conversation
See also #1649 See also dusk-network/dips#13
See also #1649 See also dusk-network/dips#13
769854f
to
55ef85a
Compare
As far as I remember, we decided to reject the DIP proposal. And the #1649 is to avoid meaningless timestamps (e.g., as stated in the issue: avoid blocks from one day in the future) not to introduce strict timestamp conditions on blocks. |
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.
These checks are very strict. Didn't we agree on leaving some margin of error (i.e. the slippage
)?
Also, we should probably make sure all nodes use NTP, or are synced in some way, before enforcing these checks on mainnet (see dusk-network/dips#12).
} | ||
|
||
if candidate_block.timestamp < self.prev_header.timestamp { | ||
anyhow::bail!("invalid timestamp"); |
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.
anyhow::bail!("invalid timestamp"); | |
anyhow::bail!("invalid timestamp: lower than previous block"); |
} | ||
|
||
if candidate_block.timestamp > get_current_timestamp() { | ||
anyhow::bail!("invalid future timestamp"); |
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.
anyhow::bail!("invalid future timestamp"); | |
anyhow::bail!("invalid timestamp: candidate from the future"); |
candidate_block.timestamp - self.prev_header.timestamp; | ||
if current_delta > max_delta { | ||
anyhow::bail!( | ||
"invalid timestamp, delta: {current_delta}/{max_delta}" |
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.
"invalid timestamp, delta: {current_delta}/{max_delta}" | |
"invalid timestamp: candidate is too late (delta: {current_delta}/{max_delta})" |
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.
Shouldn't we check against the current timestamp (or better, the time at which the candidate was received), instead of the timestamp in the candidate?
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.
Why? If I check the current timestamp the candidate timestamp can be invalid (checks for current timestamp/iteration are natives performed by the iteration timeout mechanism).
Timestamp of candidate should always be lower than the receiving timestamp.
Furthermore this check is performed for any block, not only for live votes
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.
My reasoning was that a candidate that is maliciously sent at a later time could still pass this check by setting an appropriate timestamp. But I guess in non-relaxed iterations, the candidate would be equally rejected due to step timeout.
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.
Still, I'm not sure about the benefit of this check on Candidate messages...
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.
Having this check while accepting the candidate, will reject the candidate itself and let the provisioner to not vote (even if the provisioner has some iteration delay)
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.
For my benefit, can someone explain how this PR is addressing consensus forks issues? We used to have consensus forks before removing the block timestamp too.
if msg.header.prev_block_hash != p.candidate.header().prev_block_hash { | ||
return Err(ConsensusError::InvalidBlockHash); | ||
} | ||
|
||
if p.candidate.header().timestamp > get_current_timestamp() { |
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.
Maybe we can be more error-tolerant here?
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.
What do you suggest as a fair more tolerant check?
Having a more tolerant check here will introduce some delay effect in the chain.
Eg: if we accept block 5 mins from the future, that mean that the next block cannot be produced for the next 5 mins due to sequential check
_committee: &Committee, | ||
) -> Result<HandleMsgOutput, ConsensusError> { | ||
// store candidate block | ||
let p = Self::unwrap_msg(&msg)?; | ||
|
||
if p.candidate.header().timestamp < ru.timestamp() { |
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.
Do you have any intention by putting this check here instead of ProposalHandler::verify(
? If so, let's have it as a comment.
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 should be the same, since both will discard the block. I'm not strongly opinionated on this
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 ProposalHandler::verify(
considers it valid (returns true) the message is rebroadcast. (if that is the intention then it's fine)
anyhow::bail!("invalid timestamp"); | ||
} | ||
|
||
if candidate_block.iteration < RELAX_ITERATION_THRESHOLD { |
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 we have a candidate above RELAX_ITERATION_THRESHOLD
, shouldn't we still have some max_delta
?
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.
Relaxed iterations have no time limits, they move on to the next step only when the current step reaches a result.
In the case of Proposal, the step waits indefinitely for a Candidate block.
So there shouldn't be any timestamp check beyond the condition of being subsequent to the previous block
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.
max_delta is depending to the iteration
Indeed is MAX_ITERATION_TIMEOUT*ITERATION
I agree with that. |
How can rusk binary be sure that NTPd is enabled on the host OS? |
The original intent of (re)introducing some timestamp check on consensus messages was to deal with the "Nothing at stake" problem, where we needed to determine some temporal order of actions (of the same provisioner) on parallel branches. However, as I commented, I'm not sure about the actual benefit of these checks. |
This PR is not completely addressing that, but it should (IMO) mitigate it. It will mitigate it because delayed provisioners will not vote for candidates anymore if the MAX_DELAY has been reached.
Checks we used to have in the past were not addressing this, they were just checking if a block timestamp was too much from the future and then edit it to be no more than 5 mins |
The problem is that the check is done on the timestamp included in the candidate. So, if the candidate was produced in time, late voters will still vote for it. That's why I was suggesting to compare against the current timestamp instead of the candidate's one. |
Solves #1494 |
Closing as obsolete |
Can we delete the branch? |
Added the following checks:
H
must be greater or equal to timestamp of block with height=H-1
H
generated at iteration=I
must be lower or equal to the timestamp of block with height=H-1
plusI*MAX_ITER_TIMEOUT
1See also #1649
See also dusk-network/dips#13
EDIT: This PR has to be repurposed to address #1494
Footnotes
This check is enforced only if the iteration
I
is not relaxed ->I
<RELAX_ITERATION_THRESHOLD
↩