1use crate::{
2 wallet::ArWallet,
3 chunks::{Chunks},
4 b64::b64_encode,
5 chunk_validator::Validator,
6};
7
8use std::error::Error;
9use serde::{Deserialize, Serialize};
10use primitive_types::U256;
11
12#[derive(Debug, Clone, Default, Serialize, Deserialize)]
17pub struct Uploader {
18 current_idx: usize,
19 data_root: Vec<u8>,
20 data_size: usize,
21 chunk_size: usize,
22 chunks: Chunks
23}
24
25impl Uploader {
26 pub fn new(raw_data: &[u8]) -> Self {
28 let mut chunks = Chunks::new();
29 chunks.finalize(&raw_data);
30 Self {
31 current_idx: 0,
32 data_root: chunks.data_root().clone(),
33 data_size: chunks.data_size(),
34 chunk_size: chunks.chunks_len(),
35 chunks: chunks,
36 }
37 }
38
39 pub fn uploader_json(&self) -> String {
42 serde_json::to_string_pretty(self).unwrap()
43 }
44
45 pub fn resume(uploader_json: &str) -> Self {
48 let uploader: Uploader = serde_json::from_str(uploader_json).unwrap();
49 uploader
50 }
51
52 pub fn current_idx(&self) -> usize {
53 self.current_idx
54 }
55
56 pub fn chunk_size(&self) -> usize {
57 self.chunk_size
58 }
59
60 pub fn verify_uploader(&self, chunks: &Chunks) -> bool {
61 if b64_encode(&self.data_root) == b64_encode(&chunks.data_root())
62 && self.data_size == chunks.data_size()
63 && self.chunk_size == chunks.chunks_len()
64 {
65 true
66 } else {
67 false
68 }
69 }
70
71 pub async fn upload(&mut self,
73 arwallet: &ArWallet)
74 -> Result<String, Box<dyn Error>> {
75 let chunks = &self.chunks;
76 if self.verify_uploader(&chunks) == false {
77 return Err("Your uploader is invalid. Please create a data transaction first, or resume a previous interrupted uploader.".into());
78 }
79 let _validate = Validator::validate(
80 chunks.data_root(),
81 U256::from(chunks.proofs()[self.current_idx].offset()),
82 U256::zero(),
83 U256::from(chunks.data_size()),
84 chunks.proofs()[self.current_idx].proof(),
85 )?;
86 let chunk_json = chunks.chunk_json(self.current_idx);
87 let mut api_url = arwallet.gateway();
88 api_url.push_str("chunk");
89 let res = arwallet.http_client().post(&api_url)
90 .body(chunk_json).send().await;
91 match res {
92 Ok(r) => {
93 let res_status = r.status().as_u16();
94 if res_status == 200 {
95 self.current_idx = self.current_idx + 1;
96 let completed_percentage
97 = self.current_idx as f64
98 / self.chunk_size as f64 * 100.0;
99 let mut completed_percentage
100 = (completed_percentage as usize).to_string();
101 completed_percentage.push_str("%");
102 let mut ok_string = "Chunk ".to_string();
103 ok_string.push_str(&self.current_idx.to_string());
104 ok_string.push_str(" completed. ");
105 ok_string.push_str(&completed_percentage);
106 Ok(ok_string)
107 } else {
108 let res_text = r.text().await?;
109 let mut err_string = "Error: ".to_string();
110 err_string.push_str(&res_status.to_string());
111 err_string.push_str(" - ");
112 err_string.push_str(&res_text);
113 Err(err_string.into())
114 }
115 },
116 Err(e) => {
117 Err(e.into())
118 }
119 }
120 }
121}