mirror of
https://github.com/transatoshi-mw/grin-explorer.git
synced 2025-10-21 05:23:41 +00:00
93
Cargo.lock
generated
93
Cargo.lock
generated
@@ -41,6 +41,55 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.86"
|
||||
@@ -234,6 +283,12 @@ dependencies = [
|
||||
"phf_codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.1.0"
|
||||
@@ -482,6 +537,29 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
|
||||
dependencies = [
|
||||
"log",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"env_filter",
|
||||
"humantime",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
@@ -736,13 +814,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "grin-explorer"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"colored",
|
||||
"config",
|
||||
"either",
|
||||
"env_logger",
|
||||
"fs_extra",
|
||||
"futures",
|
||||
"humantime",
|
||||
@@ -982,6 +1061,12 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
@@ -2540,6 +2625,12 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "grin-explorer"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -20,6 +20,7 @@ lazy_static = "1.4.0"
|
||||
shellexpand = "3.1.0"
|
||||
either = "1.11.0"
|
||||
anyhow = "1.0.86"
|
||||
env_logger = "0.11.3"
|
||||
|
||||
[dependencies.reqwest]
|
||||
version = "0.11.23"
|
||||
|
@@ -7,20 +7,38 @@ port = "3413"
|
||||
# Node protocol. Either HTTP or HTTPS.
|
||||
proto = "http"
|
||||
|
||||
# API username.
|
||||
# API username. Comment out if user is not required.
|
||||
user = "grin"
|
||||
|
||||
# API secret path.
|
||||
# API secret path. Comment out if secret is not required.
|
||||
api_secret_path = "~/.grin/main/.api_secret"
|
||||
|
||||
# Foreign API secret path.
|
||||
# Foreign API secret path. Comment out if secret is not required.
|
||||
foreign_api_secret_path = "~/.grin/main/.foreign_api_secret"
|
||||
|
||||
# Path to Grin directory.
|
||||
grin_dir = "~/.grin"
|
||||
|
||||
# CoinGecko API on/off switch.
|
||||
coingecko_api = "on"
|
||||
# Enable or disable CoinGecko API.
|
||||
coingecko_api = "enabled"
|
||||
|
||||
# Enable or disable node POST API public access.
|
||||
public_api = "enabled"
|
||||
|
||||
|
||||
# Grinnode config
|
||||
# ip = "grinnode.live"
|
||||
# port = "3413"
|
||||
# proto = "https"
|
||||
# coingecko_api = "enabled"
|
||||
# public_api = "enabled"
|
||||
|
||||
|
||||
# Grincoin config
|
||||
# ip = "grincoin.org"
|
||||
# proto = "https"
|
||||
# coingecko_api = "enabled"
|
||||
# public_api = "enabled"
|
||||
|
||||
|
||||
# Testnet config
|
||||
@@ -31,5 +49,6 @@ coingecko_api = "on"
|
||||
# api_secret_path = "~/.grin/test/.api_secret"
|
||||
# foreign_api_secret_path = "~/.grin/test/.foreign_api_secret"
|
||||
# grin_dir = "~/.grin"
|
||||
# coingecko_api = "off"
|
||||
# coingecko_api = "disabled"
|
||||
# public_api = "enabled"
|
||||
|
||||
|
@@ -22,15 +22,14 @@ Grin is the very first, simple and fair MimbleWimble blockchain implementation.
|
||||
cd grin-explorer
|
||||
cargo build --release
|
||||
```
|
||||
4. Run executable: `./target/release/grin-explorer`
|
||||
4. Run executable: `RUST_LOG=rocket=warn,grin_explorer ./target/release/grin-explorer`
|
||||
|
||||
You will see the following output:
|
||||
|
||||
```
|
||||
[ INFO ] Starting up Explorer.
|
||||
[ INFO ] Starting up Rocket engine.
|
||||
🚀 Rocket has launched from http://127.0.0.1:8000
|
||||
[ OK ] Explorer Ready.
|
||||
[2024-06-19T13:12:34Z INFO grin_explorer] starting up.
|
||||
[2024-06-19T13:12:34Z WARN rocket::launch] 🚀 Rocket has launched from http://127.0.0.1:8000
|
||||
[2024-06-19T13:12:34Z INFO grin_explorer] ready.
|
||||
```
|
||||
|
||||
5. Open explorer in your browser: http://127.0.0.1:8000
|
||||
|
@@ -1,6 +1,5 @@
|
||||
[default]
|
||||
address = "127.0.0.1"
|
||||
log_level = "critical"
|
||||
|
||||
# Uncomment and change default port number (8000) if another instance of the explorer is needed to run.
|
||||
# E.g. Mainnet (8000) and Testnet (8001) instances.
|
||||
|
27
src/data.rs
27
src/data.rs
@@ -35,8 +35,6 @@ pub struct Dashboard {
|
||||
// mempool
|
||||
pub txns: String,
|
||||
pub stem: String,
|
||||
// coingecko api
|
||||
pub cg_api: String,
|
||||
}
|
||||
|
||||
impl Dashboard {
|
||||
@@ -66,7 +64,6 @@ impl Dashboard {
|
||||
breakeven_cost: String::new(),
|
||||
txns: String::new(),
|
||||
stem: String::new(),
|
||||
cg_api: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,6 +167,7 @@ pub struct ExplorerConfig {
|
||||
pub api_secret: String,
|
||||
pub foreign_api_secret: String,
|
||||
pub coingecko_api: String,
|
||||
pub public_api: String,
|
||||
}
|
||||
|
||||
impl ExplorerConfig {
|
||||
@@ -185,6 +183,29 @@ impl ExplorerConfig {
|
||||
api_secret: String::new(),
|
||||
foreign_api_secret: String::new(),
|
||||
coingecko_api: String::new(),
|
||||
public_api: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
181
src/main.rs
181
src/main.rs
@@ -5,7 +5,6 @@ use rocket::fs::FileServer;
|
||||
use rocket::State;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
use colored::Colorize;
|
||||
use rocket::tokio;
|
||||
use rocket::response::Redirect;
|
||||
use either::Either;
|
||||
@@ -19,6 +18,8 @@ use crate::data::Dashboard;
|
||||
use crate::data::Block;
|
||||
use crate::data::Transactions;
|
||||
use crate::data::Kernel;
|
||||
use crate::data::Output;
|
||||
use crate::requests::CONFIG;
|
||||
|
||||
|
||||
// Rendering main (Dashboard) page.
|
||||
@@ -30,7 +31,7 @@ fn index(dashboard: &State<Arc<Mutex<Dashboard>>>) -> Template {
|
||||
route: "index",
|
||||
node_ver: &data.node_ver,
|
||||
proto_ver: &data.proto_ver,
|
||||
cg_api: &data.cg_api,
|
||||
cg_api: CONFIG.coingecko_api.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -39,7 +40,8 @@ fn index(dashboard: &State<Arc<Mutex<Dashboard>>>) -> Template {
|
||||
#[get("/block_list")]
|
||||
fn block_list() -> Template {
|
||||
Template::render("block_list", context! {
|
||||
route: "block_list",
|
||||
route: "block_list",
|
||||
cg_api: CONFIG.coingecko_api.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -60,11 +62,13 @@ async fn block_list_by_height(input_height: &str) -> Template {
|
||||
|
||||
if index >= height {
|
||||
Template::render("block_list", context! {
|
||||
route: "block_list",
|
||||
route: "block_list",
|
||||
cg_api: CONFIG.coingecko_api.clone(),
|
||||
})
|
||||
} else {
|
||||
Template::render("block_list", context! {
|
||||
route: "block_list_by_height",
|
||||
route: "block_list_by_height",
|
||||
cg_api: CONFIG.coingecko_api.clone(),
|
||||
index,
|
||||
blocks,
|
||||
height,
|
||||
@@ -72,7 +76,8 @@ async fn block_list_by_height(input_height: &str) -> Template {
|
||||
}
|
||||
} else {
|
||||
Template::render("block_list", context! {
|
||||
route: "block_list",
|
||||
route: "block_list",
|
||||
cg_api: CONFIG.coingecko_api.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -88,14 +93,16 @@ async fn block_details_by_height(height: &str) -> Template {
|
||||
|
||||
if block.height.is_empty() == false {
|
||||
return Template::render("block_details", context! {
|
||||
route: "block_details",
|
||||
route: "block_details",
|
||||
cg_api: CONFIG.coingecko_api.clone(),
|
||||
block,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Template::render("error", context! {
|
||||
route: "error",
|
||||
route: "error",
|
||||
cg_api: CONFIG.coingecko_api.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -114,7 +121,8 @@ async fn block_header_by_hash(hash: &str) -> Either<Template, Redirect> {
|
||||
}
|
||||
|
||||
return Either::Left(Template::render("error", context! {
|
||||
route: "error",
|
||||
route: "error",
|
||||
cg_api: CONFIG.coingecko_api.clone(),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -128,13 +136,37 @@ async fn kernel(excess: &str) -> Template {
|
||||
|
||||
if kernel.excess.is_empty() == false {
|
||||
return Template::render("kernel", context! {
|
||||
route: "kernel",
|
||||
route: "kernel",
|
||||
cg_api: CONFIG.coingecko_api.clone(),
|
||||
kernel,
|
||||
})
|
||||
}
|
||||
|
||||
return Template::render("error", context! {
|
||||
route: "error",
|
||||
route: "error",
|
||||
cg_api: CONFIG.coingecko_api.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 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(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -143,11 +175,14 @@ async fn kernel(excess: &str) -> Template {
|
||||
// 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,
|
||||
None => return Either::Left(Template::render("search", context! { route: "search", })),
|
||||
None => return Either::Left(Template::render("search", context! {
|
||||
route: "search",
|
||||
cg_api: CONFIG.coingecko_api.clone(),
|
||||
})),
|
||||
};
|
||||
|
||||
// Check for valid chars
|
||||
@@ -161,46 +196,68 @@ 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 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))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Either::Left(Template::render("error", context! {
|
||||
route: "error",
|
||||
route: "error",
|
||||
cg_api: CONFIG.coingecko_api.clone(),
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
// Owner API.
|
||||
// Whitelisted methods: get_connected_peers, get_peers, get_status.
|
||||
#[post("/v2/owner", data="<data>")]
|
||||
async fn api_owner(data: &str) -> String {
|
||||
let result = serde_json::from_str(data);
|
||||
if CONFIG.public_api == "enabled" {
|
||||
let result = serde_json::from_str(data);
|
||||
|
||||
let v: Value = match result {
|
||||
Ok(value) => value,
|
||||
Err(_err) => return "{\"error\":\"bad syntax\"}".to_string(),
|
||||
};
|
||||
|
||||
let method = match v["method"].as_str() {
|
||||
Some(value) => value,
|
||||
_ => 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;
|
||||
|
||||
let result = match resp {
|
||||
let v: Value = match result {
|
||||
Ok(value) => value,
|
||||
Err(_err) => return "{\"error\":\"rpc call failed\"}".to_string(),
|
||||
Err(_err) => return "{\"error\":\"bad syntax\"}".to_string(),
|
||||
};
|
||||
|
||||
return result.to_string();
|
||||
}
|
||||
let method = match v["method"].as_str() {
|
||||
Some(value) => value,
|
||||
_ => return "{\"error\":\"bad syntax\"}".to_string(),
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
"{\"error\":\"not allowed\"}".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()
|
||||
} else {
|
||||
"{\"error\":\"not allowed\"}".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -208,26 +265,30 @@ async fn api_owner(data: &str) -> String {
|
||||
// All methods are whitelisted.
|
||||
#[post("/v2/foreign", data="<data>")]
|
||||
async fn api_foreign(data: &str) -> String {
|
||||
let result = serde_json::from_str(data);
|
||||
if CONFIG.public_api == "enabled" {
|
||||
let result = serde_json::from_str(data);
|
||||
|
||||
let v: Value = match result {
|
||||
Ok(value) => value,
|
||||
Err(_err) => return "{\"error\":\"bad syntax\"}".to_string(),
|
||||
};
|
||||
let v: Value = match result {
|
||||
Ok(value) => value,
|
||||
Err(_err) => return "{\"error\":\"bad syntax\"}".to_string(),
|
||||
};
|
||||
|
||||
let method = match v["method"].as_str() {
|
||||
Some(value) => value,
|
||||
_ => return "{\"error\":\"bad syntax\"}".to_string(),
|
||||
};
|
||||
let method = match v["method"].as_str() {
|
||||
Some(value) => value,
|
||||
_ => return "{\"error\":\"bad syntax\"}".to_string(),
|
||||
};
|
||||
|
||||
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(),
|
||||
};
|
||||
|
||||
result.to_string()
|
||||
return result.to_string();
|
||||
} else {
|
||||
"{\"error\":\"not allowed\"}".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -369,7 +430,11 @@ fn last_block_age(blocks: &State<Arc<Mutex<Vec<Block>>>>) -> String {
|
||||
fn disk_usage(dashboard: &State<Arc<Mutex<Dashboard>>>) -> String {
|
||||
let data = dashboard.lock().unwrap();
|
||||
|
||||
format!("{} GB", data.disk_usage)
|
||||
if data.disk_usage.is_empty() == false {
|
||||
return format!("{} GB", data.disk_usage);
|
||||
} else {
|
||||
return format!("<i class=\"bi bi-x-lg\"></i>");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -575,7 +640,9 @@ fn block_list_index(dashboard: &State<Arc<Mutex<Dashboard>>>) -> String {
|
||||
// Main
|
||||
#[rocket::main]
|
||||
async fn main() {
|
||||
println!("{} Starting up Explorer.", "[ INFO ]".cyan());
|
||||
env_logger::init();
|
||||
|
||||
info!("starting up.");
|
||||
|
||||
let dash = Arc::new(Mutex::new(Dashboard::new()));
|
||||
let dash_clone = dash.clone();
|
||||
@@ -595,12 +662,12 @@ async fn main() {
|
||||
Ok(_v) => {
|
||||
if ready == false {
|
||||
ready = true;
|
||||
println!("{} Explorer Ready.", "[ OK ]".green());
|
||||
info!("ready.");
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
ready = false;
|
||||
println!("{} {}.", "[ ERROR ]".red(), e);
|
||||
error!("{}", e);
|
||||
},
|
||||
}
|
||||
|
||||
@@ -608,8 +675,6 @@ async fn main() {
|
||||
}
|
||||
});
|
||||
|
||||
println!("{} Starting up Rocket engine.", "[ INFO ]".cyan());
|
||||
|
||||
// Starting Rocket engine.
|
||||
let _ = rocket::build()
|
||||
.manage(dash)
|
||||
@@ -624,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()
|
||||
|
136
src/requests.rs
136
src/requests.rs
@@ -16,12 +16,13 @@ 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
|
||||
lazy_static! {
|
||||
static ref CONFIG: ExplorerConfig = {
|
||||
pub static ref CONFIG: ExplorerConfig = {
|
||||
let mut cfg = ExplorerConfig::new();
|
||||
let settings = Config::builder().add_source(config::File::with_name("Explorer"))
|
||||
.build().unwrap();
|
||||
@@ -38,15 +39,22 @@ lazy_static! {
|
||||
"foreign_api_secret_path" => cfg.foreign_api_secret_path = value,
|
||||
"grin_dir" => cfg.grin_dir = value,
|
||||
"coingecko_api" => cfg.coingecko_api = value,
|
||||
"public_api" => cfg.public_api = value,
|
||||
_ => println!("{} Unknown config setting '{}'.", "[ ERROR ]".red(), name),
|
||||
}
|
||||
}
|
||||
|
||||
cfg.api_secret = fs::read_to_string(format!("{}",
|
||||
shellexpand::tilde(&cfg.api_secret_path))).unwrap();
|
||||
cfg.foreign_api_secret = fs::read_to_string(format!("{}",
|
||||
shellexpand::tilde(&cfg.foreign_api_secret_path))).unwrap();
|
||||
cfg.grin_dir = format!("{}", shellexpand::tilde(&cfg.grin_dir));
|
||||
if cfg.api_secret_path.is_empty() == false {
|
||||
cfg.api_secret = fs::read_to_string(format!("{}", shellexpand::tilde(&cfg.api_secret_path))).unwrap();
|
||||
}
|
||||
|
||||
if cfg.foreign_api_secret_path.is_empty() == false {
|
||||
cfg.foreign_api_secret = fs::read_to_string(format!("{}", shellexpand::tilde(&cfg.foreign_api_secret_path))).unwrap();
|
||||
}
|
||||
|
||||
if cfg.grin_dir.is_empty() == false {
|
||||
cfg.grin_dir = format!("{}", shellexpand::tilde(&cfg.grin_dir));
|
||||
}
|
||||
|
||||
cfg
|
||||
};
|
||||
@@ -58,13 +66,16 @@ pub async fn call(method: &str, params: &str, id: &str, rpc_type: &str) -> Resul
|
||||
let rpc_url;
|
||||
let secret;
|
||||
|
||||
if rpc_type == "owner" {
|
||||
rpc_url = format!("{}://{}:{}/v2/owner", CONFIG.proto, CONFIG.ip, CONFIG.port);
|
||||
secret = CONFIG.api_secret.clone();
|
||||
if CONFIG.port.is_empty() == false {
|
||||
rpc_url = format!("{}://{}:{}/v2/{}", CONFIG.proto, CONFIG.ip, CONFIG.port, rpc_type);
|
||||
} else {
|
||||
rpc_url = format!("{}://{}/v2/{}", CONFIG.proto, CONFIG.ip, rpc_type);
|
||||
}
|
||||
else {
|
||||
rpc_url = format!("{}://{}:{}/v2/foreign", CONFIG.proto, CONFIG.ip, CONFIG.port);
|
||||
secret = CONFIG.foreign_api_secret.clone();
|
||||
|
||||
if rpc_type == "owner" {
|
||||
secret = CONFIG.api_secret.clone();
|
||||
} else {
|
||||
secret = CONFIG.foreign_api_secret.clone();
|
||||
}
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
@@ -75,6 +86,11 @@ pub async fn call(method: &str, params: &str, id: &str, rpc_type: &str) -> Resul
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
match result.error_for_status_ref() {
|
||||
Ok(_res) => (),
|
||||
Err(err) => { error!("rpc failed, status code: {:?}", err.status().unwrap()); },
|
||||
}
|
||||
|
||||
let val: Value = serde_json::from_str(&result.text().await.unwrap())?;
|
||||
|
||||
Ok(val)
|
||||
@@ -94,9 +110,6 @@ pub async fn get_status(dashboard: Arc<Mutex<Dashboard>>) -> Result<(), anyhow::
|
||||
data.proto_ver = resp["result"]["Ok"]["protocol_version"].to_string();
|
||||
}
|
||||
|
||||
// Also set cg_api value
|
||||
data.cg_api = CONFIG.coingecko_api.clone();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -150,7 +163,7 @@ pub async fn get_market(dashboard: Arc<Mutex<Dashboard>>) -> Result<(), Error> {
|
||||
let result;
|
||||
let mut val = Value::Null;
|
||||
|
||||
if CONFIG.coingecko_api == "on" {
|
||||
if CONFIG.coingecko_api == "enabled" {
|
||||
client = reqwest::Client::new();
|
||||
result = client.get("https://api.coingecko.com/api/v3/simple/price?ids=grin&vs_currencies=usd%2Cbtc&include_24hr_vol=true").send().await?;
|
||||
val = serde_json::from_str(&result.text().await.unwrap()).unwrap();
|
||||
@@ -170,23 +183,18 @@ pub async fn get_market(dashboard: Arc<Mutex<Dashboard>>) -> Result<(), Error> {
|
||||
data.supply = supply.to_formatted_string(&Locale::en);
|
||||
|
||||
// https://john-tromp.medium.com/a-case-for-using-soft-total-supply-1169a188d153
|
||||
data.soft_supply = format!("{:.2}",
|
||||
supply.to_string().parse::<f64>().unwrap() / 3150000000.0 * 100.0);
|
||||
data.soft_supply = format!("{:.2}", supply.to_string().parse::<f64>().unwrap() / 3150000000.0 * 100.0);
|
||||
|
||||
if CONFIG.coingecko_api == "on" && val != Value::Null {
|
||||
if CONFIG.coingecko_api == "enabled" && val != Value::Null {
|
||||
// Check if CoingGecko API returned error
|
||||
if let Some(status) = val.get("status") {
|
||||
println!("{} {}.", "[ WARNING ]".yellow(),
|
||||
status["error_message"].as_str().unwrap().to_string());
|
||||
warn!("{}", status["error_message"].as_str().unwrap().to_string());
|
||||
} else {
|
||||
data.price_usd = format!("{:.3}", val["grin"]["usd"].to_string().parse::<f64>()
|
||||
.unwrap());
|
||||
data.price_btc = format!("{:.8}", val["grin"]["btc"].to_string().parse::<f64>()
|
||||
.unwrap());
|
||||
data.price_usd = format!("{:.3}", val["grin"]["usd"].to_string().parse::<f64>().unwrap());
|
||||
data.price_btc = format!("{:.8}", val["grin"]["btc"].to_string().parse::<f64>().unwrap());
|
||||
data.volume_usd = (val["grin"]["usd_24h_vol"].to_string().parse::<f64>().unwrap() as u64)
|
||||
.to_formatted_string(&Locale::en);
|
||||
data.volume_btc = format!("{:.2}", val["grin"]["btc_24h_vol"].to_string().parse::<f64>()
|
||||
.unwrap());
|
||||
data.volume_btc = format!("{:.2}", val["grin"]["btc_24h_vol"].to_string().parse::<f64>().unwrap());
|
||||
data.cap_usd = (((supply as f64) * data.price_usd.parse::<f64>().unwrap()) as u64)
|
||||
.to_formatted_string(&Locale::en);
|
||||
data.cap_btc = (((supply as f64) * data.price_btc.parse::<f64>().unwrap()) as u64)
|
||||
@@ -202,16 +210,24 @@ pub async fn get_market(dashboard: Arc<Mutex<Dashboard>>) -> Result<(), Error> {
|
||||
// Collecting: disk_usage.
|
||||
pub fn get_disk_usage(dashboard: Arc<Mutex<Dashboard>>) -> Result<(), Error> {
|
||||
let mut data = dashboard.lock().unwrap();
|
||||
let chain_data;
|
||||
let chain_dir;
|
||||
|
||||
if CONFIG.coingecko_api == "on" {
|
||||
chain_data = format!("{}/main/chain_data", CONFIG.grin_dir);
|
||||
if CONFIG.coingecko_api == "enabled" {
|
||||
chain_dir = format!("{}/main/chain_data", CONFIG.grin_dir);
|
||||
} else {
|
||||
chain_data = format!("{}/test/chain_data", CONFIG.grin_dir);
|
||||
chain_dir = format!("{}/test/chain_data", CONFIG.grin_dir);
|
||||
}
|
||||
|
||||
data.disk_usage = format!("{:.2}", (get_size(chain_data).unwrap() as f64)
|
||||
/ 1000.0 / 1000.0 / 1000.0);
|
||||
match get_size(chain_dir.clone()) {
|
||||
Ok(chain_size) => data.disk_usage = format!("{:.2}", (chain_size as f64) / 1000.0 / 1000.0 / 1000.0),
|
||||
Err(e) => {
|
||||
if CONFIG.ip == "127.0.0.1" || CONFIG.ip == "0.0.0.0" {
|
||||
error!("{}: \"{}\"", e, chain_dir);
|
||||
} else {
|
||||
// Ignore error for external node connection
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -224,8 +240,7 @@ pub async fn get_mining_stats(dashboard: Arc<Mutex<Dashboard>>) -> Result<(), an
|
||||
|
||||
if height.is_empty() == false && height.parse::<u64>().unwrap() > 1440 {
|
||||
let params1 = &format!("[{}, null, null]", height)[..];
|
||||
let params2 = &format!("[{}, null, null]", height.parse::<u64>().unwrap()
|
||||
- difficulty_window)[..];
|
||||
let params2 = &format!("[{}, null, null]", height.parse::<u64>().unwrap() - difficulty_window)[..];
|
||||
let resp1 = call("get_block", params1, "1", "foreign").await?;
|
||||
let resp2 = call("get_block", params2, "1", "foreign").await?;
|
||||
|
||||
@@ -243,9 +258,9 @@ pub async fn get_mining_stats(dashboard: Arc<Mutex<Dashboard>>) -> Result<(), an
|
||||
// https://forum.grin.mw/t/difference-c31-and-c32-c33/7018/7
|
||||
let hashrate = (net_diff as f64) * 42.0 / 60.0 / 16384.0;
|
||||
|
||||
// KG/s
|
||||
// kG/s
|
||||
if hashrate > 1000.0 {
|
||||
data.hashrate = format!("{:.2} KG/s", hashrate / 1000.0);
|
||||
data.hashrate = format!("{:.2} kG/s", hashrate / 1000.0);
|
||||
// G/s
|
||||
} else {
|
||||
data.hashrate = format!("{:.2} G/s", hashrate);
|
||||
@@ -253,7 +268,7 @@ pub async fn get_mining_stats(dashboard: Arc<Mutex<Dashboard>>) -> Result<(), an
|
||||
|
||||
data.difficulty = net_diff.to_string();
|
||||
|
||||
if CONFIG.coingecko_api == "on" {
|
||||
if CONFIG.coingecko_api == "enabled" {
|
||||
// Calculating G1-mini production per hour
|
||||
let coins_per_hour = 1.2 / hashrate * 60.0 * 60.0;
|
||||
|
||||
@@ -261,10 +276,12 @@ pub async fn get_mining_stats(dashboard: Arc<Mutex<Dashboard>>) -> Result<(), an
|
||||
// Assuming $0.07 per kW/h
|
||||
data.production_cost = format!("{:.3}", 120.0 / 1000.0 * 0.07 * (1.0 / coins_per_hour));
|
||||
|
||||
data.reward_ratio = format!("{:.2}", data.price_usd.parse::<f64>().unwrap()
|
||||
if data.price_usd.is_empty() == false {
|
||||
data.reward_ratio = format!("{:.2}", data.price_usd.parse::<f64>().unwrap()
|
||||
/ data.production_cost.parse::<f64>().unwrap());
|
||||
data.breakeven_cost = format!("{:.2}", data.price_usd.parse::<f64>().unwrap()
|
||||
/ (120.0 / 1000.0 * (1.0 / coins_per_hour)));
|
||||
data.breakeven_cost = format!("{:.2}", data.price_usd.parse::<f64>().unwrap()
|
||||
/ (120.0 / 1000.0 * (1.0 / coins_per_hour)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -403,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)
|
||||
|
@@ -21,7 +21,7 @@
|
||||
|
||||
<body class="d-flex flex-column min-vh-100 dark-mode">
|
||||
|
||||
<nav class="navbar bg-style navbar-expand-lg shadow-sm py-0">
|
||||
<nav class="navbar bg-style navbar-expand-lg shadow-sm py-0">
|
||||
<div class="container-fluid">
|
||||
<code><a class="navbar-brand fs-2" href="/"><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 191 191" height="35" width="35" style="enable-background:new 0 0 191 191;" xml:space="preserve"><style type="text/css">.st0{fill:#f2a900;}</style><circle cx="95.5" cy="95.5" r="95.5"/><circle class="st0" cx="95.3" cy="95.5" r="85.3"/><path d="M135.7,63.5c-2-4.6-3.9-13.6-9.1-15.6c-6.7-2.6-10.9,14.3-11.9,18.6h-1c-1.7-7.3-4-17.7-12-20c-3.7,15.6,3.9,32.5,11,46
|
||||
c7.6-3.6,11.3-15,12-23h1l8,24c6.4-1.8,8.7-8.2,11-14c5-12.4,9.2-27.7,6-41C141.1,41.2,138.2,55,135.7,63.5 M39.7,95.5
|
||||
@@ -72,16 +72,22 @@
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid">
|
||||
<br>
|
||||
|
||||
{% block content %}{% endblock content %}
|
||||
</div>
|
||||
|
||||
<footer class="shadow mt-auto">
|
||||
<code>
|
||||
<br>
|
||||
<div class="container-fluid">
|
||||
<div class="container-fluid">
|
||||
<div class="d-flex justify-content-center mb-2">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 191 191" height="35" width="35" style="enable-background:new 0 0 191 191;" xml:space="preserve"><style type="text/css">.st0{fill:#f2a900;}</style><circle cx="95.5" cy="95.5" r="95.5"/><circle class="st0" cx="95.3" cy="95.5" r="85.3"/><path d="M135.7,63.5c-2-4.6-3.9-13.6-9.1-15.6c-6.7-2.6-10.9,14.3-11.9,18.6h-1c-1.7-7.3-4-17.7-12-20c-3.7,15.6,3.9,32.5,11,46
|
||||
c7.6-3.6,11.3-15,12-23h1l8,24c6.4-1.8,8.7-8.2,11-14c5-12.4,9.2-27.7,6-41C141.1,41.2,138.2,55,135.7,63.5 M39.7,95.5
|
||||
c8.6-4.1,12.7-17.1,14-26h1c1,4.3,3.7,15.4,9.1,16.1c6.8,0.9,10.4-14.3,10.9-19.1h1c2.2,7.5,4.5,17.3,12,21
|
||||
c2.8-11.7-0.5-24.1-4.8-35c-1.1-2.8-3.3-10.4-7.2-10.4c-6.3,0-9.9,16.7-11,21.4h-1l-8-24C41.6,46.2,34,82,39.7,95.5 M24.7,107.5
|
||||
c10.4,45.6,58.6,71.4,102,52.1c15.8-7,28.3-19.5,35.5-35.1c1.9-4,5.5-11.4,3.4-16.7c-2.1-5.3-22.8,3.4-27.9,5.6
|
||||
c-0.7,0.3-1.2,1-1.4,1.8c-0.3,2.2,1.2,4.3,3.4,4.6c0.1,0,0.2,0,0.3,0l8.7,0.7c-15.7,30.4-58.1,43.8-88,19.5
|
||||
C54,134.6,48,128.2,44,120.5c-1.9-3.7-3.7-8.4-7-11C33.8,107.1,29,106.3,24.7,107.5L24.7,107.5z"/></svg>
|
||||
</div>
|
||||
<div class="card-group">
|
||||
<div class="card border-0">
|
||||
<div class="card-body" align="left">
|
||||
@@ -212,20 +218,22 @@
|
||||
<div class="row mb-2">
|
||||
<div class="col d-flex justify-content-center">
|
||||
<a class="text-decoration-none me-2" href="https://github.com/aglkm/grin-explorer">
|
||||
<span style="color:grey"><i class="bi bi-github me-1"></i>v.0.1.4</span>
|
||||
<span style="color:grey"><i class="bi bi-github me-1"></i>v.0.1.5</span>
|
||||
</a>
|
||||
<a class="text-decoration-none" href="/search">
|
||||
<span style="color:grey"><i class="bi bi-search me-1"></i>search</span>
|
||||
<span style="color:grey"><i class="bi bi-search me-1"></i>Search</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% if cg_api == "enabled" %}
|
||||
<div class="row">
|
||||
<div class="col d-flex justify-content-center" style="color:grey">
|
||||
Powered by CoinGecko
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
</code>
|
||||
</footer>
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
<code>
|
||||
|
||||
<div class="card">
|
||||
<div class="card border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-box"></i> BLOCK {{ block.height }}</div>
|
||||
<br>
|
||||
@@ -35,9 +35,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="card">
|
||||
<div class="card border-top-0 border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body mb-2" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-receipt"></i> TRANSACTIONS</div>
|
||||
<br>
|
||||
@@ -110,9 +108,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="card">
|
||||
<div class="card border-top-0 border-start-0 border-end-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>
|
||||
@@ -120,8 +116,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
</code>
|
||||
|
||||
{% endblock %}
|
||||
|
@@ -4,51 +4,51 @@
|
||||
|
||||
<code>
|
||||
|
||||
<div class="d-none d-md-block mb-4"> <!-- Show on >= md screens -->
|
||||
<div class="d-none d-md-block"> <!-- Show on >= md screens -->
|
||||
<div class="card-group">
|
||||
<div class="card rounded-0">
|
||||
<div class="card border-bottom-0 border-start-0 rounded-0">
|
||||
<div class="card-body">
|
||||
<div class="darkorange-text">
|
||||
HEIGHT
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card rounded-0">
|
||||
<div class="card border-bottom-0 rounded-0">
|
||||
<div class="card-body">
|
||||
<div class="darkorange-text">
|
||||
AGE
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card rounded-0">
|
||||
<div class="card border-bottom-0 rounded-0">
|
||||
<div class="card-body">
|
||||
<div class="darkorange-text">
|
||||
KERNELS
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card rounded-0">
|
||||
<div class="card border-bottom-0 rounded-0">
|
||||
<div class="card-body">
|
||||
<div class="darkorange-text">
|
||||
INPUTS
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card rounded-0">
|
||||
<div class="card border-bottom-0 rounded-0">
|
||||
<div class="card-body">
|
||||
<div class="darkorange-text">
|
||||
OUTPUTS
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card rounded-0">
|
||||
<div class="card border-bottom-0 rounded-0">
|
||||
<div class="card-body">
|
||||
<div class="darkorange-text">
|
||||
FEES
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card rounded-0">
|
||||
<div class="card border-bottom-0 border-end-0 rounded-0">
|
||||
<div class="card-body">
|
||||
<div class="darkorange-text">
|
||||
WEIGHT
|
||||
@@ -60,7 +60,7 @@
|
||||
|
||||
{% for i in range(end=10) %}
|
||||
<div class="card-group rounded-0">
|
||||
<div class="card rounded-0 mt-1">
|
||||
<div class="card border-bottom-0 border-start-0 rounded-0">
|
||||
<div class="card-body">
|
||||
{% if route == "block_list_by_height" %}
|
||||
<div class="value-text">
|
||||
@@ -73,7 +73,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card rounded-0 mt-1">
|
||||
<div class="card border-bottom-0 rounded-0">
|
||||
<div class="card-body">
|
||||
{% if route == "block_list_by_height" %}
|
||||
<div class="value-text">{{ blocks[i].time }}</div>
|
||||
@@ -82,7 +82,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card rounded-0 mt-1">
|
||||
<div class="card border-bottom-0 rounded-0">
|
||||
<div class="card-body">
|
||||
{% if route == "block_list_by_height" %}
|
||||
<div class="value-text">{{ blocks[i].ker_len }}</div>
|
||||
@@ -91,7 +91,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card rounded-0 mt-1">
|
||||
<div class="card border-bottom-0 rounded-0">
|
||||
<div class="card-body">
|
||||
{% if route == "block_list_by_height" %}
|
||||
<div class="value-text">{{ blocks[i].in_len }}</div>
|
||||
@@ -100,7 +100,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card rounded-0 mt-1">
|
||||
<div class="card border-bottom-0 rounded-0">
|
||||
<div class="card-body">
|
||||
{% if route == "block_list_by_height" %}
|
||||
<div class="value-text">{{ blocks[i].out_len }}</div>
|
||||
@@ -109,7 +109,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card rounded-0 mt-1">
|
||||
<div class="card border-bottom-0 rounded-0">
|
||||
<div class="card-body">
|
||||
{% if route == "block_list_by_height" %}
|
||||
<div class="value-text">ツ {{ blocks[i].fees / 1000000000.0 }}</div>
|
||||
@@ -118,7 +118,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card rounded-0 mt-1">
|
||||
<div class="card border-bottom-0 border-end-0 rounded-0">
|
||||
<div class="card-body">
|
||||
{% if route == "block_list_by_height" %}
|
||||
<div class="value-text">{{ blocks[i].weight }} %</div>
|
||||
@@ -133,12 +133,12 @@
|
||||
</div>
|
||||
|
||||
|
||||
<div class="d-md-none mb-4"> <!-- Show on < md screens-->
|
||||
<div class="d-md-none"> <!-- Show on < md screens-->
|
||||
{% for i in range(end=10) %}
|
||||
<div class="card mb-3">
|
||||
<div class="card border-bottom-0 border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="value-text">Block</div>
|
||||
<div class="d-flex justify-content-start">
|
||||
<i class="bi bi-box darkorange-text"></i>
|
||||
{% if route == "block_list_by_height" %}
|
||||
<div class="value-text">
|
||||
<a class="text-decoration-none darkorange-text" href="/block/{{ blocks[i].height }}">
|
||||
@@ -211,49 +211,53 @@
|
||||
|
||||
{% if route == "block_list_by_height" %}
|
||||
|
||||
<div class="d-flex justify-content-center sticky-bottom mb-3">
|
||||
<div class="me-5">
|
||||
{% if height >= (index + 10) %}
|
||||
<a class="text-decoration-none" href="/block_list/{{ index + 10 }}">
|
||||
{% elif height >= (index) and height < (index + 10) %}
|
||||
<a class="text-decoration-none" href="/block_list/{{ height }}">
|
||||
{% endif %}
|
||||
<h2><i class="bi bi-arrow-left-square"></i></h2>
|
||||
</a>
|
||||
</div>
|
||||
<div class="me-5">
|
||||
<a class="text-decoration-none" href="/block_list">
|
||||
<h2><i class="bi bi-house"></i></h2>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
{% if index >= 20 %}
|
||||
<a class="text-decoration-none" href="/block_list/{{ index - 10 }}">
|
||||
{% elif index >= 10 and index < 20 %}
|
||||
<a class="text-decoration-none" href="/block_list/{{ 9 }}">
|
||||
{% else %}
|
||||
<a class="text-decoration-none disabled" href="">
|
||||
{% endif %}
|
||||
<h2><i class="bi bi-arrow-right-square"></i></h2>
|
||||
</a>
|
||||
<div class="card rounded-0 border-start-0 border-end-0 sticky-bottom">
|
||||
<div class="mt-2 d-flex justify-content-center">
|
||||
<div class="me-5">
|
||||
{% if height >= (index + 10) %}
|
||||
<a class="text-decoration-none" href="/block_list/{{ index + 10 }}">
|
||||
{% elif height >= (index) and height < (index + 10) %}
|
||||
<a class="text-decoration-none" href="/block_list/{{ height }}">
|
||||
{% endif %}
|
||||
<h2><i class="bi bi-arrow-left-square"></i></h2>
|
||||
</a>
|
||||
</div>
|
||||
<div class="me-5">
|
||||
<a class="text-decoration-none" href="/block_list">
|
||||
<h2><i class="bi bi-house"></i></h2>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
{% if index >= 20 %}
|
||||
<a class="text-decoration-none" href="/block_list/{{ index - 10 }}">
|
||||
{% elif index >= 10 and index < 20 %}
|
||||
<a class="text-decoration-none" href="/block_list/{{ 9 }}">
|
||||
{% else %}
|
||||
<a class="text-decoration-none disabled" href="">
|
||||
{% endif %}
|
||||
<h2><i class="bi bi-arrow-right-square"></i></h2>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
|
||||
<div class="d-flex justify-content-center sticky-bottom mb-3">
|
||||
<div class="me-5">
|
||||
<a class="text-decoration-none disabled" href="">
|
||||
<h2><i class="bi bi-arrow-left-square"></i></h2>
|
||||
</a>
|
||||
</div>
|
||||
<div class="me-5">
|
||||
<a class="text-decoration-none" href="/block_list">
|
||||
<h2><i class="bi bi-house"></i></h2>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<div hx-get="/rpc/block_list/index" hx-trigger="load, every 10s"></div>
|
||||
<div class="card rounded-0 border-start-0 border-end-0 sticky-bottom">
|
||||
<div class="mt-2 d-flex justify-content-center">
|
||||
<div class="me-5">
|
||||
<a class="text-decoration-none disabled" href="">
|
||||
<h2><i class="bi bi-arrow-left-square"></i></h2>
|
||||
</a>
|
||||
</div>
|
||||
<div class="me-5">
|
||||
<a class="text-decoration-none" href="/block_list">
|
||||
<h2><i class="bi bi-house"></i></h2>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<div hx-get="/rpc/block_list/index" hx-trigger="load, every 10s"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@@ -4,19 +4,18 @@
|
||||
|
||||
<code>
|
||||
|
||||
<div class="card">
|
||||
<div class="card border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body">
|
||||
<h4>No results found.</h4><br>
|
||||
<div class="value-text mb-2">Supported search inputs:</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> block number</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> block hash</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> kernel</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> Block Number</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> Block Hash</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> Kernel</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> Unspent Output</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</code>
|
||||
|
||||
<br>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
@@ -6,12 +6,12 @@
|
||||
|
||||
{# We have different UI to display if CoinGecko API is disabled by user #}
|
||||
|
||||
{% if cg_api == "on" %}
|
||||
{% if cg_api == "enabled" %}
|
||||
{# CoinGecko API is enabled #}
|
||||
|
||||
<div class="d-none d-md-block"> <!-- Show on >= md screens -->
|
||||
<div class="card-group mb-2">
|
||||
<div class="card me-2">
|
||||
<div class="card-group">
|
||||
<div class="card border-bottom-0 border-start-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-cash-coin"></i> PRICE</div>
|
||||
<br>
|
||||
@@ -33,7 +33,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card border-bottom-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-bank"></i> MARKET</div>
|
||||
<br>
|
||||
@@ -65,10 +65,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="card-group mb-2">
|
||||
<div class="card card-background border-0 me-2">
|
||||
<div class="card rounded-end-0 mx-0 mt-0 mb-2">
|
||||
<div class="card-group">
|
||||
<div class="card card-background border-0">
|
||||
<div class="card border-bottom-0 border-start-0 rounded-0 mx-0 mt-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-grid"></i> BLOCKCHAIN</div>
|
||||
<br>
|
||||
@@ -85,7 +84,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card rounded-end-0 mx-0 my-0">
|
||||
<div class="card border-bottom-0 border-start-0 rounded-0 mx-0 my-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-speedometer2"></i> TRANSACTIONS & FEES</div>
|
||||
<br>
|
||||
@@ -100,7 +99,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card border-bottom-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-hammer"></i> MINING</div>
|
||||
<br>
|
||||
@@ -146,9 +145,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="card-group mb-4">
|
||||
<div class="card me-2">
|
||||
<div class="card-group">
|
||||
<div class="card rounded-0 border-start-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-receipt"></i> MEMPOOL</div>
|
||||
<br>
|
||||
@@ -161,7 +159,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card me-2">
|
||||
<div class="card rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-diagram-3"></i> CONNECTIONS</div>
|
||||
<br>
|
||||
@@ -174,7 +172,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card rounded-0 border-end-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-pc-display-horizontal"></i> NODE</div>
|
||||
<br>
|
||||
@@ -196,7 +194,7 @@
|
||||
|
||||
<div class="d-md-none"> <!-- Show on < md screens-->
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card border-bottom-0 border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-cash-coin"></i> PRICE</div>
|
||||
<br>
|
||||
@@ -218,7 +216,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card border-bottom-0 border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-bank"></i> MARKET</div>
|
||||
<br>
|
||||
@@ -249,8 +247,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card border-bottom-0 border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="darkorange-text"><i class="bi bi-grid"></i> BLOCKCHAIN</div>
|
||||
@@ -269,7 +266,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card border-bottom-0 border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-hammer"></i> MINING</div>
|
||||
<br>
|
||||
@@ -313,7 +310,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card border-bottom-0 border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-speedometer2"></i> TRANSACTIONS & FEES</div>
|
||||
<br>
|
||||
@@ -327,9 +324,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card border-bottom-0 border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-receipt"></i> MEMPOOL</div>
|
||||
<br>
|
||||
@@ -342,7 +337,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card border-bottom-0 border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-diagram-3"></i> CONNECTIONS</div>
|
||||
<br>
|
||||
@@ -355,7 +350,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-4">
|
||||
<div class="card rounded-0 border-start-0 border-end-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-pc-display-horizontal"></i> NODE</div>
|
||||
<br>
|
||||
@@ -465,8 +460,8 @@
|
||||
{# CoinGecko API is disabled #}
|
||||
|
||||
<div class="d-none d-md-block"> <!-- Show on >= md screens -->
|
||||
<div class="card-group mb-2">
|
||||
<div class="card me-2">
|
||||
<div class="card-group">
|
||||
<div class="card border-bottom-0 border-start-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-bank"></i> MARKET</div>
|
||||
<br>
|
||||
@@ -489,7 +484,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card border-bottom-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-hammer"></i> MINING</div>
|
||||
<br>
|
||||
@@ -508,9 +503,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="card-group mb-2">
|
||||
<div class="card me-2">
|
||||
<div class="card-group">
|
||||
<div class="card border-bottom-0 border-start-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-grid"></i> BLOCKCHAIN</div>
|
||||
<br>
|
||||
@@ -527,7 +521,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card border-bottom-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-speedometer2"></i> TRANSACTIONS & FEES</div>
|
||||
<br>
|
||||
@@ -542,9 +536,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="card-group mb-4">
|
||||
<div class="card me-2">
|
||||
<div class="card-group">
|
||||
<div class="card border-start-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-receipt"></i> MEMPOOL</div>
|
||||
<br>
|
||||
@@ -557,7 +550,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card me-2">
|
||||
<div class="card rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-diagram-3"></i> CONNECTIONS</div>
|
||||
<br>
|
||||
@@ -570,7 +563,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-pc-display-horizontal"></i> NODE</div>
|
||||
<br>
|
||||
@@ -593,7 +586,7 @@
|
||||
|
||||
<div class="d-md-none"> <!-- Show on < md screens-->
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card border-bottom-0 border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-bank"></i> MARKET</div>
|
||||
<br>
|
||||
@@ -616,8 +609,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card border-bottom-0 border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="darkorange-text"><i class="bi bi-grid"></i> BLOCKCHAIN</div>
|
||||
@@ -636,7 +628,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card border-bottom-0 border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-hammer"></i> MINING</div>
|
||||
<br>
|
||||
@@ -653,7 +645,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card border-bottom-0 border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-speedometer2"></i> TRANSACTIONS & FEES</div>
|
||||
<br>
|
||||
@@ -667,9 +659,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card border-bottom-0 border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-receipt"></i> MEMPOOL</div>
|
||||
<br>
|
||||
@@ -682,7 +672,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card border-bottom-0 border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-diagram-3"></i> CONNECTIONS</div>
|
||||
<br>
|
||||
@@ -695,7 +685,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-4">
|
||||
<div class="card border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body" align="left">
|
||||
<div class="darkorange-text"><i class="bi bi-pc-display-horizontal"></i> NODE</div>
|
||||
<br>
|
||||
@@ -738,70 +728,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal fade" id="mining_cost" tabindex="-1" aria-labelledby="mining_cost_label" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="mining_cost_label">Estimated Mining Cost</h1>
|
||||
<div data-bs-theme="light">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Mining cost to produce 1 grin coin.<br>
|
||||
Assuming that:<br>
|
||||
Miner is G1-mini ASIC.<br>
|
||||
Electricity cost is $0.07 per kW/h.<br>
|
||||
<br>
|
||||
<a class="text-decoration-none" href="https://ipollo.com/products/ipollo-g1-mini">https://ipollo.com/products/ipollo-g1-mini</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="ratio" tabindex="-1" aria-labelledby="ratio_label" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="ratio_label">Reward/Cost Ratio</h1>
|
||||
<div data-bs-theme="light">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Shows the result of the following formula:<br>
|
||||
Price of 1 Grin (USD) / Mining Cost of 1 Grin (USD).<br>
|
||||
<br>
|
||||
<i class="bi bi-hand-thumbs-down"></i> - <= 1<br>
|
||||
<i class="bi bi-hand-thumbs-up"></i> - from 1 to 2<br>
|
||||
<i class="bi bi-emoji-sunglasses"></i> - from 2 to 3<br>
|
||||
<i class='bi bi-rocket-takeoff'></i> - >= 3
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="breakeven" tabindex="-1" aria-labelledby="breakeven_label" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-5" id="breakeven_label">Breakeven Electricity Cost</h1>
|
||||
<div data-bs-theme="light">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Electricity threshold cost below which mining is profitable.<br>
|
||||
Assuming G1-mini ASIC as a miner device.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endif %}
|
||||
|
||||
</code>
|
||||
|
@@ -4,7 +4,7 @@
|
||||
|
||||
<code>
|
||||
|
||||
<div class="card">
|
||||
<div class="card border-start-0 border-end-0 rounded-0">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="darkorange-text"><i class="bi bi-card-text"></i> KERNEL</div>
|
||||
@@ -44,8 +44,7 @@
|
||||
</div>
|
||||
|
||||
{% if kernel.status != "Unconfirmed" %}
|
||||
<br>
|
||||
<div class="card">
|
||||
<div class="card border-top-0 border-start-0 border-end-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>
|
||||
@@ -54,8 +53,6 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<br>
|
||||
|
||||
</code>
|
||||
|
||||
{% endblock %}
|
||||
|
52
templates/output.html.tera
Normal file
52
templates/output.html.tera
Normal 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 </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 </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 </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 %}
|
||||
|
@@ -4,11 +4,11 @@
|
||||
|
||||
<code>
|
||||
|
||||
<div class="card">
|
||||
<div class="card border-start-0 border-end-0 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 me-2"></i>
|
||||
<div class="value-text text-end" hx-get="/rpc/block/latest" hx-trigger="load, every 10s"></div>
|
||||
<i class="bi bi-box darkorange-text"></i>
|
||||
<div class="darkorange-text text-end" hx-get="/rpc/block/latest" hx-trigger="load, every 10s"></div>
|
||||
</div>
|
||||
<form class="input-group" role="search" action="/search" method="GET" autocomplete="off">
|
||||
<input class="form-control text-center ms-0 me-2" type="search" placeholder="Explore Grin Network" aria-label="Search" name="input" required>
|
||||
@@ -18,14 +18,13 @@
|
||||
</form>
|
||||
<br><br>
|
||||
<div class="value-text mb-2">Supported search inputs:</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> block number</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> block hash</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> kernel</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> Block Number</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> Block Hash</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> Kernel</div>
|
||||
<div class="value-text"><i class="bi bi-dot"></i> Unspent Output</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
</code>
|
||||
|
||||
{% endblock %}
|
||||
|
Reference in New Issue
Block a user