github.com/badrootd/nibiru-cometbft@v0.37.5-0.20240307173500-2a75559eee9b/docs/architecture/adr-071-proposer-based-timestamps.md (about)

     1  # ADR 71: Proposer-Based Timestamps
     2  
     3  ## Changelog
     4  
     5   - July 15 2021: Created by @williambanfield
     6   - Aug 4 2021: Draft completed by @williambanfield
     7   - Aug 5 2021: Draft updated to include data structure changes by @williambanfield
     8   - Aug 20 2021: Language edits completed by @williambanfield
     9   - Oct 25 2021: Update the ADR to match updated spec from @cason by @williambanfield
    10   - Nov 10 2021: Additional language updates by @williambanfield per feedback from @cason
    11   - Feb 2 2022: Synchronize logic for timely with latest version of the spec by @williambanfield
    12  
    13  ## Status
    14  
    15   **Accepted**
    16  
    17  ## Context
    18  
    19  Tendermint currently provides a monotonically increasing source of time known as [BFTTime](https://github.com/tendermint/tendermint/blob/v0.37.x/spec/consensus/bft-time.md).
    20  This mechanism for producing a source of time is reasonably simple.
    21  Each correct validator adds a timestamp to each `Precommit` message it sends.
    22  The timestamp it sends is either the validator's current known Unix time or one millisecond greater than the previous block time, depending on which value is greater.
    23  When a block is produced, the proposer chooses the block timestamp as the weighted median of the times in all of the `Precommit` messages the proposer received.
    24  The weighting is proportional to the amount of voting power, or stake, a validator has on the network.
    25  This mechanism for producing timestamps is both deterministic and byzantine fault tolerant.
    26  
    27  This current mechanism for producing timestamps has a few drawbacks.
    28  Validators do not have to agree at all on how close the selected block timestamp is to their own currently known Unix time.
    29  Additionally, any amount of voting power `>1/3` may directly control the block timestamp.
    30  As a result, it is quite possible that the timestamp is not particularly meaningful.
    31  
    32  These drawbacks present issues in the Tendermint protocol.
    33  Timestamps are used by light clients to verify blocks.
    34  Light clients rely on correspondence between their own currently known Unix time and the block timestamp to verify blocks they see;
    35  However, their currently known Unix time may be greatly divergent from the block timestamp as a result of the limitations of `BFTTime`.
    36  
    37  The proposer-based timestamps specification suggests an alternative approach for producing block timestamps that remedies these issues.
    38  Proposer-based timestamps alter the current mechanism for producing block timestamps in two main ways:
    39  
    40  1. The block proposer is amended to offer up its currently known Unix time as the timestamp for the next block instead of the `BFTTime`.
    41  1. Correct validators only approve the proposed block timestamp if it is close enough to their own currently known Unix time.
    42  
    43  The result of these changes is a more meaningful timestamp that cannot be controlled by `<= 2/3` of the validator voting power.
    44  This document outlines the necessary code changes in Tendermint to implement the corresponding [proposer-based timestamps specification](https://github.com/tendermint/tendermint/tree/v0.37.x/spec/consensus/proposer-based-timestamp).
    45  
    46  ## Alternative Approaches
    47  
    48  ### Remove timestamps altogether
    49  
    50  Computer clocks are bound to skew for a variety of reasons.
    51  Using timestamps in our protocol means either accepting the timestamps as not reliable or impacting the protocol’s liveness guarantees.
    52  This design requires impacting the protocol’s liveness in order to make the timestamps more reliable.
    53  An alternate approach is to remove timestamps altogether from the block protocol.
    54  `BFTTime` is deterministic but may be arbitrarily inaccurate.
    55  However, having a reliable source of time is quite useful for applications and protocols built on top of a blockchain.
    56  
    57  We therefore decided not to remove the timestamp.
    58  Applications often wish for some transactions to occur on a certain day, on a regular period, or after some time following a different event.
    59  All of these require some meaningful representation of agreed upon time.
    60  The following protocols and application features require a reliable source of time:
    61  * Tendermint Light Clients [rely on correspondence between their known time](https://github.com/tendermint/tendermint/blob/v0.37.x/spec/light-client/verification/README.md#definitions-1) and the block time for block verification.
    62  * Tendermint Evidence validity is determined [either in terms of heights or in terms of time](https://github.com/tendermint/tendermint/blob/8029cf7a0fcc89a5004e173ec065aa48ad5ba3c8/spec/consensus/evidence.md#verification).
    63  * Unbonding of staked assets in the Cosmos Hub [occurs after a period of 21 days](https://github.com/cosmos/governance/blob/ce75de4019b0129f6efcbb0e752cd2cc9e6136d3/params-change/Staking.md#unbondingtime).
    64  * IBC packets can use either a [timestamp or a height to timeout packet delivery](https://docs.cosmos.network/v0.45/ibc/overview.html#acknowledgements)
    65  
    66  Finally, inflation distribution in the Cosmos Hub uses an approximation of time to calculate an annual percentage rate.
    67  This approximation of time is calculated using [block heights with an estimated number of blocks produced in a year](https://github.com/cosmos/governance/blob/master/params-change/Mint.md#blocksperyear).
    68  Proposer-based timestamps will allow this inflation calculation to use a more meaningful and accurate source of time.
    69  
    70  
    71  ## Decision
    72  
    73  Implement proposer-based timestamps and remove `BFTTime`.
    74  
    75  ## Detailed Design
    76  
    77  ### Overview
    78  
    79  Implementing proposer-based timestamps will require a few changes to Tendermint’s code.
    80  These changes will be to the following components:
    81  * The `internal/consensus/` package.
    82  * The `state/` package.
    83  * The `Vote`, `CommitSig` and `Header` types.
    84  * The consensus parameters.
    85  
    86  ### Changes to `CommitSig`
    87  
    88  The [CommitSig](https://github.com/tendermint/tendermint/blob/a419f4df76fe4aed668a6c74696deabb9fe73211/types/block.go#L604) struct currently contains a timestamp.
    89  This timestamp is the current Unix time known to the validator when it issued a `Precommit` for the block.
    90  This timestamp is no longer used and will be removed in this change.
    91  
    92  `CommitSig` will be updated as follows:
    93  
    94  ```diff
    95  type CommitSig struct {
    96  	BlockIDFlag      BlockIDFlag `json:"block_id_flag"`
    97  	ValidatorAddress Address     `json:"validator_address"`
    98  --	Timestamp        time.Time   `json:"timestamp"`
    99  	Signature        []byte      `json:"signature"`
   100  }
   101  ```
   102  
   103  ### Changes to `Vote` messages
   104  
   105  `Precommit` and `Prevote` messages use a common [Vote struct](https://github.com/tendermint/tendermint/blob/a419f4df76fe4aed668a6c74696deabb9fe73211/types/vote.go#L50).
   106  This struct currently contains a timestamp.
   107  This timestamp is set using the [voteTime](https://github.com/tendermint/tendermint/blob/e8013281281985e3ada7819f42502b09623d24a0/internal/consensus/state.go#L2241) function and therefore vote times correspond to the current Unix time known to the validator, provided this time is greater than the timestamp of the previous block.
   108  For precommits, this timestamp is used to construct the [CommitSig that is included in the block in the LastCommit](https://github.com/tendermint/tendermint/blob/e8013281281985e3ada7819f42502b09623d24a0/types/block.go#L754) field.
   109  For prevotes, this field is currently unused.
   110  Proposer-based timestamps will use the timestamp that the proposer sets into the block and will therefore no longer require that a timestamp be included in the vote messages.
   111  This timestamp is therefore no longer useful as part of consensus and may optionally be dropped from the message.
   112  
   113  `Vote` will be updated as follows:
   114  
   115  ```diff
   116  type Vote struct {
   117  	Type             tmproto.SignedMsgType `json:"type"`
   118  	Height           int64                 `json:"height"`
   119  	Round            int32                 `json:"round"`
   120  	BlockID          BlockID               `json:"block_id"` // zero if vote is nil.
   121  --	Timestamp        time.Time             `json:"timestamp"`
   122  	ValidatorAddress Address               `json:"validator_address"`
   123  	ValidatorIndex   int32                 `json:"validator_index"`
   124  	Signature        []byte                `json:"signature"`
   125  }
   126  ```
   127  
   128  ### New consensus parameters
   129  
   130  The proposer-based timestamp specification includes a pair of new parameters that must be the same among all validators.
   131  These parameters are `PRECISION`, and `MSGDELAY`.
   132  
   133  The `PRECISION` and `MSGDELAY` parameters are used to determine if the proposed timestamp is acceptable.
   134  A validator will only Prevote a proposal if the proposal timestamp is considered `timely`.
   135  A proposal timestamp is considered `timely` if it is within `PRECISION` and `MSGDELAY` of the Unix time known to the validator.
   136  More specifically, a proposal timestamp is `timely` if `proposalTimestamp - PRECISION ≤ validatorLocalTime ≤ proposalTimestamp + PRECISION + MSGDELAY`.
   137  
   138  Because the `PRECISION` and `MSGDELAY` parameters must be the same across all validators, they will be added to the [consensus parameters](https://github.com/tendermint/tendermint/blob/v0.37.x/proto/tendermint/types/params.proto#L11) as [durations](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Duration).
   139  
   140  The consensus parameters will be updated to include this `Synchrony` field as follows:
   141  
   142  ```diff
   143  type ConsensusParams struct {
   144  	Block     BlockParams     `json:"block"`
   145  	Evidence  EvidenceParams  `json:"evidence"`
   146  	Validator ValidatorParams `json:"validator"`
   147  	Version   VersionParams   `json:"version"`
   148  ++	Synchrony SynchronyParams `json:"synchrony"`
   149  }
   150  ```
   151  
   152  ```go
   153  type SynchronyParams struct {
   154  	MessageDelay time.Duration `json:"message_delay"`
   155  	Precision    time.Duration `json:"precision"`
   156  }
   157  ```
   158  
   159  ### Changes to the block proposal step
   160  
   161  #### Proposer selects block timestamp
   162  
   163  Tendermint currently uses the `BFTTime` algorithm to produce the block's `Header.Timestamp`.
   164  The [proposal logic](https://github.com/tendermint/tendermint/blob/68ca65f5d79905abd55ea999536b1a3685f9f19d/internal/state/state.go#L269) sets the weighted median of the times in the `LastCommit.CommitSigs` as the proposed block's `Header.Timestamp`.
   165  
   166  In proposer-based timestamps, the proposer will still set a timestamp into the `Header.Timestamp`.
   167  The timestamp the proposer sets into the `Header` will change depending on if the block has previously received a [polka](https://github.com/tendermint/tendermint/blob/053651160f496bb44b107a434e3e6482530bb287/docs/introduction/what-is-tendermint.md#consensus-overview) or not.
   168  
   169  #### Proposal of a block that has not previously received a polka
   170  
   171  If a proposer is proposing a new block then it will set the Unix time currently known to the proposer into the `Header.Timestamp` field.
   172  The proposer will also set this same timestamp into the `Timestamp` field of the `Proposal` message that it issues.
   173  
   174  #### Re-proposal of a block that has previously received a polka
   175  
   176  If a proposer is re-proposing a block that has previously received a polka on the network, then the proposer does not update the `Header.Timestamp` of that block.
   177  Instead, the proposer simply re-proposes the exact same block.
   178  This way, the proposed block has the exact same block ID as the previously proposed block and the validators that have already received that block do not need to attempt to receive it again.
   179  
   180  The proposer will set the re-proposed block's `Header.Timestamp` as the `Proposal` message's `Timestamp`.
   181  
   182  #### Proposer waits
   183  
   184  Block timestamps must be monotonically increasing.
   185  In `BFTTime`, if a validator’s clock was behind, the [validator added 1 millisecond to the previous block’s time and used that in its vote messages](https://github.com/tendermint/tendermint/blob/e8013281281985e3ada7819f42502b09623d24a0/internal/consensus/state.go#L2246).
   186  A goal of adding proposer-based timestamps is to enforce some degree of clock synchronization, so having a mechanism that completely ignores the Unix time of the validator time no longer works.
   187  Validator clocks will not be perfectly in sync.
   188  Therefore, the proposer’s current known Unix time may be less than the previous block's `Header.Time`.
   189  If the proposer’s current known Unix time is less than the previous block's `Header.Time`, the proposer will sleep until its known Unix time exceeds it.
   190  
   191  This change will require amending the [defaultDecideProposal](https://github.com/tendermint/tendermint/blob/822893615564cb20b002dd5cf3b42b8d364cb7d9/internal/consensus/state.go#L1180) method.
   192  This method should now schedule a timeout that fires when the proposer’s time is greater than the previous block's `Header.Time`.
   193  When the timeout fires, the proposer will finally issue the `Proposal` message.
   194  
   195  ### Changes to proposal validation rules
   196  
   197  The rules for validating a proposed block will be modified to implement proposer-based timestamps.
   198  We will change the validation logic to ensure that a proposal is `timely`.
   199  
   200  Per the proposer-based timestamps spec, `timely` only needs to be checked if a block has not received a +2/3 majority of `Prevotes` in a round.
   201  If a block previously received a +2/3 majority of prevotes in a previous round, then +2/3 of the voting power considered the block's timestamp near enough to their own currently known Unix time in that round.
   202  
   203  The validation logic will be updated to check `timely` for blocks that did not previously receive +2/3 prevotes in a round.
   204  Receiving +2/3 prevotes in a round is frequently referred to as a 'polka' and we will use this term for simplicity.
   205  
   206  #### Current timestamp validation logic
   207  
   208  To provide a better understanding of the changes needed to timestamp validation, we will first detail how timestamp validation works currently in Tendermint.
   209  
   210  The [validBlock function](https://github.com/tendermint/tendermint/blob/c3ae6f5b58e07b29c62bfdc5715b6bf8ae5ee951/state/validation.go#L14) currently [validates the proposed block timestamp in three ways](https://github.com/tendermint/tendermint/blob/c3ae6f5b58e07b29c62bfdc5715b6bf8ae5ee951/state/validation.go#L118).
   211  First, the validation logic checks that this timestamp is greater than the previous block’s timestamp.
   212  
   213  Second, it validates that the block timestamp is correctly calculated as the weighted median of the timestamps in the [block’s LastCommit](https://github.com/tendermint/tendermint/blob/e8013281281985e3ada7819f42502b09623d24a0/types/block.go#L48).
   214  
   215  Finally, the validation logic authenticates the timestamps in the `LastCommit.CommitSig`.
   216  The cryptographic signature in each `CommitSig` is created by signing a hash of fields in the block with the voting validator’s private key.
   217  One of the items in this `signedBytes` hash is the timestamp in the `CommitSig`.
   218  To authenticate the `CommitSig` timestamp, the validator authenticating votes builds a hash of fields that includes the `CommitSig` timestamp and checks this hash against the signature.
   219  This takes place in the [VerifyCommit function](https://github.com/tendermint/tendermint/blob/e8013281281985e3ada7819f42502b09623d24a0/types/validation.go#L25).
   220  
   221  #### Remove unused timestamp validation logic
   222  
   223  `BFTTime` validation is no longer applicable and will be removed.
   224  This means that validators will no longer check that the block timestamp is a weighted median of `LastCommit` timestamps.
   225  Specifically, we will remove the call to [MedianTime in the validateBlock function](https://github.com/tendermint/tendermint/blob/4db71da68e82d5cb732b235eeb2fd69d62114b45/state/validation.go#L117).
   226  The `MedianTime` function can be completely removed.
   227  
   228  Since `CommitSig`s will no longer contain a timestamp, the validator authenticating a commit will no longer include the `CommitSig` timestamp in the hash of fields it builds to check against the cryptographic signature.
   229  
   230  #### Timestamp validation when a block has not received a polka
   231  
   232  The [POLRound](https://github.com/tendermint/tendermint/blob/68ca65f5d79905abd55ea999536b1a3685f9f19d/types/proposal.go#L29) in the `Proposal` message indicates which round the block received a polka.
   233  A negative value in the `POLRound` field indicates that the block has not previously been proposed on the network.
   234  Therefore the validation logic will check for timely when `POLRound < 0`.
   235  
   236  When a validator receives a `Proposal` message, the validator will check that the `Proposal.Timestamp` is at most `PRECISION` greater than the current Unix time known to the validator, and at maximum `PRECISION + MSGDELAY` less than the current Unix time known to the validator.
   237  If the timestamp is not within these bounds, the proposed block will not be considered `timely`.
   238  
   239  Once a full block matching the `Proposal` message is received, the validator will also check that the timestamp in the `Header.Timestamp` of the block matches this `Proposal.Timestamp`.
   240  Using the `Proposal.Timestamp` to check `timely` allows for the `MSGDELAY` parameter to be more finely tuned since `Proposal` messages do not change sizes and are therefore faster to gossip than full blocks across the network.
   241  
   242  A validator will also check that the proposed timestamp is greater than the timestamp of the block for the previous height.
   243  If the timestamp is not greater than the previous block's timestamp, the block will not be considered valid, which is the same as the current logic.
   244  
   245  #### Timestamp validation when a block has received a polka
   246  
   247  When a block is re-proposed that has already received a +2/3 majority of `Prevote`s on the network, the `Proposal` message for the re-proposed block is created with a `POLRound` that is `>= 0`.
   248  A validator will not check that the `Proposal` is `timely` if the propose message has a non-negative `POLRound`.
   249  If the `POLRound` is non-negative, each validator will simply ensure that it received the `Prevote` messages for the proposed block in the round indicated by `POLRound`.
   250  
   251  If the validator does not receive `Prevote` messages for the proposed block before the proposal timeout, then it will prevote nil.
   252  Validators already check that +2/3 prevotes were seen in `POLRound`, so this does not represent a change to the prevote logic.
   253  
   254  A validator will also check that the proposed timestamp is greater than the timestamp of the block for the previous height.
   255  If the timestamp is not greater than the previous block's timestamp, the block will not be considered valid, which is the same as the current logic.
   256  
   257  Additionally, this validation logic can be updated to check that the `Proposal.Timestamp` matches the `Header.Timestamp` of the proposed block, but it is less relevant since checking that votes were received is sufficient to ensure the block timestamp is correct.
   258  
   259  #### Relaxation of the 'Timely' check
   260  
   261  The `Synchrony` parameters, `MessageDelay` and `Precision` provide a means to bound the timestamp of a proposed block.
   262  Selecting values that are too small presents a possible liveness issue for the network.
   263  If a Tendermint network selects a `MessageDelay` parameter that does not accurately reflect the time to broadcast a proposal message to all of the validators on the network, nodes will begin rejecting proposals from otherwise correct proposers because these proposals will appear to be too far in the past.
   264  
   265  `MessageDelay` and `Precision` are planned to be configured as `ConsensusParams`.
   266  A very common way to update `ConsensusParams` is by executing a transaction included in a block that specifies new values for them.
   267  However, if the network is unable to produce blocks because of this liveness issue, no such transaction may be executed.
   268  To prevent this dangerous condition, we will add a relaxation mechanism to the `Timely` predicate.
   269  If consensus takes more than 10 rounds to produce a block for any reason, the `MessageDelay` will be doubled.
   270  This doubling will continue for each subsequent 10 rounds of consensus.
   271  This will enable chains that selected too small of a value for the `MessageDelay` parameter to eventually issue a transaction and readjust the parameters to more accurately reflect the broadcast time.
   272  
   273  This liveness issue is not as problematic for chains with very small `Precision` values.
   274  Operators can more easily readjust local validator clocks to be more aligned.
   275  Additionally, chains that wish to increase a small `Precision` value can still take advantage of the `MessageDelay` relaxation, waiting for the `MessageDelay` value to grow significantly and issuing proposals with timestamps that are far in the past of their peers.
   276  
   277  For more discussion of this, see [issue 371](https://github.com/tendermint/spec/issues/371).
   278  
   279  ### Changes to the prevote step
   280  
   281  Currently, a validator will prevote a proposal in one of three cases:
   282  
   283  * Case 1:  Validator has no locked block and receives a valid proposal.
   284  * Case 2:  Validator has a locked block and receives a valid proposal matching its locked block.
   285  * Case 3:  Validator has a locked block, sees a valid proposal not matching its locked block but sees +2/3 prevotes for the proposal’s block, either in the current round or in a round greater than or equal to the round in which it locked its locked block.
   286  
   287  The only change we will make to the prevote step is to what a validator considers a valid proposal as detailed above.
   288  
   289  ### Changes to the precommit step
   290  
   291  The precommit step will not require much modification.
   292  Its proposal validation rules will change in the same ways that validation will change in the prevote step with the exception of the `timely` check: precommit validation will never check that the timestamp is `timely`.
   293  
   294  ### Remove voteTime Completely
   295  
   296  [voteTime](https://github.com/tendermint/tendermint/blob/822893615564cb20b002dd5cf3b42b8d364cb7d9/internal/consensus/state.go#L2229) is a mechanism for calculating the next `BFTTime` given both the validator's current known Unix time and the previous block timestamp.
   297  If the previous block timestamp is greater than the validator's current known Unix time, then voteTime returns a value one millisecond greater than the previous block timestamp.
   298  This logic is used in multiple places and is no longer needed for proposer-based timestamps.
   299  It should therefore be removed completely.
   300  
   301  ## Future Improvements
   302  
   303  * Implement BLS signature aggregation.
   304  By removing fields from the `Precommit` messages, we are able to aggregate signatures.
   305  
   306  ## Consequences
   307  
   308  ### Positive
   309  
   310  * `<2/3` of validators can no longer influence block timestamps.
   311  * Block timestamp will have stronger correspondence to real time.
   312  * Improves the reliability of light client block verification.
   313  * Enables BLS signature aggregation.
   314  * Enables evidence handling to use time instead of height for evidence validity.
   315  
   316  ### Neutral
   317  
   318  * Alters Tendermint’s liveness properties.
   319  Liveness now requires that all correct validators have synchronized clocks within a bound.
   320  Liveness will now also require that validators’ clocks move forward, which was not required under `BFTTime`.
   321  
   322  ### Negative
   323  
   324  * May increase the length of the propose step if there is a large skew between the previous proposer and the current proposer’s local Unix time.
   325  This skew will be bound by the `PRECISION` value, so it is unlikely to be too large.
   326  
   327  * Current chains with block timestamps far in the future will either need to pause consensus until after the erroneous block timestamp or must maintain synchronized but very inaccurate clocks.
   328  
   329  ## References
   330  
   331  * [PBTS Spec](https://github.com/tendermint/tendermint/tree/v0.37.x/spec/consensus/proposer-based-timestamp)
   332  * [BFTTime spec](https://github.com/tendermint/tendermint/blob/v0.37.x/spec/consensus/bft-time.md)
   333  * [Issue 371](https://github.com/tendermint/spec/issues/371)