search over unspent outputs

This commit is contained in:
aglkm
2024-06-04 16:33:50 +03:00
parent 2adb567ce7
commit 814722fdfb
5 changed files with 155 additions and 7 deletions

View File

@@ -133,6 +133,29 @@ impl Kernel {
}
// 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(),
}
}
}
// Transactions data
#[derive(Debug)]
pub struct Transactions {

View File

@@ -19,6 +19,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;
@@ -149,11 +150,33 @@ async fn kernel(excess: &str) -> Template {
}
// Rendering page for a specified output.
#[get("/output/<commit>")]
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?<input>")]
fn search(input: Option<&str>) -> Either<Template, Redirect> {
pub async fn search(input: Option<&str>) -> Either<Template, Redirect> {
// Unwrap Option and forward to Search page if no parameters
let input = match input {
Some(value) => value,
@@ -174,9 +197,23 @@ fn search(input: Option<&str>) -> Either<Template, Redirect> {
} else if input.len() == 64 {
return Either::Right(Redirect::to(uri!(block_header_by_hash(input))));
// Kernel
// Kernel or Output
} else if input.len() == 66 {
return Either::Right(Redirect::to(uri!(kernel(input))));
// First search for Kernel
let mut kernel = Kernel::new();
let _ = requests::get_kernel(&input, &mut kernel).await;
if kernel.excess.is_empty() == false {
return Either::Left(Template::render("kernel", context! {
route: "kernel",
cg_api: CONFIG.coingecko_api.clone(),
kernel,
}));
} else {
// If Kernel not found, then search for Output
return Either::Right(Redirect::to(uri!(output(input))));
}
}
}
@@ -188,6 +225,7 @@ fn search(input: Option<&str>) -> Either<Template, Redirect> {
// Owner API.
// Whitelisted methods: get_connected_peers, get_peers, get_status.
#[post("/v2/owner", data="<data>")]
async fn api_owner(data: &str) -> String {
if CONFIG.public_api == "enabled" {
@@ -203,7 +241,6 @@ async fn api_owner(data: &str) -> String {
_ => return "{\"error\":\"bad syntax\"}".to_string(),
};
// Whitelisted methods: get_connected_peer, get_peers, get_status.
if method == "get_connected_peers" || method == "get_peers" || method == "get_status" {
let resp = requests::call(method, v["params"].to_string().as_str(), v["id"].to_string().as_str(), "owner").await;
@@ -646,7 +683,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()

View File

@@ -16,7 +16,8 @@ use crate::data::Dashboard;
use crate::data::Block;
use crate::data::Transactions;
use crate::data::ExplorerConfig;
use crate::Kernel;
use crate::data::Kernel;
use crate::data::Output;
// Static explorer config structure
@@ -447,6 +448,41 @@ pub async fn get_kernel(excess: &str, kernel: &mut Kernel) -> Result<(), anyhow:
}
// 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(())
}
// Collecting block kernels for transactions stats.
pub async fn get_block_kernels(height: &String, blocks: &mut Vec<Block>)
-> Result<(), anyhow::Error> {

View File

@@ -0,0 +1,52 @@
{% extends "base" %}
{% block content %}
<code>
<div class="card rounded-0">
<div class="card-body">
<div class="d-flex justify-content-between">
<div class="darkorange-text"><i class="bi bi-card-text"></i> OUTPUT</div>
{% if output.status == "Unconfirmed" %}
<span class="badge text-bg-warning px-2 py-2">UNCONFIRMED</span>
{% endif %}
</div>
<br>
<div class="d-flex justify-content-between">
<div class="value-text">Commitment&nbsp;</div>
<div class="value-text text-break text-end">{{ output.commit }}</div>
</div>
{% if output.status != "Unconfirmed" %}
<br>
<div class="d-flex justify-content-between">
<div class="value-text">Block Height&nbsp;</div>
<div class="value-text text-end">
<a class="text-decoration-none" href="/block/{{ output.height }}">
{{ output.height }} <i class="bi bi-box-arrow-up-right"></i>
</a>
</div>
</div>
{% endif %}
<br>
<div class="d-flex justify-content-between">
<div class="value-text">Type&nbsp;</div>
<div class="value-text text-end">{{ output.out_type }}</div>
</div>
</div>
</div>
{% if output.status != "Unconfirmed" %}
<div class="card border-top-0 rounded-0">
<div class="card-body" align="left">
<div class="darkorange-text"><i class="bi bi-layout-text-sidebar-reverse"></i> RAW DATA</div>
<br>
<div class="value-text">{{ output.raw_data }}</div>
</div>
</div>
{% endif %}
</code>
{% endblock %}

View File

@@ -4,7 +4,7 @@
<code>
<div class="card">
<div class="card rounded-0">
<div class="card-body mx-2 mt-2 mb-3">
<div class="d-flex justify-content-start mb-1">
<i class="bi bi-box darkorange-text"></i>&nbsp;