iota_client/api/
address.rs

1// Copyright 2021 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{Client, Error, Result};
5
6use bee_message::prelude::{Address, Ed25519Address};
7use core::convert::TryInto;
8use crypto::{
9    hashes::{blake2b::Blake2b256, Digest},
10    keys::slip10::{Chain, Curve, Seed},
11};
12use std::ops::Range;
13
14/// Builder of get_addresses API
15pub struct GetAddressesBuilder<'a> {
16    client: Option<&'a Client>,
17    seed: Option<&'a Seed>,
18    account_index: usize,
19    range: Range<usize>,
20    bech32_hrp: Option<String>,
21}
22
23impl<'a> Default for GetAddressesBuilder<'a> {
24    fn default() -> Self {
25        Self {
26            client: None,
27            seed: None,
28            account_index: 0,
29            range: 0..super::ADDRESS_GAP_RANGE,
30            bech32_hrp: None,
31        }
32    }
33}
34
35impl<'a> GetAddressesBuilder<'a> {
36    /// Create get_addresses builder
37    pub fn new(seed: &'a Seed) -> Self {
38        Self {
39            seed: Some(seed),
40            ..Default::default()
41        }
42    }
43
44    /// Provide a client to get the bech32_hrp from the node
45    pub fn with_client(mut self, client: &'a Client) -> Self {
46        self.client.replace(client);
47        self
48    }
49
50    /// Set the account index
51    pub fn with_account_index(mut self, account_index: usize) -> Self {
52        self.account_index = account_index;
53        self
54    }
55
56    /// Set range to the builder
57    pub fn with_range(mut self, range: Range<usize>) -> Self {
58        self.range = range;
59        self
60    }
61
62    /// Set bech32 human readable part (hrp)
63    pub fn with_bech32_hrp(mut self, bech32_hrp: String) -> Self {
64        self.bech32_hrp.replace(bech32_hrp);
65        self
66    }
67
68    /// Consume the builder and get a vector of public addresses bech32 encoded
69    pub async fn finish(self) -> Result<Vec<String>> {
70        Ok(self
71            .get_all()
72            .await?
73            .into_iter()
74            .filter(|(_, internal)| !internal)
75            .map(|(a, _)| a)
76            .collect())
77    }
78
79    /// Consume the builder and get the vector of public and internal addresses bech32 encoded
80    pub async fn get_all(self) -> Result<Vec<(String, bool)>> {
81        let bech32_hrp = match self.bech32_hrp.clone() {
82            Some(bech32_hrp) => bech32_hrp,
83            None => {
84                self.client
85                    .ok_or(Error::MissingParameter("Client or bech32_hrp"))?
86                    .get_bech32_hrp()
87                    .await?
88            }
89        };
90        let addresses = self
91            .get_all_raw()
92            .await?
93            .into_iter()
94            .map(|(a, b)| (a.to_bech32(&bech32_hrp), b))
95            .collect();
96
97        Ok(addresses)
98    }
99    /// Consume the builder and get the vector of public and internal addresses
100    pub async fn get_all_raw(self) -> Result<Vec<(Address, bool)>> {
101        let mut addresses = Vec::new();
102        for address_index in self.range {
103            let address = generate_address(
104                self.seed.ok_or(Error::MissingParameter("Seed"))?,
105                self.account_index as u32,
106                address_index as u32,
107                false,
108            )?;
109            let internal_address = generate_address(
110                self.seed.ok_or(Error::MissingParameter("Seed"))?,
111                self.account_index as u32,
112                address_index as u32,
113                true,
114            )?;
115            addresses.push((address, false));
116            addresses.push((internal_address, true));
117        }
118
119        Ok(addresses)
120    }
121}
122
123fn generate_address(seed: &Seed, account_index: u32, address_index: u32, internal: bool) -> Result<Address> {
124    // 44 is for BIP 44 (HD wallets) and 4218 is the registered index for IOTA https://github.com/satoshilabs/slips/blob/master/slip-0044.md
125    let chain = Chain::from_u32_hardened(vec![44, 4218, account_index, internal as u32, address_index]);
126    let public_key = seed
127        .derive(Curve::Ed25519, &chain)?
128        .secret_key()
129        .public_key()
130        .to_bytes();
131    // Hash the public key to get the address
132    let result = Blake2b256::digest(&public_key)
133        .try_into()
134        .map_err(|_e| Error::Blake2b256Error("Hashing the public key while generating the address failed."));
135
136    Ok(Address::Ed25519(Ed25519Address::new(result?)))
137}
138
139/// Function to find the index and public or internal type of an Bech32 encoded address
140pub async fn search_address(
141    seed: &Seed,
142    bech32_hrp: &str,
143    account_index: usize,
144    range: Range<usize>,
145    address: &Address,
146) -> Result<(usize, bool)> {
147    let addresses = GetAddressesBuilder::new(seed)
148        .with_bech32_hrp(bech32_hrp.to_owned())
149        .with_account_index(account_index)
150        .with_range(range.clone())
151        .get_all()
152        .await?;
153    let mut index_counter = range.start;
154    for address_internal in addresses {
155        if address_internal.0 == *address.to_bech32(bech32_hrp) {
156            return Ok((index_counter, address_internal.1));
157        }
158        if !address_internal.1 {
159            index_counter += 1;
160        }
161    }
162    Err(crate::error::Error::InputAddressNotFound(
163        address.to_bech32(bech32_hrp),
164        format!("{range:?}"),
165    ))
166}