oasis_core_runtime/consensus/
address.rs1use std::{convert::TryInto, fmt};
3
4use anyhow::{anyhow, Result};
5use bech32::{Bech32, Hrp};
6use lazy_static::lazy_static;
7
8use crate::common::{
9 crypto::{hash::Hash, signature::PublicKey},
10 key_format::KeyFormatAtom,
11 namespace::Namespace,
12};
13
14lazy_static! {
15 pub static ref COMMON_POOL_ADDRESS: Address = Address::from_pk(&PublicKey::from("1abe11edc001ffffffffffffffffffffffffffffffffffffffffffffffffffff"));
17
18 pub static ref FEE_ACC_ADDRESS: Address = Address::from_pk(&PublicKey::from("1abe11edfeeaccffffffffffffffffffffffffffffffffffffffffffffffffff"));
20
21 pub static ref GOVERNANCE_DEPOSITS_ADDRESS: Address = Address::from_pk(&PublicKey::from("1abe11eddeaccfffffffffffffffffffffffffffffffffffffffffffffffffff"));
23}
24
25const ADDRESS_VERSION_SIZE: usize = 1;
26const ADDRESS_DATA_SIZE: usize = 20;
27const ADDRESS_SIZE: usize = ADDRESS_VERSION_SIZE + ADDRESS_DATA_SIZE;
28
29const ADDRESS_V0_CONTEXT: &[u8] = b"oasis-core/address: staking";
31const ADDRESS_V0_VERSION: u8 = 0;
32
33const ADDRESS_RUNTIME_V0_CONTEXT: &[u8] = b"oasis-core/address: runtime";
35const ADDRESS_RUNTIME_V0_VERSION: u8 = 0;
36
37const ADDRESS_BECH32_HRP: Hrp = Hrp::parse_unchecked("oasis");
38
39#[derive(Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
41pub struct Address([u8; ADDRESS_SIZE]);
42
43impl Address {
44 pub fn new(ctx: &'static [u8], version: u8, data: &[u8]) -> Self {
46 let h = Hash::digest_bytes_list(&[ctx, &[version], data]);
47
48 let mut a = [0; ADDRESS_SIZE];
49 a[..ADDRESS_VERSION_SIZE].copy_from_slice(&[version]);
50 a[ADDRESS_VERSION_SIZE..].copy_from_slice(h.truncated(ADDRESS_DATA_SIZE));
51
52 Address(a)
53 }
54
55 pub fn from_pk(pk: &PublicKey) -> Self {
57 Address::new(ADDRESS_V0_CONTEXT, ADDRESS_V0_VERSION, pk.as_ref())
58 }
59
60 pub fn from_runtime_id(id: &Namespace) -> Self {
62 Address::new(
63 ADDRESS_RUNTIME_V0_CONTEXT,
64 ADDRESS_RUNTIME_V0_VERSION,
65 id.as_ref(),
66 )
67 }
68
69 pub fn from_bech32(data: &str) -> Result<Self> {
71 let (hrp, data) = bech32::decode(data).map_err(|_| anyhow!("malformed address"))?;
72
73 if hrp != ADDRESS_BECH32_HRP {
74 return Err(anyhow!("malformed address"));
75 }
76
77 let sized: &[u8; ADDRESS_SIZE] = &data.as_slice().try_into()?;
78 Ok(sized.into())
79 }
80
81 pub fn to_bech32(&self) -> String {
83 bech32::encode::<Bech32>(ADDRESS_BECH32_HRP, &self.0).unwrap()
84 }
85}
86
87impl fmt::LowerHex for Address {
88 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89 for i in &self.0[..] {
90 write!(f, "{i:02x}")?;
91 }
92 Ok(())
93 }
94}
95
96impl<'a> From<&'a str> for Address {
97 fn from(s: &'a str) -> Address {
98 Address::from_bech32(s).unwrap()
99 }
100}
101
102impl AsRef<[u8]> for Address {
103 fn as_ref(&self) -> &[u8] {
104 &self.0
105 }
106}
107
108impl From<Address> for [u8; ADDRESS_SIZE] {
109 fn from(val: Address) -> Self {
110 val.0
111 }
112}
113
114impl From<&[u8; ADDRESS_SIZE]> for Address {
115 fn from(b: &[u8; ADDRESS_SIZE]) -> Address {
116 let mut data = [0; ADDRESS_SIZE];
117 data.copy_from_slice(b);
118 Address(data)
119 }
120}
121
122impl fmt::Debug for Address {
123 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
124 write!(f, "{}", self.to_bech32())?;
125 Ok(())
126 }
127}
128
129impl cbor::Encode for Address {
130 fn into_cbor_value(self) -> cbor::Value {
131 cbor::Value::ByteString(self.as_ref().to_vec())
132 }
133}
134
135impl cbor::Decode for Address {
136 fn try_default() -> Result<Self, cbor::DecodeError> {
137 Ok(Default::default())
138 }
139
140 fn try_from_cbor_value(value: cbor::Value) -> Result<Self, cbor::DecodeError> {
141 match value {
142 cbor::Value::ByteString(data) => Ok(Address(
143 data.try_into()
144 .map_err(|_| cbor::DecodeError::UnexpectedType)?,
145 )),
146 _ => Err(cbor::DecodeError::UnexpectedType),
147 }
148 }
149}
150
151impl KeyFormatAtom for Address {
152 fn size() -> usize {
153 ADDRESS_SIZE
154 }
155
156 fn encode_atom(self) -> Vec<u8> {
157 self.as_ref().to_vec()
158 }
159
160 fn decode_atom(data: &[u8]) -> Self
161 where
162 Self: Sized,
163 {
164 let sized: &[u8; ADDRESS_SIZE] =
165 &data.try_into().expect("address: invalid decode atom data");
166 sized.into()
167 }
168}
169
170#[cfg(test)]
171mod test {
172 use super::Address;
173 use crate::common::{crypto::signature::PublicKey, namespace::Namespace};
174
175 #[test]
176 fn test_address() {
177 let pk =
178 PublicKey::from("badadd1e55ffffffffffffffffffffffffffffffffffffffffffffffffffffff");
179
180 let addr = Address::from_pk(&pk);
181 assert_eq!(
182 addr.to_bech32(),
183 "oasis1qryqqccycvckcxp453tflalujvlf78xymcdqw4vz"
184 );
185
186 assert_eq!(
187 Address::from("oasis1qryqqccycvckcxp453tflalujvlf78xymcdqw4vz").to_bech32(),
188 "oasis1qryqqccycvckcxp453tflalujvlf78xymcdqw4vz"
189 );
190
191 let runtime_id =
192 Namespace::from("80000000000000002aff7f6dfb62720cfd735f2b037b81572fad1b7937d826b3");
193 let addr = Address::from_runtime_id(&runtime_id);
194 assert_eq!(
195 addr.to_bech32(),
196 "oasis1qpllh99nhwzrd56px4txvl26atzgg4f3a58jzzad"
197 );
198 }
199
200 #[test]
201 fn test_deserialization() {
202 let addr: Address = cbor::from_slice(&[0xF6]).unwrap();
203 assert_eq!(
204 addr.to_bech32(),
205 "oasis1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq0ltrq9"
206 );
207 }
208}