penumbra_sdk_stake/validator/
bonding.rs

1use penumbra_sdk_proto::{penumbra::core::component::stake::v1 as pb, DomainType};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)]
5#[serde(try_from = "pb::BondingState", into = "pb::BondingState")]
6pub enum State {
7    /// The validator is in the active set.
8    ///
9    /// All stake to the validator is bonded.
10    ///
11    /// Undelegations must wait for an unbonding period to pass before
12    /// their undelegation completes.
13    Bonded,
14    /// The validator is not in the active set.
15    ///
16    /// Delegations to the validator are unbonded and free to be undelegated at
17    /// any time.
18    Unbonded,
19    /// The validator has been removed from the active set.
20    ///
21    /// All delegations to the validator will be unbonded at `unbonds_at_height`.
22    Unbonding { unbonds_at_height: u64 },
23}
24
25impl std::fmt::Display for State {
26    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
27        match self {
28            State::Bonded => write!(f, "Bonded"),
29            State::Unbonded => write!(f, "Unbonded"),
30            State::Unbonding { unbonds_at_height } => {
31                write!(f, "Unbonding (until height: {unbonds_at_height})")
32            }
33        }
34    }
35}
36
37impl DomainType for State {
38    type Proto = pb::BondingState;
39}
40
41impl From<State> for pb::BondingState {
42    #[allow(deprecated)]
43    fn from(v: State) -> Self {
44        pb::BondingState {
45            state: match v {
46                State::Bonded => pb::bonding_state::BondingStateEnum::Bonded as i32,
47                State::Unbonded => pb::bonding_state::BondingStateEnum::Unbonded as i32,
48                State::Unbonding { .. } => pb::bonding_state::BondingStateEnum::Unbonding as i32,
49            },
50            unbonds_at_epoch: 0,
51            unbonds_at_height: match v {
52                State::Unbonding { unbonds_at_height } => unbonds_at_height,
53                _ => 0,
54            },
55        }
56    }
57}
58
59impl TryFrom<pb::BondingState> for State {
60    type Error = anyhow::Error;
61    fn try_from(v: pb::BondingState) -> Result<Self, Self::Error> {
62        let bonding_state = pb::bonding_state::BondingStateEnum::try_from(v.state)
63            .map_err(|e| anyhow::anyhow!("invalid bonding state, error: {e}"))?;
64
65        match bonding_state {
66            pb::bonding_state::BondingStateEnum::Bonded => Ok(State::Bonded),
67            pb::bonding_state::BondingStateEnum::Unbonded => Ok(State::Unbonded),
68            pb::bonding_state::BondingStateEnum::Unbonding => {
69                let unbonds_at_height = if v.unbonds_at_height > 0 {
70                    v.unbonds_at_height
71                } else {
72                    anyhow::bail!("unbonding epoch should be set for unbonding state")
73                };
74                Ok(State::Unbonding { unbonds_at_height })
75            }
76            pb::bonding_state::BondingStateEnum::Unspecified => {
77                Err(anyhow::anyhow!("unspecified bonding state!"))
78            }
79        }
80    }
81}