Skip to content

Commit

Permalink
feat(minor-gateway): add gateway client (#604)
Browse files Browse the repository at this point in the history
  • Loading branch information
cjcobb23 authored Sep 6, 2024
1 parent ca6a84d commit 65b0bd7
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 0 deletions.
3 changes: 3 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 packages/gateway-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@ edition = { workspace = true }

[dependencies]
axelar-wasm-std = { workspace = true }
client = { workspace = true }
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
error-stack = { workspace = true }
msgs-derive = { workspace = true }
router-api = { workspace = true }
thiserror = { workspace = true }

[dev-dependencies]
goldie = { workspace = true }

[lints]
workspace = true
146 changes: 146 additions & 0 deletions packages/gateway-api/src/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
use axelar_wasm_std::vec::VecExt;
use cosmwasm_std::WasmMsg;
use error_stack::ResultExt;
use router_api::{CrossChainId, Message};

use crate::msg::{ExecuteMsg, QueryMsg};

type Result<T> = error_stack::Result<T, Error>;

#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("failed to query gateway for outgoing messages. message ids: {0:?}")]
QueryOutgoingMessages(Vec<CrossChainId>),
}

impl From<QueryMsg> for Error {
fn from(value: QueryMsg) -> Self {
match value {
QueryMsg::OutgoingMessages(message_ids) => Error::QueryOutgoingMessages(message_ids),
}
}
}

impl<'a> From<client::Client<'a, ExecuteMsg, QueryMsg>> for Client<'a> {
fn from(client: client::Client<'a, ExecuteMsg, QueryMsg>) -> Self {
Client { client }
}
}

pub struct Client<'a> {
client: client::Client<'a, ExecuteMsg, QueryMsg>,
}

impl<'a> Client<'a> {
pub fn outgoing_messages(&self, message_ids: Vec<CrossChainId>) -> Result<Vec<Message>> {
let msg = QueryMsg::OutgoingMessages(message_ids);
self.client.query(&msg).change_context_lazy(|| msg.into())
}

pub fn verify_messages(&self, messages: Vec<Message>) -> Option<WasmMsg> {
messages
.to_none_if_empty()
.map(|messages| self.client.execute(&ExecuteMsg::VerifyMessages(messages)))
}

pub fn route_messages(&self, messages: Vec<Message>) -> Option<WasmMsg> {
messages
.to_none_if_empty()
.map(|messages| self.client.execute(&ExecuteMsg::RouteMessages(messages)))
}
}

#[cfg(test)]
mod tests {
use cosmwasm_std::testing::MockQuerier;
use cosmwasm_std::{from_json, to_json_binary, Addr, QuerierWrapper, SystemError, WasmQuery};
use router_api::{CrossChainId, Message};

use crate::client::Client;
use crate::msg::QueryMsg;

#[test]
fn query_outgoing_messages_should_return_error_when_query_errors() {
let (querier, addr) = setup_queries_to_fail();

let client: Client = client::Client::new(QuerierWrapper::new(&querier), &addr).into();
let cc_id = CrossChainId {
source_chain: "ethereum".parse().unwrap(),
message_id: "0x13548ac28fe95805ad2b8b824472d08e3b45cbc023a5a45a912f11ea98f81e97-0"
.parse()
.unwrap(),
};
let res = client.outgoing_messages(vec![cc_id.clone()]);
assert!(res.is_err());
goldie::assert!(res.unwrap_err().to_string());
}

#[test]
fn query_outgoing_messages_should_return_outgoing_messages() {
let (querier, addr) = setup_queries_to_succeed();

let client: Client = client::Client::new(QuerierWrapper::new(&querier), &addr).into();
let cc_id = CrossChainId {
source_chain: "ethereum".parse().unwrap(),
message_id: "0x13548ac28fe95805ad2b8b824472d08e3b45cbc023a5a45a912f11ea98f81e97-0"
.parse()
.unwrap(),
};
let res = client.outgoing_messages(vec![cc_id.clone()]);
println!("{:?}", res);
assert!(res.is_ok());
goldie::assert_json!(res.unwrap());
}

fn setup_queries_to_fail() -> (MockQuerier, Addr) {
let addr = "gateway";

let mut querier = MockQuerier::default();
querier.update_wasm(move |msg| match msg {
WasmQuery::Smart {
contract_addr,
msg: _,
} if contract_addr == addr => {
Err(SystemError::Unknown {}).into() // simulate cryptic error seen in production
}
_ => panic!("unexpected query: {:?}", msg),
});

(querier, Addr::unchecked(addr))
}

fn setup_queries_to_succeed() -> (MockQuerier, Addr) {
let addr = "gateway";

let mut querier = MockQuerier::default();
querier.update_wasm(move |msg| match msg {
WasmQuery::Smart { contract_addr, msg } if contract_addr == addr => {
let msg = from_json::<QueryMsg>(msg).unwrap();
match msg {
QueryMsg::OutgoingMessages(cc_ids) => {
println!("returning ok");
let res = Ok(to_json_binary(
&cc_ids
.into_iter()
.map(|cc_id| Message {
cc_id,
source_address: "foobar".parse().unwrap(),
destination_chain: "ethereum".parse().unwrap(),
destination_address: "foobar".parse().unwrap(),
payload_hash: [0u8; 32],
})
.collect::<Vec<Message>>(),
)
.into())
.into();
println!("made res");
res
}
}
}
_ => panic!("unexpected query: {:?}", msg),
});

(querier, Addr::unchecked(addr))
}
}
1 change: 1 addition & 0 deletions packages/gateway-api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod client;
pub mod msg;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
failed to query gateway for outgoing messages. message ids: [CrossChainId { source_chain: ChainNameRaw("ethereum"), message_id: String("0x13548ac28fe95805ad2b8b824472d08e3b45cbc023a5a45a912f11ea98f81e97-0") }]
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"cc_id": {
"source_chain": "ethereum",
"message_id": "0x13548ac28fe95805ad2b8b824472d08e3b45cbc023a5a45a912f11ea98f81e97-0"
},
"source_address": "foobar",
"destination_chain": "ethereum",
"destination_address": "foobar",
"payload_hash": "0000000000000000000000000000000000000000000000000000000000000000"
}
]

0 comments on commit 65b0bd7

Please sign in to comment.