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
All functionality in voyager is provided by modules and plugins. Modules provide various forms of read-only data, such as the latest height of a chain or a state proof. Plugins, on the other hand, directly interact with the queue - every plugin has their own [topic queue](../lib/voyager-vm/README.md) with it's plugin name as the topic, along with an interest filter that can pull messages into this queue. Plugins also define their own internal message types that they can use to pass data around between calls to their internal queue (or even between other plugins).
7
+
All functionality in voyager is provided by modules and plugins. Modules provide various forms of read-only data, such as the latest height of a chain or a state proof. Plugins, on the other hand, directly interact with the queue (see [*Plugins and the Queue*](#plugins-and-the-queue) for more information).
8
8
9
9
## Types
10
10
11
11
### IBC Specification
12
12
13
13
An IBC specification defines the semantics of a light client based bridging protocol. A specification must have the following properties:
14
14
15
-
-some notion of a "light client update"
16
-
-a store specification, where client and consensus states are stored (among any other states required by the IBC specification)
17
-
-this store is required to be provable (i.e. the host environment must have some form of "proof" for it's storage, most likely merkleized)
15
+
-Some notion of a "light client update"
16
+
-A store specification, where client and consensus states are stored (among any other states required by the IBC specification)
17
+
-This store is required to be provable, i.e. the host environment must have some form of "proof" for it's storage. This is most likely achieved via a merkleized state trie, although this is not strictly required.
18
18
19
-
Everything else is an implementation detail of the IBC specification.
19
+
Everything else is an implementation detail of the IBC specification. This flexibility allows voyager to trivially support other IBC-like protocols, such as traditional IBC (referred to as `ibc-classic` throughout these docs) alongside `ibc-union`.
20
20
21
21
### Chain
22
22
23
23
A chain is defined by a few basic properties:
24
24
25
-
-produces blocks with an incrementing height (sometimes also referred to as "block number" or "slot")
26
-
-a consensus with some notion of finality, where blocks older than the latest finalized height will never reorg and are considered finalized
27
-
-a storage layer with provable state
28
-
-one or more IBC interfaces
25
+
-Produces blocks with an incrementing height (sometimes also referred to as "block number" or "slot")
26
+
-A consensus with some notion of finality, where blocks older than the latest finalized height will never reorg and are considered finalized
27
+
-A storage layer with provable state
28
+
-One or more IBC interfaces
29
29
30
30
### Consensus
31
31
32
-
A chain's consensus defines the client and consensus state types stored in the clients that verify this consensus.
32
+
A chain's consensus defines the client and consensus state types stored in the clients that verify said consensus mechanism.
33
33
34
34
#### Examples
35
35
@@ -51,18 +51,152 @@ An IBC interface defines the entrypoints of an IBC specification implementation
51
51
52
52
Clients are the mechanism used to verify a counterparty consensus. Clients are defined by 4 properties:
53
53
54
-
-compatible with an IBC specification
55
-
-on an IBC interface
56
-
-for a specific consensus mechanism
57
-
-which is verified via a consensus verification specification
The voyager binary exposes a JSON-RPC interface to allow for querying any configured chain. For example, you can query the state of any client on any chain, as long as the state module for the host chain is configured (using the voyager binary's cli):
And finally, if the client module is configured for whatever type of client this is (in this case, it happens to be `ethereum` on `ibc-cosmwasm`), `--decode` can be passed as well to receive the client state as a JSON value instead of the raw bytes:
This general concept of modularity is present in all areas of voyager. As another example, many EVM chains (various EVM L2s, custom geth fork L1s such as BSC, or fully custom EVM-compatible chains such as SEI), many of the interfaces are the exact same as ethereum mainnet. In these cases, the ethereum state module can be completely reused for these chains, just configured with a different chain ID and RPC url. The same applies to all modules, meaning that when adding support to voyager for a new chain, often times a vast majority of the work required can be fully reused from existing plugins and modules.
127
+
128
+
## Plugins and the queue
129
+
130
+
Plugins are a special type of module that also have access to the message queue. Every plugin has their own [topic queue](../lib/voyager-vm/README.md) with it's plugin name as the topic, along with an interest filter that can pull messages into this queue. Plugins also define their own internal message types that they can use to pass data around between calls to their internal queue (or even between other plugins).
131
+
132
+
For more information about plugin lifecycle and management, see the [`voyager-plugin-protocol`](../lib/voyager-plugin-protocol) crate.
133
+
134
+
## Putting it all together
135
+
136
+
The ability to query any chain in an abstract manner also drastically improves the DX and reliability of writing new plugins and modules. One area in particular where this architecture shines is when dealing with [recursive clients] (sometimes also referred to as conditional clients). Recursive clients inherently rely on state from other chains, such as L2 settlement in relation to L1 finality for the L2 finality, or requiring potentially multiple clients to be updated before the recursive client itself can be updated.
137
+
138
+
A good example of this is our [state lens client architecture][state-lens], where many modules are fully reused from existing modules. The finality of a state lens client is the finality of the "L2" client being tracked through the hop chain - this means that no additional module is required for finality, as the target chain's finality module will be used directly. Additionally, no *new* state or proof modules are required to be loaded when dealing with state lens clients, since these modules will need to be loaded for the host chain where the state lens client is on anyways. There are, however, several new plugins and modules that are required for this architecture to work:
139
+
140
+
-**Client Module**: This is standard for all new client types. The client module provides the coded for encoding and decoding various states for this client.
141
+
-**Client Bootstrap Module**: Similar to the client module, this is also standard for all new client types, however this is only required for creating new clients.
142
+
-**Client Update Plugin**: This is the most complex part of the state lens architecture. Up to two individual client updates are required to update a state lens client: the L2 client on the L1 and the L1 client on the L0 (the host chain).
143
+
144
+
This is trivially achieved by leveraging the voyager-vm messages:
145
+
146
+
```rs
147
+
// do all contained operations concurrently
148
+
conc([
149
+
// update the l2 client on the l1
150
+
promise(
151
+
[
152
+
// fetch the update headers of the l2 client
153
+
call(FetchUpdateHeaders { /* snip */ })
154
+
],
155
+
// this is the data queue of the promise callback, this allows for configuring data on creation of the promise
156
+
// in this case, there is no extra data, so it can be left empty
157
+
[],
158
+
// this is the callback that will process the data once all messages in the internal queue are processed
// do all contained operations in sequence, waiting until the head message fully resolves (i.e. returns no additional non-data messages) before processing the next message
162
+
seq([
163
+
// wait for the trusted height of the client we just updated to be finalized on the hop chain
164
+
// without this, weird things can happen with transaction ordering and reorgs
165
+
call(WaitForTrustedHeight { /* snip */ }),
166
+
// call back into this plugin to update the other clients
Thismayseemlikealotofrequirements, howeverrememberthatallofthedependencieslistedabovewereinthiscasealreadywritten-allthatneededtobedonewastoconfigurethemforthechainsweneedtouse here, and to build the state lens client logic only 1 plugin (client update) and 2 modules (client and client bootstrap) needed to be written from scratch.The same concepts, with differing degrees of reusability, apply to L2s (arbitrum, optimism, various types of rollups), customized execution environments (SEI/ethermint), novel consensus mechanisms (beacon-kit), and even entirely new chains (sui, aptos).
198
+
199
+
The full non-abridged implementation of the state lens client update plugin can be found [here](../voyager/plugins/client-update/state-lens).
0 commit comments