feat: add boilerplate templates, utxo page
This commit is contained in:
parent
951758902a
commit
4509b1f9a6
137
Cargo.lock
generated
137
Cargo.lock
generated
@ -451,6 +451,20 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "boilerplate"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1906889b1f805a715eac02b2dea416e25c5cfa00f099530fa9d137a3cff93113"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"mime",
|
||||
"new_mime_guess",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.16.0"
|
||||
@ -569,7 +583,7 @@ dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
"strsim 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -838,6 +852,41 @@ dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.10.0",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.11"
|
||||
@ -1250,6 +1299,12 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "html-escaper"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "459a0ca33ee92551e0a3bb1774f2d3bdd1c09fb6341845736662dd25e1fcb52a"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.12"
|
||||
@ -1306,6 +1361,12 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-range-header"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ce4ef31cda248bbdb6e6820603b82dfcd9e833db65a43e997a0ccec777d11fe"
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.8.0"
|
||||
@ -1418,6 +1479,12 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
@ -1629,6 +1696,16 @@ version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
@ -1673,6 +1750,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "neptune-core"
|
||||
version = "0.0.5"
|
||||
source = "git+https://github.com/Neptune-Crypto/neptune-core.git?rev=9a7973259d63f148f8bf353866f260ba939d4649#9a7973259d63f148f8bf353866f260ba939d4649"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes-gcm",
|
||||
@ -1730,17 +1808,30 @@ name = "neptune-explorer"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"axum 0.7.5",
|
||||
"boilerplate",
|
||||
"clap",
|
||||
"html-escaper",
|
||||
"neptune-core",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tarpc",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "new_mime_guess"
|
||||
version = "4.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2d684d1b59e0dc07b37e2203ef576987473288f530082512aff850585c61b1f"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
@ -2207,7 +2298,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.10.5",
|
||||
"itertools 0.12.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
@ -2655,6 +2746,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
@ -2790,7 +2887,7 @@ dependencies = [
|
||||
"const_format",
|
||||
"derive_tasm_object",
|
||||
"hex",
|
||||
"itertools 0.10.5",
|
||||
"itertools 0.12.1",
|
||||
"ndarray",
|
||||
"num",
|
||||
"num-traits",
|
||||
@ -3048,6 +3145,31 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body-util",
|
||||
"http-range-header",
|
||||
"httpdate",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.2"
|
||||
@ -3232,6 +3354,15 @@ version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
|
||||
@ -12,7 +12,7 @@ serde_json = "1.0.115"
|
||||
tokio = { version = "1.37.0", features = ["full", "tracing"] }
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
neptune-core = {path = "../neptune-core"}
|
||||
neptune-core = {git = "https://github.com/Neptune-Crypto/neptune-core.git", rev = "9a7973259d63f148f8bf353866f260ba939d4649"}
|
||||
tarpc = { version = "^0.34", features = [
|
||||
"tokio1",
|
||||
"serde-transport",
|
||||
@ -21,6 +21,10 @@ tarpc = { version = "^0.34", features = [
|
||||
] }
|
||||
clap = "4.5.4"
|
||||
thiserror = "1.0.58"
|
||||
#boilerplate = { version = "1.0.0", features = ["axum"] }
|
||||
boilerplate = { version = "1.0.0" }
|
||||
html-escaper = "0.2.0"
|
||||
tower-http = { version = "0.5.2", features = ["fs"] }
|
||||
|
||||
|
||||
[patch.crates-io]
|
||||
|
||||
206
src/main.rs
206
src/main.rs
@ -5,10 +5,15 @@ use axum::{
|
||||
routing::get,
|
||||
Json, Router,
|
||||
};
|
||||
use tower_http::{
|
||||
services::ServeFile,
|
||||
};
|
||||
use clap::Parser;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
use std::ops::Deref;
|
||||
use thiserror::Error;
|
||||
use html_escaper::{Escape, Trusted};
|
||||
|
||||
use neptune_core::config_models::network::Network;
|
||||
use neptune_core::models::blockchain::block::block_height::BlockHeight;
|
||||
@ -32,7 +37,7 @@ pub struct Config {
|
||||
port: u16,
|
||||
}
|
||||
|
||||
struct AppState {
|
||||
pub struct AppState {
|
||||
network: Network,
|
||||
#[allow(dead_code)]
|
||||
config: Config,
|
||||
@ -205,76 +210,18 @@ async fn root(
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> Html<String> {
|
||||
|
||||
let network = state.network;
|
||||
let genesis_block_hex = state.genesis_digest.to_hex();
|
||||
#[derive(boilerplate::Boilerplate)]
|
||||
#[boilerplate(filename = "web/html/page/root.html")]
|
||||
pub struct RootHtmlPage(Arc<AppState>);
|
||||
impl Deref for RootHtmlPage {
|
||||
type Target = AppState;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
let html = format!(r#"
|
||||
<html>
|
||||
<head>
|
||||
<title>Neptune Block Explorer: (network: {network})</title>
|
||||
<style>
|
||||
div.indent {{position: relative; left: 20px;}}
|
||||
body {{font-family: arial, helvetica;}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Neptune Block Explorer (network: {network})</h1>
|
||||
<script>
|
||||
function handle_submit(form){{
|
||||
let value = form.height_or_digest.value;
|
||||
var is_digest = value.length == 80;
|
||||
var type = is_digest ? "digest" : "height";
|
||||
var uri = form.action + "/" + type + "/" + value;
|
||||
window.location.href = uri;
|
||||
return false;
|
||||
}}
|
||||
</script>
|
||||
<form action="/block" method="get" onsubmit="return handle_submit(this)">
|
||||
Block height or digest:
|
||||
<input type="text" size="80" name="height_or_digest"/>
|
||||
<input type="submit" name="height" value="Lookup Block"/>
|
||||
</form>
|
||||
|
||||
Quick Lookup:
|
||||
<a href="/block/genesis">Genesis Block</a> |
|
||||
<a href="/block/tip">Tip</a><br/>
|
||||
|
||||
<h2>REST RPCs</h2>
|
||||
|
||||
<h3>/block_info</h3>
|
||||
<div class="indent">
|
||||
<h4>Examples</h4>
|
||||
|
||||
<a href="/rpc/block_info/genesis">/rpc/block_info/genesis</a><br/>
|
||||
<a href="/rpc/block_info/tip">/rpc/block_info/tip</a><br/>
|
||||
<a href="/rpc/block_info/height/2">/rpc/block_info/height/2</a><br/>
|
||||
<a href="/rpc/block_info/digest/{genesis_block_hex}">/rpc/block_info/digest/{genesis_block_hex}</a><br/>
|
||||
<a href="/rpc/block_info/height_or_digest/1">/rpc/block_info/height_or_digest/1</a><br/>
|
||||
</div>
|
||||
|
||||
<h3>/block_digest</h3>
|
||||
<div class="indent">
|
||||
<h4>Examples</h4>
|
||||
|
||||
<a href="/rpc/block_digest/genesis">/rpc/block_digest/genesis</a><br/>
|
||||
<a href="/rpc/block_digest/tip">/rpc/block_digest/tip</a><br/>
|
||||
<a href="/rpc/block_digest/height/2">/rpc/block_digest/height/2</a><br/>
|
||||
<a href="/rpc/block_digest/digest/{genesis_block_hex}">/rpc/block_digest/digest/{genesis_block_hex}</a><br/>
|
||||
<a href="/rpc/block_digest/height_or_digest/{genesis_block_hex}">/rpc/block_digest/height_or_digest/{genesis_block_hex}</a><br/>
|
||||
</div>
|
||||
|
||||
<h3>/utxo_digest</h3>
|
||||
<div class="indent">
|
||||
<h4>Examples</h4>
|
||||
|
||||
<a href="/rpc/utxo_digest/2">/rpc/utxo_digest/2</a><br/>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
"#);
|
||||
|
||||
Html(html)
|
||||
let root_page = RootHtmlPage(state);
|
||||
Html(root_page.to_string())
|
||||
}
|
||||
|
||||
async fn block_page(
|
||||
@ -285,78 +232,64 @@ async fn block_page(
|
||||
block_page_with_value(value_path, state).await
|
||||
}
|
||||
|
||||
#[derive(boilerplate::Boilerplate)]
|
||||
#[boilerplate(filename = "web/html/components/header.html")]
|
||||
pub struct HeaderHtml{
|
||||
site_name: String,
|
||||
state: Arc<AppState>,
|
||||
}
|
||||
|
||||
#[axum::debug_handler]
|
||||
async fn block_page_with_value(
|
||||
Path((path_block_selector, value)): Path<(PathBlockSelector, String)>,
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> Result<Html<String>, Response> {
|
||||
|
||||
#[derive(boilerplate::Boilerplate)]
|
||||
#[boilerplate(filename = "web/html/page/block_info.html")]
|
||||
pub struct BlockInfoHtmlPage{
|
||||
header: HeaderHtml,
|
||||
block_info: BlockInfo
|
||||
}
|
||||
|
||||
let header = HeaderHtml{site_name: "Neptune Explorer".to_string(), state: state.clone()};
|
||||
|
||||
let block_info = block_info_with_value_worker(state, path_block_selector, &value).await?;
|
||||
let BlockInfo {height, digest, timestamp, num_inputs, num_outputs, num_uncle_blocks, difficulty, mining_reward, fee, is_genesis, is_tip, ..} = block_info;
|
||||
|
||||
let digest_hex = digest.to_hex();
|
||||
|
||||
let prev_link = match is_genesis {
|
||||
true => "".to_string(),
|
||||
false => format!("<a href='/block/height/{}'>Previous Block</a>", height.previous())
|
||||
};
|
||||
|
||||
let next_link = match is_tip {
|
||||
true => "".to_string(),
|
||||
false => format!("<a href='/block/height/{}'>Next Block</a>", height.next())
|
||||
};
|
||||
|
||||
let special_block_notice = match (is_genesis, is_tip) {
|
||||
(true, false) => "<p>This is the Genesis Block</p>",
|
||||
(false, true) => "<p>This is the Latest Block (tip)</p>",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
let timestamp_display = timestamp.standard_format();
|
||||
|
||||
let html = format!( r#"
|
||||
<html>
|
||||
<head>
|
||||
<title>Neptune Block Explorer: Block Height {height}</title>
|
||||
<style>
|
||||
div.indent {{position: relative; left: 20px;}}
|
||||
body {{font-family: arial, helvetica;}}
|
||||
table.alt {{margin-top: 10px; margin-bottom: 10px; padding: 10px; border-collapse: collapse; }}
|
||||
table.alt td, th {{ padding: 5px;}}
|
||||
table.alt tr:nth-child(odd) td{{background:#eee;}}
|
||||
table.alt tr:nth-child(even) td{{background:#fff;}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Block height: {height}</h1>
|
||||
<b>Digest:</b> {digest_hex}
|
||||
|
||||
{special_block_notice}
|
||||
|
||||
<table class="alt">
|
||||
<tr><td>Created</td><td>{timestamp_display}</td></tr>
|
||||
<tr><td>Inputs</td><td>{num_inputs}<br/></td></tr>
|
||||
<tr><td>Outputs</td><td>{num_outputs}</td></tr>
|
||||
<tr><td>Uncle blocks</td><td>{num_uncle_blocks}</td></tr>
|
||||
<tr><td>Difficulty</td><td>{difficulty}</td></tr>
|
||||
<tr><td>Mining Reward</td><td>{mining_reward}</td></tr>
|
||||
<tr><td>Fee</td><td>{fee}</td></tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
<a href="/">Home</a>
|
||||
{prev_link}
|
||||
{next_link}
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
"# );
|
||||
|
||||
Ok(Html(html))
|
||||
let block_info_page = BlockInfoHtmlPage{header, block_info};
|
||||
Ok(Html(block_info_page.to_string()))
|
||||
}
|
||||
|
||||
|
||||
#[axum::debug_handler]
|
||||
async fn utxo_page(
|
||||
Path(index): Path<u64>,
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> Result<Html<String>, Response> {
|
||||
|
||||
#[derive(boilerplate::Boilerplate)]
|
||||
#[boilerplate(filename = "web/html/page/utxo.html")]
|
||||
pub struct UtxoHtmlPage{
|
||||
header: HeaderHtml,
|
||||
index: u64,
|
||||
digest: Digest,
|
||||
}
|
||||
|
||||
let digest = match state
|
||||
.rpc_client
|
||||
.utxo_digest(context::current(), index)
|
||||
.await
|
||||
.map_err(rpc_err)?
|
||||
{
|
||||
Some(digest) => digest,
|
||||
None => return Err(not_found_err()),
|
||||
};
|
||||
|
||||
let header = HeaderHtml{site_name: "Neptune Explorer".to_string(), state: state.clone()};
|
||||
|
||||
let utxo_page = UtxoHtmlPage{index, header, digest};
|
||||
Ok(Html(utxo_page.to_string()))
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), RpcError> {
|
||||
let rpc_client = rpc_client().await;
|
||||
@ -371,6 +304,7 @@ async fn main() -> Result<(), RpcError> {
|
||||
});
|
||||
|
||||
let app = Router::new()
|
||||
// -- RPC calls --
|
||||
.route("/rpc/block_info/:selector", get(block_info))
|
||||
.route("/rpc/block_info/:selector/:value", get(block_info_with_value))
|
||||
.route(
|
||||
@ -379,9 +313,17 @@ async fn main() -> Result<(), RpcError> {
|
||||
)
|
||||
.route("/rpc/block_digest/:selector", get(block_digest))
|
||||
.route("/rpc/utxo_digest/:index", get(utxo_digest))
|
||||
|
||||
// -- Dynamic HTML pages --
|
||||
.route("/", get(root))
|
||||
.route("/block/:selector", get(block_page))
|
||||
.route("/block/:selector/:value", get(block_page_with_value))
|
||||
.route("/utxo/:value", get(utxo_page))
|
||||
|
||||
// -- Static files --
|
||||
.route_service("/css/styles.css", ServeFile::new(concat!(env!("CARGO_MANIFEST_DIR"), "/src/web/css/styles.css")))
|
||||
|
||||
// add state
|
||||
.with_state(shared_state);
|
||||
|
||||
println!("Running on http://localhost:3000");
|
||||
|
||||
42
src/web/css/styles.css
Normal file
42
src/web/css/styles.css
Normal file
@ -0,0 +1,42 @@
|
||||
div.box h1,
|
||||
div.box h2,
|
||||
div.box h3 {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
div.indent {
|
||||
position: relative;
|
||||
left: 20px;
|
||||
}
|
||||
|
||||
div.box {
|
||||
margin-bottom: 10px;
|
||||
border: solid 1px #ccc;
|
||||
border-top-left-radius: 15px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: arial, helvetica;
|
||||
}
|
||||
|
||||
table.alt {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table.alt td,
|
||||
th {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
table.alt tr:nth-child(odd) td {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
table.alt tr:nth-child(even) td {
|
||||
background: #fff;
|
||||
}
|
||||
1
src/web/html/components/header.html
Normal file
1
src/web/html/components/header.html
Normal file
@ -0,0 +1 @@
|
||||
<h1>{{self.site_name}} : {{self.state.network}}</h1>
|
||||
74
src/web/html/page/block_info.html
Normal file
74
src/web/html/page/block_info.html
Normal file
@ -0,0 +1,74 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Neptune Block Explorer: Block Height {{self.block_info.height}}</title>
|
||||
<link rel="stylesheet" type="text/css" href="/css/styles.css" media="screen" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{Trusted(self.header.to_string())}}
|
||||
|
||||
<h2>Block height: {{self.block_info.height}}</h2>
|
||||
|
||||
<!-- special_block_notice -->
|
||||
%% if self.block_info.is_genesis {
|
||||
<p>This is the Genesis Block</p>
|
||||
%% }
|
||||
%% if self.block_info.is_tip {
|
||||
<p>This is the Latest Block (tip)</p>
|
||||
%% }
|
||||
|
||||
<table class="alt">
|
||||
<tr>
|
||||
<td>Digest</td>
|
||||
<td>{{self.block_info.digest.to_hex()}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Created</td>
|
||||
<td>{{self.block_info.timestamp.standard_format()}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Inputs</td>
|
||||
<td>{{self.block_info.num_inputs}}<br /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Outputs</td>
|
||||
<td>{{self.block_info.num_outputs}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Uncle blocks</td>
|
||||
<td>{{self.block_info.num_uncle_blocks}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Difficulty</td>
|
||||
<td>{{self.block_info.difficulty}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mining Reward</td>
|
||||
<td>{{self.block_info.mining_reward}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fee</td>
|
||||
<td>{{self.block_info.fee}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
<a href="/">Home</a>
|
||||
| <a href='/block/genesis'>Genesis</a>
|
||||
| <a href='/block/tip'>Tip</a>
|
||||
%% if self.block_info.is_genesis {
|
||||
| Previous Block
|
||||
%% } else {
|
||||
| <a href='/block/height/{{self.block_info.height.previous()}}'>Previous Block</a>
|
||||
%% }
|
||||
|
||||
%% if self.block_info.is_tip {
|
||||
| Next Block
|
||||
%% } else {
|
||||
| <a href='/block/height/{{self.block_info.height.next()}}'>Next Block</a>
|
||||
%% }
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
80
src/web/html/page/root.html
Normal file
80
src/web/html/page/root.html
Normal file
@ -0,0 +1,80 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Neptune Block Explorer: (network: {{self.network}})</title>
|
||||
<link rel="stylesheet" type="text/css" href="/css/styles.css" media="screen" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>Neptune Block Explorer (network: {{self.network}})</h1>
|
||||
<script>
|
||||
function handle_submit(form){
|
||||
let value = form.height_or_digest.value;
|
||||
var is_digest = value.length == 80;
|
||||
var type = is_digest ? "digest" : "height";
|
||||
var uri = form.action + "/" + type + "/" + value;
|
||||
window.location.href = uri;
|
||||
return false;
|
||||
}
|
||||
function handle_utxo_submit(form) {
|
||||
let value = form.utxo.value;
|
||||
var uri = form.action + "/" + value;
|
||||
window.location.href = uri;
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="box">
|
||||
<h3>Block Lookup</h3>
|
||||
<form action="/block" method="get" onsubmit="return handle_submit(this)">
|
||||
Block height or digest:
|
||||
<input type="text" size="80" name="height_or_digest"/>
|
||||
<input type="submit" name="height" value="Lookup Block"/>
|
||||
</form>
|
||||
|
||||
Quick Lookup:
|
||||
<a href="/block/genesis">Genesis Block</a> |
|
||||
<a href="/block/tip">Tip</a><br/>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="box">
|
||||
<h3>Utxo Lookup</h3>
|
||||
<form action="/utxo" method="get" onsubmit="return handle_utxo_submit(this)">
|
||||
Utxo index:
|
||||
<input type="text" size="10" name="utxo" />
|
||||
<input type="submit" name="height" value="Lookup Utxo" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<h2>REST RPCs</h2>
|
||||
|
||||
<h3>/block_info</h3>
|
||||
<div class="indent">
|
||||
<h4>Examples</h4>
|
||||
|
||||
<a href="/rpc/block_info/genesis">/rpc/block_info/genesis</a><br/>
|
||||
<a href="/rpc/block_info/tip">/rpc/block_info/tip</a><br/>
|
||||
<a href="/rpc/block_info/height/2">/rpc/block_info/height/2</a><br/>
|
||||
<a href="/rpc/block_info/digest/{{self.genesis_digest.to_hex()}}">/rpc/block_info/digest/{{self.genesis_digest.to_hex()}}</a><br/>
|
||||
<a href="/rpc/block_info/height_or_digest/1">/rpc/block_info/height_or_digest/1</a><br/>
|
||||
</div>
|
||||
|
||||
<h3>/block_digest</h3>
|
||||
<div class="indent">
|
||||
<h4>Examples</h4>
|
||||
|
||||
<a href="/rpc/block_digest/genesis">/rpc/block_digest/genesis</a><br/>
|
||||
<a href="/rpc/block_digest/tip">/rpc/block_digest/tip</a><br/>
|
||||
<a href="/rpc/block_digest/height/2">/rpc/block_digest/height/2</a><br/>
|
||||
<a href="/rpc/block_digest/digest/{genesis_block_hex}">/rpc/block_digest/digest/{genesis_block_hex}</a><br/>
|
||||
<a href="/rpc/block_digest/height_or_digest/{genesis_block_hex}">/rpc/block_digest/height_or_digest/{genesis_block_hex}</a><br/>
|
||||
</div>
|
||||
|
||||
<h3>/utxo_digest</h3>
|
||||
<div class="indent">
|
||||
<h4>Examples</h4>
|
||||
|
||||
<a href="/rpc/utxo_digest/2">/rpc/utxo_digest/2</a><br/>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
30
src/web/html/page/utxo.html
Normal file
30
src/web/html/page/utxo.html
Normal file
@ -0,0 +1,30 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Neptune Block Explorer: Utxo {{self.index}}</title>
|
||||
<link rel="stylesheet" type="text/css" href="/css/styles.css" media="screen" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{Trusted(self.header.to_string())}}
|
||||
|
||||
<h3>Utxo Information</h3>
|
||||
<table class="alt">
|
||||
<tr>
|
||||
<td>Index</td>
|
||||
<td>{{self.index}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Digest</td>
|
||||
<td>{{self.digest.to_hex()}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
<a href="/">Home</a>
|
||||
| <a href='/block/genesis'>Genesis</a>
|
||||
| <a href='/block/tip'>Tip</a>
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
1
templates/web
Symbolic link
1
templates/web
Symbolic link
@ -0,0 +1 @@
|
||||
../src/web
|
||||
Loading…
x
Reference in New Issue
Block a user