tg_bindings/
validator.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use std::cmp::Ordering;
4use std::convert::TryFrom;
5use std::convert::TryInto;
6
7use sha2::{Digest, Sha256};
8
9use cosmwasm_std::Binary;
10
11/// This is returned by most queries from Tendermint
12/// See https://github.com/tendermint/tendermint/blob/v0.34.8/proto/tendermint/abci/types.proto#L336-L340
13#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)]
14pub struct Validator {
15    // The first 20 bytes of SHA256(public key)
16    pub address: Binary,
17    pub power: u64,
18}
19
20/// This is used to update the validator set
21/// See https://github.com/tendermint/tendermint/blob/v0.34.8/proto/tendermint/abci/types.proto#L343-L346
22#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)]
23pub struct ValidatorUpdate {
24    /// This is the pubkey used in Tendermint consensus
25    pub pubkey: Pubkey,
26    /// This is the voting power in the consensus rounds
27    pub power: u64,
28}
29
30/// This is taken from BeginBlock.LastCommitInfo
31/// See https://github.com/tendermint/tendermint/blob/v0.34.8/proto/tendermint/abci/types.proto#L348-L352
32#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)]
33pub struct ValidatorVote {
34    // The first 20 bytes of SHA256(public key)
35    pub address: Binary,
36    pub power: u64,
37    pub voted: bool,
38}
39
40/// A Tendermint validator pubkey.
41///
42/// See https://github.com/tendermint/tendermint/blob/master/proto/tendermint/crypto/keys.proto for
43/// a list of available types. Sr25519 is added here as it is likely to join the party.
44///
45/// This type is optimized for the JSON interface. No data validation on the enum cases is performed.
46/// If you don't trust the data source, you can create a `ValidatedPubkey` enum that mirrors this
47/// type and uses fixed sized data fields.
48#[non_exhaustive]
49#[derive(Serialize, Deserialize, Clone, JsonSchema, Debug, PartialEq, Eq)]
50#[serde(rename_all = "snake_case")]
51pub enum Pubkey {
52    /// 32 bytes Ed25519 pubkey
53    Ed25519(Binary),
54    /// Must use 33 bytes 0x02/0x03 prefixed compressed pubkey format
55    Secp256k1(Binary),
56    /// 32 bytes Sr25519 pubkey
57    Sr25519(Binary),
58}
59
60// TODO: check if we can derive this automatically when Binary implements PartialOrd.
61impl PartialOrd for Pubkey {
62    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
63        Some(self.cmp(other))
64    }
65}
66
67// TODO: check if we can derive this automatically when Binary implements Ord.
68impl Ord for Pubkey {
69    fn cmp(&self, other: &Self) -> Ordering {
70        match (self, other) {
71            // Sort by case name, value
72            (Pubkey::Ed25519(a), Pubkey::Ed25519(b)) => a.0.cmp(&b.0),
73            (Pubkey::Ed25519(_), Pubkey::Secp256k1(_)) => Ordering::Less,
74            (Pubkey::Ed25519(_), Pubkey::Sr25519(_)) => Ordering::Less,
75            (Pubkey::Secp256k1(_), Pubkey::Ed25519(_)) => Ordering::Greater,
76            (Pubkey::Secp256k1(a), Pubkey::Secp256k1(b)) => a.0.cmp(&b.0),
77            (Pubkey::Secp256k1(_), Pubkey::Sr25519(_)) => Ordering::Less,
78            (Pubkey::Sr25519(_), Pubkey::Ed25519(_)) => Ordering::Greater,
79            (Pubkey::Sr25519(_), Pubkey::Secp256k1(_)) => Ordering::Greater,
80            (Pubkey::Sr25519(a), Pubkey::Sr25519(b)) => a.0.cmp(&b.0),
81        }
82    }
83}
84
85/// An Ed25519 public key.
86///
87/// This type is known to have the correct length, which serves as a minimal validation. This
88/// does not mean it is a valid curve point though.
89///
90/// Similar types `struct Secp256k1Pubkey([u8; 33])` and `struct Sr25519Pubkey([u8; 32])`
91/// should be created on demand.
92///
93/// ## Examples
94///
95/// This is generated from `Pubkey` as follows:
96///
97/// ```
98/// # use hex_literal::hex;
99/// use std::convert::TryFrom;
100/// use tg_bindings::{Ed25519Pubkey, Pubkey};
101///
102/// let pubkey = Pubkey::Ed25519(hex!("14253d61ef42d166d02e68d540d07fdf8d65a9af0acaa46302688e788a8521e2").into());
103/// let ed25519_pubkey = Ed25519Pubkey::try_from(pubkey);
104/// assert!(ed25519_pubkey.is_ok());
105///
106/// let pubkey = Pubkey::Secp256k1(hex!("0292a066ec32d37c607519d7a86eb2107013a26b160ce3da732ee76e9b2e502492").into());
107/// let ed25519_pubkey = Ed25519Pubkey::try_from(pubkey);
108/// assert!(ed25519_pubkey.is_err());
109/// ```
110///
111/// When we have an [Ed25519Pubkey] we can derive an address:
112///
113/// ```
114/// # use hex_literal::hex;
115/// use std::convert::TryFrom;
116/// use tg_bindings::{Ed25519Pubkey, Pubkey, ToAddress};
117///
118/// let pubkey = Pubkey::Ed25519(hex!("14253d61ef42d166d02e68d540d07fdf8d65a9af0acaa46302688e788a8521e2").into());
119/// let ed25519_pubkey = Ed25519Pubkey::try_from(pubkey).unwrap();
120/// let address = ed25519_pubkey.to_address();
121/// assert_eq!(address, hex!("0CDA3F47EF3C4906693B170EF650EB968C5F4B2C"));
122/// ```
123#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq, Eq)]
124pub struct Ed25519Pubkey([u8; 32]);
125
126impl Ed25519Pubkey {
127    pub fn to_vec(&self) -> Vec<u8> {
128        self.0.to_vec()
129    }
130
131    /// Returns the base64 encoded raw pubkey data.
132    pub fn to_base64(&self) -> String {
133        base64::encode(self.0)
134    }
135}
136
137impl ToAddress for Ed25519Pubkey {
138    fn to_address(&self) -> [u8; 20] {
139        let hash = Sha256::digest(&self.0);
140        hash[0..20].try_into().unwrap()
141    }
142}
143
144pub trait ToAddress {
145    fn to_address(&self) -> [u8; 20];
146}
147
148impl From<Ed25519Pubkey> for Pubkey {
149    fn from(ed: Ed25519Pubkey) -> Self {
150        Pubkey::Ed25519(ed.0.into())
151    }
152}
153
154#[derive(Debug)]
155pub enum Ed25519PubkeyConversionError {
156    WrongType,
157    InvalidDataLength,
158}
159
160impl<'a> TryFrom<&'a Pubkey> for Ed25519Pubkey {
161    type Error = Ed25519PubkeyConversionError;
162
163    fn try_from(pubkey: &'a Pubkey) -> Result<Self, Self::Error> {
164        match pubkey {
165            Pubkey::Ed25519(data) => {
166                let data: [u8; 32] = data
167                    .as_slice()
168                    .try_into()
169                    .map_err(|_| Ed25519PubkeyConversionError::InvalidDataLength)?;
170                Ok(Ed25519Pubkey(data))
171            }
172            _ => Err(Ed25519PubkeyConversionError::WrongType),
173        }
174    }
175}
176
177impl TryFrom<Pubkey> for Ed25519Pubkey {
178    type Error = Ed25519PubkeyConversionError;
179
180    fn try_from(pubkey: Pubkey) -> Result<Self, Self::Error> {
181        Ed25519Pubkey::try_from(&pubkey)
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188    use hex_literal::hex;
189
190    #[test]
191    fn pubkey_implements_ord() {
192        let ed_a = Pubkey::Ed25519(b"abc".into());
193        let ed_b = Pubkey::Ed25519(b"bcd".into());
194        let se_a = Pubkey::Secp256k1(b"abc".into());
195        let se_b = Pubkey::Secp256k1(b"bcd".into());
196        let sr_a = Pubkey::Sr25519(b"abc".into());
197        let sr_b = Pubkey::Sr25519(b"bcd".into());
198        assert!(ed_a < ed_b);
199        assert!(se_a < se_b);
200        assert!(sr_a < sr_b);
201
202        assert!(ed_a < se_a);
203        assert!(se_a < sr_a);
204        assert!(ed_a < sr_a);
205    }
206
207    #[test]
208    fn ed25519pubkey_address() {
209        // Test values from https://github.com/informalsystems/tendermint-rs/blob/v0.18.1/tendermint/src/account.rs#L153-L192
210
211        // Ed25519
212        let pubkey = Ed25519Pubkey(hex!(
213            "14253D61EF42D166D02E68D540D07FDF8D65A9AF0ACAA46302688E788A8521E2"
214        ));
215        let address = pubkey.to_address();
216        assert_eq!(address, hex!("0CDA3F47EF3C4906693B170EF650EB968C5F4B2C"))
217    }
218}