diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..7b065c8 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,283 @@ +#![feature(proc_macro_hygiene, decl_macro)] +#[macro_use] extern crate rocket; +#[macro_use] extern crate rocket_contrib; +#[macro_use] extern crate serde_derive; +extern crate reqwest; +extern crate qrcode_generator; +mod data_types; + +use rocket::http::RawStr; +use rocket::response::Redirect; +use rocket_contrib::json::{Json,JsonValue}; +use rocket_contrib::templates::Template; +use rocket_contrib::serve::StaticFiles; +use reqwest::blocking::{RequestBuilder, Client}; +use reqwest::Error; +use qrcode_generator::QrCodeEcc; +use std::env; +use std::clone; +use data_types::*; + +// fn issue_rpc(method: &str, params: Option) -> RequestBuilder { +// let http_client = Client::new(); +// let url = format!( +// "{}/json_rpc", +// env::var("DAEMON_URI").unwrap() +// ); +// let post_data = RPCPayload { +// method: Some(method.to_string()), +// params: params, +// ..Default::default() +// }; +// http_client.post(&url).json(&post_data) +// } + +// fn issue_raw_rpc(method: &str, params: JsonValue) -> RequestBuilder { +// let http_client = Client::new(); +// let url = format!( +// "{}/{}", +// env::var("DAEMON_URI").unwrap(), +// &method +// ); +// http_client.post(&url).json(¶ms) +// } +// +fn build_rpc(method: &str, data: Option, raw: bool) -> RequestBuilder { + let http_client = Client::new(); + let daemon_uri = env::var("DAEMON_URI").unwrap(); + match raw { + true => { + let uri = format!("{}/{}", &daemon_uri, &method); + if let None = data { + http_client.post(&uri) + } else { + http_client.post(&uri).json(&data) + } + }, + false => { + let uri = format!("{}/json_rpc", &daemon_uri); + let data = RPCPayload { + method: Some(method.to_string()), + ..Default::default() + }; + http_client.post(&uri).json(&data) + } + } +} +// +// #[get("/block/hash/")] +// fn get_block_by_hash(block_hash: String) -> Template { +// let params = RPCParams { +// hash: Some(block_hash), +// ..Default::default() +// }; +// let res: GetBlock = issue_rpc(&"get_block", Some(params)) +// .send().unwrap().json().unwrap(); +// Template::render("block", &res.result) +// } +// +// #[get("/block/height/")] +// fn get_block_by_height(block_height: String) -> Template { +// let params = RPCParams { +// height: Some(block_height), +// ..Default::default() +// }; +// let res: GetBlock = issue_rpc(&"get_block", Some(params)) +// .send().unwrap().json().unwrap(); +// Template::render("block", &res.result) +// } + +#[get("/transaction/")] +fn get_transaction_by_hash(tx_hash: String) -> Template { + let params: JsonValue = json!({ + "txs_hashes": [&tx_hash], + "decode_as_json": true + }); + + let mut res: GetTransactions = build_rpc( + &"get_transactions", Some(params), true + ).send().unwrap().json().unwrap(); + + if res.txs.len() > 0 { + for f in &mut res.txs { + f.process(); + }; + }; + + let context = json!({ + "tx_info": res.txs, + "tx_hash": tx_hash + }); + Template::render("transaction", context) +} + +// #[get("/address/?&&&")] +// fn show_wallet_address( +// wallet_address: String, +// tx_amount: Option, +// tx_description: Option, +// recipient_name: Option, +// tx_payment_id: Option +// ) -> Template { +// let address_uri = format!( +// "wownero:{}&tx_amount={}&tx_description={}&recipient_name={}&tx_payment_id={}", +// wallet_address, +// tx_amount.unwrap_or("".to_string()), +// tx_description.unwrap_or("".to_string()), +// recipient_name.unwrap_or("".to_string()), +// tx_payment_id.unwrap_or("".to_string()) +// ); +// let qr_code: String = qrcode_generator::to_svg_to_string(address_uri, QrCodeEcc::Low, 256, None) +// .unwrap(); +// let qr_code: String = base64::encode(qr_code); +// let context: JsonValue = json!({ +// "qr_code": qr_code, +// "wallet_address": wallet_address +// }); +// Template::render("address", context) +// } +// +// #[get("/search?")] +// fn search(value: &RawStr) -> Redirect { +// // This search implementation is not ideal but it works. +// // We basically check the length of the search value and +// // attempt to redirect to the appropriate route. +// let sl: usize = value.len(); +// if sl == 0 { +// return Redirect::found(uri!(index)); +// } else if sl < 8 { +// // Less than 8 characters is probably a block height. If it can +// // be parsed as valid u32 then redirect to `get_block_by_height`, +// // otherwise redirect to the error response. +// match value.parse::() { +// Ok(_) => return Redirect::found(uri!(get_block_by_height: value.as_str())), +// Err(_) => return Redirect::found(uri!(error)) +// } +// } else if sl == 64 { +// // Equal to 64 characters is probably a hash; block or tx. +// // For this we attempt to query for a block with +// // given hash. If we don't receive a valid/expected +// // response then we attempt to query for a transaction hash. +// // If neither works then redirect to error response. +// let block_hash_params = RPCParams { +// hash: Some(value.to_string()), +// ..Default::default() +// }; +// let check_valid_block_hash: Result = issue_rpc( +// &"get_block", Some(block_hash_params) +// ).send().unwrap().json(); +// +// match check_valid_block_hash { +// Ok(_) => return Redirect::found(uri!(get_block_by_hash: value.as_str())), +// Err(_) => { +// let tx_hash_params: JsonValue = json!({"txs_hashes": [&value.as_str()]}); +// let check_valid_tx_hash: Result = issue_raw_rpc( +// &"get_transactions", tx_hash_params +// ).send().unwrap().json(); +// +// match check_valid_tx_hash { +// Ok(_) => return Redirect::found(uri!(get_transaction_by_hash: value.as_str())), +// Err(_) => return Redirect::found(uri!(error)) +// } +// } +// } +// } else if sl == 95 { +// // Equal to 95 characters is probably a wallet address. +// // For this let's just redirect to the `show_wallet_address` route. +// return Redirect::found(uri!(show_wallet_address: value.as_str(), "", "", "", "")) +// } else if sl == 105 { +// // Equal to 105 characters is probably an integrated address. +// // For this let's just redirect to the `show_wallet_address` route. +// return Redirect::found(uri!(show_wallet_address: value.as_str(), "", "", "", "")) +// } else { +// // Anything else hasn't been implemented yet +// // so redirect to error response. +// return Redirect::found(uri!(error)); +// }; +// } +// +// #[get("/tx_pool")] +// fn show_tx_pool() -> Json { +// let mut tx_pool: GetTransactionPool = build_rpc( +// &"get_transaction_pool", None, true +// ).send().unwrap().json().unwrap(); +// +// for f in &mut tx_pool.transactions { +// f.process(); +// }; +// +// Json(tx_pool) +// } + +#[get("/")] +fn index() -> Template { + let daemon_info: GetInfo = build_rpc( + &"get_info", None, false + ).send().unwrap().json().unwrap(); + + let tx_pool: GetTransactionPool = build_rpc( + &"get_transaction_pool", None, true + ).send().unwrap().json().unwrap(); + + // let mut tx_pool_txs = tx_pool.transactions; + // + // match tx_pool_txs { + // Some(s) => { + // for mut f in s { + // let mut _fp = f.process(); + // } + // }, + // None => {} + // }; + + // for f in &mut tx_pool.transactions { + // f.process(); + // }; + + let context: JsonValue = json!({ + "daemon_info": daemon_info.result, + "tx_pool_txs": tx_pool.transactions + }); + + Template::render("index", context) +} + +#[get("/error", )] +fn error() -> JsonValue { + json!({ + "status": "error", + "reason": "There was an error while searching the provided values." + }) +} + +#[catch(404)] +fn not_found() -> JsonValue { + json!({ + "status": "error", + "reason": "Resource was not found." + }) +} + +fn main() { + let env_url = env::var("DAEMON_URI"); + match env_url { + Ok(_) => { + rocket::ignite() + .mount("/", routes![ + index, + // search, + // show_tx_pool, + // get_block_by_height, + // get_block_by_hash, + get_transaction_by_hash, + // show_wallet_address, + error + ]) + .mount("/static", StaticFiles::from("./static")) + .register(catchers![not_found]) + .attach(Template::fairing()) + .launch(); + }, + Err(_) => panic!("Environment variable `DAEMON_URI` not provided.") + } +}