From 4aaf39cf5f7c5c01d305ac1a6197497be3445ba7 Mon Sep 17 00:00:00 2001 From: aglkm <39521015+aglkm@users.noreply.github.com> Date: Thu, 20 Jun 2024 15:16:34 +0300 Subject: [PATCH] return back search by unspent output feature --- src/data.rs | 22 +++++++++++++ src/main.rs | 67 +++++++++++++++++++++++++++++--------- src/requests.rs | 36 ++++++++++++++++++++ templates/error.html.tera | 1 + templates/output.html.tera | 52 +++++++++++++++++++++++++++++ templates/search.html.tera | 1 + 6 files changed, 163 insertions(+), 16 deletions(-) create mode 100644 templates/output.html.tera diff --git a/src/data.rs b/src/data.rs index 5622eea..8f0b454 100644 --- a/src/data.rs +++ b/src/data.rs @@ -188,3 +188,25 @@ impl ExplorerConfig { } } +// Output data +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Output { + pub height: String, + pub commit: String, + pub out_type: String, + pub status: String, + pub raw_data: String, +} + +impl Output { + pub fn new() -> Output { + Output { + height: String::new(), + commit: String::new(), + out_type: String::new(), + status: String::new(), + raw_data: String::new(), + } + } +} + diff --git a/src/main.rs b/src/main.rs index a7c9f72..77afd63 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,7 @@ use crate::data::Dashboard; use crate::data::Block; use crate::data::Transactions; use crate::data::Kernel; +use crate::data::Output; use crate::requests::CONFIG; @@ -148,11 +149,33 @@ async fn kernel(excess: &str) -> Template { } +// Rendering page for a specified output. +#[get("/output/")] +async fn output(commit: &str) -> Template { + let mut output = Output::new(); + + let _ = requests::get_output(&commit, &mut output).await; + + if output.commit.is_empty() == false { + return Template::render("output", context! { + route: "output", + cg_api: CONFIG.coingecko_api.clone(), + output, + }) + } + + return Template::render("error", context! { + route: "error", + cg_api: CONFIG.coingecko_api.clone(), + }) +} + + // Handling search request. // Using Option<&str> to match '/search' query without input params. // https://github.com/rwf2/Rocket/issues/608 #[get("/search?")] -fn search(input: Option<&str>) -> Either { +pub async fn search(input: Option<&str>) -> Either { // Unwrap Option and forward to Search page if no parameters let input = match input { Some(value) => value, @@ -173,9 +196,26 @@ fn search(input: Option<&str>) -> Either { } else if input.len() == 64 { return Either::Right(Redirect::to(uri!(block_header_by_hash(input)))); - // Kernel + // Kernel or Unspent Output } else if input.len() == 66 { - return Either::Right(Redirect::to(uri!(kernel(input)))); + // First search for Kernel. + // If found, redirect to Kernel page, otherwise search for Unspent Output. + // As we can't distinguish between Kernel and Output, this will produce a redundant + // get_kernel call, but will allow for better UI (no need to ask user to input the type + // of the search request). + let mut kernel = Kernel::new(); + + let _ = requests::get_kernel(&input, &mut kernel).await; + + if kernel.excess.is_empty() == false { + // Here we are redirecting to kernel page and call get_kernel again there. + // Kernel page is a separate route and we want it to be accessed directly and + // via search functionality. + return Either::Right(Redirect::to(uri!(kernel(input)))); + } else { + // If Kernel not found, then search for Unspent Output + return Either::Right(Redirect::to(uri!(output(input)))); + } } } @@ -222,8 +262,7 @@ async fn api_owner(data: &str) -> String { // Foreign API. -// All methods are whitelisted, except get_outputs. -// get_outputs consumes CPU and blocks certain other rpc calls. +// All methods are whitelisted. #[post("/v2/foreign", data="")] async fn api_foreign(data: &str) -> String { if CONFIG.public_api == "enabled" { @@ -239,18 +278,14 @@ async fn api_foreign(data: &str) -> String { _ => return "{\"error\":\"bad syntax\"}".to_string(), }; - if method != "get_outputs" { - let resp = requests::call(method, v["params"].to_string().as_str(), v["id"].to_string().as_str(), "foreign").await; + let resp = requests::call(method, v["params"].to_string().as_str(), v["id"].to_string().as_str(), "foreign").await; - let result = match resp { - Ok(value) => value, - Err(_err) => return "{\"error\":\"rpc call failed\"}".to_string(), - }; + let result = match resp { + Ok(value) => value, + Err(_err) => return "{\"error\":\"rpc call failed\"}".to_string(), + }; - return result.to_string(); - } - - "{\"error\":\"not allowed\"}".to_string() + return result.to_string(); } else { "{\"error\":\"not allowed\"}".to_string() } @@ -654,7 +689,7 @@ async fn main() { block_weight, block_details_by_height, block_header_by_hash, soft_supply, production_cost, reward_ratio, breakeven_cost, last_block_age, block_list_by_height, block_list_index, search, kernel, - api_owner, api_foreign]) + output, api_owner, api_foreign]) .mount("/static", FileServer::from("static")) .attach(Template::fairing()) .launch() diff --git a/src/requests.rs b/src/requests.rs index c26acec..9681f5a 100644 --- a/src/requests.rs +++ b/src/requests.rs @@ -17,6 +17,7 @@ use crate::data::Block; use crate::data::Transactions; use crate::data::ExplorerConfig; use crate::data::Kernel; +use crate::data::Output; // Static explorer config structure @@ -419,6 +420,41 @@ pub async fn get_block_header(hash: &str, height: &mut String) } +// Get output. +pub async fn get_output(commit: &str, output: &mut Output) -> Result<(), anyhow::Error> { + // First check whether output is broadcasted but not confirmed yet (in mempool) + let mut resp = call("get_unconfirmed_transactions", "[]", "1", "foreign").await?; + + if resp["result"]["Ok"].is_null() == false { + for tx in resp["result"]["Ok"].as_array().unwrap() { + for out in tx["tx"]["body"]["outputs"].as_array().unwrap() { + if out["commit"].as_str().unwrap() == commit { + // Only Plain outputs in the mempool + output.out_type = "Plain".to_string(); + output.commit = out["commit"].as_str().unwrap().to_string(); + output.status = "Unconfirmed".to_string(); + // Found it, no need to continue + return Ok(()); + } + } + } + } + + let params = &format!("[[\"{}\"], null, null, true, true]", commit)[..]; + + resp = call("get_outputs", params, "1", "foreign").await?; + + if resp["result"]["Ok"][0].is_null() == false { + output.height = resp["result"]["Ok"][0]["block_height"].to_string(); + output.commit = resp["result"]["Ok"][0]["commit"].as_str().unwrap().to_string(); + output.out_type = resp["result"]["Ok"][0]["output_type"].as_str().unwrap().to_string(); + output.raw_data = serde_json::to_string_pretty(&resp).unwrap() + } + + Ok(()) +} + + // Get kernel. pub async fn get_kernel(excess: &str, kernel: &mut Kernel) -> Result<(), anyhow::Error> { // First check whether kernel is broadcasted but not confirmed yet (in mempool) diff --git a/templates/error.html.tera b/templates/error.html.tera index 536b6b0..d44052e 100644 --- a/templates/error.html.tera +++ b/templates/error.html.tera @@ -11,6 +11,7 @@
Block Number
Block Hash
Kernel
+
Unspent Output
diff --git a/templates/output.html.tera b/templates/output.html.tera new file mode 100644 index 0000000..2ef9414 --- /dev/null +++ b/templates/output.html.tera @@ -0,0 +1,52 @@ +{% extends "base" %} + +{% block content %} + + + +
+
+
+
OUTPUT
+ {% if output.status == "Unconfirmed" %} + UNCONFIRMED + {% endif %} +
+
+
+
Commitment 
+
{{ output.commit }}
+
+ {% if output.status != "Unconfirmed" %} +
+
+
Block Height 
+ +
+ {% endif %} +
+
+
Type 
+
{{ output.out_type }}
+
+
+
+ + {% if output.status != "Unconfirmed" %} +
+
+
RAW DATA
+
+
{{ output.raw_data }}
+
+
+ {% endif %} + +
+ +{% endblock %} + diff --git a/templates/search.html.tera b/templates/search.html.tera index 33f5509..bc55ba2 100644 --- a/templates/search.html.tera +++ b/templates/search.html.tera @@ -21,6 +21,7 @@
Block Number
Block Hash
Kernel
+
Unspent Output