penumbra_sdk_stake/undelegate_claim/
action.rs

1use penumbra_sdk_asset::balance;
2use penumbra_sdk_proto::{penumbra::core::component::stake::v1 as pb, DomainType};
3use penumbra_sdk_txhash::{EffectHash, EffectingData};
4use serde::{Deserialize, Serialize};
5
6use crate::{IdentityKey, Penalty, UndelegateClaimProof};
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(try_from = "pb::UndelegateClaimBody", into = "pb::UndelegateClaimBody")]
10pub struct UndelegateClaimBody {
11    /// The identity key of the validator to undelegate from.
12    pub validator_identity: IdentityKey,
13    /// The penalty applied to undelegation, in bps^2.
14    pub penalty: Penalty,
15    /// The action's contribution to the transaction's value balance.
16    pub balance_commitment: balance::Commitment,
17    /// The height at which unbonding started.
18    pub unbonding_start_height: u64,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
22#[serde(try_from = "pb::UndelegateClaim", into = "pb::UndelegateClaim")]
23pub struct UndelegateClaim {
24    pub body: UndelegateClaimBody,
25    pub proof: UndelegateClaimProof,
26}
27
28impl EffectingData for UndelegateClaimBody {
29    fn effect_hash(&self) -> EffectHash {
30        EffectHash::from_proto_effecting_data(&self.to_proto())
31    }
32}
33impl EffectingData for UndelegateClaim {
34    fn effect_hash(&self) -> EffectHash {
35        // The effecting data is in the body of the undelegate claim, so we can
36        // just use hash the proto-encoding of the body.
37        self.body.effect_hash()
38    }
39}
40
41impl DomainType for UndelegateClaimBody {
42    type Proto = pb::UndelegateClaimBody;
43}
44
45impl From<UndelegateClaimBody> for pb::UndelegateClaimBody {
46    #[allow(deprecated)]
47    fn from(d: UndelegateClaimBody) -> Self {
48        pb::UndelegateClaimBody {
49            validator_identity: Some(d.validator_identity.into()),
50            start_epoch_index: 0,
51            penalty: Some(d.penalty.into()),
52            balance_commitment: Some(d.balance_commitment.into()),
53            unbonding_start_height: d.unbonding_start_height,
54        }
55    }
56}
57
58impl TryFrom<pb::UndelegateClaimBody> for UndelegateClaimBody {
59    type Error = anyhow::Error;
60    fn try_from(d: pb::UndelegateClaimBody) -> Result<Self, Self::Error> {
61        Ok(Self {
62            validator_identity: d
63                .validator_identity
64                .ok_or_else(|| anyhow::anyhow!("missing validator identity"))?
65                .try_into()?,
66            penalty: d
67                .penalty
68                .ok_or_else(|| anyhow::anyhow!("missing penalty"))?
69                .try_into()?,
70            balance_commitment: d
71                .balance_commitment
72                .ok_or_else(|| anyhow::anyhow!("missing balance_commitment"))?
73                .try_into()?,
74            unbonding_start_height: d.unbonding_start_height,
75        })
76    }
77}
78
79impl DomainType for UndelegateClaim {
80    type Proto = pb::UndelegateClaim;
81}
82
83impl From<UndelegateClaim> for pb::UndelegateClaim {
84    fn from(d: UndelegateClaim) -> Self {
85        pb::UndelegateClaim {
86            body: Some(d.body.into()),
87            proof: d.proof.encode_to_vec(),
88        }
89    }
90}
91
92impl TryFrom<pb::UndelegateClaim> for UndelegateClaim {
93    type Error = anyhow::Error;
94    fn try_from(d: pb::UndelegateClaim) -> Result<Self, Self::Error> {
95        Ok(Self {
96            body: d
97                .body
98                .ok_or_else(|| anyhow::anyhow!("missing body"))?
99                .try_into()?,
100            proof: UndelegateClaimProof::decode(d.proof.as_slice())?,
101        })
102    }
103}