Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
37471ba
feat: initialize Cardano integration in Hermes fork
floor-licker Dec 3, 2025
2d7f0c6
fix: resolve import errors and ChainEndpoint method signatures
floor-licker Dec 3, 2025
8f3577e
fix: correct CrossChainQueryRequest import path and expand request im…
floor-licker Dec 3, 2025
871161f
feat: implement SigningKeyPair trait for CardanoSigningKeyPair
floor-licker Dec 3, 2025
d8af624
feat: implement ConsensusState and ClientState traits for Cardano types
floor-licker Dec 3, 2025
05bbb38
feat: add stub From trait implementations and fix remaining compilati…
floor-licker Dec 3, 2025
838541d
fix: resolve all compilation errors achieving clean build, fixing Err…
floor-licker Dec 3, 2025
0a3e681
feat: add Cardano chain configuration to Hermes, creating CardanoConf…
floor-licker Dec 3, 2025
031c1ad
feat: implement bootstrap method for CardanoChainEndpoint, extracting…
floor-licker Dec 3, 2025
6dfdd0d
feat: implement core query methods and keyring integration for Cardan…
floor-licker Dec 3, 2025
e0cc20d
feat: document Cardano chain integration architecture and circular de…
floor-licker Dec 4, 2025
d95997f
feat: integrate Cardano chain implementation directly into ibc-relaye…
floor-licker Dec 5, 2025
e9cdf98
feat: implement Phase 1 core relaying methods including send_messages…
floor-licker Dec 5, 2025
575c493
feat: move CardanoHeader to ibc-relayer-types, add Cardano variant to…
floor-licker Dec 5, 2025
e76d326
fix: add Cardano variant handling to Tendermint light client, resolve…
floor-licker Dec 5, 2025
18defbd
feat: implement Gateway gRPC client with real protobuf code generatio…
floor-licker Dec 5, 2025
82082ab
docs: add comprehensive event parsing TODO with implementation roadma…
floor-licker Dec 5, 2025
115b8b9
feat: implement real Gateway gRPC query methods for consensus state a…
floor-licker Dec 5, 2025
d2262f1
docs: add comprehensive documentation for build_ibc_tx and fetch_mith…
floor-licker Dec 5, 2025
26e6552
feat: extend build.rs to generate comprehensive IBC proto services, a…
floor-licker Dec 5, 2025
b0b688c
fix: resolve proto generation compilation errors, add prost-types 0.1…
floor-licker Dec 5, 2025
94ba55a
feat: wire up generated Msg service gRPC clients in gateway_client, i…
floor-licker Dec 5, 2025
c3a06ff
feat: implement complete IBC message routing in build_ibc_tx, add typ…
floor-licker Dec 5, 2025
5cee4c8
feat: implement CBOR extraction from Gateway responses for all IBC me…
floor-licker Dec 5, 2025
42993c8
feat: implement complete IBC event parsing for Cardano Gateway events…
floor-licker Dec 5, 2025
7c08c4a
feat: implement proto parsing for Gateway query responses, create pro…
floor-licker Dec 5, 2025
21717de
feat: implement Connection Channel and Packet query response parsing,…
floor-licker Dec 5, 2025
6ecb829
feat: add example Hermes configuration with Cardano testnet and Cheqd…
floor-licker Dec 5, 2025
e461baa
fix: correct Gateway port from 3002 to 3001 in example config, fix te…
floor-licker Dec 5, 2025
04629f2
feat: implement Hermes keyring integration for Cardano following Cosm…
floor-licker Dec 5, 2025
9b412f1
refactor: rename cardano-testnet to cardano-devnet to accurately refl…
floor-licker Dec 8, 2025
1db9ff3
chore: remove config.example.toml
floor-licker Dec 10, 2025
3183116
feat: implement complete packet query methods for Cardano IBC relayin…
floor-licker Dec 10, 2025
ad0a019
feat: implement list query methods for Cardano IBC discovery and enum…
floor-licker Dec 10, 2025
bb2361a
feat: implement event subscription polling for Cardano chain enabling…
floor-licker Dec 10, 2025
9f81e54
feat: implement real Gateway event query in Hermes GatewayClient
floor-licker Dec 10, 2025
924bf47
fix: resolve Hermes transaction signing for Conway-era Cardano transa…
floor-licker Jan 13, 2026
315620f
feat: implement Mithril light client types and header fetching in Her…
floor-licker Jan 14, 2026
582b3cc
docs: update README.md to reflect considerations and limitations arou…
floor-licker Jan 14, 2026
4137bc6
feat: implement proof plumbing and query methods in Cardano endpoint …
floor-licker Jan 14, 2026
ae77ec3
fix: resolve core Cardano endpoint plumbing issues by implementing se…
floor-licker Jan 14, 2026
1b3a4de
feat: add channel close and timeout-on-close support in Cardano endpo…
floor-licker Jan 15, 2026
6c1f2da
refactor(cardano): unify mithril client as 08-cardano
floor-licker Jan 24, 2026
24cae3a
refactor(cardano): align mithril type URLs with ibc.lightclients.mith…
floor-licker Jan 24, 2026
d6e66f0
chore(cardano): cleanup conventions
floor-licker Jan 24, 2026
daa4ed2
refactor(cardano): rename ics2000_mithril module
floor-licker Jan 24, 2026
b434abd
chore(cardano): improve mithril wait and tests
floor-licker Jan 26, 2026
11edfa8
test(integration-test): handle Cardano config/client variants
floor-licker Jan 26, 2026
391f371
chore(fmt): rustfmt cardano code
floor-licker Jan 26, 2026
1f999d1
chore(ci): fix clippy -D warnings
floor-licker Jan 26, 2026
c2d572b
chore: revert .gitignore local state entries
floor-licker Jan 30, 2026
14ef947
docs: update Cardano light client model
floor-licker Jan 30, 2026
71182a8
docs: update README.md
floor-licker Jan 30, 2026
e0d346e
fix: support MsgTransfer for Cardano tx building
floor-licker Feb 3, 2026
ab9e38c
fix(cardano): correct enterprise address derivation
floor-licker Feb 4, 2026
c4307d1
fix(cardano): unblock handshake and parse packet data bytes
floor-licker Feb 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 177 additions & 26 deletions Cargo.lock

Large diffs are not rendered by default.

46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,49 @@ Unless required by applicable law or agreed to in writing, software distributed
[cosmos-shield]: https://img.shields.io/static/v1?label=&labelColor=1B1E36&color=1B1E36&message=cosmos%20ecosystem&style=for-the-badge&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjMuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAyNTAwIDI1MDAiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDI1MDAgMjUwMDsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLnN0MHtmaWxsOiM2RjczOTA7fQoJLnN0MXtmaWxsOiNCN0I5Qzg7fQo8L3N0eWxlPgo8cGF0aCBjbGFzcz0ic3QwIiBkPSJNMTI1Mi42LDE1OS41Yy0xMzQuOSwwLTI0NC4zLDQ4OS40LTI0NC4zLDEwOTMuMXMxMDkuNCwxMDkzLjEsMjQ0LjMsMTA5My4xczI0NC4zLTQ4OS40LDI0NC4zLTEwOTMuMQoJUzEzODcuNSwxNTkuNSwxMjUyLjYsMTU5LjV6IE0xMjY5LjQsMjI4NGMtMTUuNCwyMC42LTMwLjksNS4xLTMwLjksNS4xYy02Mi4xLTcyLTkzLjItMjA1LjgtOTMuMi0yMDUuOAoJYy0xMDguNy0zNDkuOC04Mi44LTExMDAuOC04Mi44LTExMDAuOGM1MS4xLTU5Ni4yLDE0NC03MzcuMSwxNzUuNi03NjguNGM2LjctNi42LDE3LjEtNy40LDI0LjctMmM0NS45LDMyLjUsODQuNCwxNjguNSw4NC40LDE2OC41CgljMTEzLjYsNDIxLjgsMTAzLjMsODE3LjksMTAzLjMsODE3LjljMTAuMywzNDQuNy01Ni45LDczMC41LTU2LjksNzMwLjVDMTM0MS45LDIyMjIuMiwxMjY5LjQsMjI4NCwxMjY5LjQsMjI4NHoiLz4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTIyMDAuNyw3MDguNmMtNjcuMi0xMTcuMS01NDYuMSwzMS42LTEwNzAsMzMycy04OTMuNSw2MzguOS04MjYuMyw3NTUuOXM1NDYuMS0zMS42LDEwNzAtMzMyCglTMjI2Ny44LDgyNS42LDIyMDAuNyw3MDguNkwyMjAwLjcsNzA4LjZ6IE0zNjYuNCwxNzgwLjRjLTI1LjctMy4yLTE5LjktMjQuNC0xOS45LTI0LjRjMzEuNi04OS43LDEzMi0xODMuMiwxMzItMTgzLjIKCWMyNDkuNC0yNjguNCw5MTMuOC02MTkuNyw5MTMuOC02MTkuN2M1NDIuNS0yNTIuNCw3MTEuMS0yNDEuOCw3NTMuOC0yMzBjOS4xLDIuNSwxNSwxMS4yLDE0LDIwLjZjLTUuMSw1Ni0xMDQuMiwxNTctMTA0LjIsMTU3CgljLTMwOS4xLDMwOC42LTY1Ny44LDQ5Ni44LTY1Ny44LDQ5Ni44Yy0yOTMuOCwxODAuNS02NjEuOSwzMTQuMS02NjEuOSwzMTQuMUM0NTYsMTgxMi42LDM2Ni40LDE3ODAuNCwzNjYuNCwxNzgwLjRMMzY2LjQsMTc4MC40CglMMzY2LjQsMTc4MC40eiIvPgo8cGF0aCBjbGFzcz0ic3QwIiBkPSJNMjE5OC40LDE4MDAuNGM2Ny43LTExNi44LTMwMC45LTQ1Ni44LTgyMy03NTkuNVMzNzQuNCw1ODcuOCwzMDYuOCw3MDQuN3MzMDAuOSw0NTYuOCw4MjMuMyw3NTkuNQoJUzIxMzAuNywxOTE3LjQsMjE5OC40LDE4MDAuNHogTTM1MS42LDc0OS44Yy0xMC0yMy43LDExLjEtMjkuNCwxMS4xLTI5LjRjOTMuNS0xNy42LDIyNC43LDIyLjYsMjI0LjcsMjIuNgoJYzM1Ny4yLDgxLjMsOTk0LDQ4MC4yLDk5NCw0ODAuMmM0OTAuMywzNDMuMSw1NjUuNSw0OTQuMiw1NzYuOCw1MzcuMWMyLjQsOS4xLTIuMiwxOC42LTEwLjcsMjIuNGMtNTEuMSwyMy40LTE4OC4xLTExLjUtMTg4LjEtMTEuNQoJYy00MjIuMS0xMTMuMi03NTkuNi0zMjAuNS03NTkuNi0zMjAuNWMtMzAzLjMtMTYzLjYtNjAzLjItNDE1LjMtNjAzLjItNDE1LjNjLTIyNy45LTE5MS45LTI0NS0yODUuNC0yNDUtMjg1LjRMMzUxLjYsNzQ5Ljh6Ii8+CjxjaXJjbGUgY2xhc3M9InN0MSIgY3g9IjEyNTAiIGN5PSIxMjUwIiByPSIxMjguNiIvPgo8ZWxsaXBzZSBjbGFzcz0ic3QxIiBjeD0iMTc3Ny4zIiBjeT0iNzU2LjIiIHJ4PSI3NC42IiByeT0iNzcuMiIvPgo8ZWxsaXBzZSBjbGFzcz0ic3QxIiBjeD0iNTUzIiBjeT0iMTAxOC41IiByeD0iNzQuNiIgcnk9Ijc3LjIiLz4KPGVsbGlwc2UgY2xhc3M9InN0MSIgY3g9IjEwOTguMiIgY3k9IjE5NjUiIHJ4PSI3NC42IiByeT0iNzcuMiIvPgo8L3N2Zz4K
[cosmos-link]: https://cosmos.network


## Note on Cardano Integration

The Cosmos SDK chains follow a standard pattern:

```rust
// How Cosmos chains work:
impl SigningKeyPair for Secp256k1KeyPair {
// Creates key from mnemonic
fn from_mnemonic(
mnemonic: &str,
hd_path: &StandardHDPath,
address_type: &AddressType,
account_prefix: &str,
) -> Result<Self, Error> {
let private_key = private_key_from_mnemonic(mnemonic, hd_path)?;
let public_key = Xpub::from_priv(&Secp256k1::signing_only(), &private_key);
let address = get_address(&public_key.public_key, address_type);
let account = encode_address(account_prefix, &address)?;

Ok(Self {
private_key,
public_key,
address_type,
account,
})
}

// Must be Serialize + Deserialize for storage
fn account(&self) -> String { self.account.clone() }
fn sign(&self, message: &[u8]) -> Vec<u8> { /* ... */ }
}
```
Where keys are stored in `~/.hermes/keys/{chain-id}/keyring-test/{key-name}.json` serialized as JSON including the mnemonic, then get loaded on demand. The usage would be:

`hermes keys add --chain cosmos-hub --mnemonic-file ~/mnemonic.txt`
`hermes keys list --chain cosmos-hub`
`hermes keys delete --chain cosmos-hub --key-name my-key`

### Cardano Light Client Model

On the Cosmos side, Cardano is tracked using a single client type, `08-cardano`, with headers encoded as `/ibc.lightclients.mithril.v1.MithrilHeader`. The header carries Mithril-certified evidence for a HostState update transaction, which allows the verifier to extract the committed 32-byte `ibc_state_root` and store it in consensus state. Membership and non-membership then use standard ICS-23 proofs (protobuf `ibc.core.commitment.v1.MerkleProof` bytes) against that `ibc_state_root`.

Height semantics follow Mithril transaction snapshots: `Height.revision_height` is treated as a Cardano block number (as surfaced by db-sync and the Mithril snapshot `block_number`), not a Cardano slot. Because Mithril certificates are checkpoint-based, Hermes may need to wait after a Cardano transaction is included until that inclusion is covered by a certified snapshot before it can safely build or use proofs at that height.

This design intentionally keeps header progression and state proof verification under a single IBC client identifier, matching the core IBC connection and channel machinery.
39 changes: 38 additions & 1 deletion crates/relayer-cli/src/commands/keys/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use abscissa_core::{Command, Runnable};
use eyre::eyre;
use hdpath::StandardHDPath;
use ibc_relayer::{
chain::namada::wallet::CliWalletUtils,
chain::{cardano::signing_key_pair::CardanoSigningKeyPair, namada::wallet::CliWalletUtils},
config::{ChainConfig, Config},
keyring::{
AnySigningKeyPair, KeyRing, NamadaKeyPair, Secp256k1KeyPair, SigningKeyPair,
Expand Down Expand Up @@ -253,6 +253,23 @@ pub fn add_key(
namada_key.into()
}
ChainConfig::Penumbra(_) => unimplemented!("no key storage support for penumbra"),
ChainConfig::Cardano(config) => {
let mut keyring = KeyRing::new(
config.key_store_type,
"cardano", // account_prefix not used for Cardano
&config.id,
&config.key_store_folder,
)?;

check_key_exists(&keyring, key_name, overwrite);

let key_contents =
fs::read_to_string(file).map_err(|_| eyre!("error reading the key file"))?;
let key_pair = CardanoSigningKeyPair::from_seed_file(&key_contents, hd_path)?;

keyring.add_key(key_name, key_pair.clone())?;
key_pair.into()
}
};

Ok(key_pair)
Expand Down Expand Up @@ -295,6 +312,26 @@ pub fn restore_key(
));
}
ChainConfig::Penumbra(_) => return Err(eyre!("no key storage support for penumbra")),
ChainConfig::Cardano(config) => {
let mut keyring = KeyRing::new(
config.key_store_type,
"cardano", // account_prefix not used for Cardano
&config.id,
&config.key_store_folder,
)?;

check_key_exists(&keyring, key_name, overwrite);

let key_pair = CardanoSigningKeyPair::from_mnemonic(
&mnemonic_content,
hdpath,
&ibc_relayer::config::AddressType::Cosmos, // Not used for Cardano
"cardano", // Not used for Cardano
)?;

keyring.add_key(key_name, key_pair.clone())?;
key_pair.into()
}
};

Ok(key_pair)
Expand Down
2 changes: 2 additions & 0 deletions crates/relayer-cli/src/commands/keys/balance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ fn get_balance(chain: impl ChainHandle, key_name: Option<String>, denom: Option<
chain_config.key_name
}
ChainConfig::Penumbra(_) => unimplemented!("not yet supported for penumbra"),
ChainConfig::Cardano(chain_config) => chain_config.key_name,
}
});

Expand All @@ -106,6 +107,7 @@ fn get_balances(chain: impl ChainHandle, key_name: Option<String>) {
chain_config.key_name
}
ChainConfig::Penumbra(_) => unimplemented!("not yet supported for penumbra"),
ChainConfig::Cardano(chain_config) => chain_config.key_name,
}
});

Expand Down
22 changes: 22 additions & 0 deletions crates/relayer-cli/src/commands/keys/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use abscissa_core::{Command, Runnable};

use eyre::eyre;
use ibc_relayer::{
chain::cardano::signing_key_pair::CardanoSigningKeyPair,
config::{ChainConfig, Config},
keyring::{KeyRing, Store},
};
Expand Down Expand Up @@ -129,6 +130,15 @@ pub fn delete_key(config: &ChainConfig, key_name: &str) -> eyre::Result<()> {
keyring.remove_key(key_name)?;
}
ChainConfig::Penumbra(_) => unimplemented!("no key support for penumbra"),
ChainConfig::Cardano(config) => {
let mut keyring: KeyRing<CardanoSigningKeyPair> = KeyRing::new(
config.key_store_type,
"cardano",
&config.id,
&config.key_store_folder,
)?;
keyring.remove_key(key_name)?;
}
}
Ok(())
}
Expand Down Expand Up @@ -156,6 +166,18 @@ pub fn delete_all_keys(config: &ChainConfig) -> eyre::Result<()> {
}
}
ChainConfig::Penumbra(_) => unimplemented!("no key support for penumbra"),
ChainConfig::Cardano(config) => {
let mut keyring: KeyRing<CardanoSigningKeyPair> = KeyRing::new(
config.key_store_type,
"cardano",
&config.id,
&config.key_store_folder,
)?;
let keys = keyring.keys()?;
for (key_name, _) in keys {
keyring.remove_key(&key_name)?;
}
}
}
Ok(())
}
Expand Down
13 changes: 13 additions & 0 deletions crates/relayer-cli/src/commands/listen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ fn subscribe(
let subscription = monitor_tx.subscribe()?;
Ok(subscription)
}
ChainConfig::Cardano(_) => Err(eyre!(
"event subscription is not implemented for Cardano; requires Gateway-backed event source support in `hermes listen`"
)),
}
}

Expand All @@ -218,6 +221,11 @@ fn detect_compatibility_mode(
let rpc_addr = match config {
ChainConfig::CosmosSdk(config) | ChainConfig::Namada(config) => config.rpc_addr.clone(),
ChainConfig::Penumbra(config) => config.rpc_addr.clone(),
ChainConfig::Cardano(_) => {
return Err(eyre!(
"compatibility mode detection is not applicable for Cardano (no Tendermint RPC)"
));
}
};

let client = HttpClient::builder(rpc_addr.try_into()?)
Expand All @@ -232,6 +240,11 @@ fn detect_compatibility_mode(
let status = rt.block_on(client.status())?;
penumbra::util::compat_mode_from_version(&config.compat_mode, status.node_info.version)?
}
ChainConfig::Cardano(_) => {
return Err(eyre!(
"compatibility mode detection is not applicable for Cardano (no Tendermint RPC)"
));
}
};

Ok(compat_mode)
Expand Down
5 changes: 5 additions & 0 deletions crates/relayer-cli/src/commands/tx/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ impl Runnable for TxUpdateClientCmd {
ChainConfig::Penumbra(chain_config) => {
chain_config.genesis_restart = Some(restart_params)
}
ChainConfig::Cardano(_) => Output::error(
"genesis restart parameters are not supported for Cardano chains"
.to_string(),
)
.exit(),
},
None => {
Output::error(format!(
Expand Down
Loading