state: add an 'IsTimely' method to implement the 'timely' check for proposer-based timestamps#7170
Conversation
…proposer-based timestamps
state/state.go
Outdated
| // | ||
| // For more information on the meaning of 'timely', see the proposer-based timestamp specification: | ||
| // https://github.com/tendermint/spec/tree/master/spec/consensus/proposer-based-timestamp | ||
| func IsTimely(bt time.Time, lt time.Time, precision time.Duration, msgDelay time.Duration) bool { |
There was a problem hiding this comment.
is there a way that this can be attached as a method to an existing type?
Alternately, seems like there should be a POD type to wrap up these arguments, as "function which takes two time.Time and two time.Duration" would be very easy to flip one or both of the pairs of arguments in a way that would be difficult to notice at the call site.
There was a problem hiding this comment.
Yeah good call. I believe this could be a method attached to the proposals themselves (if we first check IsTimely and then that the proposal time matches the received block time). To avoid confusion between precision and msgDelay it may be better to take the new TimeParams (or whatever it's called) from ConsensusParams as an argument.
There was a problem hiding this comment.
I've moved this to be a method on the block (since that will be what timely is checked against) and moved the method to take in the timestamp params instead. I also added a mechansim for defining a local time source, that this method will take as an argument, but perhaps that is overkill and a time.Time would suffice instead.
There was a problem hiding this comment.
I see the new method, but this code is still there
types/block.go
Outdated
| // | ||
| // For more information on the meaning of 'timely', see the proposer-based timestamp specification: | ||
| // https://github.com/tendermint/spec/tree/master/spec/consensus/proposer-based-timestamp | ||
| func (b *Block) IsTimely(clock tmtime.Source, p TimestampParams) bool { |
There was a problem hiding this comment.
Don't we measure timeliness when we receive the proposal or when we receive the block. I thought it was based on the proposal
There was a problem hiding this comment.
After a discussion with @josef-widder and @cason, I determined that it's acceptable to use the propose message. This has the benefit of using a message with a constant size, meaning block size will not affect MSGDELAY.
There was a problem hiding this comment.
In terms of the implementation, this also seems easier. If a Proposal is not timely, when it is received and processed at first by the consensus implementation, there is no point in "asking for" and receiving the corresponding BlockParts. In addition, if the timely call is made for the block, when in the implementation will it be called? This is relevant, as different calls to timely may return true and the false, depending on when they are invoked.
| } | ||
|
|
||
| // TimestampParams influence the validity of block timestamps. | ||
| type TimestampParams struct { |
There was a problem hiding this comment.
Do we want to add a default?
There was a problem hiding this comment.
Set some basics and will follow up in #7202
There was a problem hiding this comment.
These would be Synchronous or Timing parameters, wouldn't them?
cason
left a comment
There was a problem hiding this comment.
For me is not clear whether the corner cases are defined or tested properly. I also think that the main isTimely method could be easier to read, by using Before() and After() and Add() and Sub() time methods.
| // https://github.com/tendermint/tendermint/issues/7202 | ||
| return TimestampParams{ | ||
| Precision: 2 * time.Second, | ||
| Accuracy: 500 * time.Millisecond, |
There was a problem hiding this comment.
Observe that if Accuracy = 0.5ms, two clocks might differ by at most Precision = 1ms.
| // configured Precision and MsgDelay parameters. | ||
| // Specifically, a proposed block timestamp is considered timely if it is satisfies the following inequalities: | ||
| // | ||
| // proposedBlockTime > validatorLocaltime - Precision && proposedBlockTime < validatorLocalTime + Precision + MsgDelay. |
There was a problem hiding this comment.
proposedBlockTime > validatorLocaltime - Precision
Are you sure that this is correct?
There was a problem hiding this comment.
I believe so, according to this description in the spec https://github.com/tendermint/spec/blob/master/spec/consensus/proposer-based-timestamp/pbts-algorithm_001_draft.md#pbts-reception-step0
| lt := clock.Now() | ||
| lhs := lt.Add(-tp.Precision) | ||
| rhs := lt.Add(tp.Precision).Add(tp.MsgDelay) | ||
| if lhs.Before(p.Timestamp) && p.Timestamp.Before(rhs) { |
There was a problem hiding this comment.
Maybe just a detail, but I would prefer having something like "a.After(b) and a.Before(c)" for more clarity.
| { | ||
| // Checking that the following inequality evaluates to true: | ||
| // 1 - 2 < 0 < 1 + 2 + 1 | ||
| name: "basic timely", |
There was a problem hiding this comment.
For me, the "basic" case, meaning the most common, is:
- The proposal timestamp is "for sure" in the past, so: proposalTime + Precision < localTime - Precision
- The receiving time of the proposal is "fore sure" within the acceptable window, so: localTime + Precision < proposalTime + msgDelay - Precision
| { | ||
| // Checking that the following inequality evaluates to false: | ||
| // 3 - 2 < 0 < 3 + 2 + 1 | ||
| name: "local time too large", |
There was a problem hiding this comment.
Well, is not the local time that is large, but the Proposal took too long to be received.
| { | ||
| // Checking that the following inequality evaluates to false: | ||
| // 0 - 2 < 2 < 2 + 1 | ||
| name: "proposal time too large", |
There was a problem hiding this comment.
Maybe a more clear name would be: "proposal timestamp in the future"
| proposalTime: genesisTime, | ||
| localTime: genesisTime.Add(1 * time.Nanosecond), | ||
| precision: time.Nanosecond * 2, | ||
| msgDelay: time.Nanosecond, |
|
…roposer-based timestamps (#7170) * state: add an IsTimely function to implement the check for timely in proposer-based timestamps * move time checks into block.go and add time source mechanism * timestamp params comment * add todo related to pbts spec and timestamp params * remove old istimely * switch to using built in before function * lint++ * wip * move into proposal and create a default set of params * defer using default cons params for now
…roposer-based timestamps (#7170) * state: add an IsTimely function to implement the check for timely in proposer-based timestamps * move time checks into block.go and add time source mechanism * timestamp params comment * add todo related to pbts spec and timestamp params * remove old istimely * switch to using built in before function * lint++ * wip * move into proposal and create a default set of params * defer using default cons params for now
This pull requests implements the 'timely' as outlined in the proposer-based timestamps specification.
For a proposed block timestamp to be considered timely, it must satisfy the following inequality:
Where
PrecisionandMsgDelayare system parameters wherePrecisionbounds the acceptable clock drift between any two correct process andMsgDelaybounds the acceptable delay for full transmitting a message between any two processes.part of #6942