Skip to content

Commit

Permalink
feat(voyager): split self state functionality into a new module type (u…
Browse files Browse the repository at this point in the history
  • Loading branch information
benluelo authored Dec 28, 2024
2 parents c1653e8 + d7bed7c commit ac1e431
Show file tree
Hide file tree
Showing 20 changed files with 1,204 additions and 398 deletions.
109 changes: 109 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ members = [
"voyager/modules/client/movement",
"voyager/modules/client/tendermint",

"voyager/modules/client-bootstrap/cometbls",
"voyager/modules/client-bootstrap/ethereum",
"voyager/modules/client-bootstrap/movement",
"voyager/modules/client-bootstrap/tendermint",

"voyager/modules/consensus/cometbls",
"voyager/modules/consensus/ethereum",
"voyager/modules/consensus/movement",
Expand Down
29 changes: 28 additions & 1 deletion lib/voyager-message/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,10 @@ pub struct Modules {
/// map of chain id to consensus module.
consensus_modules: HashMap<ChainId, ModuleRpcClient>,

/// map of client type to ibc interface to client module.
client_modules: HashMap<(ClientType, IbcInterface, IbcSpecId), ModuleRpcClient>,

client_bootstrap_modules: HashMap<(ChainId, ClientType), ModuleRpcClient>,

chain_consensus_types: HashMap<ChainId, ConsensusType>,

client_consensus_types: HashMap<ClientType, ConsensusType>,
Expand Down Expand Up @@ -336,6 +337,7 @@ impl Context {
state_modules: Default::default(),
proof_modules: Default::default(),
client_modules: Default::default(),
client_bootstrap_modules: Default::default(),
consensus_modules: Default::default(),
chain_consensus_types: Default::default(),
client_consensus_types: Default::default(),
Expand Down Expand Up @@ -771,6 +773,22 @@ impl Modules {
}),
}
}

pub fn client_bootstrap_module<'a, 'b, 'c: 'a>(
&'a self,
chain_id: &ChainId,
client_type: &ClientType,
// ) -> Result<&'a (impl jsonrpsee::core::client::ClientT + 'a), ConsensusModuleNotFound> {
) -> Result<&'a reconnecting_jsonrpc_ws_client::Client, ClientBootstrapModuleNotFound> {
Ok(self
.client_bootstrap_modules
.get(&(chain_id.clone(), client_type.clone()))
.ok_or_else(|| ClientBootstrapModuleNotFound {
chain_id: chain_id.clone(),
client_type: client_type.clone(),
})?
.client())
}
}

#[model]
Expand Down Expand Up @@ -962,6 +980,15 @@ pub struct ConsensusModuleNotFound(pub ChainId);

module_error!(ConsensusModuleNotFound);

#[derive(Debug, Clone, PartialEq, thiserror::Error)]
#[error("no module loaded for client bootstrapping on chain `{chain_id}` for client type `{client_type}`")]
pub struct ClientBootstrapModuleNotFound {
pub chain_id: ChainId,
pub client_type: ClientType,
}

module_error!(ClientBootstrapModuleNotFound);

#[derive(Debug, Clone, PartialEq, thiserror::Error)]
pub enum ClientModuleNotFound {
#[error("no client module loaded for client type `{}`", client_type)]
Expand Down
46 changes: 43 additions & 3 deletions lib/voyager-message/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ use crate::{
data::Data,
filter::JaqInterestFilter,
module::{
ClientModuleInfo, ClientModuleServer, ConsensusModuleInfo, ConsensusModuleServer,
PluginInfo, PluginServer, ProofModuleInfo, ProofModuleServer, StateModuleInfo,
StateModuleServer,
ClientBootstrapModuleInfo, ClientBootstrapModuleServer, ClientModuleInfo,
ClientModuleServer, ConsensusModuleInfo, ConsensusModuleServer, PluginInfo, PluginServer,
ProofModuleInfo, ProofModuleServer, StateModuleInfo, StateModuleServer,
},
rpc::{json_rpc_error_to_error_object, IbcProof, IbcState, VoyagerRpcClient},
};
Expand Down Expand Up @@ -444,6 +444,46 @@ pub trait ClientModule: ClientModuleServer + Sized {
}
}

#[allow(async_fn_in_trait)]
pub trait ClientBootstrapModule: ClientBootstrapModuleServer + Sized {
type Config: DeserializeOwned + Clone;

async fn new(
config: Self::Config,
info: ClientBootstrapModuleInfo,
) -> Result<Self, BoxDynError>;

async fn run() {
init_log();

match <ModuleApp as clap::Parser>::parse() {
ModuleApp::Run {
socket,
voyager_socket,
config,
info,
} => {
let config = must_parse::<Self::Config>(&config);

let info = must_parse::<ClientBootstrapModuleInfo>(&info);

let name = info.id();

run_server(
name.clone(),
voyager_socket,
(config, info),
socket,
|(config, info)| Self::new(config, info),
Self::into_rpc,
)
.instrument(debug_span!("run_client_bootstrap_module_server", %name))
.await
}
}
}
}

#[derive(Debug, Clone)]
pub struct IdThreadClient<Inner: ClientT + Send + Sync> {
pub(crate) client: Inner,
Expand Down
52 changes: 50 additions & 2 deletions lib/voyager-message/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ pub struct ClientModuleInfo {
impl ClientModuleInfo {
pub fn id(&self) -> String {
format!(
"client/{}/{}/{}",
self.client_type, self.consensus_type, self.ibc_interface
"client/{}/{}/{}/{}",
self.client_type, self.consensus_type, self.ibc_interface, self.ibc_spec_id
)
}

Expand Down Expand Up @@ -203,6 +203,50 @@ impl ClientModuleInfo {
}
}

#[model]
#[derive(clap::Args, JsonSchema)]
pub struct ClientBootstrapModuleInfo {
/// The client type that this client bootstrap module provides functionality for.
#[arg(value_parser(|s: &str| ok(ClientType::new(s.to_owned()))))]
pub client_type: ClientType,

/// The id of the chain that this client bootstrap module provides initial state for.
#[arg(value_parser(|s: &str| ok(ConsensusType::new(s.to_owned()))))]
pub chain_id: ChainId,
}

impl ClientBootstrapModuleInfo {
pub fn id(&self) -> String {
format!("client-bootstrap/{}/{}", self.client_type, self.chain_id)
}

pub fn ensure_client_type(
&self,
client_type: impl AsRef<str>,
) -> Result<(), UnexpectedClientTypeError> {
if client_type.as_ref() != self.client_type.as_str() {
Err(UnexpectedClientTypeError {
expected: self.client_type.clone(),
found: client_type.as_ref().to_owned(),
})
} else {
Ok(())
}
}

// TODO: Add this for ibc_spec_id
pub fn ensure_chain_id(&self, chain_id: impl AsRef<str>) -> Result<(), UnexpectedChainIdError> {
if chain_id.as_ref() != self.chain_id.as_str() {
Err(UnexpectedChainIdError {
expected: self.chain_id.clone(),
found: chain_id.as_ref().to_owned(),
})
} else {
Ok(())
}
}
}

#[derive(Debug, Clone, thiserror::Error)]
#[error("invalid chain id: expected `{expected}` but the rpc responded with `{found}`")]
pub struct UnexpectedChainIdError {
Expand Down Expand Up @@ -397,7 +441,11 @@ pub trait ConsensusModule {
#[method(name = "queryLatestTimestamp", with_extensions)]
// TODO: Make this return a better type than i64
async fn query_latest_timestamp(&self, finalized: bool) -> RpcResult<i64>;
}

/// Client bootstrap modules provide the initial client and consensus states for a client. This is notably separate from the [`ConsensusModule`], since it is possible for different client types (with different state types) to track the same consensus.
#[rpc(client, server, namespace = "clientBootstrap")]
pub trait ClientBootstrapModule {
/// The client state of this chain at the specified [`Height`].
///
/// Returns the client state value as JSON, which will then be encoded to
Expand Down
2 changes: 2 additions & 0 deletions lib/voyager-message/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,15 @@ pub trait VoyagerRpc {
async fn self_client_state(
&self,
chain_id: ChainId,
client_type: ClientType,
height: QueryHeight,
) -> RpcResult<SelfClientState>;

#[method(name = "selfConsensusState")]
async fn self_consensus_state(
&self,
chain_id: ChainId,
client_type: ClientType,
height: QueryHeight,
) -> RpcResult<SelfConsensusState>;

Expand Down
Loading

0 comments on commit ac1e431

Please sign in to comment.