You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Both the interest to extend IBC to Rust based execution layers and the need to completely remove the depencency on native modules on Cosmos SDK had shown us we need to have a Rust implementation of IBC. Since we are very knowledgeable about the IBC stack at this point, it might sound straightforward but there are several things that we want to make sure we make it right:
1. The implementation should be generic over all possible Rust based execution environments
The naive way of doing this implementation could be having the protocol where it directly calls the other light clients. Such as:
let expected_connection = ConnectionEnd{ .. };
light_client.verify_membership(expected_connection)?;
store.save_connection(connection);
This simple idea actually makes some strong and highly restrictive assumption. In order to be able to use such an implementation, the execution environment should either allow direct execution of cross-contract calls (similar to EVM) or the implementation should assume that all of the ibc apps and the light clients will be embedded into the ibc implementation. The first assumption would make it impossible to implement the protocol in CosmWasm and Near (possibly others) and the second assumption just kills the whole point which is to be able to permissionlessly develop and use IBC apps.
Our implementation should not make any assumption on how read-only or write calls to other contracts are handled by also taking it into account that the cross contract calls might be executed in different blocks (eg Near).
2. The implementation should not enforce any restrictions when possible
It's important to remember once more that IBC is a fairly generic protocol that can be implemented in almost every chain and our goal should be to follow the generic IBC spec and not introduce any additional assumptions. For example, enforcing all the data types to be saved and communicated using proto is easy but it's also restrictive. But on the other hand, making it generic puts more effort into both relayer and the light client implementations. For example let's say that how ConnectionEnd is encoded before being passed to the light client for membership verification is generic, and Chain B decided to use borsh in it's IBC implementation. While communication with Chain A which uses ibc-go, the light clients tracking Chain B on Chain A would then need to first decode ConnectionEnd that is passed by ibc-go by using protobuf and then encode it using borsh to be able to do membership verification. Encoding is probably the second big design decisions to be made.
An IBC State Machine
A well defined protocol is basically a state machine with a lot of states and actions. And the nice thing about this fact is that you state machines are easy to implement in an asynchronous environment. One can think of how Rust interprets async code as a state machine. A similar model can also be used for this implementation. The main idea is "make a state machine and whenever there is a possible async call, yield a state and then progress only if the correct action is taken".
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Both the interest to extend IBC to Rust based execution layers and the need to completely remove the depencency on native modules on Cosmos SDK had shown us we need to have a Rust implementation of IBC. Since we are very knowledgeable about the IBC stack at this point, it might sound straightforward but there are several things that we want to make sure we make it right:
1. The implementation should be generic over all possible Rust based execution environments
The naive way of doing this implementation could be having the protocol where it directly calls the other light clients. Such as:
This simple idea actually makes some strong and highly restrictive assumption. In order to be able to use such an implementation, the execution environment should either allow direct execution of cross-contract calls (similar to EVM) or the implementation should assume that all of the ibc apps and the light clients will be embedded into the ibc implementation. The first assumption would make it impossible to implement the protocol in CosmWasm and Near (possibly others) and the second assumption just kills the whole point which is to be able to permissionlessly develop and use IBC apps.
Our implementation should not make any assumption on how read-only or write calls to other contracts are handled by also taking it into account that the cross contract calls might be executed in different blocks (eg Near).
2. The implementation should not enforce any restrictions when possible
It's important to remember once more that IBC is a fairly generic protocol that can be implemented in almost every chain and our goal should be to follow the generic IBC spec and not introduce any additional assumptions. For example, enforcing all the data types to be saved and communicated using proto is easy but it's also restrictive. But on the other hand, making it generic puts more effort into both relayer and the light client implementations. For example let's say that how
ConnectionEnd
is encoded before being passed to the light client for membership verification is generic, and Chain B decided to useborsh
in it's IBC implementation. While communication with Chain A which usesibc-go
, the light clients trackingChain B
onChain A
would then need to first decodeConnectionEnd
that is passed byibc-go
by usingprotobuf
and then encode it usingborsh
to be able to do membership verification. Encoding is probably the second big design decisions to be made.An IBC State Machine
A well defined protocol is basically a state machine with a lot of states and actions. And the nice thing about this fact is that you state machines are easy to implement in an asynchronous environment. One can think of how Rust interprets async code as a state machine. A similar model can also be used for this implementation. The main idea is "make a state machine and whenever there is a possible async call, yield a state and then progress only if the correct action is taken".
TBD
Beta Was this translation helpful? Give feedback.
All reactions