oasis_contract_sdk_types/
address.rs1use std::convert::TryFrom;
3
4use bech32::{Bech32, Hrp};
5use thiserror::Error;
6
7const ADDRESS_VERSION_SIZE: usize = 1;
8const ADDRESS_DATA_SIZE: usize = 20;
9const ADDRESS_SIZE: usize = ADDRESS_VERSION_SIZE + ADDRESS_DATA_SIZE;
10
11const ADDRESS_BECH32_HRP: Hrp = Hrp::parse_unchecked("oasis");
12
13#[derive(Error, Debug)]
15pub enum Error {
16 #[error("malformed address")]
17 MalformedAddress,
18}
19
20#[derive(
22 Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, cbor::Encode, cbor::Decode,
23)]
24#[cbor(transparent)]
25pub struct Address([u8; ADDRESS_SIZE]);
26
27impl Address {
28 pub const SIZE: usize = ADDRESS_SIZE;
30
31 pub fn from_bytes(data: &[u8]) -> Result<Self, Error> {
33 if data.len() != ADDRESS_SIZE {
34 return Err(Error::MalformedAddress);
35 }
36
37 let mut a = [0; ADDRESS_SIZE];
38 a.copy_from_slice(data);
39
40 Ok(Self(a))
41 }
42
43 pub fn from_bech32(data: &str) -> Result<Self, Error> {
45 let (hrp, data) = bech32::decode(data).map_err(|_| Error::MalformedAddress)?;
46 if hrp != ADDRESS_BECH32_HRP {
47 return Err(Error::MalformedAddress);
48 }
49
50 Address::from_bytes(&data)
51 }
52
53 pub fn to_bech32(self) -> String {
55 bech32::encode::<Bech32>(ADDRESS_BECH32_HRP, &self.0).unwrap()
56 }
57}
58
59impl AsRef<[u8]> for Address {
60 fn as_ref(&self) -> &[u8] {
61 &self.0
62 }
63}
64
65impl TryFrom<&[u8]> for Address {
66 type Error = Error;
67
68 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
69 Self::from_bytes(bytes)
70 }
71}
72
73#[cfg(feature = "oasis-runtime-sdk")]
74impl From<oasis_runtime_sdk::types::address::Address> for Address {
75 fn from(a: oasis_runtime_sdk::types::address::Address) -> Self {
76 Self(a.into_bytes())
77 }
78}
79
80#[cfg(feature = "oasis-runtime-sdk")]
81impl From<Address> for oasis_runtime_sdk::types::address::Address {
82 fn from(a: Address) -> Self {
83 oasis_runtime_sdk::types::address::Address::from_bytes(&a.0).unwrap()
84 }
85}
86
87#[cfg(test)]
88mod test {
89 use bech32::Bech32m;
90
91 use super::*;
92
93 #[test]
94 fn test_address_try_from_bytes() {
95 let bytes_fixture = vec![42u8; ADDRESS_SIZE + 1];
96 assert_eq!(
97 Address::try_from(&bytes_fixture[0..ADDRESS_SIZE]).unwrap(),
98 Address::from_bytes(&bytes_fixture[0..ADDRESS_SIZE]).unwrap()
99 );
100 assert!(matches!(
101 Address::try_from(bytes_fixture.as_slice()).unwrap_err(),
102 Error::MalformedAddress
103 ));
104 }
105
106 #[test]
107 fn test_address_from_bech32_invalid_hrp() {
108 assert!(matches!(
109 Address::from_bech32("sisoa1qpcprk8jxpsjxw9fadxvzrv9ln7td69yus8rmtux").unwrap_err(),
110 Error::MalformedAddress,
111 ));
112 }
113
114 #[test]
115 fn test_address_from_bech32_invalid_variant() {
116 let b = vec![42u8; ADDRESS_SIZE];
117 let bech32_addr = bech32::encode::<Bech32>(ADDRESS_BECH32_HRP, &b).unwrap();
118 let bech32m_addr = bech32::encode::<Bech32m>(ADDRESS_BECH32_HRP, &b).unwrap();
119
120 assert!(
121 Address::from_bech32(&bech32_addr).is_ok(),
122 "bech32 address should be ok"
123 );
124 assert!(
125 Address::from_bech32(&bech32m_addr).is_ok(),
126 "bech32m address should be ok",
127 );
128 }
129}