rusk_wallet/wallet/
address.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7use std::fmt;
8use std::hash::Hasher;
9use std::str::FromStr;
10
11use dusk_bytes::{DeserializableSlice, Serializable};
12
13use super::*;
14use crate::Error;
15
16/// Address to perform a transaction with.
17#[derive(Clone, Eq)]
18#[allow(missing_docs)]
19pub enum Address {
20    /// Shielded account address for shielded transactions.
21    Shielded(PhoenixPublicKey),
22    /// Public account address for public transactions and staking
23    /// operations.
24    Public(BlsPublicKey),
25}
26
27impl Address {
28    /// Check if the `other` Address uses the same transaction model
29    pub fn same_transaction_model(&self, other: &Address) -> Result<(), Error> {
30        match (self, other) {
31            (Address::Shielded(_), Address::Shielded(_)) => Ok(()),
32            (Address::Public(_), Address::Public(_)) => Ok(()),
33            _ => Err(Error::DifferentTransactionModels),
34        }
35    }
36
37    /// Returns the inner shielded key, if present.
38    ///
39    /// # Errors
40    /// If the address is a public one.
41    pub fn shielded_key(&self) -> Result<&PhoenixPublicKey, Error> {
42        if let Self::Shielded(addr) = self {
43            Ok(addr)
44        } else {
45            Err(Error::ExpectedPhoenixPublicKey)
46        }
47    }
48
49    /// Returns the inner public key, if present.
50    ///
51    /// # Errors
52    /// If the address is a shielded one.
53    pub fn public_key(&self) -> Result<&BlsPublicKey, Error> {
54        if let Self::Public(addr) = self {
55            Ok(addr)
56        } else {
57            Err(Error::ExpectedBlsPublicKey)
58        }
59    }
60
61    pub(crate) fn to_bytes(&self) -> Vec<u8> {
62        match self {
63            Self::Shielded(addr) => addr.to_bytes().to_vec(),
64            Self::Public(addr) => addr.to_bytes().to_vec(),
65        }
66    }
67
68    /// A trimmed version of the address to display as preview
69    pub fn preview(&self) -> String {
70        let addr_key_str = String::from(self);
71        format!(
72            "{}...{}",
73            &addr_key_str[..5],
74            &addr_key_str[addr_key_str.len() - 5..]
75        )
76    }
77}
78
79impl From<BlsPublicKey> for Address {
80    fn from(value: BlsPublicKey) -> Self {
81        Self::Public(value)
82    }
83}
84
85impl From<PhoenixPublicKey> for Address {
86    fn from(value: PhoenixPublicKey) -> Self {
87        Self::Shielded(value)
88    }
89}
90
91impl FromStr for Address {
92    type Err = Error;
93
94    fn from_str(s: &str) -> Result<Self, Self::Err> {
95        let address_bytes = bs58::decode(s).into_vec()?;
96
97        let address = match address_bytes.len() {
98            PhoenixPublicKey::SIZE => {
99                PhoenixPublicKey::from_slice(&address_bytes)?.into()
100            }
101            BlsPublicKey::SIZE => {
102                BlsPublicKey::from_slice(&address_bytes)?.into()
103            }
104            _ => return Err(Error::Bytes(dusk_bytes::Error::InvalidData)),
105        };
106        Ok(address)
107    }
108}
109
110impl From<&Address> for String {
111    fn from(address: &Address) -> Self {
112        match address {
113            Address::Shielded(addr) => {
114                bs58::encode(addr.to_bytes()).into_string()
115            }
116            Address::Public(addr) => {
117                bs58::encode(addr.to_bytes()).into_string()
118            }
119        }
120    }
121}
122
123impl PartialEq for Address {
124    fn eq(&self, other: &Self) -> bool {
125        match (self, other) {
126            (Address::Shielded(self_pk), Address::Shielded(other_pk)) => {
127                self_pk == other_pk
128            }
129            (Address::Public(self_pk), Address::Public(other_pk)) => {
130                self_pk == other_pk
131            }
132            _ => false,
133        }
134    }
135}
136
137impl std::hash::Hash for Address {
138    fn hash<H: Hasher>(&self, state: &mut H) {
139        self.to_bytes().hash(state);
140    }
141}
142
143impl fmt::Display for Address {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        write!(f, "{}", String::from(self))
146    }
147}
148
149impl fmt::Debug for Address {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        write!(f, "{}", String::from(self))
152    }
153}
154
155/// Profile struct containing the addresses used for shielded and public
156/// transactions as well as for staking operations.
157#[derive(Debug, PartialEq, Eq)]
158pub struct Profile {
159    /// Shielded account address for shielded transactions
160    pub shielded_addr: PhoenixPublicKey,
161    /// Public account address for public transactions and staking operations.
162    pub public_addr: BlsPublicKey,
163}
164
165impl Profile {
166    /// Format the shielded account into a string.
167    pub fn shielded_account_string(&self) -> String {
168        format!(
169            "{} - {}",
170            shielded_account_prefix(),
171            Address::Shielded(self.shielded_addr)
172        )
173    }
174
175    /// Format the public account into a string.
176    pub fn public_account_string(&self) -> String {
177        format!(
178            "{} - {}",
179            public_account_prefix(),
180            Address::Public(self.public_addr)
181        )
182    }
183
184    /// Format the staking account into a string.
185    pub fn staking_account_string(&self) -> String {
186        format!(
187            "{} - {}",
188            staking_account_prefix(),
189            Address::Public(self.public_addr)
190        )
191    }
192
193    /// Format the shortened shielded account into a string.
194    pub fn shielded_account_preview(&self) -> String {
195        format!(
196            "{} - {}",
197            shielded_account_prefix(),
198            Address::Shielded(self.shielded_addr).preview(),
199        )
200    }
201
202    /// Format the shortened public account into a string.
203    pub fn public_account_preview(&self) -> String {
204        format!(
205            "{} - {}",
206            public_account_prefix(),
207            Address::Public(self.public_addr).preview()
208        )
209    }
210
211    /// Format the shortened staking account into a string.
212    pub fn staking_account_preview(&self) -> String {
213        format!(
214            "{} - {}",
215            staking_account_prefix(),
216            Address::Public(self.public_addr).preview()
217        )
218    }
219
220    /// Format the profile's index.
221    pub fn index_string(profile_idx: u8) -> String {
222        let mut index_string = format!("Profile {:2}", profile_idx + 1);
223        if profile_idx == 0 {
224            index_string.push_str(" (Default)");
225        }
226
227        index_string
228    }
229}
230
231fn shielded_account_prefix() -> String {
232    format!("{:<16}", "Shielded account")
233}
234
235fn public_account_prefix() -> String {
236    format!("{:<16}", "Public account")
237}
238
239fn staking_account_prefix() -> String {
240    format!("{:<16}", "Staking account")
241}