1use core::borrow::Borrow;
7use std::collections::HashMap;
8
9use crypto::{
10 hashes::{blake2b::Blake2b256, Digest},
11 keys::bip39::{wordlist, Mnemonic, MnemonicRef, Passphrase, Seed},
12 utils,
13};
14use serde::{Deserialize, Serialize};
15use zeroize::{Zeroize, ZeroizeOnDrop};
16
17use super::{Client, ClientInner};
18use crate::{
19 client::{Error, Result},
20 types::block::{
21 address::{Address, Bech32Address, Ed25519Address, Hrp, ToBech32Ext},
22 output::{AliasId, NftId},
23 payload::TaggedDataPayload,
24 ConvertTo,
25 },
26};
27
28pub fn bech32_to_hex(bech32: impl ConvertTo<Bech32Address>) -> Result<String> {
30 Ok(match bech32.convert()?.inner() {
31 Address::Ed25519(ed) => ed.to_string(),
32 Address::Alias(alias) => alias.to_string(),
33 Address::Nft(nft) => nft.to_string(),
34 })
35}
36
37pub fn hex_to_bech32(hex: &str, bech32_hrp: impl ConvertTo<Hrp>) -> Result<Bech32Address> {
39 let address: Ed25519Address = hex.parse::<Ed25519Address>()?;
40 Ok(Address::Ed25519(address).try_to_bech32(bech32_hrp)?)
41}
42
43pub fn hex_public_key_to_bech32_address(hex: &str, bech32_hrp: impl ConvertTo<Hrp>) -> Result<Bech32Address> {
45 let public_key: [u8; Ed25519Address::LENGTH] = prefix_hex::decode(hex)?;
46 let address = Ed25519Address::new(Blake2b256::digest(public_key).into());
47
48 Ok(Address::Ed25519(address).try_to_bech32(bech32_hrp)?)
49}
50
51pub fn generate_mnemonic() -> Result<Mnemonic> {
53 let mut entropy = [0u8; 32];
54 utils::rand::fill(&mut entropy)?;
55 let mnemonic = wordlist::encode(&entropy, &crypto::keys::bip39::wordlist::ENGLISH)
56 .map_err(|e| crate::client::Error::InvalidMnemonic(format!("{e:?}")))?;
57 entropy.zeroize();
58 Ok(mnemonic)
59}
60
61pub fn mnemonic_to_hex_seed(mnemonic: impl Borrow<MnemonicRef>) -> Result<String> {
63 Ok(prefix_hex::encode(mnemonic_to_seed(mnemonic)?.as_ref()))
64}
65
66pub fn mnemonic_to_seed(mnemonic: impl Borrow<MnemonicRef>) -> Result<Seed> {
68 verify_mnemonic(mnemonic.borrow())?;
70 Ok(crypto::keys::bip39::mnemonic_to_seed(
71 mnemonic.borrow(),
72 &Passphrase::default(),
73 ))
74}
75
76pub fn verify_mnemonic(mnemonic: impl Borrow<MnemonicRef>) -> Result<()> {
78 crypto::keys::bip39::wordlist::verify(mnemonic.borrow(), &crypto::keys::bip39::wordlist::ENGLISH)
79 .map_err(|e| crate::client::Error::InvalidMnemonic(format!("{e:?}")))?;
80 Ok(())
81}
82
83pub async fn request_funds_from_faucet(url: &str, bech32_address: &Bech32Address) -> Result<String> {
85 let mut map = HashMap::new();
86 map.insert("address", bech32_address.to_string());
87
88 let client = reqwest::Client::new();
89 let faucet_response = client
90 .post(url)
91 .json(&map)
92 .send()
93 .await
94 .map_err(|err| Error::Node(err.into()))?
95 .text()
96 .await
97 .map_err(|err| Error::Node(err.into()))?;
98 Ok(faucet_response)
99}
100
101impl ClientInner {
102 pub async fn hex_to_bech32(
104 &self,
105 hex: &str,
106 bech32_hrp: Option<impl ConvertTo<Hrp>>,
107 ) -> crate::client::Result<Bech32Address> {
108 match bech32_hrp {
109 Some(hrp) => Ok(hex_to_bech32(hex, hrp)?),
110 None => Ok(hex_to_bech32(hex, self.get_bech32_hrp().await?)?),
111 }
112 }
113
114 pub async fn alias_id_to_bech32(
116 &self,
117 alias_id: AliasId,
118 bech32_hrp: Option<impl ConvertTo<Hrp>>,
119 ) -> crate::client::Result<Bech32Address> {
120 match bech32_hrp {
121 Some(hrp) => Ok(alias_id.to_bech32(hrp.convert()?)),
122 None => Ok(alias_id.to_bech32(self.get_bech32_hrp().await?)),
123 }
124 }
125
126 pub async fn nft_id_to_bech32(
128 &self,
129 nft_id: NftId,
130 bech32_hrp: Option<impl ConvertTo<Hrp>>,
131 ) -> crate::client::Result<Bech32Address> {
132 match bech32_hrp {
133 Some(hrp) => Ok(nft_id.to_bech32(hrp.convert()?)),
134 None => Ok(nft_id.to_bech32(self.get_bech32_hrp().await?)),
135 }
136 }
137
138 pub async fn hex_public_key_to_bech32_address(
140 &self,
141 hex: &str,
142 bech32_hrp: Option<impl ConvertTo<Hrp>>,
143 ) -> crate::client::Result<Bech32Address> {
144 match bech32_hrp {
145 Some(hrp) => Ok(hex_public_key_to_bech32_address(hex, hrp)?),
146 None => Ok(hex_public_key_to_bech32_address(hex, self.get_bech32_hrp().await?)?),
147 }
148 }
149}
150
151impl Client {
152 pub fn bech32_to_hex(bech32: impl ConvertTo<Bech32Address>) -> crate::client::Result<String> {
154 bech32_to_hex(bech32)
155 }
156
157 pub fn generate_mnemonic() -> Result<Mnemonic> {
159 generate_mnemonic()
160 }
161
162 pub fn mnemonic_to_seed(mnemonic: impl Borrow<MnemonicRef>) -> Result<Seed> {
164 mnemonic_to_seed(mnemonic)
165 }
166
167 pub fn mnemonic_to_hex_seed(mnemonic: impl Borrow<MnemonicRef>) -> Result<String> {
169 mnemonic_to_hex_seed(mnemonic)
170 }
171
172 pub fn tag_to_utf8(payload: &TaggedDataPayload) -> Result<String> {
174 String::from_utf8(payload.tag().to_vec()).map_err(|_| Error::TaggedData("found invalid UTF-8".to_string()))
175 }
176
177 pub fn data_to_utf8(payload: &TaggedDataPayload) -> Result<String> {
179 String::from_utf8(payload.data().to_vec()).map_err(|_| Error::TaggedData("found invalid UTF-8".to_string()))
180 }
181
182 pub fn tagged_data_to_utf8(payload: &TaggedDataPayload) -> Result<(String, String)> {
184 Ok((Self::tag_to_utf8(payload)?, Self::data_to_utf8(payload)?))
185 }
186}
187
188#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Zeroize, ZeroizeOnDrop, derive_more::From)]
190pub struct Password(String);
191
192impl Password {
193 pub fn as_bytes(&self) -> &[u8] {
194 self.0.as_bytes()
195 }
196}