Skip to main content

ant_act/
wallet.rs

1use autonomi::{
2    client::payment::PaymentOption, Bytes, Client, PublicKey, ScratchpadAddress, SecretKey, XorName,
3};
4use futures::Future;
5use ruint::aliases::U256;
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
10pub struct Wallet(
11    PublicKey,
12    HashMap<Option<XorName>, (U256, Vec<(PublicKey, U256)>)>,
13    U256,
14);
15// TODO: ? give index key a name
16// TODO: ? optional pubkey ("none" meaning waiting for payment)? this would require supplying amount arg for request.
17// TODO: read/write wallet to autonomi: serde.
18// TODO: read/write wallet to bytes: serde.
19
20impl Wallet {
21    pub fn new(pk: PublicKey) -> Self {
22        Self(pk, HashMap::new(), U256::ZERO)
23    }
24
25    /// If you're creating a token, `token_id` can be `None`.
26    pub fn request(&mut self, req_token_id: Option<XorName>) -> Result<PublicKey, String> {
27        println!("request token_id: {:?}", req_token_id);
28        let index = self.1.get(&req_token_id).map(|(index, _spends)| index);
29        println!("found index: {:?}", index);
30
31        let index = match index {
32            Some(index) => index,
33            None => {
34                self.2 = self
35                    .2
36                    .checked_add(U256::from(1)) // increment
37                    .ok_or("This wallet is full".to_string())?;
38                self.1.insert(req_token_id, (self.2, Vec::new()));
39                &self.2
40            }
41        };
42        println!("request index: {}", index);
43
44        let request_key = self.0.derive_child(&index.to_be_bytes::<32>());
45        println!(
46            "request Derived PublicKey: {:.4}(...), {:?}",
47            request_key.to_hex(),
48            request_key
49        );
50
51        Ok(request_key)
52    }
53
54    pub fn receive(
55        &mut self,
56        amount: U256,
57        received_token_id: XorName,
58        spend: PublicKey,
59    ) -> Result<(), String> {
60        let entry = self.1.get(&Some(received_token_id));
61
62        match entry {
63            None => {
64                let none_entry = self.1.remove(&None);
65                match none_entry {
66                    Some(ne) => {
67                        // receiveing tokens, which token_id was not known when requesting.
68                        self.1.insert(Some(received_token_id), ne.clone());
69                    }
70                    None => (),
71                }
72            }
73            Some(_) => (),
74        };
75
76        let entry = self.1.get_mut(&Some(received_token_id));
77
78        match entry {
79            Some((_index, spends)) => {
80                spends.push((spend, amount));
81                Some(())
82            }
83            None => {
84                return Err("No requested key in this wallet.".into());
85            }
86        };
87
88        Ok(())
89    }
90
91    pub fn balance_total(&self) -> HashMap<XorName, Result<U256, String>> {
92        self.1.iter().fold(
93            HashMap::<XorName, Result<U256, String>>::new(),
94            |mut token_balances, (token_id, (_index, spends))| {
95                if let Some(id) = *token_id {
96                    token_balances.insert(
97                        id,
98                        spends
99                            .iter()
100                            .fold(Ok(U256::ZERO), |sum_res, (_spend, amount)| {
101                                sum_res.and_then(|sum| match sum.overflowing_add(*amount) {
102                                    (added, false) => Ok(added),
103                                    (_, true) => Err("Overflow.".into()),
104                                })
105                            }),
106                    );
107                }
108
109                token_balances
110            },
111        )
112    }
113
114    pub fn balance(&self, token_id: XorName) -> Result<U256, String> {
115        let spends = match self.1.get(&Some(token_id)) {
116            None => {
117                return Ok(U256::ZERO);
118            }
119            Some((_index, spends)) => spends,
120        };
121
122        let (balance, overflow) = spends.iter().fold(
123            (U256::ZERO, false),
124            |(sum, any_overflow), (_spend, amount)| {
125                let (added, this_overflow) = sum.overflowing_add(*amount);
126                (added, any_overflow || this_overflow)
127            },
128        );
129
130        match overflow {
131            false => Ok(balance),
132            true => Err("Overflow.".into()),
133        }
134    }
135
136    pub fn take_to_spend(
137        &mut self,
138        token_id: XorName,
139    ) -> Result<(Vec<PublicKey>, U256, PublicKey), String> {
140        let (spends, sum, overflow) = self
141            .1
142            .remove(&Some(token_id))
143            .map(|(_index, spends)| {
144                let (sum, overflow) = spends.iter().fold(
145                    (U256::ZERO, false),
146                    |(sum, any_overflow), (spend, amount)| {
147                        println!("Input: {:?}", (token_id, spend, amount));
148                        let (sum, this_overflow) = sum.overflowing_add(*amount);
149                        (sum, any_overflow || this_overflow)
150                    },
151                );
152                (
153                    spends.iter().map(|(spend, _amount)| *spend).collect(),
154                    sum,
155                    overflow,
156                )
157            })
158            .unwrap_or((Vec::new(), U256::ZERO, false));
159
160        match overflow {
161            false => self
162                .request(Some(token_id))
163                .map(|rest_key| (spends, sum, rest_key)),
164            true => Err("Overflow.".into()),
165        }
166    }
167
168    pub fn index_of_token(&self, token_id: XorName) -> Option<U256> {
169        self.1.get(&Some(token_id)).map(|(index, _spends)| *index)
170    }
171
172    pub fn pk_of_token(&self, token_id: XorName) -> Option<PublicKey> {
173        self.index_of_token(token_id)
174            .map(|index| self.0.derive_child(&index.to_be_bytes::<32>()))
175    }
176
177    pub fn index_that_derives(&self, request: PublicKey) -> Option<U256> {
178        self.1
179            .iter()
180            .filter(|(_, (index, _))| self.0.derive_child(&index.to_be_bytes::<32>()) == request)
181            .map(|(_, (index, _))| *index)
182            .collect::<Vec<_>>()
183            .first()
184            .copied()
185    }
186
187    pub fn received_spend(&self, token_id: XorName, spend: PublicKey) -> bool {
188        self.1
189            .get(&Some(token_id))
190            .and_then(|(_index, spends)| {
191                match spends
192                    .iter()
193                    .filter(|(received_spend, _amount)| received_spend == &spend)
194                    .count()
195                {
196                    0 => None,
197                    _ => Some(()),
198                }
199            })
200            .is_some()
201    }
202}
203
204pub trait WalletExt {
205    fn act_wallet_get(
206        &self,
207        sk: &SecretKey,
208    ) -> impl Future<Output = Result<Option<Wallet>, String>> + Send;
209
210    fn act_wallet_save(
211        &mut self,
212        wallet: &Wallet,
213        sk: &SecretKey,
214        payment: &PaymentOption,
215    ) -> impl Future<Output = Result<PublicKey, String>> + Send;
216}
217
218impl WalletExt for Client {
219    async fn act_wallet_get(&self, sk: &SecretKey) -> Result<Option<Wallet>, String> {
220        let address = ScratchpadAddress::new(sk.public_key());
221
222        if !self
223            .scratchpad_check_existence(&address)
224            .await
225            .map_err(|e| format!("{e}"))?
226        {
227            return Ok(None);
228        }
229
230        match self.scratchpad_get(&address).await {
231            Ok(sp) => {
232                let bytes = sp.decrypt_data(sk).map_err(|e| format!("{e}"))?;
233                let wallet = rmp_serde::from_slice(&bytes).map_err(|e| format!("{e}"))?;
234                Ok(Some(wallet))
235            }
236            Err(e) => Err(format!("{e}")),
237        }
238    }
239
240    async fn act_wallet_save(
241        &mut self,
242        wallet: &Wallet,
243        sk: &SecretKey,
244        payment: &PaymentOption,
245    ) -> Result<PublicKey, String> {
246        println!("saving: {:?}", wallet);
247        println!("sk: {:.4}(...)", sk.to_hex());
248        let data = rmp_serde::to_vec(&wallet).map_err(|e| format!("{e}"))?;
249
250        let existing = self.act_wallet_get(sk).await?;
251
252        match existing {
253            Some(_) => self
254                .scratchpad_update(sk, 0, &Bytes::from(data))
255                .await
256                .map(|_| sk.public_key()),
257            None => self
258                .scratchpad_create(sk, 0, &Bytes::from(data), payment.clone())
259                .await
260                .map(|(_paid, address)| *address.owner()),
261        }
262        .map_err(|e| format!("{e}"))
263    }
264}
265
266#[cfg(test)]
267mod tests {
268
269    #[test]
270    fn de_ser_ialize() -> Result<(), String> {
271        use super::*;
272
273        let mut w = Wallet::new(
274            SecretKey::from_hex("4f7eedb7b093537a4402daa0769dfca018520ee3ea2107338d89cbfcc312451b")
275                .map_err(|e| format!("{e}"))?
276                .public_key(),
277        );
278
279        let token_id = XorName::from_content(&[123u8, 45u8]);
280        let spend_address = PublicKey::from_hex("a625836b8970244eae677e6338145fe90d9789777a47311d13af4141c16efdeba60d60db86d7972e86e6d17e8b4db0af").map_err(|e| format!("{e}"))?;
281
282        w.request(Some(token_id))?;
283        w.receive(U256::from(1), token_id, spend_address)?;
284
285        let data = Bytes::from(rmp_serde::to_vec(&w).map_err(|e| format!("{e}"))?);
286
287        println!("{:x}", data);
288
289        assert_eq!(
290			format!("{:x}", data),
291			"93dc0030cc876006073c4eccf1cc9d23ccdfcc94cc8fccea3e7ecc9539cc8d6a7b5acccc6a510f24cce2ccd5160bccbbccd9ccf636cca5cc8cccdfcce6cc9ccc8eccba42ccb1cccfccce0fcca60a81dc0020cca4ccfe1bccc8cca631ccbe22ccaecc96ccad524b13ccf64d68ccefccc503cced40cc86ccd6ccaf4ecca906ccc915cce8ccf492c42000000000000000000000000000000000000000000000000000000000000000019192dc0030cca625cc836bcc8970244eccae677e6338145fcce90dcc97cc89777a47311d13ccaf4141ccc16eccfdccebcca60d60ccdbcc86ccd7cc972ecc86cce6ccd17ecc8b4dccb0ccafc4200000000000000000000000000000000000000000000000000000000000000001c4200000000000000000000000000000000000000000000000000000000000000001".to_string()
292		);
293
294        let w2 = rmp_serde::from_slice::<Wallet>(&data).map_err(|e| format!("{e}"))?;
295
296        assert_eq!(w, w2);
297
298        Ok(())
299    }
300
301    #[test]
302    fn received_spend() -> Result<(), String> {
303        use super::*;
304
305        let mut w = Wallet::new(SecretKey::random().public_key());
306        println!("{w:?}");
307
308        let token_id = XorName::from_content(&[0u8]);
309        let spend_address = SecretKey::random().public_key();
310
311        assert_eq!(false, w.received_spend(token_id, spend_address));
312
313        w.request(Some(token_id))?;
314        println!("{w:?}");
315        w.receive(U256::from(1), token_id, spend_address)?;
316        println!("{w:?}");
317
318        assert_eq!(true, w.received_spend(token_id, spend_address));
319
320        Ok(())
321    }
322}