oasis_core_runtime/consensus/
address.rs

1//! Consensus account address structures.
2use 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    /// Common pool reserved address.
16    pub static ref COMMON_POOL_ADDRESS: Address = Address::from_pk(&PublicKey::from("1abe11edc001ffffffffffffffffffffffffffffffffffffffffffffffffffff"));
17
18    /// Per-block fee accumulator reserved address.
19    pub static ref FEE_ACC_ADDRESS: Address = Address::from_pk(&PublicKey::from("1abe11edfeeaccffffffffffffffffffffffffffffffffffffffffffffffffff"));
20
21    /// Governance deposits reserved address.
22    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
29// V0 staking addres.
30const ADDRESS_V0_CONTEXT: &[u8] = b"oasis-core/address: staking";
31const ADDRESS_V0_VERSION: u8 = 0;
32
33// V0 runtime address.
34const 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/// A staking account address.
40#[derive(Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
41pub struct Address([u8; ADDRESS_SIZE]);
42
43impl Address {
44    /// Creates a new address from a context, version and data.
45    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    /// Creates a new address from a public key.
56    pub fn from_pk(pk: &PublicKey) -> Self {
57        Address::new(ADDRESS_V0_CONTEXT, ADDRESS_V0_VERSION, pk.as_ref())
58    }
59
60    /// Creates a new runtime address.
61    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    /// Tries to create a new address from Bech32-encoded string.
70    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    /// Converts an address to Bech32 representation.
82    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}