Closed h5law closed 1 year ago
@h5law I looked over this briefly but will need to think about this deeply (and at length) next week. off the cuff, I definitely want to avoid adding a component as major as an interface_registry
but can't say much until I can propose an alternative.
In the meantime, I wanted to ask if simply have protobufs (e.g. like we do in utility/types/proto/message.proto
) and adding custom logic to them (e.g. like we do utility/types/message.go
) is not sufficient?
I promise I'll dive deeper into this next week (this has been a long one).
In the meantime, I wanted to ask if simply have protobufs (e.g. like we do in
utility/types/proto/message.proto
) and adding custom logic to them (e.g. like we doutility/types/message.go
) is not sufficient?
@Olshansk this is what I am proposing we have the interface and multiple implementations of the interface, however we do not know which implementation is being used at any single time. We need to be able to serialise and deserialise the interface as is without knowledge of which client is being used. In order to work on the interfaces and not have any knowledge of / abstract away the implementation we need something like this. The actual implementation may be able to be simplified.
We could tightly couple ourselves to cosmos' types and this would make it easier but this limits us to expand to NEAR - or to ETH when they work.
The reason we cannot know the implementation type when we deal with these interfaces is that they are defined in the client implementation themselves - for example a tendermint light client will have a different ConsensusState
implementation to a near light client. As such we much pass in and out serialised types that are opaque to the client.
We discussed this offline during the team's weekly sync and are looking into potentially using a protobuf oneof as an alternate solution to this.
Whatever approach we decide to go with, let's use an ADR to document tradeoffs: https://github.com/pokt-network/pocket-network-protocol/tree/main/ADRs
How do we serialize define it?
// General
type ClientState interface {}
// Specific
type NearIBCInterace interface {}
// Specific
type SolanaIBCInterace interface {}
// Specific
type PocketIBCInterace interface {}
How do we serialize it?
# Serializble
message ClientState {
# Include only one (what we need) or none
oneof client_state {
// IBC client state for Tendermint
// Reuse other protos (e.g. tendermint) if we need them
TendermintClientState tendermint_client_state = 1;
// IBC client state for Ethereum
EthereumClientState ethereum_client_state = 2;
// IBC client state for Solana
SolanaClientState solana_client_state = 3;
// intead of registr
// add an element if we need (nothing else)
}
}
How do we use it?
switch ClientState.client_state.(type) {
case Tendermint:
CustomTendermintBusinessLogic()
case Ethereum:
CustomEthereumBusinessLogic()
case Solana:
CustomSolanaBusinessLogic()
}
What are our requirments?
Requirements:
1. Serializable & Deserializable (i.e. conversion to / from bytes)
- E.g. Protobuf, amino, thrift, etc... (protobuf is the most popular)
2. General purpose interface w/o an implementation
- Golang interface that maps to implementations
3. Where are the implemtantions?
- A registry
- Protobufs?
4. What do other others do?
- Cosmos: registry
- Near: ???
- Solona: ???
5. How do we implement?
- Less code
- Less maintenance
- Simpler
- Easier to understand & test
Closing due to a more appropriate solution being found
Objective
The Problem
ICS-02 defines numerous interfaces for types that are defined on a per-client basis. For example the
ClientState
interface will have a different implementation depending on which client it is representing. Each of these implementations must be serialisable as they need to be transferred as[]byte
types.This means that we need a way for an interface to be serialised and deserialised depending on the type of client.
Assume the following interface definition:
As
proto.Message
is embedded in the interface the regularcodec.GetCodec().Marshal(interfaceInstance)
will work. However, to deserialise the same[]byte
returned we will need the knowledge of the underlying protobuf definition that implements this interface.The Solution
In order to enable us to marshal/serialise and unmarshal/deserialise interfaces without the knowledge of their implementations at compile time we must introduce an interface registry. Cosmos uses a similar approach which, we could simplify and alter for our specific use case, see:
interface_registry.go
.The general idea is that we utilise
anypb.Any{}
as well as theprotoregistry
package, and potentially a newInterfaceRegistry
to wrap the two, map type that allows for the following:anypb.Any{}
types utilising theprotoregistry
andInterfaceRegistry
wrapper.This will allow us to serialise interfaces and deserialise them without needing to know exactly which implementation of said interface was originally used. This allows us to take a more general approach to light client definitions, as each client type will define its own implementations of the required interfaces.
Origin Document
IBC Light Clients are required to use specific type definitions which will vary from client to client ICS-02.
We need to be able to marshal and unmarshal these types in order to pass them in and out of the client implementations for verification, as well as for storage reasons in the IBC store & PostgresDB.
Goals
InterfaceRegistry
that allows for the marshalling of interfaces and the unmarshalling of[]byte
back into interfacesInterfaceRegistry
to be exposed and utilised outside of thecodec
package[]byte
typesDeliverable
InterfaceRegistry
type that wraps theanypb.Any{}
object andprotoregistry
packageInterfaceRegistry
InterfaceRegistry
Pack
andLookup
functions required to:anypb.Any{}
objects for serialisation[]byte
back into their original implementation types and return the original interfaceInterfaceRegistry
enables the aboveNon-goals / Non-deliverables
General issue deliverables
Testing Methodology
make ...
make ...
make test_all
LocalNet
is still functioning correctly by following the instructions at docs/development/README.mdk8s LocalNet
is still functioning correctly by following the instructions hereCreator: @h5law Co-Owners: @h5law