cosmos / ibc

Interchain Standards (ICS) for the Cosmos network & interchain ecosystem.
Other
928 stars 383 forks source link

Upgrading Tendermint ClientState to new breaking versions #718

Open AdityaSripal opened 2 years ago

AdityaSripal commented 2 years ago

Currently the upgrades to tendermint client only support changes to particular fields in the current client state struct.

This is limiting, as future versions of the tendermint protocol may wish to make breaking changes to the light client which may require a new IBC clientstate.

Currently the upgrade protocol for Tendermint works like such:

  1. Upgrading chain commits to the new ClientState that its counterparties should upgrade right before it does the upgrade
  2. Relayers query the old chains state and submits the committed ClientState to all counterparties in a MsgUpgradeClient
  3. Counterparties will check that the committed ClientState is of the same type and then create a new ClientState of the same type, with chain-chosen fields from the committed ClientState and the relayer-chosen fields from the current ClientState.
  4. The counterparties then uses the new ClientState to verify updates on the new upgraded chains

https://github.com/cosmos/ibc-go/blob/main/modules/light-clients/07-tendermint/types/upgrade.go#L101

Note that since there is no change to the binaries themselves, these upgrades will work for all counterparties


We can allow a more permissive upgrade protocol, if we introduce a new function to the ClientState interface:

MigrateClient(exported.ClientState) exported.ClientState

New client implementations can implement this method to take in a legacy client and return the equivalent upgraded clientstate struct. It may also make modifications to the client store to migrate consensus states/metadata for the new implementation. The MigrateClient method will decide which legacy clients are supported for migration.

So for example, a future BLS Tendermint client (which may involve a new client struct and different VerifyHeader implementation) can implement a method to take in the current Tendermint client and return the equivalent BLS client struct, but will reject migrating a solomachine client.


In order for an upgrade to work, the counterparty must support both the legacy client and the new client implementations. If this is the case, then a chain can upgrade to a new breaking light client like so:

  1. Upgrading chain commits to the new ClientState that its counterparties should upgrade right before it does the upgrade. Note this may be a completely different implementation to the previous one.
  2. Relayers query the old chains state and submits the committed ClientState to all counterparties in a MsgUpgradeClient
  3. Counterparty legacy client will verify that the new clientstate was committed
  4. Counterparty's legacy client will call new clients newClient = committedClient.MigrateClient(legacyClient)
  5. Counterparty's legacy client will set the new client under the clientID path.
  6. All future client messages will get processed by the new client.

This proposal has a number of advantages. It is not as restrictive as the current upgrade process. All fields of the clientstate are now subject to being changed appropriately since responsibility is handed to light client implementations. Upgrades to light client breaking versions of consensus are supported. Upgrades to new consensus algorithms are supported (provided the new consensus can provide a secure and correct migration from the old consensus's client)

cc: @colin-axner @williambanfield

williambanfield commented 2 years ago

In order for an upgrade to work, the counterparty must support both the legacy client and the new client implementations.

If I'm reading this correctly, counterparties would need to upgrade first to be able to read the new information. Once counterparties have completed their upgrade, they're able to receive messages that would previously have been breaking. Is that roughly correct?

Additionally, once the client is upgraded, it needs to be able to verify across the breaking point, is that right? Would the proposed migration method be able to upgrade and downgrade to span the new and legacy versions or is it upgrade only? I don't see a reason that verifying across the break should not be possible, but it seems a bit complex. Perhaps this isn't a problem in the IBC context but I'm not certain so I figured I'd raise it.

AdityaSripal commented 2 years ago

If I'm reading this correctly, counterparties would need to upgrade first to be able to read the new information. Once counterparties have completed their upgrade, they're able to receive messages that would previously have been breaking. Is that roughly correct?

Correct, counterparties would need both the Upgrade mechanism sketched out here (1 time upgrade) as well as both the legacy and new client implementations (would need to add new client implementations as they come in). This is largely unavoidable.

it needs to be able to verify across the breaking point, is that right

Not sure exactly what you mean by this. The legacy chain will need to commit to the new client. This is already how the current upgrade mechanism works. We're using the same idea to make the upgrades more flexible. The legacy client is not expected to verify a new client's header if that is what you are asking.

Would the proposed migration method be able to upgrade and downgrade to span the new and legacy versions or is it upgrade only?

This depends entirely on the individual client implementations of MigrateClient. In order to upgrade, the new client implementation must be able to take in a legacy client and return a new client. In order to downgrade the legacy client implementation must be able to take in a new client and return a legacy client in MigrateClient.

From the IBC perspective there's no concept of "upgrade" and "downgrade", it doesn't understand which client is better or newer. It just follows this process.

  1. It has a client X
  2. It uses client X to verify the desire to upgrade to a client Y
  3. It asks Y to covert client X to client Y
  4. It replaces X with the converted Y

So long as Y is capable of converting X, you can change your client from X to Y using this method

williambanfield commented 2 years ago

So long as Y is capable of converting X, you can change your client from X to Y using this method

I see, so then code may need to exist to convert new clients into legacy clients using this mechanism. I'm not incredibly familiar with the IBC upgrade path but I think that the easier thing is to make the clients backwards compatible. In the case of adding BLS signatures, clients would be backwards compatible by being able to parse both old and new signature schemes. Perhaps there are cases where non-backwards compatible breaking changes would be introduced, but for BLS signatures this should not be the case.