Compare commits

...

11 Commits

Author SHA1 Message Date
Callum
30e14557f4 use bold instead of italic 2020-11-20 14:58:43 +01:00
Callum
2f5d2fe502 Merge branch 'callum/adr16-versioning' of github.com:tendermint/tendermint into callum/adr16-versioning 2020-11-20 14:44:14 +01:00
Callum Waters
e693ac37a6 Merge branch 'master' into callum/adr16-versioning 2020-11-20 14:43:53 +01:00
Callum
41496117f2 lint markdown 2020-11-20 14:43:34 +01:00
Callum
5eae97045f tidy up the document 2020-11-20 14:35:59 +01:00
Callum Waters
567ee2a4f8 add abci version to adr 2020-11-20 13:41:07 +01:00
Callum Waters
8a0169a27b checkpoint 2020-11-20 12:03:51 +01:00
Callum
7dbb8ced03 make changes with rpc versioning 2020-11-20 09:13:27 +01:00
Callum
5209993ca2 checkpoint 2020-11-13 09:28:19 +01:00
Callum
a054d326a1 fix grammar 2020-11-11 12:44:42 +01:00
Callum
519107ee39 use rpc versioning 2020-11-10 16:38:08 +01:00

View File

@@ -1,24 +1,18 @@
# ADR 016: Protocol Versions
## TODO
- How to / should we version the authenticated encryption handshake itself (ie.
upfront protocol negotiation for the P2PVersion)
- How to / should we version ABCI itself? Should it just be absorbed by the
BlockVersion?
## Changelog
- 11-11-2020: Address both RPC and ABCI versioning
- 18-09-2018: Updates after working a bit on implementation
- ABCI Handshake needs to happen independently of starting the app
conns so we can see the result
- Add question about ABCI protocol version
- ABCI Handshake needs to happen independently of starting the app conns so we
can see the result
- Add question about ABCI protocol version
- 16-08-2018: Updates after discussion with SDK team
- Remove signalling for next version from Header/ABCI
- Remove signalling for next version from Header/ABCI
- 03-08-2018: Updates from discussion with Jae:
- ProtocolVersion contains Block/AppVersion, not Current/Next
- signal upgrades to Tendermint using EndBlock fields
- dont restrict peer compatibilty by version to simplify syncing old nodes
- don't restrict peer compatibility by version to simplify syncing old nodes
- 28-07-2018: Updates from review
- split into two ADRs - one for protocol, one for chains
- include signalling for upgrades in header
@@ -27,80 +21,91 @@
## Context
Here we focus on software-agnostic protocol versions.
This ADR focuses on software-agnostic protocol versions.
The Software Version is covered by SemVer and described elsewhere.
It is not relevant to the protocol description, suffice to say that if any protocol version
changes, the software version changes, but not necessarily vice versa.
It is not relevant to the protocol description, suffice to say that if any
protocol version changes, the software version changes, but not necessarily
vice versa.
Software version should be included in NodeInfo for convenience/diagnostics.
We are also interested in versioning across different blockchains in a
meaningful way, for instance to differentiate branches of a contentious
hard-fork. We leave that for a later ADR.
We need to version components of the blockchain that may be independently
upgraded. We need to do it in a way that is scalable and maintainable - we can't
just litter the code with conditionals. We need a versioning system that
supports a range of stakeholders throughout the lifespan of a blockchain.
## Requirements
We need to version components of the blockchain that may be independently upgraded.
We need to do it in a way that is scalable and maintainable - we can't just litter
the code with conditionals.
We can consider the complete version of the protocol to contain the following sub-versions:
BlockVersion, P2PVersion, AppVersion. These versions reflect the major sub-components
of the software that are likely to evolve together, at different rates, and in different ways,
as described below.
The BlockVersion defines the core of the blockchain data structures and
should change infrequently.
The P2PVersion defines how peers connect and communicate with eachother - it's
not part of the blockchain data structures, but defines the protocols used to build the
blockchain. It may change gradually.
The AppVersion determines how we compute app specific information, like the
AppHash and the Results.
All of these versions may change over the life of a blockchain, and we need to
be able to help new nodes sync up across version changes. This means we must be willing
to connect to peers with older version.
### BlockVersion
- All tendermint hashed data-structures (headers, votes, txs, responses, etc.).
- Note the semantic meaning of a transaction may change according to the AppVersion, but the way txs are merklized into the header is part of the BlockVersion
- It should be the least frequent/likely to change.
- Tendermint should be stabilizing - it's just Atomic Broadcast.
- We can start considering for Tendermint v2.0 in a year
- It's easy to determine the version of a block from its serialized form
### P2PVersion
- All p2p and reactor messaging (messages, detectable behaviour)
- Will change gradually as reactors evolve to improve performance and support new features - eg proposed new message types BatchTx in the mempool and HasBlockPart in the consensus
- It's easy to determine the version of a peer from its first serialized message/s
- New versions must be compatible with at least one old version to allow gradual upgrades
### AppVersion
- The ABCI state machine (txs, begin/endblock behaviour, commit hashing)
- Behaviour and message types will change abruptly in the course of the life of a chain
- Need to minimize complexity of the code for supporting different AppVersions at different heights
- Ideally, each version of the software supports only a _single_ AppVersion at one time
- this means we checkout different versions of the software at different heights instead of littering the code
with conditionals
- minimize the number of data migrations required across AppVersion (ie. most AppVersion should be able to read the same state from disk as previous AppVersion).
## Ideal
Each component of the software is independently versioned in a modular way and its easy to mix and match and upgrade.
We also need to consider the lifespan of the application itself and be
accommodating to how it upgrades.
## Proposal
Each of BlockVersion, AppVersion, P2PVersion, is a monotonically increasing uint64.
Ideally, each component of the software is independently versioned in a modular
way and its easy to mix and match and upgrade.
To use these versions, we need to update the block Header, the p2p NodeInfo, and the ABCI.
We can consider the complete version of the protocol to contain the following
sub-versions: **BlockVersion**, **P2PVersion**, **AppVersion**.
These versions reflect the major sub-components of the software that are likely
to evolve together, at different rates, and in different ways.
### Header
### What does each protocol version correspond to and how are they allowed to evolve
Each of BlockVersion, AppVersion, and P2PVersion is a monotonically increasing
uint64. Incrementing the version number indicates a change that is incompatible
with the previous version.
#### BlockVersion
- This consists of all Tendermint data-structures (headers, votes, commits, txs,
responses, etc.). A complete list of data structures can be found in the
[spec](https://github.com/tendermint/spec/blob/master/spec/core/data_structures.md)
#### P2PVersion
- All p2p and reactor messaging (messages, detectable behavior)
- It should be easy to determine the version of a peer from its first serialized
message/s
- New versions must be compatible with at least one old version to allow gradual
upgrades
- We need the peer/reactor protocols to take the versions of peers into account
when sending messages so that we don't send messages that the peer won't
understand or won't expect.
#### AppVersion
- The ABCI state machine itself.
- Tendermint needs to know the version of the application to ensure that AppHash
and Results are calculated in the same manner across the entire network.
In addition, the block version is the parent of two further sub versions:
**RPCVersion** and **ABCIVersion**.These are denoted using semantic versioning.
Changing the block protocol will require either a new major or minor release
for each of these versions as the data structures they serve have changed,
however major and minor releases wouldn't necessarily mean a block protocol change.
#### RPCVersion
This covers all RPC endpoints and their respective requests and responses.
In the case of an introduction of a new interface with external users (i.e gRPC),
this would also fall under the RPCVersion. Minor changes could be adding new
endpoints or adding fields so long as these fields would not affect verification
(in other words, doesn't affect a hash).
#### ABCIVersion
Refers to the entire ABCI Library and predominantly the application interface.
Similarly with RPCVersion, minor changes could be adding fields or calls
(so long as they are optional and not connected with the replication logic).
Because the application isn't concerned with verification logic or assessing hashes,
a block protocol version change does not necessarily imply a major version change.
A minor version change could suffice.
### Where are versions located and how are they updated
To use these versions, we need to update the block Header, the p2p NodeInfo, the
ABCI and add an RPC endpoint.
#### Header
Block Header should include a `Version` struct as its first field like:
@@ -116,19 +121,17 @@ Here, `Version.Block` defines the rules for the current block, while
the `AppHash` in the current block. Together they provide a complete description
of the consensus-critical protocol.
Since we have settled on a proto3 header, the ability to read the BlockVersion out of the serialized header is unanimous.
Since we have settled on a proto3 header, the ability to read the BlockVersion
out of the serialized header is unanimous.
Using a Version struct gives us more flexibility to add fields without breaking
the header.
The ProtocolVersion struct includes both the Block and App versions - it should
serve as a complete description of the consensus-critical protocol.
### NodeInfo
#### NodeInfo
NodeInfo should include a Version struct as its first field like:
```
```golang
type Version struct {
P2P uint64
Block uint64
@@ -139,35 +142,36 @@ type Version struct {
```
Note this effectively makes `Version.P2P` the first field in the NodeInfo, so it
should be easy to read this out of the serialized header if need be to facilitate an upgrade.
should be easy to read this out of the serialized header if need be to
facilitate an upgrade.
The `Version.Other` here should include additional information like the name of the software client and
it's SemVer version - this is for convenience only. Eg.
`tendermint-core/v0.22.8`. It's a `[]string` so it can include information about
the version of Tendermint, of the app, of Tendermint libraries, etc.
The `Version.Other` here should include additional information like the name of
the software client and it's SemVer version - this is for convenience only.
Eg. `tendermint-core/v0.22.8`. It's a `[]string` so it can include information
about the version of Tendermint, of the app, of Tendermint libraries, etc.
### ABCI
Note NodeInfo is only exchanged after the authenticated encryption handshake to
ensure that it's private. Doing any version exchange before encrypting could be
considered information leakage, though I'm not sure how much that matters
compared to being able to upgrade the protocol.
#### ABCI
Since the ABCI is responsible for keeping Tendermint and the App in sync, we
need to communicate version information through it.
On startup, we use Info to perform a basic handshake. It should include all the
version information.
version information so the node can check for compatibility. This would also
include tha ABCI version so applications using a socket connection can ensure
they are using the correct version.
We also need to be able to update versions in the life of a blockchain. The
natural place to do this is EndBlock.
Note that currently the result of the Handshake isn't exposed anywhere, as the
handshaking happens inside the `proxy.AppConns` abstraction. We will need to
remove the handshaking from the `proxy` package so we can call it independently
and get the result, which should contain the application version.
#### Info
##### Info
RequestInfo should add support for protocol versions like:
```
```golang
message RequestInfo {
string abci_version
string version
uint64 block_version
uint64 p2p_version
@@ -176,7 +180,7 @@ message RequestInfo {
Similarly, ResponseInfo should return the versions:
```
```golang
message ResponseInfo {
string data
@@ -188,10 +192,11 @@ message ResponseInfo {
}
```
The existing `version` fields should be called `software_version` but we leave
them for now to reduce the number of breaking changes.
The existing `version` field should be called `software_version` but we will leave it for now to reduce the number of breaking changes.
#### EndBlock
We also need to be able to update versions throughout the life of a blockchain. The natural place to do this is EndBlock.
##### EndBlock
Updating the version could be done either with new fields or by using the
existing `tags`. Since we're trying to communicate information that will be
@@ -202,7 +207,7 @@ be communicated through EndBlock.
EndBlock already contains `ConsensusParams`. We can add version information to
the ConsensusParams as well:
```
```golang
message ConsensusParams {
BlockSize block_size
@@ -216,49 +221,18 @@ message VersionParams {
}
```
For now, the `block_version` will be ignored, as we do not allow block version
to be updated live. If the `app_version` is set, it signals that the app's
protocol version has changed, and the new `app_version` will be included in the
`Block.Header.Version.App` for the next block.
For now, the `block_version` will be ignored, as we do not allow block version
to be updated live. If the `app_version` is set, it signals that the app's
protocol version has changed, and the new `app_version` will be included in the
`Block.Header.Version.App` for the next block.
### BlockVersion
#### RPC
BlockVersion is included in both the Header and the NodeInfo.
Changing BlockVersion should happen quite infrequently and ideally only for
critical upgrades. For now, it is not encoded in ABCI, though it's always
possible to use tags to signal an external process to co-ordinate an upgrade.
Note Ethereum has not had to make an upgrade like this (everything has been at state machine level, AFAIK).
### P2PVersion
P2PVersion is not included in the block Header, just the NodeInfo.
P2PVersion is the first field in the NodeInfo. NodeInfo is also proto3 so this is easy to read out.
Note we need the peer/reactor protocols to take the versions of peers into account when sending messages:
- don't send messages they don't understand
- don't send messages they don't expect
Doing this will be specific to the upgrades being made.
Note we also include the list of reactor channels in the NodeInfo and already don't send messages for channels the peer doesn't understand.
If upgrades always use new channels, this simplifies the development cost of backwards compatibility.
Note NodeInfo is only exchanged after the authenticated encryption handshake to ensure that it's private.
Doing any version exchange before encrypting could be considered information leakage, though I'm not sure
how much that matters compared to being able to upgrade the protocol.
XXX: if needed, can we change the meaning of the first byte of the first message to encode a handshake version?
this is the first byte of a 32-byte ed25519 pubkey.
### AppVersion
AppVersion is also included in the block Header and the NodeInfo.
AppVersion essentially defines how the AppHash and LastResults are computed.
Clients can communicate the RPC version that they are able to parse in the request.
This can be either through the base URL path or in the header.
If the requested version is incompatible with the node's RPCVersion (i.e. different major versions)
then it will return a version error. If the client can support multiple RPC versions
then it can use the `/version` endpoint to ascertain the exact RPCVersion of the node.
### Peer Compatibility
@@ -268,12 +242,12 @@ help old peers, possibly on older versions, sync the blockchain.
We might be tempted to say that we only connect to peers with the same
AppVersion and BlockVersion (since these define the consensus critical
computations), and a select list of P2PVersions (ie. those compatible with
ours), but then we'd need to make accomodations for connecting to peers with the
ours), but then we'd need to make accommodations for connecting to peers with the
right Block/AppVersion for the height they're on.
For now, we will connect to peers with any version and restrict compatibility
solely based on the ChainID. We leave more restrictive rules on peer
compatibiltiy to a future proposal.
compatibility to a future proposal.
### Future Changes
@@ -283,24 +257,24 @@ checking out and installing new software versions and restarting the process. It
would subscribe to the relevant upgrade event (needs to be implemented) and call `/unsafe_stop` at
the correct height (of course only after getting approval from its user!)
## Status
Accepted
## Consequences
### Positive
- Make tendermint and application versions native to the ABCI to more clearly
communicate about them
- Distinguish clearly between protocol versions and software version to
facilitate implementations in other languages
- Make Tendermint and application versions native to the ABCI to more clearly communicate about them
- Distinguish clearly between protocol versions and software version to facilitate implementations in other languages
- Versions included in key data structures in easy to discern way
- Allows proposers to signal for upgrades and apps to decide when to actually change the
version (and start signalling for a new version)
- Allows proposers to signal for upgrades and apps to decide when to actually change the version (and start signalling for a new version)
### Neutral
- Unclear how to version the initial P2P handshake itself
- Versions aren't being used (yet) to restrict peer compatibility
- Signalling for a new version happens through the proposer and must be
tallied/tracked in the app.
- Signaling for a new version happens through the proposer and must be tallied/tracked in the app.
### Negative