cross_domain_message_gossip/
aux_schema.rs

1//! Schema for channel update storage.
2
3use parity_scale_codec::{Decode, Encode};
4use sc_client_api::backend::AuxStore;
5use sp_blockchain::{Error as ClientError, Info, Result as ClientResult};
6use sp_core::H256;
7use sp_core::bytes::to_hex;
8use sp_messenger::messages::{ChainId, ChannelId, ChannelState, Nonce};
9use sp_messenger::{ChannelNonce, XdmId};
10use sp_runtime::traits::{Block as BlockT, NumberFor};
11use subspace_runtime_primitives::BlockNumber;
12
13const CHANNEL_DETAIL: &[u8] = b"channel_detail";
14
15fn channel_detail_key(
16    src_chain_id: ChainId,
17    self_chain_id: ChainId,
18    channel_id: ChannelId,
19) -> Vec<u8> {
20    (CHANNEL_DETAIL, src_chain_id, self_chain_id, channel_id).encode()
21}
22
23fn load_decode<Backend: AuxStore, T: Decode>(
24    backend: &Backend,
25    key: &[u8],
26) -> ClientResult<Option<T>> {
27    match backend.get_aux(key)? {
28        None => Ok(None),
29        Some(t) => T::decode(&mut &t[..])
30            .map_err(|e| {
31                ClientError::Backend(format!("Relayer DB is corrupted. Decode error: {e}"))
32            })
33            .map(Some),
34    }
35}
36
37/// Channel detail between src and dst chain.
38#[derive(Debug, Encode, Decode, Clone)]
39pub struct ChannelDetail {
40    // Block number of chain at which the channel state is verified.
41    pub block_number: BlockNumber,
42    /// Block hash of the chain at which the channel state is verified.
43    pub block_hash: H256,
44    /// State root of the chain at the above block height.
45    pub state_root: H256,
46    /// Channel identifier.
47    pub channel_id: ChannelId,
48    /// State of the channel.
49    pub state: ChannelState,
50    /// Next inbox nonce.
51    pub next_inbox_nonce: Nonce,
52    /// Next outbox nonce.
53    pub next_outbox_nonce: Nonce,
54    /// Latest outbox message nonce for which response was received from dst_chain.
55    pub latest_response_received_message_nonce: Option<Nonce>,
56}
57
58/// Load the channel state of self_chain_id on src_chain_id.
59pub fn get_channel_state<Backend>(
60    backend: &Backend,
61    src_chain_id: ChainId,
62    self_chain_id: ChainId,
63    channel_id: ChannelId,
64) -> ClientResult<Option<ChannelDetail>>
65where
66    Backend: AuxStore,
67{
68    load_decode(
69        backend,
70        channel_detail_key(src_chain_id, self_chain_id, channel_id).as_slice(),
71    )
72}
73
74/// Set the channel state of self_chain_id on src_chain_id.
75pub fn set_channel_state<Backend>(
76    backend: &Backend,
77    src_chain_id: ChainId,
78    self_chain_id: ChainId,
79    channel_detail: ChannelDetail,
80) -> ClientResult<()>
81where
82    Backend: AuxStore,
83{
84    backend.insert_aux(
85        &[(
86            channel_detail_key(src_chain_id, self_chain_id, channel_detail.channel_id).as_slice(),
87            channel_detail.encode().as_slice(),
88        )],
89        vec![],
90    )
91}
92
93mod xdm_keys {
94    use parity_scale_codec::Encode;
95    use sp_domains::{ChainId, ChannelId};
96    use sp_messenger::XdmId;
97    use sp_messenger::messages::MessageKey;
98
99    const XDM: &[u8] = b"xdm";
100    const XDM_RELAY: &[u8] = b"relay_msg";
101    const XDM_RELAY_RESPONSE: &[u8] = b"relay_msg_response";
102    const XDM_LAST_CLEANUP_NONCE: &[u8] = b"xdm_last_cleanup_nonce";
103
104    pub(super) fn get_key_for_xdm_id(prefix: &[u8], xdm_id: XdmId) -> Vec<u8> {
105        match xdm_id {
106            XdmId::RelayMessage(id) => get_key_for_xdm_relay(prefix, id),
107            XdmId::RelayResponseMessage(id) => get_key_for_xdm_relay_response(prefix, id),
108        }
109    }
110
111    pub(super) fn get_key_for_last_cleanup_relay_nonce(
112        prefix: &[u8],
113        chain_id: ChainId,
114        channel_id: ChannelId,
115    ) -> Vec<u8> {
116        (
117            prefix,
118            XDM,
119            XDM_RELAY,
120            XDM_LAST_CLEANUP_NONCE,
121            chain_id,
122            channel_id,
123        )
124            .encode()
125    }
126
127    pub(super) fn get_key_for_last_cleanup_relay_response_nonce(
128        prefix: &[u8],
129        chain_id: ChainId,
130        channel_id: ChannelId,
131    ) -> Vec<u8> {
132        (
133            prefix,
134            XDM,
135            XDM_RELAY_RESPONSE,
136            XDM_LAST_CLEANUP_NONCE,
137            chain_id,
138            channel_id,
139        )
140            .encode()
141    }
142
143    pub(super) fn get_key_for_xdm_relay(prefix: &[u8], id: MessageKey) -> Vec<u8> {
144        (prefix, XDM, XDM_RELAY, id).encode()
145    }
146
147    pub(super) fn get_key_for_xdm_relay_response(prefix: &[u8], id: MessageKey) -> Vec<u8> {
148        (prefix, XDM, XDM_RELAY_RESPONSE, id).encode()
149    }
150}
151
152#[derive(Debug, Encode, Decode, Clone)]
153pub struct BlockId<Block: BlockT> {
154    pub number: NumberFor<Block>,
155    pub hash: Block::Hash,
156}
157
158impl<Block: BlockT> From<Info<Block>> for BlockId<Block> {
159    fn from(value: Info<Block>) -> Self {
160        BlockId {
161            number: value.best_number,
162            hash: value.best_hash,
163        }
164    }
165}
166
167/// Store the given XDM ID as processed at given block.
168pub fn set_xdm_message_processed_at<Backend, Block>(
169    backend: &Backend,
170    prefix: &[u8],
171    xdm_id: XdmId,
172    block_id: BlockId<Block>,
173) -> ClientResult<()>
174where
175    Backend: AuxStore,
176    Block: BlockT,
177{
178    let key = xdm_keys::get_key_for_xdm_id(prefix, xdm_id);
179    backend.insert_aux(&[(key.as_slice(), block_id.encode().as_slice())], vec![])
180}
181
182/// Returns the maybe last processed block number for given xdm.
183pub fn get_xdm_processed_block_number<Backend, Block>(
184    backend: &Backend,
185    prefix: &[u8],
186    xdm_id: XdmId,
187) -> ClientResult<Option<BlockId<Block>>>
188where
189    Backend: AuxStore,
190    Block: BlockT,
191{
192    load_decode(
193        backend,
194        xdm_keys::get_key_for_xdm_id(prefix, xdm_id).as_slice(),
195    )
196}
197
198/// Cleans up all the xdm storages until the latest nonces.
199pub fn cleanup_chain_channel_storages<Backend>(
200    backend: &Backend,
201    prefix: &[u8],
202    chain_id: ChainId,
203    channel_id: ChannelId,
204    channel_nonce: ChannelNonce,
205) -> ClientResult<()>
206where
207    Backend: AuxStore,
208{
209    let mut to_insert = vec![];
210    let mut to_delete = vec![];
211    if let Some(latest_relay_nonce) = channel_nonce.relay_msg_nonce {
212        let last_cleanup_relay_nonce_key =
213            xdm_keys::get_key_for_last_cleanup_relay_nonce(prefix, chain_id, channel_id);
214        let last_cleaned_up_nonce =
215            load_decode::<_, Nonce>(backend, last_cleanup_relay_nonce_key.as_slice())?;
216
217        let mut from_nonce = match last_cleaned_up_nonce {
218            None => Nonce::zero(),
219            Some(last_nonce) => last_nonce.saturating_add(Nonce::one()),
220        };
221
222        tracing::debug!(
223            "[{:?}]Cleaning Relay xdm keys for {:?} channel: {:?} from: {:?} to: {:?}",
224            to_hex(prefix, false),
225            chain_id,
226            channel_id,
227            from_nonce,
228            latest_relay_nonce
229        );
230
231        while from_nonce <= latest_relay_nonce {
232            to_delete.push(xdm_keys::get_key_for_xdm_relay(
233                prefix,
234                (chain_id, channel_id, from_nonce),
235            ));
236            from_nonce = from_nonce.saturating_add(Nonce::one());
237        }
238
239        to_insert.push((last_cleanup_relay_nonce_key, latest_relay_nonce.encode()));
240    }
241
242    if let Some(latest_relay_response_nonce) = channel_nonce.relay_response_msg_nonce {
243        let last_cleanup_relay_response_nonce_key =
244            xdm_keys::get_key_for_last_cleanup_relay_response_nonce(prefix, chain_id, channel_id);
245        let last_cleaned_up_nonce =
246            load_decode::<_, Nonce>(backend, last_cleanup_relay_response_nonce_key.as_slice())?;
247
248        let mut from_nonce = match last_cleaned_up_nonce {
249            None => Nonce::zero(),
250            Some(last_nonce) => last_nonce.saturating_add(Nonce::one()),
251        };
252
253        tracing::debug!(
254            "[{:?}]Cleaning Relay response xdm keys for {:?} channel: {:?} from: {:?} to: {:?}",
255            to_hex(prefix, false),
256            chain_id,
257            channel_id,
258            from_nonce,
259            latest_relay_response_nonce
260        );
261
262        while from_nonce <= latest_relay_response_nonce {
263            to_delete.push(xdm_keys::get_key_for_xdm_relay_response(
264                prefix,
265                (chain_id, channel_id, from_nonce),
266            ));
267            from_nonce = from_nonce.saturating_add(Nonce::one());
268        }
269
270        to_insert.push((
271            last_cleanup_relay_response_nonce_key,
272            latest_relay_response_nonce.encode(),
273        ));
274    }
275
276    backend.insert_aux(
277        &to_insert
278            .iter()
279            .map(|(k, v)| (k.as_slice(), v.as_slice()))
280            .collect::<Vec<_>>(),
281        &to_delete.iter().map(|k| k.as_slice()).collect::<Vec<_>>(),
282    )
283}