1use std::io::Cursor;
12
13use super::{block::{Block, BlockDetailsJSON, BlockHeader, EcdhInfo, Gen, KeyRawTx, MinerTxInfo, RawTx, RctSignatures, RctsigPrunable, TaggedKey, Target, Vin, VinRawTx, Vout, BPP, CLSAG}, nodes::DaemonNode};
14
15fn get_json_rpc_url(node: DaemonNode) -> String {
16 match node.tls {
17 true => format!("https://{}:{}/json_rpc", node.url, node.port),
18 false => format!("http://{}:{}/json_rpc", node.url, node.port),
19 }
20}
21
22fn get_rpc_url(node: DaemonNode) -> String {
23 match node.tls {
24 true => format!("https://{}:{}", node.url, node.port),
25 false => format!("http://{}:{}", node.url, node.port),
26 }
27}
28
29pub fn get_block_from_height(block_height: u64, node: DaemonNode) -> Result<Block, String> {
42 let rpc_url = get_json_rpc_url(node);
43 let response = ureq::post(&rpc_url)
44 .set("Content-Type", "application/json")
45 .send_json(ureq::json!({
46 "jsonrpc": "2.0",
47 "id": "0",
48 "method": "get_block",
49 "params": {
50 "height": block_height
51 }
52 }));
53 if let Err(e) = response.as_ref() {
54 return Err(format!("Error while getting the block from daemon: {}", e));
55 }
56 let response: serde_json::Value = response.unwrap().into_json().unwrap();
57 let block_header = BlockHeader {
58 block_size: response["result"]["block_header"]["block_size"].as_u64().unwrap(),
59 block_weight: response["result"]["block_header"]["block_weight"].as_u64().unwrap(),
60 cumulative_difficulty: response["result"]["block_header"]["cumulative_difficulty"].as_u64().unwrap(),
61 cumulative_difficulty_top64: response["result"]["block_header"]["cumulative_difficulty_top64"].as_u64().unwrap(),
62 depth: response["result"]["block_header"]["depth"].as_u64().unwrap(),
63 difficulty: response["result"]["block_header"]["difficulty"].as_u64().unwrap(),
64 difficulty_top64: response["result"]["block_header"]["difficulty_top64"].as_u64().unwrap(),
65 hash: response["result"]["block_header"]["hash"].as_str().unwrap().to_string(),
66 height: response["result"]["block_header"]["height"].as_u64().unwrap(),
67 long_term_weight: response["result"]["block_header"]["long_term_weight"].as_u64().unwrap(),
68 major_version: response["result"]["block_header"]["major_version"].as_u64().unwrap(),
69 miner_tx_hash: response["result"]["block_header"]["miner_tx_hash"].as_str().unwrap().to_string(),
70 minor_version: response["result"]["block_header"]["minor_version"].as_u64().unwrap(),
71 nonce: response["result"]["block_header"]["nonce"].as_u64().unwrap(),
72 num_txes: response["result"]["block_header"]["num_txes"].as_u64().unwrap(),
73 orphan_status: response["result"]["block_header"]["orphan_status"].as_bool().unwrap(),
74 pow_hash: response["result"]["block_header"]["pow_hash"].as_str().unwrap().to_string(),
75 prev_hash: response["result"]["block_header"]["prev_hash"].as_str().unwrap().to_string(),
76 reward: response["result"]["block_header"]["reward"].as_u64().unwrap(),
77 timestamp: response["result"]["block_header"]["timestamp"].as_u64().unwrap(),
78 wide_cumulative_difficulty: response["result"]["block_header"]["wide_cumulative_difficulty"].as_str().unwrap().to_string(),
79 wide_difficulty: response["result"]["block_header"]["wide_difficulty"].as_str().unwrap().to_string(),
80 };
81 let json = response["result"]["json"].as_str().unwrap().to_string();
82 let parsed_json: serde_json::Value = serde_json::from_str(&json).unwrap_or(serde_json::Value::Null);
83 if parsed_json.is_null() {
84 return Err("Error while parsing the block JSON".to_string());
85 }
86 let vin_array = parsed_json["miner_tx"]["vin"].as_array().unwrap();
87 let mut vin_vec: Vec<Vin> = Vec::new();
88 for vin in vin_array {
89 vin_vec.push(Vin {
90 gen: Gen {
91 height: vin["gen"]["height"].as_u64().unwrap_or(0),
92 }
93 });
94 };
95 let vout_array = parsed_json["miner_tx"]["vout"].as_array().unwrap();
96 let mut vout_vec: Vec<Vout> = Vec::new();
97 for vout in vout_array {
98 vout_vec.push(Vout {
99 amount: vout["amount"].as_u64().unwrap_or(0),
100 target: Target {
101 tagged_key: TaggedKey {
102 key: vout["target"]["key"].as_str().unwrap_or("").to_string(),
103 view_tag: vout["target"]["view_tag"].as_str().unwrap_or("").to_string(),
104 }
105 }
106 });
107 };
108 Ok(Block {
109 blob: response["result"]["blob"].as_str().unwrap_or("").to_string(),
110 block_header,
111 credits: response["result"]["credits"].as_u64().unwrap_or(0),
112 json: BlockDetailsJSON {
113 major_version: parsed_json["major_version"].as_u64().unwrap_or(0),
114 minor_version: parsed_json["minor_version"].as_u64().unwrap_or(0),
115 timestamp: parsed_json["timestamp"].as_u64().unwrap_or(0),
116 prev_id: parsed_json["prev_id"].as_str().unwrap_or("").to_string(),
117 nonce: parsed_json["nonce"].as_u64().unwrap_or(0),
118 miner_tx: MinerTxInfo {
119 version: parsed_json["miner_tx"]["version"].as_u64().unwrap_or(0),
120 unlock_time: parsed_json["miner_tx"]["unlock_time"].as_u64().unwrap_or(0),
121 vin: vin_vec,
122 vout: vout_vec,
123 extra: parsed_json["miner_tx"]["extra"].as_str().unwrap_or("").as_bytes().to_vec(),
124 rct_signatures: RctSignatures {
125 type_int: parsed_json["miner_tx"]["rct_signatures"]["type"].as_u64().unwrap_or(0),
126 txn_fee: 0,
127 ecdh_info: Vec::new(),
128 out_pk: Vec::new(),
129 }
130 },
131 tx_hashes: parsed_json["tx_hashes"].as_array().unwrap().to_vec().iter().map(|x| x.as_str().unwrap().to_string()).collect(),
132 },
133 miner_tx_hash: response["result"]["miner_tx_hash"].as_str().unwrap_or("").to_string(),
134 status: response["result"]["status"].as_str().unwrap_or("ERROR").to_string(),
135 top_hash: response["result"]["top_hash"].as_str().unwrap_or("").to_string(),
136 untrusted: response["result"]["untrusted"].as_bool().unwrap_or(false),
137 })
138}
139
140pub fn get_height(node: DaemonNode) -> Result<u64, String> {
153 let rpc_url = get_rpc_url(node);
154 let reader = Cursor::new(Vec::new());
155 let response = ureq::get(format!("{}/get_height", &rpc_url).as_str())
156 .set("Content-Type", "application/json").send(reader);
157 if let Err(e) = response.as_ref() {
158 return Err(format!("Error while getting the block count (height) from daemon: {}", e));
159 }
160 let response: serde_json::Value = response.unwrap().into_json().unwrap_or(serde_json::Value::Null);
161 if response.is_null() {
162 return Err("Error while parsing the block count (height) JSON".to_string());
163 }
164 Ok(response["height"].as_u64().unwrap_or(0))
165}
166
167pub fn get_transaction_from_hash(hash: String, node: DaemonNode) -> Result<RawTx, String> {
181 let rpc_url = format!("{}/get_transactions", get_rpc_url(node));
182 let response = ureq::post(&rpc_url)
183 .set("Content-Type", "application/json")
184 .send_json(ureq::json!({
185 "txs_hashes": [hash],
186 "decode_as_json": true,
187 }));
188 if let Err(e) = response.as_ref() {
189 return Err(format!("Error while getting the transaction from daemon: {}", e));
190 }
191 let response: serde_json::Value = response.unwrap().into_json().unwrap_or(serde_json::Value::Null);
192 if response.is_null() {
193 return Err("Error while parsing the transaction JSON".to_string());
194 }
195 let json_part = response["txs"][0]["as_json"].as_str().unwrap_or("").to_string();
196 if json_part.is_empty() {
197 return Err("Error while getting the as_json part".to_string());
198 }
199 let json_final = serde_json::from_str(&json_part).unwrap_or(serde_json::Value::Null);
200 if json_final.is_null() {
201 return Err("Error while parsing the as_json part".to_string());
202 }
203 let mut vin_raw_tx = Vec::new();
204 for vin in json_final["vin"].as_array().unwrap().iter() {
205 vin_raw_tx.push(VinRawTx {
206 key: KeyRawTx {
207 amount: vin["key"]["amount"].as_u64().unwrap_or(0),
208 key_offsets: vin["key"]["key_offsets"].as_array().unwrap_or(&Vec::new()).to_vec().iter().map(|x| x.as_u64().unwrap_or(0)).collect(),
209 k_image: vin["key"]["k_image"].as_str().unwrap_or("").to_string(),
210 }
211 });
212 }
213 let mut vout_raw_tx = Vec::new();
214 for vout in json_final["vout"].as_array().unwrap().iter() {
215 vout_raw_tx.push(Vout {
216 amount: vout["amount"].as_u64().unwrap_or(0),
217 target: Target {
218 tagged_key: TaggedKey {
219 key: vout["target"]["tagged_key"]["key"].as_str().unwrap_or("").to_string(),
220 view_tag: vout["target"]["tagged_key"]["view_tag"].as_str().unwrap_or("").to_string(),
221 }
222 }
223 });
224 }
225 let mut ecdh_raw_tx = Vec::new();
226 for ecdh in json_final["rct_signatures"]["ecdhInfo"].as_array().unwrap().iter() {
227 ecdh_raw_tx.push(EcdhInfo {
228 trunc_amount: ecdh["trunc_amount"].as_str().unwrap_or("").to_string(),
229 });
230 }
231 let mut bpp_raw_tx = Vec::new();
232 for bpp in json_final["rctsig_prunable"]["bpp"].as_array().unwrap().iter() {
233 bpp_raw_tx.push(BPP {
234 A: bpp["A"].as_str().unwrap_or("").to_string(),
235 A1: bpp["A1"].as_str().unwrap_or("").to_string(),
236 B: bpp["B"].as_str().unwrap_or("").to_string(),
237 r1: bpp["r1"].as_str().unwrap_or("").to_string(),
238 s1: bpp["s1"].as_str().unwrap_or("").to_string(),
239 d1: bpp["d1"].as_str().unwrap_or("").to_string(),
240 L: {
241 let mut l = Vec::new();
242 for l_part in bpp["L"].as_array().unwrap_or(&Vec::new()).to_vec().iter() {
243 l.push(l_part.as_str().unwrap_or("").to_string());
244 }
245 l
246 },
247 R: {
248 let mut r = Vec::new();
249 for r_part in bpp["R"].as_array().unwrap_or(&Vec::new()).to_vec().iter() {
250 r.push(r_part.as_str().unwrap_or("").to_string());
251 }
252 r
253 }
254 });
255 }
256 let mut clsags_raw_tx = Vec::new();
257 for clsag in json_final["rctsig_prunable"]["CLSAGs"].as_array().unwrap().iter() {
258 clsags_raw_tx.push(CLSAG {
259 s: {
260 let mut s = Vec::new();
261 for s_part in clsag["s"].as_array().unwrap_or(&Vec::new()).to_vec().iter() {
262 s.push(s_part.as_str().unwrap_or("").to_string());
263 }
264 s
265 },
266 c1: clsag["c1"].as_str().unwrap_or("").to_string(),
267 D: clsag["D"].as_str().unwrap_or("").to_string(),
268 });
269 }
270 Ok(RawTx {
271 version: json_final["version"].as_u64().unwrap_or(0),
272 unlock_time: json_final["unlock_time"].as_u64().unwrap_or(0),
273 vin: vin_raw_tx,
274 vout: vout_raw_tx,
275 extra: json_final["extra"].as_str().unwrap_or("").as_bytes().to_vec(),
276 rct_signatures: RctSignatures {
277 type_int: json_final["rct_signatures"]["type"].as_u64().unwrap_or(0),
278 txn_fee: json_final["rct_signatures"]["txnFee"].as_u64().unwrap_or(0),
279 ecdh_info: ecdh_raw_tx,
280 out_pk: json_final["rct_signatures"]["outPk"].as_array().unwrap().to_vec().iter().map(|x| x.as_str().unwrap_or("").to_string()).collect(),
281 },
282 rctsig_prunable: RctsigPrunable {
283 nbp: json_final["rctsig_prunable"]["nbp"].as_u64().unwrap_or(0),
284 bpp: bpp_raw_tx,
285 CLSAGs: clsags_raw_tx,
286 pseudo_outs: json_final["rctsig_prunable"]["pseudoOuts"].as_array().unwrap().to_vec().iter().map(|x| x.as_str().unwrap_or("").to_string()).collect(),
287 }
288 })
289}