|
|
@ -0,0 +1,445 @@ |
|
|
|
# ADR 71: Proposer-Based Timestamps |
|
|
|
|
|
|
|
* [Changelog](#changelog) |
|
|
|
* [Status](#status) |
|
|
|
* [Context](#context) |
|
|
|
* [Alternative Approaches](#alternative-approaches) |
|
|
|
* [Remove timestamps altogether](#remove-timestamps-altogether) |
|
|
|
* [Decision](#decision) |
|
|
|
* [Detailed Design](#detailed-design) |
|
|
|
* [Overview](#overview) |
|
|
|
* [Proposal Timestamp and Block Timestamp](#proposal-timestamp-and-block-timestamp) |
|
|
|
* [Saving the timestamp across heights](#saving-the-timestamp-across-heights) |
|
|
|
* [Changes to `CommitSig`](#changes-to-commitsig) |
|
|
|
* [Changes to `Commit`](#changes-to-commit) |
|
|
|
* [Changes to `Vote` messages](#changes-to-vote-messages) |
|
|
|
* [New consensus parameters](#new-consensus-parameters) |
|
|
|
* [Changes to `Header`](#changes-to-header) |
|
|
|
* [Changes to the block proposal step](#changes-to-the-block-proposal-step) |
|
|
|
* [Proposer selects proposal timestamp](#proposer-selects-proposal-timestamp) |
|
|
|
* [Proposer selects block timestamp](#proposer-selects-block-timestamp) |
|
|
|
* [Proposer waits](#proposer-waits) |
|
|
|
* [Changes to the propose step timeout](#changes-to-the-propose-step-timeout) |
|
|
|
* [Changes to validation rules](#changes-to-validation-rules) |
|
|
|
* [Proposal timestamp validation](#proposal-timestamp-validation) |
|
|
|
* [Block timestamp validation](#block-timestamp-validation) |
|
|
|
* [Changes to the prevote step](#changes-to-the-prevote-step) |
|
|
|
* [Changes to the precommit step](#changes-to-the-precommit-step) |
|
|
|
* [Changes to locking a block](#changes-to-locking-a-block) |
|
|
|
* [Remove voteTime Completely](#remove-votetime-completely) |
|
|
|
* [Future Improvements](#future-improvements) |
|
|
|
* [Consequences](#consequences) |
|
|
|
* [Positive](#positive) |
|
|
|
* [Neutral](#neutral) |
|
|
|
* [Negative](#negative) |
|
|
|
* [References](#references) |
|
|
|
|
|
|
|
## Changelog |
|
|
|
|
|
|
|
- July 15 2021: Created by @williambanfield |
|
|
|
- Aug 4 2021: Draft completed by @williambanfield |
|
|
|
- Aug 5 2021: Draft updated to include data structure changes by @williambanfield |
|
|
|
- Aug 20 2021: Language edits completed by @williambanfield |
|
|
|
|
|
|
|
## Status |
|
|
|
|
|
|
|
**Accepted** |
|
|
|
|
|
|
|
## Context |
|
|
|
|
|
|
|
Tendermint currently provides a monotonically increasing source of time known as [BFTTime](https://github.com/tendermint/spec/blob/master/spec/consensus/bft-time.md). |
|
|
|
This mechanism for producing a source of time is reasonably simple. |
|
|
|
Each correct validator adds a timestamp to each `Precommit` message it sends. |
|
|
|
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. |
|
|
|
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. |
|
|
|
The weighting is proportional to the amount of voting power, or stake, a validator has on the network. |
|
|
|
This mechanism for producing timestamps is both deterministic and byzantine fault tolerant. |
|
|
|
|
|
|
|
This current mechanism for producing timestamps has a few drawbacks. |
|
|
|
Validators do not have to agree at all on how close the selected block timestamp is to their own currently known Unix time. |
|
|
|
Additionally, any amount of voting power `>1/3` may directly control the block timestamp. |
|
|
|
As a result, it is quite possible that the timestamp is not particularly meaningful. |
|
|
|
|
|
|
|
These drawbacks present issues in the Tendermint protocol. |
|
|
|
Timestamps are used by light clients to verify blocks. |
|
|
|
Light clients rely on correspondence between their own currently known Unix time and the block timestamp to verify blocks they see; |
|
|
|
However, their currently known Unix time may be greatly divergent from the block timestamp as a result of the limitations of `BFTTime`. |
|
|
|
|
|
|
|
The proposer-based timestamps specification suggests an alternative approach for producing block timestamps that remedies these issues. |
|
|
|
Proposer-based timestamps alter the current mechanism for producing block timestamps in two main ways: |
|
|
|
|
|
|
|
1. The block proposer is amended to offer up its currently known Unix time as the timestamp for the next block. |
|
|
|
1. Correct validators only approve the proposed block timestamp if it is close enough to their own currently known Unix time. |
|
|
|
|
|
|
|
The result of these changes is a more meaningful timestamp that cannot be controlled by `<= 2/3` of the validator voting power. |
|
|
|
This document outlines the necessary code changes in Tendermint to implement the corresponding [proposer-based timestamps specification](https://github.com/tendermint/spec/tree/master/spec/consensus/proposer-based-timestamp). |
|
|
|
|
|
|
|
## Alternative Approaches |
|
|
|
|
|
|
|
### Remove timestamps altogether |
|
|
|
|
|
|
|
Computer clocks are bound to skew for a variety of reasons. |
|
|
|
Using timestamps in our protocol means either accepting the timestamps as not reliable or impacting the protocol’s liveness guarantees. |
|
|
|
This design requires impacting the protocol’s liveness in order to make the timestamps more reliable. |
|
|
|
An alternate approach is to remove timestamps altogether from the block protocol. |
|
|
|
`BFTTime` is deterministic but may be arbitrarily inaccurate. |
|
|
|
However, having a reliable source of time is quite useful for applications and protocols built on top of a blockchain. |
|
|
|
|
|
|
|
We therefore decided not to remove the timestamp. |
|
|
|
Applications often wish for some transactions to occur on a certain day, on a regular period, or after some time following a different event. |
|
|
|
All of these require some meaningful representation of agreed upon time. |
|
|
|
The following protocols and application features require a reliable source of time: |
|
|
|
* Tendermint Light Clients [rely on correspondence between their known time](https://github.com/tendermint/spec/blob/master/spec/light-client/verification/README.md#definitions-1) and the block time for block verification. |
|
|
|
* Tendermint Evidence validity is determined [either in terms of heights or in terms of time](https://github.com/tendermint/spec/blob/8029cf7a0fcc89a5004e173ec065aa48ad5ba3c8/spec/consensus/evidence.md#verification). |
|
|
|
* 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). |
|
|
|
* IBC packets can use either a [timestamp or a height to timeout packet delivery](https://docs.cosmos.network/v0.43/ibc/overview.html#acknowledgements). |
|
|
|
|
|
|
|
Finally, inflation distribution in the Cosmos Hub uses an approximation of time to calculate an annual percentage rate. |
|
|
|
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). |
|
|
|
Proposer-based timestamps will allow this inflation calculation to use a more meaningful and accurate source of time. |
|
|
|
|
|
|
|
|
|
|
|
## Decision |
|
|
|
|
|
|
|
Implement proposer-based timestamps and remove `BFTTime`. |
|
|
|
|
|
|
|
## Detailed Design |
|
|
|
|
|
|
|
### Overview |
|
|
|
|
|
|
|
Implementing proposer-based timestamps will require a few changes to Tendermint’s code. |
|
|
|
These changes will be to the following components: |
|
|
|
* The `internal/consensus/` package. |
|
|
|
* The `state/` package. |
|
|
|
* The `Vote`, `CommitSig`, `Commit` and `Header` types. |
|
|
|
* The consensus parameters. |
|
|
|
|
|
|
|
### Proposal Timestamp and Block Timestamp |
|
|
|
|
|
|
|
This design discusses two timestamps: (1) The timestamp in the block and (2) the timestamp in the proposal message. |
|
|
|
The existence and use of both of these timestamps can get a bit confusing, so some background is given here to clarify their uses. |
|
|
|
|
|
|
|
The [proposal message currently has a timestamp](https://github.com/tendermint/tendermint/blob/e5312942e30331e7c42b75426da2c6c9c00ae476/types/proposal.go#L31). |
|
|
|
This timestamp is the current Unix time known to the proposer when sending the `Proposal` message. |
|
|
|
This timestamp is not currently used as part of consensus. |
|
|
|
The changes in this ADR will begin using the proposal message timestamp as part of consensus. |
|
|
|
We will refer to this as the **proposal timestamp** throughout this design. |
|
|
|
|
|
|
|
The block has a timestamp field [in the header](https://github.com/tendermint/tendermint/blob/dc7c212c41a360bfe6eb38a6dd8c709bbc39aae7/types/block.go#L338). |
|
|
|
This timestamp is set currently as part of Tendermint’s `BFTtime` algorithm. |
|
|
|
It is set when a block is proposed and it is checked by the validators when they are deciding to prevote the block. |
|
|
|
This field will continue to be used but the logic for creating and validating this timestamp will change. |
|
|
|
We will refer to this as the **block timestamp** throughout this design. |
|
|
|
|
|
|
|
At a high level, the proposal timestamp from height `H` is used as the block timestamp at height `H+1`. |
|
|
|
The following image shows this relationship. |
|
|
|
The rest of this document describes the code changes that will make this possible. |
|
|
|
|
|
|
|
![](./img/pbts-message.png) |
|
|
|
|
|
|
|
### Saving the timestamp across heights |
|
|
|
|
|
|
|
Currently, `BFTtime` uses `LastCommit` to construct the block timestamp. |
|
|
|
The `LastCommit` is created at height `H-1` and is saved in the state store to be included in the block at height `H`. |
|
|
|
`BFTtime` takes the weighted median of the timestamps in `LastCommit.CommitSig` to build the timestamp for height `H`. |
|
|
|
|
|
|
|
For proposer-based timestamps, the `LastCommit.CommitSig` timestamps will no longer be used to build the timestamps for height `H`. |
|
|
|
Instead, the proposal timestamp from height `H-1` will become the block timestamp for height `H`. |
|
|
|
To enable this, we will add a `Timestamp` field to the `Commit` struct. |
|
|
|
This field will be populated at each height with the proposal timestamp decided on at the previous height. |
|
|
|
This timestamp will also be saved with the rest of the commit in the state store [when the commit is finalized](https://github.com/tendermint/tendermint/blob/e8013281281985e3ada7819f42502b09623d24a0/internal/consensus/state.go#L1611) so that it can be recovered if Tendermint crashes. |
|
|
|
Changes to the `CommitSig` and `Commit` struct are detailed below. |
|
|
|
|
|
|
|
### Changes to `CommitSig` |
|
|
|
|
|
|
|
The [CommitSig](https://github.com/tendermint/tendermint/blob/a419f4df76fe4aed668a6c74696deabb9fe73211/types/block.go#L604) struct currently contains a timestamp. |
|
|
|
This timestamp is the current Unix time known to the validator when it issued a `Precommit` for the block. |
|
|
|
This timestamp is no longer used and will be removed in this change. |
|
|
|
|
|
|
|
`CommitSig` will be updated as follows: |
|
|
|
|
|
|
|
```diff |
|
|
|
type CommitSig struct { |
|
|
|
BlockIDFlag BlockIDFlag `json:"block_id_flag"` |
|
|
|
ValidatorAddress Address `json:"validator_address"` |
|
|
|
-- Timestamp time.Time `json:"timestamp"` |
|
|
|
Signature []byte `json:"signature"` |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
### Changes to `Commit` |
|
|
|
|
|
|
|
The [Commit](https://github.com/tendermint/tendermint/blob/a419f4df76fe4aed668a6c74696deabb9fe73211/types/block.go#L746) struct does not currently contain a timestamp. |
|
|
|
The timestamps in the `Commit.CommitSig` entries are currently used to build the block timestamp. |
|
|
|
With these timestamps removed, the commit time will instead be stored in the `Commit` struct. |
|
|
|
|
|
|
|
`Commit` will be updated as follows. |
|
|
|
|
|
|
|
```diff |
|
|
|
type Commit struct { |
|
|
|
Height int64 `json:"height"` |
|
|
|
Round int32 `json:"round"` |
|
|
|
++ Timestamp time.Time `json:"timestamp"` |
|
|
|
BlockID BlockID `json:"block_id"` |
|
|
|
Signatures []CommitSig `json:"signatures"` |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
### Changes to `Vote` messages |
|
|
|
|
|
|
|
`Precommit` and `Prevote` messages use a common [Vote struct](https://github.com/tendermint/tendermint/blob/a419f4df76fe4aed668a6c74696deabb9fe73211/types/vote.go#L50). |
|
|
|
This struct currently contains a timestamp. |
|
|
|
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. |
|
|
|
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. |
|
|
|
For prevotes, this field is unused. |
|
|
|
Proposer-based timestamps will use the [RoundState.Proposal](https://github.com/tendermint/tendermint/blob/c3ae6f5b58e07b29c62bfdc5715b6bf8ae5ee951/internal/consensus/types/round_state.go#L76) timestamp to construct the `signedBytes` `CommitSig`. |
|
|
|
This timestamp is therefore no longer useful and will be dropped. |
|
|
|
|
|
|
|
`Vote` will be updated as follows: |
|
|
|
|
|
|
|
```diff |
|
|
|
type Vote struct { |
|
|
|
Type tmproto.SignedMsgType `json:"type"` |
|
|
|
Height int64 `json:"height"` |
|
|
|
Round int32 `json:"round"` |
|
|
|
BlockID BlockID `json:"block_id"` // zero if vote is nil. |
|
|
|
-- Timestamp time.Time `json:"timestamp"` |
|
|
|
ValidatorAddress Address `json:"validator_address"` |
|
|
|
ValidatorIndex int32 `json:"validator_index"` |
|
|
|
Signature []byte `json:"signature"` |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
### New consensus parameters |
|
|
|
|
|
|
|
The proposer-based timestamp specification includes multiple new parameters that must be the same among all validators. |
|
|
|
These parameters are `PRECISION`, `MSGDELAY`, and `ACCURACY`. |
|
|
|
|
|
|
|
The `PRECISION` and `MSGDELAY` parameters are used to determine if the proposed timestamp is acceptable. |
|
|
|
A validator will only Prevote a proposal if the proposal timestamp is considered `timely`. |
|
|
|
A proposal timestamp is considered `timely` if it is within `PRECISION` and `MSGDELAY` of the Unix time known to the validator. |
|
|
|
More specifically, a proposal timestamp is `timely` if `validatorLocalTime - PRECISION < proposalTime < validatorLocalTime + PRECISION + MSGDELAY`. |
|
|
|
|
|
|
|
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/master/proto/tendermint/types/params.proto#L13) as [durations](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Duration). |
|
|
|
|
|
|
|
The proposer-based timestamp specification also includes a [new ACCURACY parameter](https://github.com/tendermint/spec/blob/master/spec/consensus/proposer-based-timestamp/pbts-sysmodel_001_draft.md#pbts-clocksync-external0). |
|
|
|
Intuitively, `ACCURACY` represents the difference between the ‘real’ time and the currently known time of correct validators. |
|
|
|
The currently known Unix time of any validator is always somewhat different from real time. |
|
|
|
`ACCURACY` is the largest such difference between each validator's time and real time taken as an absolute value. |
|
|
|
This is not something a computer can determine on its own and must be specified as an estimate by community running a Tendermint-based chain. |
|
|
|
It is used in the new algorithm to [calculate a timeout for the propose step](https://github.com/tendermint/spec/blob/master/spec/consensus/proposer-based-timestamp/pbts-algorithm_001_draft.md#pbts-alg-startround0). |
|
|
|
`ACCURACY` is assumed to be the same across all validators and therefore should be included as a consensus parameter. |
|
|
|
|
|
|
|
The consensus will be updated to include this `Timestamp` field as follows: |
|
|
|
|
|
|
|
```diff |
|
|
|
type ConsensusParams struct { |
|
|
|
Block BlockParams `json:"block"` |
|
|
|
Evidence EvidenceParams `json:"evidence"` |
|
|
|
Validator ValidatorParams `json:"validator"` |
|
|
|
Version VersionParams `json:"version"` |
|
|
|
++ Timestamp TimestampParams `json:"timestamp"` |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
```go |
|
|
|
type TimestampParams struct { |
|
|
|
Accuracy time.Duration `json:"accuracy"` |
|
|
|
Precision time.Duration `json:"precision"` |
|
|
|
MsgDelay time.Duration `json:"msg_delay"` |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
### Changes to `Header` |
|
|
|
|
|
|
|
The [Header](https://github.com/tendermint/tendermint/blob/a419f4df76fe4aed668a6c74696deabb9fe73211/types/block.go#L338) struct currently contains a timestamp. |
|
|
|
This timestamp is set as the `BFTtime` derived from the block's `LastCommit.CommitSig` timestamps. |
|
|
|
This timestamp will no longer be derived from the `LastCommit.CommitSig` timestamps and will instead be included directly into the block's `LastCommit`. |
|
|
|
This timestamp will therfore be identical in both the `Header` and the `LastCommit`. |
|
|
|
To clarify that the timestamp in the header corresponds to the `LastCommit`'s time, we will rename this timestamp field to `last_timestamp`. |
|
|
|
|
|
|
|
`Header` will be updated as follows: |
|
|
|
|
|
|
|
```diff |
|
|
|
type Header struct { |
|
|
|
// basic block info |
|
|
|
Version version.Consensus `json:"version"` |
|
|
|
ChainID string `json:"chain_id"` |
|
|
|
Height int64 `json:"height"` |
|
|
|
-- Time time.Time `json:"time"` |
|
|
|
++ LastTimestamp time.Time `json:"last_timestamp"` |
|
|
|
|
|
|
|
// prev block info |
|
|
|
LastBlockID BlockID `json:"last_block_id"` |
|
|
|
|
|
|
|
// hashes of block data |
|
|
|
LastCommitHash tmbytes.HexBytes `json:"last_commit_hash"` |
|
|
|
DataHash tmbytes.HexBytes `json:"data_hash"` |
|
|
|
|
|
|
|
// hashes from the app output from the prev block |
|
|
|
ValidatorsHash tmbytes.HexBytes `json:"validators_hash"` |
|
|
|
NextValidatorsHash tmbytes.HexBytes `json:"next_validators_hash"` |
|
|
|
ConsensusHash tmbytes.HexBytes `json:"consensus_hash"` |
|
|
|
AppHash tmbytes.HexBytes `json:"app_hash"` |
|
|
|
|
|
|
|
// root hash of all results from the txs from the previous block |
|
|
|
LastResultsHash tmbytes.HexBytes `json:"last_results_hash"` |
|
|
|
|
|
|
|
// consensus info |
|
|
|
EvidenceHash tmbytes.HexBytes `json:"evidence_hash"` |
|
|
|
ProposerAddress Address `json:"proposer_address"` |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
### Changes to the block proposal step |
|
|
|
|
|
|
|
#### Proposer selects proposal timestamp |
|
|
|
|
|
|
|
The proposal logic already [sets the Unix time known to the validator](https://github.com/tendermint/tendermint/blob/2abfe20114ee3bb3adfee817589033529a804e4d/types/proposal.go#L44) into the `Proposal` message. |
|
|
|
This satisfies the proposer-based timestamp specification and does not need to change. |
|
|
|
|
|
|
|
#### Proposer selects block timestamp |
|
|
|
|
|
|
|
The proposal timestamp that was decided in height `H-1` will be stored in the `State` struct's in the `RoundState.LastCommit` field. |
|
|
|
The proposer will select this timestamp to use as the block timestamp at height `H`. |
|
|
|
|
|
|
|
#### Proposer waits |
|
|
|
|
|
|
|
Block timestamps must be monotonically increasing. |
|
|
|
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). |
|
|
|
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. |
|
|
|
|
|
|
|
Validator clocks will not be perfectly in sync. |
|
|
|
Therefore, the proposer’s current known Unix time may be less than the `LastCommit.Timestamp`. |
|
|
|
If the proposer’s current known Unix time is less than the `LastCommit.Timestamp`, the proposer will sleep until its known Unix time exceeds `LastCommit.Timestamp`. |
|
|
|
|
|
|
|
This change will require amending the [defaultDecideProposal](https://github.com/tendermint/tendermint/blob/822893615564cb20b002dd5cf3b42b8d364cb7d9/internal/consensus/state.go#L1180) method. |
|
|
|
This method should now block until the proposer’s time is greater than `LastCommit.Timestamp`. |
|
|
|
|
|
|
|
#### Changes to the propose step timeout |
|
|
|
|
|
|
|
Currently, a validator waiting for a proposal will proceed past the propose step if the configured propose timeout is reached and no proposal is seen. |
|
|
|
Proposer-based timestamps requires changing this timeout logic. |
|
|
|
|
|
|
|
The proposer will now wait until its current known Unix time exceeds the `LastCommit.Timestamp` to propose a block. |
|
|
|
The validators must now take this and some other factors into account when deciding when to timeout the propose step. |
|
|
|
Specifically, the propose step timeout must also take into account potential inaccuracy in the validator’s clock and in the clock of the proposer. |
|
|
|
Additionally, there may be a delay communicating the proposal message from the proposer to the other validators. |
|
|
|
|
|
|
|
Therefore, validators waiting for a proposal must wait until after the `LastCommit.Timestamp` before timing out. |
|
|
|
To account for possible inaccuracy in its own clock, inaccuracy in the proposer’s clock, and message delay, validators waiting for a proposal will wait until `LastCommit.Timesatmp + 2*ACCURACY + MSGDELAY`. |
|
|
|
The spec defines this as `waitingTime`. |
|
|
|
|
|
|
|
The [propose step’s timeout is set in enterPropose](https://github.com/tendermint/tendermint/blob/822893615564cb20b002dd5cf3b42b8d364cb7d9/internal/consensus/state.go#L1108) in `state.go`. |
|
|
|
`enterPropose` will be changed to calculate waiting time using the new consensus parameters. |
|
|
|
The timeout in `enterPropose` will then be set as the maximum of `waitingTime` and the [configured proposal step timeout](https://github.com/tendermint/tendermint/blob/dc7c212c41a360bfe6eb38a6dd8c709bbc39aae7/config/config.go#L1013). |
|
|
|
|
|
|
|
### Changes to validation rules |
|
|
|
|
|
|
|
The rules for validating that a proposal is valid will need slight modification to implement proposer-based timestamps. |
|
|
|
Specifically, we will change the validation logic to ensure that the proposal timestamp is `timely` and we will modify the way the block timestamp is validated as well. |
|
|
|
|
|
|
|
#### Proposal timestamp validation |
|
|
|
|
|
|
|
Adding proposal timestamp validation is a reasonably straightforward change. |
|
|
|
The current Unix time known to the proposer is already included in the [Proposal message](https://github.com/tendermint/tendermint/blob/dc7c212c41a360bfe6eb38a6dd8c709bbc39aae7/types/proposal.go#L31). |
|
|
|
Once the proposal is received, the complete message is stored in the `RoundState.Proposal` field. |
|
|
|
The precommit and prevote validation logic does not currently use this timestamp. |
|
|
|
This validation logic will be updated to check that the proposal timestamp is within `PRECISION` of the current Unix time known to the validators. |
|
|
|
If the timestamp is not within `PRECISION` of the current Unix time known to the validator, the proposal will not be considered it valid. |
|
|
|
The validator will also check that the proposal time is greater than the block timestamp from the previous height. |
|
|
|
|
|
|
|
If no valid proposal is received by the proposal timeout, the validator will prevote nil. |
|
|
|
This is identical to the current logic. |
|
|
|
|
|
|
|
#### Block timestamp validation |
|
|
|
|
|
|
|
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). |
|
|
|
First, the validation logic checks that this timestamp is greater than the previous block’s timestamp. |
|
|
|
Additionally, 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). |
|
|
|
Finally, the logic also authenticates the timestamps in the `LastCommit`. |
|
|
|
The cryptographic signature in each `CommitSig` is created by signing a hash of fields in the block with the validator’s private key. |
|
|
|
One of the items in this `signedBytes` hash is derived from the timestamp in the `CommitSig`. |
|
|
|
To authenticate the `CommitSig` timestamp, the validator builds a hash of fields that includes the timestamp and checks this hash against the provided signature. |
|
|
|
This takes place in the [VerifyCommit function](https://github.com/tendermint/tendermint/blob/e8013281281985e3ada7819f42502b09623d24a0/types/validation.go#L25). |
|
|
|
|
|
|
|
The logic to validate that the block timestamp is greater than the previous block’s timestamp also works for proposer-based timestamps and will not change. |
|
|
|
|
|
|
|
`BFTTime` validation is no longer applicable and will be removed. |
|
|
|
Validators will no longer check that the block timestamp is a weighted median of `LastCommit` timestamps. |
|
|
|
This will mean removing the call to [MedianTime in the validateBlock function](https://github.com/tendermint/tendermint/blob/4db71da68e82d5cb732b235eeb2fd69d62114b45/state/validation.go#L117). |
|
|
|
The `MedianTime` function can be completely removed. |
|
|
|
The `LastCommit` timestamps may also be removed. |
|
|
|
|
|
|
|
The `signedBytes` validation logic in `VerifyCommit` will be slightly altered. |
|
|
|
The `CommitSig`s in the block’s `LastCommit` will no longer each contain a timestamp. |
|
|
|
The validation logic will instead include the `LastCommit.Timestamp` in the hash of fields for generating the `signedBytes`. |
|
|
|
The cryptographic signatures included in the `CommitSig`s will then be checked against this `signedBytes` hash to authenticate the timestamp. |
|
|
|
Specifically, the `VerifyCommit` function will be updated to use this new timestamp. |
|
|
|
|
|
|
|
### Changes to the prevote step |
|
|
|
|
|
|
|
Currently, a validator will prevote a proposal in one of three cases: |
|
|
|
|
|
|
|
* Case 1: Validator has no locked block and receives a valid proposal. |
|
|
|
* Case 2: Validator has a locked block and receives a valid proposal matching its locked block. |
|
|
|
* Case 3: Validator has a locked block, sees a valid proposal not matching its locked block but sees +⅔ prevotes for the new proposal’s block. |
|
|
|
|
|
|
|
The only change we will make to the prevote step is to what a validator considers a valid proposal as detailed above. |
|
|
|
|
|
|
|
### Changes to the precommit step |
|
|
|
|
|
|
|
The precommit step will not require much modification. |
|
|
|
Its proposal validation rules will change in the same ways that validation will change in the prevote step. |
|
|
|
|
|
|
|
### Changes to locking a block |
|
|
|
When a validator receives a valid proposed block and +2/3 prevotes for that block, it stores the block as its ‘locked block’ in the [RoundState.ValidBlock](https://github.com/tendermint/tendermint/blob/e8013281281985e3ada7819f42502b09623d24a0/internal/consensus/types/round_state.go#L85) field. |
|
|
|
In each subsequent round it will prevote that block. |
|
|
|
A validator will only change which block it has locked if it sees +2/3 prevotes for a different block. |
|
|
|
|
|
|
|
This mechanism will remain largely unchanged. |
|
|
|
The only difference is the addition of proposal timestamp validation. |
|
|
|
A validator will prevote nil in a round if the proposal message it received is not `timely`. |
|
|
|
Prevoting nil in this case will not cause a validator to ‘unlock’ its locked block. |
|
|
|
This difference is an incidental result of the changes to prevote validation. |
|
|
|
It is included in this design for completeness and to clarify that no additional changes will be made to block locking. |
|
|
|
|
|
|
|
### Remove voteTime Completely |
|
|
|
|
|
|
|
[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. |
|
|
|
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. |
|
|
|
This logic is used in multiple places and is no longer needed for proposer-based timestamps. |
|
|
|
It should therefore be removed completely. |
|
|
|
|
|
|
|
## Future Improvements |
|
|
|
|
|
|
|
* Implement BLS signature aggregation. |
|
|
|
By removing fields from the `Precommit` messages, we are able to aggregate signatures. |
|
|
|
|
|
|
|
## Consequences |
|
|
|
|
|
|
|
### Positive |
|
|
|
|
|
|
|
* `<2/3` of validators can no longer influence block timestamps. |
|
|
|
* Block timestamp will have stronger correspondence to real time. |
|
|
|
* Improves the reliability of light client block verification. |
|
|
|
* Enables BLS signature aggregation. |
|
|
|
* Enables evidence handling to use time instead of height for evidence validity. |
|
|
|
|
|
|
|
### Neutral |
|
|
|
|
|
|
|
* Alters Tendermint’s liveness properties. |
|
|
|
Liveness now requires that all correct validators have synchronized clocks within a bound. |
|
|
|
Liveness will now also require that validators’ clocks move forward, which was not required under `BFTTime`. |
|
|
|
|
|
|
|
### Negative |
|
|
|
|
|
|
|
* 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. |
|
|
|
This skew will be bound by the `PRECISION` value, so it is unlikely to be too large. |
|
|
|
|
|
|
|
* 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. |
|
|
|
|
|
|
|
## References |
|
|
|
|
|
|
|
* [PBTS Spec](https://github.com/tendermint/spec/tree/master/spec/consensus/proposer-based-timestamp) |
|
|
|
* [BFTTime spec](https://github.com/tendermint/spec/blob/master/spec/consensus/bft-time.md) |