arrs/
uploader.rs

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/// Manage all uploading information; handle uplaoding and
13/// resuming. However, this struct does not include a copy of raw file
14/// or chunks. They shoud be either borrowed from the current
15/// transaction or import from a file path (such as resuming).
16#[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    /// Create a new uploader from a raw_data.
27    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    /// Save the uploader information as a json string. The saved data
40    /// can be later used for resuming the uploading.
41    pub fn uploader_json(&self) -> String {
42        serde_json::to_string_pretty(self).unwrap()
43    }
44
45    /// Resume an uploading by input a saved uploader json
46    /// information.
47    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    /// Upload the current chunk.
72    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}