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