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