dusk_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 crate::Error;
8use dusk_bytes::{DeserializableSlice, Error as BytesError, Serializable};
9use dusk_pki::PublicSpendKey;
10use std::fmt;
11use std::hash::Hasher;
12use std::str::FromStr;
13
14#[derive(Clone, Eq)]
15/// A public address within the Dusk Network
16pub struct Address {
17    pub(crate) index: Option<u8>,
18    pub(crate) psk: PublicSpendKey,
19}
20
21impl Address {
22    pub(crate) fn new(index: u8, psk: PublicSpendKey) -> Self {
23        Self {
24            index: Some(index),
25            psk,
26        }
27    }
28
29    /// Returns true if the current user owns this address
30    pub fn is_owned(&self) -> bool {
31        self.index.is_some()
32    }
33
34    pub(crate) fn psk(&self) -> &PublicSpendKey {
35        &self.psk
36    }
37
38    pub(crate) fn index(&self) -> Result<u8, Error> {
39        self.index.ok_or(Error::AddressNotOwned)
40    }
41
42    /// A trimmed version of the address to display as preview
43    pub fn preview(&self) -> String {
44        let addr = bs58::encode(self.psk.to_bytes()).into_string();
45        format!("{}...{}", &addr[..7], &addr[addr.len() - 7..])
46    }
47}
48
49impl FromStr for Address {
50    type Err = Error;
51
52    fn from_str(s: &str) -> Result<Self, Self::Err> {
53        let bytes = bs58::decode(s).into_vec()?;
54
55        let psk = PublicSpendKey::from_reader(&mut &bytes[..])
56            .map_err(|_| Error::BadAddress)?;
57
58        let addr = Address { index: None, psk };
59
60        Ok(addr)
61    }
62}
63
64impl TryFrom<String> for Address {
65    type Error = Error;
66
67    fn try_from(s: String) -> Result<Self, Self::Error> {
68        Address::from_str(s.as_str())
69    }
70}
71
72impl TryFrom<&[u8; PublicSpendKey::SIZE]> for Address {
73    type Error = Error;
74
75    fn try_from(
76        bytes: &[u8; PublicSpendKey::SIZE],
77    ) -> Result<Self, Self::Error> {
78        let addr = Address {
79            index: None,
80            psk: dusk_pki::PublicSpendKey::from_bytes(bytes)?,
81        };
82        Ok(addr)
83    }
84}
85
86impl PartialEq for Address {
87    fn eq(&self, other: &Self) -> bool {
88        self.index == other.index && self.psk == other.psk
89    }
90}
91
92impl std::hash::Hash for Address {
93    fn hash<H: Hasher>(&self, state: &mut H) {
94        self.index.hash(state);
95        self.psk.to_bytes().hash(state);
96    }
97}
98
99impl fmt::Display for Address {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        write!(f, "{}", bs58::encode(self.psk.to_bytes()).into_string())
102    }
103}
104
105impl fmt::Debug for Address {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        write!(f, "{}", bs58::encode(self.psk.to_bytes()).into_string())
108    }
109}
110
111/// Addresses holds address-related metadata that needs to be
112/// persisted in the wallet file.
113pub(crate) struct Addresses {
114    pub(crate) count: u8,
115}
116
117impl Default for Addresses {
118    fn default() -> Self {
119        Self { count: 1 }
120    }
121}
122
123impl Serializable<1> for Addresses {
124    type Error = BytesError;
125
126    fn from_bytes(buf: &[u8; Addresses::SIZE]) -> Result<Self, Self::Error>
127    where
128        Self: Sized,
129    {
130        Ok(Self { count: buf[0] })
131    }
132
133    fn to_bytes(&self) -> [u8; Addresses::SIZE] {
134        [self.count]
135    }
136}
137
138#[test]
139fn addresses_serde() -> Result<(), Box<dyn std::error::Error>> {
140    let addrs = Addresses { count: 6 };
141    let read = Addresses::from_bytes(&addrs.to_bytes())
142        .map_err(|_| Error::WalletFileCorrupted)?;
143    assert!(read.count == addrs.count);
144    Ok(())
145}