use std::io::Cursor;
use super::{block::{Block, BlockDetailsJSON, BlockHeader, EcdhInfo, Gen, KeyRawTx, MinerTxInfo, RawTx, RctSignatures, RctsigPrunable, TaggedKey, Target, Vin, VinRawTx, Vout, BPP, CLSAG}, nodes::DaemonNode};
fn get_json_rpc_url(node: DaemonNode) -> String {
match node.tls {
true => format!("https://{}:{}/json_rpc", node.url, node.port),
false => format!("http://{}:{}/json_rpc", node.url, node.port),
}
}
fn get_rpc_url(node: DaemonNode) -> String {
match node.tls {
true => format!("https://{}:{}", node.url, node.port),
false => format!("http://{}:{}", node.url, node.port),
}
}
pub fn get_block_from_height(block_height: u64, node: DaemonNode) -> Result<Block, String> {
let rpc_url = get_json_rpc_url(node);
let response = ureq::post(&rpc_url)
.set("Content-Type", "application/json")
.send_json(ureq::json!({
"jsonrpc": "2.0",
"id": "0",
"method": "get_block",
"params": {
"height": block_height
}
}));
if let Err(e) = response.as_ref() {
return Err(format!("Error while getting the block from daemon: {}", e));
}
let response: serde_json::Value = response.unwrap().into_json().unwrap();
let block_header = BlockHeader {
block_size: response["result"]["block_header"]["block_size"].as_u64().unwrap(),
block_weight: response["result"]["block_header"]["block_weight"].as_u64().unwrap(),
cumulative_difficulty: response["result"]["block_header"]["cumulative_difficulty"].as_u64().unwrap(),
cumulative_difficulty_top64: response["result"]["block_header"]["cumulative_difficulty_top64"].as_u64().unwrap(),
depth: response["result"]["block_header"]["depth"].as_u64().unwrap(),
difficulty: response["result"]["block_header"]["difficulty"].as_u64().unwrap(),
difficulty_top64: response["result"]["block_header"]["difficulty_top64"].as_u64().unwrap(),
hash: response["result"]["block_header"]["hash"].as_str().unwrap().to_string(),
height: response["result"]["block_header"]["height"].as_u64().unwrap(),
long_term_weight: response["result"]["block_header"]["long_term_weight"].as_u64().unwrap(),
major_version: response["result"]["block_header"]["major_version"].as_u64().unwrap(),
miner_tx_hash: response["result"]["block_header"]["miner_tx_hash"].as_str().unwrap().to_string(),
minor_version: response["result"]["block_header"]["minor_version"].as_u64().unwrap(),
nonce: response["result"]["block_header"]["nonce"].as_u64().unwrap(),
num_txes: response["result"]["block_header"]["num_txes"].as_u64().unwrap(),
orphan_status: response["result"]["block_header"]["orphan_status"].as_bool().unwrap(),
pow_hash: response["result"]["block_header"]["pow_hash"].as_str().unwrap().to_string(),
prev_hash: response["result"]["block_header"]["prev_hash"].as_str().unwrap().to_string(),
reward: response["result"]["block_header"]["reward"].as_u64().unwrap(),
timestamp: response["result"]["block_header"]["timestamp"].as_u64().unwrap(),
wide_cumulative_difficulty: response["result"]["block_header"]["wide_cumulative_difficulty"].as_str().unwrap().to_string(),
wide_difficulty: response["result"]["block_header"]["wide_difficulty"].as_str().unwrap().to_string(),
};
let json = response["result"]["json"].as_str().unwrap().to_string();
let parsed_json: serde_json::Value = serde_json::from_str(&json).unwrap_or(serde_json::Value::Null);
if parsed_json.is_null() {
return Err("Error while parsing the block JSON".to_string());
}
let vin_array = parsed_json["miner_tx"]["vin"].as_array().unwrap();
let mut vin_vec: Vec<Vin> = Vec::new();
for vin in vin_array {
vin_vec.push(Vin {
gen: Gen {
height: vin["gen"]["height"].as_u64().unwrap_or(0),
}
});
};
let vout_array = parsed_json["miner_tx"]["vout"].as_array().unwrap();
let mut vout_vec: Vec<Vout> = Vec::new();
for vout in vout_array {
vout_vec.push(Vout {
amount: vout["amount"].as_u64().unwrap_or(0),
target: Target {
tagged_key: TaggedKey {
key: vout["target"]["key"].as_str().unwrap_or("").to_string(),
view_tag: vout["target"]["view_tag"].as_str().unwrap_or("").to_string(),
}
}
});
};
Ok(Block {
blob: response["result"]["blob"].as_str().unwrap_or("").to_string(),
block_header,
credits: response["result"]["credits"].as_u64().unwrap_or(0),
json: BlockDetailsJSON {
major_version: parsed_json["major_version"].as_u64().unwrap_or(0),
minor_version: parsed_json["minor_version"].as_u64().unwrap_or(0),
timestamp: parsed_json["timestamp"].as_u64().unwrap_or(0),
prev_id: parsed_json["prev_id"].as_str().unwrap_or("").to_string(),
nonce: parsed_json["nonce"].as_u64().unwrap_or(0),
miner_tx: MinerTxInfo {
version: parsed_json["miner_tx"]["version"].as_u64().unwrap_or(0),
unlock_time: parsed_json["miner_tx"]["unlock_time"].as_u64().unwrap_or(0),
vin: vin_vec,
vout: vout_vec,
extra: parsed_json["miner_tx"]["extra"].as_str().unwrap_or("").as_bytes().to_vec(),
rct_signatures: RctSignatures {
type_int: parsed_json["miner_tx"]["rct_signatures"]["type"].as_u64().unwrap_or(0),
txn_fee: 0,
ecdh_info: Vec::new(),
out_pk: Vec::new(),
}
},
tx_hashes: parsed_json["tx_hashes"].as_array().unwrap().to_vec().iter().map(|x| x.as_str().unwrap().to_string()).collect(),
},
miner_tx_hash: response["result"]["miner_tx_hash"].as_str().unwrap_or("").to_string(),
status: response["result"]["status"].as_str().unwrap_or("ERROR").to_string(),
top_hash: response["result"]["top_hash"].as_str().unwrap_or("").to_string(),
untrusted: response["result"]["untrusted"].as_bool().unwrap_or(false),
})
}
pub fn get_height(node: DaemonNode) -> Result<u64, String> {
let rpc_url = get_rpc_url(node);
let reader = Cursor::new(Vec::new());
let response = ureq::get(format!("{}/get_height", &rpc_url).as_str())
.set("Content-Type", "application/json").send(reader);
if let Err(e) = response.as_ref() {
return Err(format!("Error while getting the block count (height) from daemon: {}", e));
}
let response: serde_json::Value = response.unwrap().into_json().unwrap_or(serde_json::Value::Null);
if response.is_null() {
return Err("Error while parsing the block count (height) JSON".to_string());
}
Ok(response["height"].as_u64().unwrap_or(0))
}
pub fn get_transaction_from_hash(hash: String, node: DaemonNode) -> Result<RawTx, String> {
let rpc_url = format!("{}/get_transactions", get_rpc_url(node));
let response = ureq::post(&rpc_url)
.set("Content-Type", "application/json")
.send_json(ureq::json!({
"txs_hashes": [hash],
"decode_as_json": true,
}));
if let Err(e) = response.as_ref() {
return Err(format!("Error while getting the transaction from daemon: {}", e));
}
let response: serde_json::Value = response.unwrap().into_json().unwrap_or(serde_json::Value::Null);
if response.is_null() {
return Err("Error while parsing the transaction JSON".to_string());
}
let json_part = response["txs"][0]["as_json"].as_str().unwrap_or("").to_string();
if json_part.is_empty() {
return Err("Error while getting the as_json part".to_string());
}
let json_final = serde_json::from_str(&json_part).unwrap_or(serde_json::Value::Null);
if json_final.is_null() {
return Err("Error while parsing the as_json part".to_string());
}
let mut vin_raw_tx = Vec::new();
for vin in json_final["vin"].as_array().unwrap().iter() {
vin_raw_tx.push(VinRawTx {
key: KeyRawTx {
amount: vin["key"]["amount"].as_u64().unwrap_or(0),
key_offsets: vin["key"]["key_offsets"].as_array().unwrap_or(&Vec::new()).to_vec().iter().map(|x| x.as_u64().unwrap_or(0)).collect(),
k_image: vin["key"]["k_image"].as_str().unwrap_or("").to_string(),
}
});
}
let mut vout_raw_tx = Vec::new();
for vout in json_final["vout"].as_array().unwrap().iter() {
vout_raw_tx.push(Vout {
amount: vout["amount"].as_u64().unwrap_or(0),
target: Target {
tagged_key: TaggedKey {
key: vout["target"]["tagged_key"]["key"].as_str().unwrap_or("").to_string(),
view_tag: vout["target"]["tagged_key"]["view_tag"].as_str().unwrap_or("").to_string(),
}
}
});
}
let mut ecdh_raw_tx = Vec::new();
for ecdh in json_final["rct_signatures"]["ecdhInfo"].as_array().unwrap().iter() {
ecdh_raw_tx.push(EcdhInfo {
trunc_amount: ecdh["trunc_amount"].as_str().unwrap_or("").to_string(),
});
}
let mut bpp_raw_tx = Vec::new();
for bpp in json_final["rctsig_prunable"]["bpp"].as_array().unwrap().iter() {
bpp_raw_tx.push(BPP {
A: bpp["A"].as_str().unwrap_or("").to_string(),
A1: bpp["A1"].as_str().unwrap_or("").to_string(),
B: bpp["B"].as_str().unwrap_or("").to_string(),
r1: bpp["r1"].as_str().unwrap_or("").to_string(),
s1: bpp["s1"].as_str().unwrap_or("").to_string(),
d1: bpp["d1"].as_str().unwrap_or("").to_string(),
L: {
let mut l = Vec::new();
for l_part in bpp["L"].as_array().unwrap_or(&Vec::new()).to_vec().iter() {
l.push(l_part.as_str().unwrap_or("").to_string());
}
l
},
R: {
let mut r = Vec::new();
for r_part in bpp["R"].as_array().unwrap_or(&Vec::new()).to_vec().iter() {
r.push(r_part.as_str().unwrap_or("").to_string());
}
r
}
});
}
let mut clsags_raw_tx = Vec::new();
for clsag in json_final["rctsig_prunable"]["CLSAGs"].as_array().unwrap().iter() {
clsags_raw_tx.push(CLSAG {
s: {
let mut s = Vec::new();
for s_part in clsag["s"].as_array().unwrap_or(&Vec::new()).to_vec().iter() {
s.push(s_part.as_str().unwrap_or("").to_string());
}
s
},
c1: clsag["c1"].as_str().unwrap_or("").to_string(),
D: clsag["D"].as_str().unwrap_or("").to_string(),
});
}
Ok(RawTx {
version: json_final["version"].as_u64().unwrap_or(0),
unlock_time: json_final["unlock_time"].as_u64().unwrap_or(0),
vin: vin_raw_tx,
vout: vout_raw_tx,
extra: json_final["extra"].as_str().unwrap_or("").as_bytes().to_vec(),
rct_signatures: RctSignatures {
type_int: json_final["rct_signatures"]["type"].as_u64().unwrap_or(0),
txn_fee: json_final["rct_signatures"]["txnFee"].as_u64().unwrap_or(0),
ecdh_info: ecdh_raw_tx,
out_pk: json_final["rct_signatures"]["outPk"].as_array().unwrap().to_vec().iter().map(|x| x.as_str().unwrap_or("").to_string()).collect(),
},
rctsig_prunable: RctsigPrunable {
nbp: json_final["rctsig_prunable"]["nbp"].as_u64().unwrap_or(0),
bpp: bpp_raw_tx,
CLSAGs: clsags_raw_tx,
pseudo_outs: json_final["rctsig_prunable"]["pseudoOuts"].as_array().unwrap().to_vec().iter().map(|x| x.as_str().unwrap_or("").to_string()).collect(),
}
})
}