penumbra_sdk_ibc/component/
ibc_component.rs

1use std::sync::Arc;
2
3use anyhow::Result;
4use cnidarium::StateWrite;
5use ibc_types::{
6    core::client::Height, lightclients::tendermint::ConsensusState as TendermintConsensusState,
7};
8use tendermint::abci;
9use tracing::instrument;
10
11use crate::{
12    component::{client::StateWriteExt as _, client_counter::ClientCounter},
13    genesis, StateWriteExt as _,
14};
15
16use super::HostInterface;
17
18pub struct Ibc {}
19
20// Note: [`Ibc`] does not implement the [`cnidarium_component::Component`] trait
21// this is because we want to have a bound on [`HostInterface`] in the `begin_block`
22// processing.
23impl Ibc {
24    #[instrument(name = "ibc", skip(state, app_state))]
25    pub async fn init_chain<S: StateWrite>(mut state: S, app_state: Option<&genesis::Content>) {
26        match app_state {
27            Some(genesis) => {
28                state.put_ibc_params(genesis.ibc_params.clone());
29                state.put_client_counter(ClientCounter(0))
30            }
31            None => { /* perform upgrade specific check */ }
32        }
33    }
34
35    #[instrument(name = "ibc", skip(state, begin_block))]
36    pub async fn begin_block<HI: HostInterface, S: StateWrite + 'static>(
37        state: &mut Arc<S>,
38        begin_block: &abci::request::BeginBlock,
39    ) {
40        let state = Arc::get_mut(state).expect("state should be unique");
41        // In BeginBlock, we want to save a copy of our consensus state to our
42        // own state tree, so that when we get a message from our
43        // counterparties, we can verify that they are committing the correct
44        // consensus states for us to their state tree.
45        let commitment_root: Vec<u8> = begin_block.header.app_hash.clone().into();
46        let cs = TendermintConsensusState::new(
47            ibc_types::core::commitment::MerkleRoot {
48                hash: commitment_root,
49            },
50            begin_block.header.time,
51            begin_block.header.next_validators_hash,
52        );
53
54        // Currently, we don't use a revision number, because we don't have
55        // any further namespacing of blocks than the block height.
56        let height = Height::new(
57            HI::get_revision_number(&state)
58                .await
59                .expect("must be able to get revision number in begin block"),
60            begin_block.header.height.into(),
61        )
62        .expect("block height cannot be zero");
63
64        state.put_penumbra_sdk_consensus_state(height, cs);
65    }
66
67    #[instrument(name = "ibc", skip(_state, _end_block))]
68    pub async fn end_block<S: StateWrite + 'static>(
69        mut _state: &mut Arc<S>,
70        _end_block: &abci::request::EndBlock,
71    ) {
72    }
73
74    #[instrument(name = "ibc", skip(_state))]
75    pub async fn end_epoch<S: StateWrite + 'static>(mut _state: &mut Arc<S>) -> Result<()> {
76        Ok(())
77    }
78}