penumbra_sdk_stake/
identity_key.rs

1use penumbra_sdk_proto::{
2    core::component::stake::v1::CurrentValidatorRateRequest,
3    // TODO: why is this not in the keys crate?
4    core::keys::v1 as pb,
5    serializers::bech32str::{self, validator_identity_key::BECH32_PREFIX},
6    DomainType,
7};
8use serde::{Deserialize, Serialize};
9
10use decaf377_rdsa::{SpendAuth, VerificationKeyBytes};
11
12/// The root of a validator's identity.
13///
14/// This key is a [`SpendAuth`] [`VerificationKey`]; currently, the wallet
15/// software reuses an account's spend authorization key as the validator
16/// identity, but there is no real requirement that it must be generated that
17/// way.
18///
19/// Using a [`SpendAuth`] key means that validators can reuse code and processes
20/// designed for custodying funds to protect their identity.
21#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
22#[serde(try_from = "pb::IdentityKey", into = "pb::IdentityKey")]
23pub struct IdentityKey(pub VerificationKeyBytes<SpendAuth>);
24
25impl IdentityKey {
26    pub fn to_bytes(&self) -> [u8; 32] {
27        self.0.into()
28    }
29}
30
31// IMPORTANT: Changing this implementation is state-breaking.
32impl std::str::FromStr for IdentityKey {
33    type Err = anyhow::Error;
34    fn from_str(s: &str) -> Result<Self, Self::Err> {
35        pb::IdentityKey {
36            ik: bech32str::decode(s, BECH32_PREFIX, bech32str::Bech32m)?,
37        }
38        .try_into()
39    }
40}
41
42// IMPORTANT: Changing this implementation is state-breaking.
43impl std::fmt::Display for IdentityKey {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        f.write_str(&bech32str::encode(
46            self.0.as_ref(),
47            BECH32_PREFIX,
48            bech32str::Bech32m,
49        ))
50    }
51}
52
53impl std::fmt::Debug for IdentityKey {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        <IdentityKey as std::fmt::Display>::fmt(self, f)
56    }
57}
58
59impl DomainType for IdentityKey {
60    type Proto = pb::IdentityKey;
61}
62
63impl From<IdentityKey> for pb::IdentityKey {
64    fn from(ik: IdentityKey) -> Self {
65        pb::IdentityKey {
66            ik: ik.0.as_ref().to_vec(),
67        }
68    }
69}
70
71impl TryFrom<pb::IdentityKey> for IdentityKey {
72    type Error = anyhow::Error;
73    fn try_from(ik: pb::IdentityKey) -> Result<Self, Self::Error> {
74        Ok(Self(ik.ik.as_slice().try_into()?))
75    }
76}
77
78impl From<IdentityKey> for CurrentValidatorRateRequest {
79    fn from(k: IdentityKey) -> Self {
80        CurrentValidatorRateRequest {
81            identity_key: Some(k.into()),
82        }
83    }
84}
85
86impl TryFrom<CurrentValidatorRateRequest> for IdentityKey {
87    type Error = anyhow::Error;
88    fn try_from(value: CurrentValidatorRateRequest) -> Result<Self, Self::Error> {
89        value
90            .identity_key
91            .ok_or_else(|| anyhow::anyhow!("empty CurrentValidatorRateRequest message"))?
92            .try_into()
93    }
94}