ibc_core_client/handler/
recover_client.rs

1//! Protocol logic for processing ICS02 messages of type `MsgRecoverClient`.
2
3use ibc_core_client_context::prelude::*;
4use ibc_core_client_types::error::ClientError;
5use ibc_core_client_types::msgs::MsgRecoverClient;
6use ibc_core_host::types::path::ClientConsensusStatePath;
7use ibc_core_host::{ExecutionContext, ValidationContext};
8
9/// Performs the validation steps associated with the client recovery process. This
10/// includes validating that the parameters of the subject and substitute clients match,
11/// as well as validating that the substitute client *is* active and that the subject
12/// client is *not* active.
13pub fn validate<Ctx>(ctx: &Ctx, msg: MsgRecoverClient) -> Result<(), ClientError>
14where
15    Ctx: ValidationContext,
16{
17    let signer = msg.signer;
18    let subject_client_id = msg.subject_client_id.clone();
19    let substitute_client_id = msg.substitute_client_id.clone();
20
21    ctx.validate_message_signer(&signer)?;
22
23    let client_val_ctx = ctx.get_client_validation_context();
24
25    let subject_client_state = client_val_ctx.client_state(&subject_client_id)?;
26    let substitute_client_state = client_val_ctx.client_state(&substitute_client_id)?;
27
28    let subject_height = subject_client_state.latest_height();
29    let substitute_height = substitute_client_state.latest_height();
30
31    if subject_height >= substitute_height {
32        return Err(ClientError::InvalidClientRecoveryHeights {
33            subject_height,
34            substitute_height,
35        });
36    }
37
38    substitute_client_state
39        .status(ctx.get_client_validation_context(), &substitute_client_id)?
40        .verify_is_active()?;
41
42    // Verify that the subject client is inactive, i.e., that it is either frozen or expired
43    subject_client_state
44        .status(ctx.get_client_validation_context(), &subject_client_id)?
45        .verify_is_inactive()?;
46
47    // Check that the subject client state and substitute client states match, i.e., that
48    // all their respective client state parameters match except for frozen height, latest
49    // height, trusting period, and chain ID
50    subject_client_state.check_substitute(
51        ctx.get_client_validation_context(),
52        substitute_client_state.into(),
53    )?;
54
55    Ok(())
56}
57
58/// Executes the steps needed to recover the subject client, namely:
59///  - setting the subject's status from either `frozen` or `expired` to `active`
60///  - copying the substitute client's consensus state as the subject's consensus state
61///  - setting the subject client's processed height and processed time values to match the substitute client's
62///  - setting the subject client's latest height, trusting period, and chain ID values to match the substitute client's
63pub fn execute<Ctx>(ctx: &mut Ctx, msg: MsgRecoverClient) -> Result<(), ClientError>
64where
65    Ctx: ExecutionContext,
66{
67    let subject_client_id = msg.subject_client_id.clone();
68    let substitute_client_id = msg.substitute_client_id.clone();
69
70    let client_exec_ctx = ctx.get_client_execution_context();
71
72    let subject_client_state = client_exec_ctx.client_state(&subject_client_id)?;
73    let substitute_client_state = client_exec_ctx.client_state(&substitute_client_id)?;
74    let substitute_consensus_state =
75        client_exec_ctx.consensus_state(&ClientConsensusStatePath::new(
76            substitute_client_id.clone(),
77            substitute_client_state.latest_height().revision_number(),
78            substitute_client_state.latest_height().revision_height(),
79        ))?;
80
81    subject_client_state.update_on_recovery(
82        ctx.get_client_execution_context(),
83        &subject_client_id,
84        substitute_client_state.into(),
85        substitute_consensus_state.into(),
86    )?;
87
88    Ok(())
89}