Upgrade Issues/Examples - Authenticated encryption - currently, first exchange is random 32-byte pubkeys, no protocol identification - should be a struct like `{ AuthEncVersion, ConnectionType } - should AuthEncVersion just be P2PVersion? - NodeInfo - Make it a bunch of structs, since we can easily add fields to the end of structs - If we want to change the high level structure, we would need code to: - first read out the version by deserializing into `{ Version }` or possibly { {Version} }` - then unmarshal again but into the correct struct version - So we'd have structs like `NodeInfoV6` and `NodeInfoV7`. Both would have `Version` as first field, but otherwise could be totally different. - Note the NodeInfo is embedded in RPC response types, so if there's a new version, it will break the RPC response. We either would need to: - keep using the old version in the RPC response - offer both versions some how (?!) - could be included in the same response - or do we version the endpoints? - New reactor messages - Increment the P2PVersion and don't send the new msgs to peers below that version - Updated reactor message: - Can add fields, but generally just make new ones - If a field type was updated, also bump P2P version (?) Examples of Updates to Block: - Add new field to header - eg v5 has a field that v4 doesnt - v5 nodes accept v4 headers without the field - v4 nodes accept v5 headers with the field, just ignoring it - v5 nodes enforce rules on v5 headers - Change how a hash is computed in the header - eg v5 uses hash of X, v4 uses hash of Y - structs are compatible, but verification will fail - v5 could verify hashes in v4 headers - would require version info to be threaded through more data structures - eg. make `.Hash()` depend on a version number - eg. version the data types themselves (eg. `ValidatorV4`, `ValidatorV5`) - v4 headers could not verify v5 headers State - not really part of protocol (we don't hash it) - used in many places - persisted to disk - can easily add fields - we should put a version at the top In general we have the following kinds of changes: - Adding fields - Easy, just do it - Re-arranging fields or changing field types - Read the version first - Then deserialize into the correct versioned type - Code may need to support multiple versions of a type (eg. two versions of Vote or Header) - Merklizing ... - New types ## Older notes / Example Scenarios - Starting two versions of a new network, both named "gaia-8000", but with different initial state. - The ChainID will include `gaia-8000` and some reference to the initial state (eg. its hash) - A single network, but peers are running different compatible protocol versions, eg. - to support a new reactor or new reactor message type - like HasBlockPart in the consensus, or BatchOfTxs in the mempool - should still be able to talk to some peers that cant understand these messages - to add fields to a reactor message type - Amino should allow this - NOTE: the field will have to fit in the existing "maxMsgSize" and other versions have to be able to ignore it and be compatible .. - to soft fork - ie. implement some form of additional restrictions on the ruleset - NOT to add fields to hashed datastructures or change the state machine since these things need to be verified and integrity driven and thus require incompatible upgrades - A single network upgrades to an incompatible protocol version - to add a new field to the header - Amino is supposed to allow us to add new fields, but we don't actually want to do this in a way that supports peers that haven't upgraded because we need to verify - Could be a new merkle root of something we want, the proposer, or some application data - to remove some fields from the Commit structure eg. so it just contains a signature - to execute blocks before proposing them so the AppHash is included in the block (not the next one) - to add a new transaction type to the state machine - to change the way the state machine processes an existing transaction - to change the state of the state machine at some height