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);
15impl Wallet {
21 pub fn new(pk: PublicKey) -> Self {
22 Self(pk, HashMap::new(), U256::ZERO)
23 }
24
25 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)) .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 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}