celestia_types/state/
address.rs

1use std::fmt::Display;
2use std::str::FromStr;
3
4use bech32::Hrp;
5use enum_dispatch::enum_dispatch;
6use serde::{Deserialize, Serialize};
7#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))]
8use wasm_bindgen::prelude::*;
9
10use crate::consts::appconsts;
11use crate::consts::cosmos::*;
12use crate::{Error, Result};
13
14pub use k256::ecdsa::VerifyingKey;
15pub use tendermint::account::Id;
16
17/// A generic representation of an address in Celestia network.
18#[enum_dispatch(Address)]
19pub trait AddressTrait: FromStr + Display + private::Sealed {
20    /// Get a reference to the account's ID.
21    fn id_ref(&self) -> &Id;
22    /// Get the kind of address.
23    fn kind(&self) -> AddressKind;
24
25    /// Get the account's ID.
26    #[inline]
27    fn id(&self) -> Id {
28        *self.id_ref()
29    }
30
31    /// Convert the address to a byte slice.
32    #[inline]
33    fn as_bytes(&self) -> &[u8] {
34        self.id_ref().as_bytes()
35    }
36
37    /// Get a `bech32` human readable prefix of the account kind.
38    #[inline]
39    fn prefix(&self) -> &'static str {
40        self.kind().prefix()
41    }
42}
43
44mod private {
45    pub trait Sealed {}
46}
47
48/// Different kinds of addresses supported by Celestia.
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum AddressKind {
51    /// Account address kind.
52    Account,
53    /// Validator address kind.
54    Validator,
55    /// Consensus address kind.
56    Consensus,
57}
58
59/// A Celestia address. Either account, consensus or validator.
60#[enum_dispatch]
61#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
62#[serde(try_from = "Raw", into = "Raw")]
63pub enum Address {
64    /// Account address.
65    AccAddress,
66    /// Validator address.
67    ValAddress,
68    /// Consensus address.
69    ConsAddress,
70}
71
72impl Address {
73    /// Create a account address for the provided account public key
74    pub fn from_account_veryfing_key(key: VerifyingKey) -> Self {
75        Address::AccAddress(key.into())
76    }
77
78    /// Create a validator address for the provided validator public key
79    pub fn from_validator_veryfing_key(key: VerifyingKey) -> Self {
80        Address::ValAddress(key.into())
81    }
82
83    /// Create a consensus address for the provided consensus public key
84    pub fn from_consensus_veryfing_key(key: VerifyingKey) -> Self {
85        Address::ConsAddress(key.into())
86    }
87}
88
89/// Address of an account.
90#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
91#[serde(try_from = "Raw", into = "Raw")]
92#[cfg_attr(
93    all(feature = "wasm-bindgen", target_arch = "wasm32"),
94    wasm_bindgen(inspectable)
95)]
96#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
97pub struct AccAddress {
98    id: Id,
99}
100
101/// Address of a validator.
102#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
103#[serde(try_from = "Raw", into = "Raw")]
104#[cfg_attr(
105    all(feature = "wasm-bindgen", target_arch = "wasm32"),
106    wasm_bindgen(inspectable)
107)]
108#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
109pub struct ValAddress {
110    id: Id,
111}
112
113/// Address of a consensus node.
114#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
115#[serde(try_from = "Raw", into = "Raw")]
116#[cfg_attr(
117    all(feature = "wasm-bindgen", target_arch = "wasm32"),
118    wasm_bindgen(inspectable)
119)]
120#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
121pub struct ConsAddress {
122    id: Id,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
126#[serde(transparent)]
127struct Raw {
128    #[serde(with = "tendermint_proto::serializers::from_str")]
129    addr: String,
130}
131
132impl AddressKind {
133    /// Get the `bech32` human readable prefix.
134    pub fn prefix(&self) -> &'static str {
135        match self {
136            AddressKind::Account => BECH32_PREFIX_ACC_ADDR,
137            AddressKind::Validator => BECH32_PREFIX_VAL_ADDR,
138            AddressKind::Consensus => BECH32_PREFIX_CONS_ADDR,
139        }
140    }
141}
142
143impl FromStr for AddressKind {
144    type Err = Error;
145
146    fn from_str(s: &str) -> Result<Self, Self::Err> {
147        match s {
148            BECH32_PREFIX_ACC_ADDR => Ok(AddressKind::Account),
149            BECH32_PREFIX_VAL_ADDR => Ok(AddressKind::Validator),
150            BECH32_PREFIX_CONS_ADDR => Ok(AddressKind::Consensus),
151            _ => Err(Error::InvalidAddressPrefix(s.to_owned())),
152        }
153    }
154}
155
156impl private::Sealed for Address {}
157
158impl Display for Address {
159    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160        match self {
161            Address::AccAddress(v) => <AccAddress as Display>::fmt(v, f),
162            Address::ValAddress(v) => <ValAddress as Display>::fmt(v, f),
163            Address::ConsAddress(v) => <ConsAddress as Display>::fmt(v, f),
164        }
165    }
166}
167
168impl FromStr for Address {
169    type Err = Error;
170
171    fn from_str(s: &str) -> Result<Self, Self::Err> {
172        let (kind, id) = string_to_kind_and_id(s)?;
173
174        match kind {
175            AddressKind::Account => Ok(AccAddress::new(id).into()),
176            AddressKind::Validator => Ok(ValAddress::new(id).into()),
177            AddressKind::Consensus => Ok(ConsAddress::new(id).into()),
178        }
179    }
180}
181
182impl TryFrom<Raw> for Address {
183    type Error = Error;
184
185    fn try_from(value: Raw) -> Result<Self, Self::Error> {
186        value.addr.parse()
187    }
188}
189
190impl From<Address> for Raw {
191    fn from(value: Address) -> Self {
192        let addr = value.to_string();
193        Raw { addr }
194    }
195}
196
197macro_rules! impl_address_type {
198    ($name:ident, $kind:ident) => {
199        impl $name {
200            /// Create a new address with given ID.
201            pub fn new(id: Id) -> Self {
202                $name { id }
203            }
204        }
205
206        impl AddressTrait for $name {
207            #[inline]
208            fn id_ref(&self) -> &Id {
209                &self.id
210            }
211
212            #[inline]
213            fn kind(&self) -> AddressKind {
214                AddressKind::$kind
215            }
216        }
217
218        impl private::Sealed for $name {}
219
220        impl Display for $name {
221            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222                let s = address_to_string(self);
223                f.write_str(&s)
224            }
225        }
226
227        impl FromStr for $name {
228            type Err = Error;
229
230            fn from_str(s: &str) -> Result<Self, Self::Err> {
231                let (kind, id) = string_to_kind_and_id(s)?;
232
233                match kind {
234                    AddressKind::$kind => Ok($name::new(id)),
235                    _ => Err(Error::InvalidAddressPrefix(kind.prefix().to_owned())),
236                }
237            }
238        }
239
240        impl TryFrom<Raw> for $name {
241            type Error = Error;
242
243            fn try_from(value: Raw) -> Result<Self, Self::Error> {
244                value.addr.parse()
245            }
246        }
247
248        impl From<$name> for Raw {
249            fn from(value: $name) -> Self {
250                let addr = value.to_string();
251                Raw { addr }
252            }
253        }
254
255        impl From<[u8; appconsts::SIGNER_SIZE]> for $name {
256            fn from(value: [u8; appconsts::SIGNER_SIZE]) -> Self {
257                Self::new(Id::new(value))
258            }
259        }
260
261        impl From<VerifyingKey> for $name {
262            fn from(value: VerifyingKey) -> Self {
263                Self::new(Id::from(value))
264            }
265        }
266
267        impl From<&VerifyingKey> for $name {
268            fn from(value: &VerifyingKey) -> Self {
269                value.to_owned().into()
270            }
271        }
272
273        impl TryFrom<&[u8]> for $name {
274            type Error = Error;
275
276            fn try_from(value: &[u8]) -> Result<Self> {
277                let id = value
278                    .try_into()
279                    .map_err(|_| Error::InvalidAddressSize(value.len()))?;
280                Ok(Self::new(Id::new(id)))
281            }
282        }
283
284        impl TryFrom<Vec<u8>> for $name {
285            type Error = Error;
286
287            fn try_from(value: Vec<u8>) -> Result<Self> {
288                let len = value.len();
289                let id = value
290                    .try_into()
291                    .map_err(|_| Error::InvalidAddressSize(len))?;
292                Ok(Self::new(id))
293            }
294        }
295    };
296}
297
298impl_address_type!(AccAddress, Account);
299impl_address_type!(ValAddress, Validator);
300impl_address_type!(ConsAddress, Consensus);
301
302fn address_to_string(addr: &impl AddressTrait) -> String {
303    // We have full control of address length and prefix, so we know the following will not fail
304    let hrp = Hrp::parse(addr.prefix()).expect("Invalid prefix");
305    bech32::encode::<bech32::Bech32>(hrp, addr.as_bytes()).expect("Invalid address length")
306}
307
308fn string_to_kind_and_id(s: &str) -> Result<(AddressKind, Id)> {
309    let (hrp, data) = bech32::decode(s).map_err(|_| Error::InvalidAddress(s.to_owned()))?;
310
311    let kind = hrp.as_str().parse()?;
312    let bytes = data[..]
313        .try_into()
314        .map_err(|_| Error::InvalidAddressSize(data.len()))?;
315
316    Ok((kind, Id::new(bytes)))
317}
318
319/// uniffi conversion types
320#[cfg(feature = "uniffi")]
321pub(crate) mod uniffi_types {
322    use super::{Address, Id};
323    use uniffi::Record;
324
325    use crate::error::UniffiConversionError;
326
327    uniffi::custom_type!(Address, String, {
328        remote,
329        try_lift: |address| Ok(address.parse()?),
330        lower: |address| format!("{address}")
331    });
332
333    /// Account ID
334    #[derive(Record, Clone)]
335    pub struct AccountId {
336        /// id value
337        pub id: Vec<u8>,
338    }
339
340    impl From<Id> for AccountId {
341        fn from(value: Id) -> Self {
342            AccountId {
343                id: value.as_ref().to_vec(),
344            }
345        }
346    }
347
348    impl TryFrom<AccountId> for Id {
349        type Error = UniffiConversionError;
350
351        fn try_from(value: AccountId) -> std::result::Result<Self, Self::Error> {
352            Id::try_from(value.id).map_err(|_| UniffiConversionError::InvalidAccountIdLength)
353        }
354    }
355
356    uniffi::custom_type!(Id, AccountId, {
357        remote,
358        try_lift: |value| Ok(value.try_into()?),
359        lower: |value| value.into()
360    });
361}
362
363#[cfg(test)]
364mod tests {
365    use super::*;
366
367    #[cfg(target_arch = "wasm32")]
368    use wasm_bindgen_test::wasm_bindgen_test as test;
369
370    const ADDR1: [u8; 20] = [
371        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
372    ];
373    const ADDR1_ACC_STR: &str = "celestia1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5wgawu3";
374    const ADDR1_VAL_STR: &str = "celestiavaloper1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5thlh2h";
375    const ADDR1_CONS_STR: &str = "celestiavalcons1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5lyvtxk";
376
377    #[test]
378    fn parse_acc_addr() {
379        let addr: Address = ADDR1_ACC_STR.parse().unwrap();
380        assert_eq!(addr.kind(), AddressKind::Account);
381        assert_eq!(addr.as_bytes(), ADDR1);
382        assert_eq!(&addr.to_string(), ADDR1_ACC_STR);
383
384        let addr: AccAddress = ADDR1_ACC_STR.parse().unwrap();
385        assert_eq!(addr.as_bytes(), ADDR1);
386        assert_eq!(&addr.to_string(), ADDR1_ACC_STR);
387
388        ADDR1_ACC_STR.parse::<ValAddress>().unwrap_err();
389        ADDR1_ACC_STR.parse::<ConsAddress>().unwrap_err();
390    }
391
392    #[test]
393    fn serde_acc_addr() {
394        let addr_json = format!("\"{ADDR1_ACC_STR}\"");
395
396        let addr: Address = serde_json::from_str(&addr_json).unwrap();
397        assert_eq!(addr.kind(), AddressKind::Account);
398        assert_eq!(addr.as_bytes(), ADDR1);
399
400        let addr: AccAddress = serde_json::from_str(&addr_json).unwrap();
401        assert_eq!(addr.as_bytes(), ADDR1);
402
403        serde_json::from_str::<ValAddress>(&addr_json).unwrap_err();
404        serde_json::from_str::<ConsAddress>(&addr_json).unwrap_err();
405    }
406
407    #[test]
408    fn parse_val_addr() {
409        let addr: Address = ADDR1_VAL_STR.parse().unwrap();
410        assert_eq!(addr.kind(), AddressKind::Validator);
411        assert_eq!(addr.as_bytes(), ADDR1);
412        assert_eq!(&addr.to_string(), ADDR1_VAL_STR);
413
414        let addr: ValAddress = ADDR1_VAL_STR.parse().unwrap();
415        assert_eq!(addr.as_bytes(), ADDR1);
416        assert_eq!(&addr.to_string(), ADDR1_VAL_STR);
417
418        ADDR1_VAL_STR.parse::<AccAddress>().unwrap_err();
419        ADDR1_VAL_STR.parse::<ConsAddress>().unwrap_err();
420    }
421
422    #[test]
423    fn serde_val_addr() {
424        let addr_json = format!("\"{ADDR1_VAL_STR}\"");
425
426        let addr: Address = serde_json::from_str(&addr_json).unwrap();
427        assert_eq!(addr.kind(), AddressKind::Validator);
428        assert_eq!(addr.as_bytes(), ADDR1);
429
430        let addr: ValAddress = serde_json::from_str(&addr_json).unwrap();
431        assert_eq!(addr.as_bytes(), ADDR1);
432
433        serde_json::from_str::<AccAddress>(&addr_json).unwrap_err();
434        serde_json::from_str::<ConsAddress>(&addr_json).unwrap_err();
435    }
436
437    #[test]
438    fn parse_cons_addr() {
439        let addr: Address = ADDR1_CONS_STR.parse().unwrap();
440        assert_eq!(addr.kind(), AddressKind::Consensus);
441        assert_eq!(addr.as_bytes(), ADDR1);
442        assert_eq!(&addr.to_string(), ADDR1_CONS_STR);
443
444        let addr: ConsAddress = ADDR1_CONS_STR.parse().unwrap();
445        assert_eq!(addr.as_bytes(), ADDR1);
446        assert_eq!(&addr.to_string(), ADDR1_CONS_STR);
447
448        ADDR1_CONS_STR.parse::<AccAddress>().unwrap_err();
449        ADDR1_CONS_STR.parse::<ValAddress>().unwrap_err();
450    }
451
452    #[test]
453    fn serde_cons_addr() {
454        let addr_json = format!("\"{ADDR1_CONS_STR}\"");
455
456        let addr: Address = serde_json::from_str(&addr_json).unwrap();
457        assert_eq!(addr.kind(), AddressKind::Consensus);
458        assert_eq!(addr.as_bytes(), ADDR1);
459
460        let addr: ConsAddress = serde_json::from_str(&addr_json).unwrap();
461        assert_eq!(addr.as_bytes(), ADDR1);
462
463        serde_json::from_str::<AccAddress>(&addr_json).unwrap_err();
464        serde_json::from_str::<ValAddress>(&addr_json).unwrap_err();
465    }
466
467    #[test]
468    fn parse_invalid_addr() {
469        // Account address of 1 byte
470        let addr = "celestia1qyu009tf";
471        addr.parse::<Address>().unwrap_err();
472        addr.parse::<AccAddress>().unwrap_err();
473        addr.parse::<ValAddress>().unwrap_err();
474        addr.parse::<ConsAddress>().unwrap_err();
475
476        // Validator address of 1 byte
477        let addr = "celestiavaloper1qy2jc8nq";
478        addr.parse::<Address>().unwrap_err();
479        addr.parse::<AccAddress>().unwrap_err();
480        addr.parse::<ValAddress>().unwrap_err();
481        addr.parse::<ConsAddress>().unwrap_err();
482
483        // Consensus address of 1 byte
484        let addr = "celestiavalcons1qy2zlull";
485        addr.parse::<Address>().unwrap_err();
486        addr.parse::<AccAddress>().unwrap_err();
487        addr.parse::<ValAddress>().unwrap_err();
488        addr.parse::<ConsAddress>().unwrap_err();
489
490        // Unknown prefix
491        let addr = "foobar1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5avgsnn";
492        addr.parse::<Address>().unwrap_err();
493        addr.parse::<AccAddress>().unwrap_err();
494        addr.parse::<ValAddress>().unwrap_err();
495        addr.parse::<ConsAddress>().unwrap_err();
496
497        // Malformed string
498        let addr = "asdsdfsdgsfd";
499        addr.parse::<Address>().unwrap_err();
500        addr.parse::<AccAddress>().unwrap_err();
501        addr.parse::<ValAddress>().unwrap_err();
502        addr.parse::<ConsAddress>().unwrap_err();
503    }
504
505    #[test]
506    fn convert() {
507        let addr: Address = ADDR1_ACC_STR.parse().unwrap();
508        let acc_addr: AccAddress = addr.try_into().unwrap();
509        let _addr: Address = acc_addr.into();
510
511        let addr: Address = ADDR1_VAL_STR.parse().unwrap();
512        let val_addr: ValAddress = addr.try_into().unwrap();
513        let _addr: Address = val_addr.into();
514
515        let addr: Address = ADDR1_CONS_STR.parse().unwrap();
516        let cons_addr: ConsAddress = addr.try_into().unwrap();
517        let _addr: Address = cons_addr.into();
518    }
519}