Compare commits
No commits in common. "8f900fd70a040943b26f02a6659784a4faa67d3d" and "e7c7fcc91ff11a65fb14169a8c9e7a904d48f007" have entirely different histories.
8f900fd70a
...
e7c7fcc91f
1302
Cargo.lock
generated
1302
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
53
Cargo.toml
53
Cargo.toml
@ -2,70 +2,53 @@
|
|||||||
name = "neptune-explorer"
|
name = "neptune-explorer"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
default-run = "neptune-explorer"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "high_rate_attack"
|
|
||||||
path = "src/bin/high_rate_attack.rs"
|
|
||||||
required-features = ["attacks"]
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "scraper"
|
|
||||||
path = "src/bin/scraper.rs"
|
|
||||||
required-features = ["attacks"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = { version = "0.7.9", features = ["macros"] }
|
axum = { version = "0.7.9", features = ["macros"] }
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
serde_json = "1.0.145"
|
serde_json = "1.0.115"
|
||||||
tokio = { version = "1.48.0", features = ["full", "tracing"] }
|
tokio = { version = "1.37.0", features = ["full", "tracing"] }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
|
|
||||||
neptune-cash = "0.4.0"
|
neptune-cash = "0.3.0"
|
||||||
tarpc = { version = "^0.34", features = [
|
tarpc = { version = "^0.34", features = [
|
||||||
"tokio1",
|
"tokio1",
|
||||||
"serde-transport",
|
"serde-transport",
|
||||||
"serde-transport-json",
|
"serde-transport-json",
|
||||||
"tcp",
|
"tcp",
|
||||||
] }
|
] }
|
||||||
clap = "4.5.50"
|
clap = "4.5.4"
|
||||||
thiserror = "1.0.69"
|
thiserror = "1.0.59"
|
||||||
boilerplate = { version = "1.0.1" }
|
boilerplate = { version = "1.0.0" }
|
||||||
html-escaper = "0.2.0"
|
html-escaper = "0.2.0"
|
||||||
tower-http = { version = "0.5.2", features = ["fs"] }
|
tower-http = { version = "0.5.2", features = ["fs"] }
|
||||||
readonly = "0.2.13"
|
readonly = "0.2.12"
|
||||||
url = "2.5.7"
|
url = "2.5.0"
|
||||||
lettre = { version = "0.11.19", features = ["tokio1-native-tls"] }
|
lettre = {version = "0.11.7", features = ["tokio1-native-tls"]}
|
||||||
chrono = "0.4.42"
|
chrono = "0.4.34"
|
||||||
|
|
||||||
# only should be used inside main.rs, for the binary.
|
# only should be used inside main.rs, for the binary.
|
||||||
anyhow = "1.0.100"
|
anyhow = "1.0.86"
|
||||||
arc-swap = "1.7.1"
|
arc-swap = "1.7.1"
|
||||||
derive_more = { version = "1.0.0", features = ["display"] }
|
derive_more = { version = "1.0.0", features = ["display"] }
|
||||||
|
|
||||||
# not a direct dep. workaround for weird "could not resolve" cargo error
|
# not a direct dep. workaround for weird "could not resolve" cargo error
|
||||||
indexmap = "2.12.0"
|
indexmap = "2.7.0"
|
||||||
|
|
||||||
blake3 = { version = "1.8.2", optional = true }
|
blake3 = {version = "1.8.2", optional = true}
|
||||||
rand = { version = "0.9.2", optional = true }
|
rand = {version = "0.9.2", optional = true}
|
||||||
reqwest = { version = "0.12.24", optional = true }
|
|
||||||
log = {version = "0.4.28", optional = true}
|
|
||||||
env_logger = {version = "0.11.8", optional = true}
|
|
||||||
regex = {version = "1.12.2", optional = true }
|
|
||||||
futures = {version = "0.3.31", optional = true }
|
|
||||||
|
|
||||||
#[dev-dependencies]
|
#[dev-dependencies]
|
||||||
test-strategy = "0.4.3"
|
test-strategy = "0.4.3"
|
||||||
proptest = "1.9.0"
|
proptest = "1.7.0"
|
||||||
arbitrary = "1.4.2"
|
arbitrary = "1.4.1"
|
||||||
proptest-arbitrary-interop = "0.1.0"
|
proptest-arbitrary-interop = "0.1.0"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
neptune-cash = { git = "https://github.com/Neptune-Crypto/neptune-core.git", rev = "71f471a526a13ddd41ab4400e1a873471d03ede6" }
|
neptune-cash = { git = "https://github.com/Neptune-Crypto/neptune-core.git", branch = "asz/transparent-transactions" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
mock = ["dep:blake3", "dep:rand"]
|
mock = ["dep:blake3", "dep:rand"]
|
||||||
attacks = ["reqwest", "log", "env_logger", "regex", "dep:rand", "futures"]
|
|
||||||
|
|||||||
@ -34,15 +34,14 @@ not tested or supported. Please let us know if you get it work. patches accep
|
|||||||
2. start neptune-explorer
|
2. start neptune-explorer
|
||||||
|
|
||||||
```
|
```
|
||||||
nohup neptune-explorer --site-domain testdomain 2>&1 > /path/to/logs/neptune-explorer.log &
|
nohup neptune-explorer 2>&1 > /path/to/logs/neptune-explorer.log &
|
||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
* The block-explorer automatically uses the same network (mainnet, testnet, etc) as the neptune-core instance it is connected to, and the network is displayed in the web interface.
|
* The block-explorer automatically uses the same network (mainnet, testnet, etc) as the neptune-core instance it is connected to, and the network is displayed in the web interface.
|
||||||
* If neptune-core RPC server is running on a non-standard port, you can provide it with the `--neptune-rpc-port` flag.
|
* If neptune-core RPC server is running on a non-standard port, you can provide it with the `--neptune-rpc-port` flag.
|
||||||
* neptune-explorer listens for http requests on port 3000 by default. This can be changed with the `--listen-port` flag.
|
* neptune-explorer listens for http requests on port 3000 by default. This can be changed with the `--listen-port` flag.
|
||||||
* Site name can be specified with the --site-name flag.
|
* Site name must be specified with the `--site-name` flag.
|
||||||
* Site domain *must* be specified with the `--site-domain` flag.
|
|
||||||
|
|
||||||
|
|
||||||
## Connecting via Browser
|
## Connecting via Browser
|
||||||
@ -53,7 +52,7 @@ Just navigate to http://localhost:3000/
|
|||||||
|
|
||||||
When connected to an out-of-date or unsynced neptune-core node, it might be a good idea to turn on mocking so that whenever a resource is unavailable, a random one is generated and returned. To do this, compile with the feature flag "mock" and make sure that the "MOCK" environment variable is set.
|
When connected to an out-of-date or unsynced neptune-core node, it might be a good idea to turn on mocking so that whenever a resource is unavailable, a random one is generated and returned. To do this, compile with the feature flag "mock" and make sure that the "MOCK" environment variable is set.
|
||||||
|
|
||||||
In one command: `MOCK=1 cargo run --features "mock" -- --site-domain testdomain`
|
In one command: `MOCK=1 cargo run --features "mock" -- --site-name testname`
|
||||||
|
|
||||||
## SSL/TLS, Nginx, etc.
|
## SSL/TLS, Nginx, etc.
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
use clap::Parser;
|
|
||||||
use lettre::AsyncSmtpTransport;
|
|
||||||
use lettre::AsyncTransport;
|
|
||||||
use lettre::Message;
|
|
||||||
use lettre::Tokio1Executor;
|
|
||||||
use tracing::info;
|
|
||||||
use tracing::warn;
|
|
||||||
|
|
||||||
use crate::model::app_state::AppState;
|
use crate::model::app_state::AppState;
|
||||||
use crate::model::config::AlertConfig;
|
use crate::model::config::AlertConfig;
|
||||||
use crate::model::config::Config;
|
use crate::model::config::Config;
|
||||||
use crate::model::config::SmtpMode;
|
use crate::model::config::SmtpMode;
|
||||||
|
use clap::Parser;
|
||||||
|
use lettre::{AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor};
|
||||||
|
use tracing::{info, warn};
|
||||||
|
|
||||||
|
// pub fn alert_params_configured() -> bool {
|
||||||
|
// Config::parse().alert_config().is_some()
|
||||||
|
// }
|
||||||
|
|
||||||
pub fn check_alert_params() -> bool {
|
pub fn check_alert_params() -> bool {
|
||||||
match Config::parse().alert_config() {
|
match Config::parse().alert_config() {
|
||||||
|
|||||||
@ -1,27 +0,0 @@
|
|||||||
use futures::future::join_all;
|
|
||||||
use reqwest::Client;
|
|
||||||
use tokio::time::Instant;
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() {
|
|
||||||
let client = Client::new();
|
|
||||||
let url = "http://127.0.0.1:3000/your_endpoint";
|
|
||||||
let num_requests = 200; // adjust as needed
|
|
||||||
let concurrency = 20; // parallel tasks
|
|
||||||
|
|
||||||
let start = Instant::now();
|
|
||||||
let futures = (0..num_requests).map(|_| {
|
|
||||||
let client = &client;
|
|
||||||
async move {
|
|
||||||
let _ = client.get(url).send().await;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
join_all(futures).await;
|
|
||||||
|
|
||||||
let elapsed = start.elapsed();
|
|
||||||
println!(
|
|
||||||
"Sent {} requests in {:.2?} (concurrency {})",
|
|
||||||
num_requests, elapsed, concurrency
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,102 +0,0 @@
|
|||||||
use log::LevelFilter;
|
|
||||||
use log::{error, info, warn};
|
|
||||||
use rand::seq::IteratorRandom;
|
|
||||||
use regex::Regex;
|
|
||||||
use reqwest::Client;
|
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::time::Duration;
|
|
||||||
use tokio::{signal, time};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
/// Scrape the explorer website when running locally.
|
|
||||||
///
|
|
||||||
/// This program maintains a dictionary of URLs, which is initially populated
|
|
||||||
/// with 'http://localhost:3000'. It fetches a random URL from the dictionary in
|
|
||||||
/// each iteration, logs positive messages if successful, extracts new URLs from
|
|
||||||
/// the response body to add to the dictionary, logs warnings or errors for
|
|
||||||
/// request failures or timeouts, sleeps for a bit, and continues until Ctrl-C
|
|
||||||
/// is pressed.
|
|
||||||
///
|
|
||||||
/// Run with:
|
|
||||||
/// `> cargo run --bin scraper`
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() {
|
|
||||||
// Initialize logger
|
|
||||||
env_logger::builder().filter_level(LevelFilter::Info).init();
|
|
||||||
|
|
||||||
let client = Client::builder()
|
|
||||||
.timeout(Duration::from_millis(300))
|
|
||||||
.build()
|
|
||||||
.expect("Failed to build HTTP client");
|
|
||||||
|
|
||||||
let root_url = "http://localhost:3000".to_string();
|
|
||||||
let urls = Arc::new(Mutex::new(HashSet::from([root_url.clone()])));
|
|
||||||
|
|
||||||
let href_regex = Regex::new(r#"<a\s+(?:[^>]*?\s+)?href=['\"](.*?)['\"]"#).unwrap();
|
|
||||||
|
|
||||||
info!("Starting fetch loop. Press Ctrl-C to stop.");
|
|
||||||
|
|
||||||
let urls_clone = Arc::clone(&urls);
|
|
||||||
let fetch_loop = async move {
|
|
||||||
loop {
|
|
||||||
// Pick a random URL safely
|
|
||||||
let url_opt = {
|
|
||||||
let urls_guard = urls_clone.lock().unwrap();
|
|
||||||
urls_guard.iter().choose(&mut rand::rng()).cloned()
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(url) = url_opt {
|
|
||||||
match client.get(&url).send().await {
|
|
||||||
Ok(resp) => {
|
|
||||||
if resp.status().is_success() {
|
|
||||||
match resp.text().await {
|
|
||||||
Ok(text) => {
|
|
||||||
info!("Success fetching {}", url);
|
|
||||||
let mut urls_guard = urls_clone.lock().unwrap();
|
|
||||||
for cap in href_regex.captures_iter(&text) {
|
|
||||||
let href = &cap[1];
|
|
||||||
if let Ok(parsed_url) =
|
|
||||||
Url::parse(&[&root_url.clone(), href].concat())
|
|
||||||
{
|
|
||||||
let normalized = parsed_url.as_str();
|
|
||||||
if urls_guard.insert(normalized.to_owned()) {
|
|
||||||
info!(
|
|
||||||
"Added new URL to dictionary: {}",
|
|
||||||
normalized
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Failed to read response body from {}: {}", url, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
warn!("Non-success status {} from {}", resp.status(), url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
if err.is_timeout() {
|
|
||||||
warn!("Timeout fetching {}", url);
|
|
||||||
} else {
|
|
||||||
error!("Error fetching {}: {}", url, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
warn!("URL dictionary is empty, no URL to fetch");
|
|
||||||
}
|
|
||||||
|
|
||||||
time::sleep(Duration::from_millis(500)).await;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
tokio::select! {
|
|
||||||
_ = fetch_loop => {}, // This runs indefinitely unless stopped
|
|
||||||
_ = signal::ctrl_c() => {
|
|
||||||
info!("Ctrl-C received, stopping...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +1,5 @@
|
|||||||
use html_escaper::Escape;
|
|
||||||
|
|
||||||
use crate::model::app_state::AppStateInner;
|
use crate::model::app_state::AppStateInner;
|
||||||
|
use html_escaper::Escape;
|
||||||
|
|
||||||
#[derive(Debug, Clone, boilerplate::Boilerplate)]
|
#[derive(Debug, Clone, boilerplate::Boilerplate)]
|
||||||
#[boilerplate(filename = "web/html/components/header.html")]
|
#[boilerplate(filename = "web/html/components/header.html")]
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
use std::collections::HashMap;
|
use crate::html::component::header::HeaderHtml;
|
||||||
use std::sync::Arc;
|
use crate::html::page::not_found::not_found_html_response;
|
||||||
|
use crate::http_util::rpc_method_err;
|
||||||
|
use crate::model::announcement_selector::AnnouncementSelector;
|
||||||
|
use crate::model::announcement_type::AnnouncementType;
|
||||||
|
use crate::model::app_state::AppState;
|
||||||
|
use crate::model::transparent_utxo_tuple::TransparentUtxoTuple;
|
||||||
use axum::extract::rejection::PathRejection;
|
use axum::extract::rejection::PathRejection;
|
||||||
use axum::extract::Path;
|
use axum::extract::Path;
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
@ -13,16 +17,10 @@ use neptune_cash::prelude::tasm_lib::prelude::Digest;
|
|||||||
use neptune_cash::prelude::triton_vm::prelude::BFieldCodec;
|
use neptune_cash::prelude::triton_vm::prelude::BFieldCodec;
|
||||||
use neptune_cash::prelude::twenty_first::tip5::Tip5;
|
use neptune_cash::prelude::twenty_first::tip5::Tip5;
|
||||||
use neptune_cash::util_types::mutator_set::addition_record::AdditionRecord;
|
use neptune_cash::util_types::mutator_set::addition_record::AdditionRecord;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
use tarpc::context;
|
use tarpc::context;
|
||||||
|
|
||||||
use crate::html::component::header::HeaderHtml;
|
|
||||||
use crate::html::page::not_found::not_found_html_response;
|
|
||||||
use crate::http_util::rpc_method_err;
|
|
||||||
use crate::model::announcement_selector::AnnouncementSelector;
|
|
||||||
use crate::model::announcement_type::AnnouncementType;
|
|
||||||
use crate::model::app_state::AppState;
|
|
||||||
use crate::model::transparent_utxo_tuple::TransparentUtxoTuple;
|
|
||||||
|
|
||||||
#[axum::debug_handler]
|
#[axum::debug_handler]
|
||||||
pub async fn announcement_page(
|
pub async fn announcement_page(
|
||||||
maybe_path: Result<Path<AnnouncementSelector>, PathRejection>,
|
maybe_path: Result<Path<AnnouncementSelector>, PathRejection>,
|
||||||
@ -86,19 +84,12 @@ pub async fn announcement_page(
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|output| output.addition_record())
|
.map(|output| output.addition_record())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
addition_record_indices = state
|
addition_record_indices = state.rpc_client.addition_record_indices_for_block(context::current(), state.token(), block_selector, &addition_records).await
|
||||||
.rpc_client
|
.map_err(|e| not_found_html_response(state, Some(e.to_string())))?
|
||||||
.addition_record_indices_for_block(
|
.map_err(rpc_method_err)?
|
||||||
context::current(),
|
.expect(
|
||||||
state.token(),
|
"block guaranteed to exist because we got here; getting its announcements should work",
|
||||||
block_selector,
|
);
|
||||||
&addition_records,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|e| not_found_html_response(state, Some(e.to_string())))?
|
|
||||||
.map_err(rpc_method_err)?
|
|
||||||
.into_iter()
|
|
||||||
.collect::<HashMap<_, _>>();
|
|
||||||
|
|
||||||
let mut transparent_utxos_cache = state.transparent_utxos_cache.lock().await;
|
let mut transparent_utxos_cache = state.transparent_utxos_cache.lock().await;
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
use std::sync::Arc;
|
use crate::html::component::header::HeaderHtml;
|
||||||
|
use crate::html::page::not_found::not_found_html_response;
|
||||||
|
use crate::http_util::rpc_method_err;
|
||||||
|
use crate::model::app_state::AppState;
|
||||||
|
use crate::model::block_selector_extended::BlockSelectorExtended;
|
||||||
use axum::extract::rejection::PathRejection;
|
use axum::extract::rejection::PathRejection;
|
||||||
use axum::extract::Path;
|
use axum::extract::Path;
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
@ -7,15 +10,10 @@ use axum::response::Html;
|
|||||||
use axum::response::Response;
|
use axum::response::Response;
|
||||||
use html_escaper::Escape;
|
use html_escaper::Escape;
|
||||||
use html_escaper::Trusted;
|
use html_escaper::Trusted;
|
||||||
use neptune_cash::protocol::consensus::block::block_info::BlockInfo;
|
use neptune_cash::models::blockchain::block::block_info::BlockInfo;
|
||||||
|
use std::sync::Arc;
|
||||||
use tarpc::context;
|
use tarpc::context;
|
||||||
|
|
||||||
use crate::html::component::header::HeaderHtml;
|
|
||||||
use crate::html::page::not_found::not_found_html_response;
|
|
||||||
use crate::http_util::rpc_method_err;
|
|
||||||
use crate::model::app_state::AppState;
|
|
||||||
use crate::model::block_selector_extended::BlockSelectorExtended;
|
|
||||||
|
|
||||||
#[axum::debug_handler]
|
#[axum::debug_handler]
|
||||||
pub async fn block_page(
|
pub async fn block_page(
|
||||||
user_input_maybe: Result<Path<BlockSelectorExtended>, PathRejection>,
|
user_input_maybe: Result<Path<BlockSelectorExtended>, PathRejection>,
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
|
use crate::http_util::not_found_html_err;
|
||||||
|
use crate::http_util::not_found_html_handler;
|
||||||
|
use crate::model::app_state::AppStateInner;
|
||||||
use axum::http::StatusCode;
|
use axum::http::StatusCode;
|
||||||
use axum::response::Html;
|
use axum::response::Html;
|
||||||
use axum::response::Response;
|
use axum::response::Response;
|
||||||
use html_escaper::Escape;
|
use html_escaper::Escape;
|
||||||
|
|
||||||
use crate::http_util::not_found_html_err;
|
|
||||||
use crate::http_util::not_found_html_handler;
|
|
||||||
use crate::model::app_state::AppStateInner;
|
|
||||||
|
|
||||||
pub fn not_found_page(error_msg: Option<String>) -> Html<String> {
|
pub fn not_found_page(error_msg: Option<String>) -> Html<String> {
|
||||||
#[derive(boilerplate::Boilerplate)]
|
#[derive(boilerplate::Boilerplate)]
|
||||||
#[boilerplate(filename = "web/html/page/not_found.html")]
|
#[boilerplate(filename = "web/html/page/not_found.html")]
|
||||||
|
|||||||
@ -1,14 +1,17 @@
|
|||||||
use std::collections::HashSet;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use axum::extract::RawQuery;
|
use axum::extract::RawQuery;
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum::response::IntoResponse;
|
|
||||||
use axum::response::Redirect;
|
use axum::response::Redirect;
|
||||||
use axum::response::Response;
|
use axum::response::Response;
|
||||||
|
use std::sync::Arc;
|
||||||
|
// use axum::routing::get;
|
||||||
|
// use axum::routing::Router;
|
||||||
use super::not_found::not_found_html_response;
|
use super::not_found::not_found_html_response;
|
||||||
|
use axum::response::IntoResponse;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
// use super::root::root;
|
||||||
|
// use super::utxo::utxo_page;
|
||||||
use crate::model::app_state::AppState;
|
use crate::model::app_state::AppState;
|
||||||
|
// use neptune_explorer::model::config::Config;
|
||||||
|
|
||||||
/// This converts a query string into a path and redirects browser.
|
/// This converts a query string into a path and redirects browser.
|
||||||
///
|
///
|
||||||
|
|||||||
@ -1,16 +1,14 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use axum::extract::State;
|
|
||||||
use axum::response::Html;
|
|
||||||
use axum::response::Response;
|
|
||||||
use html_escaper::Escape;
|
|
||||||
use neptune_cash::api::export::BlockHeight;
|
|
||||||
use tarpc::context;
|
|
||||||
|
|
||||||
use crate::html::page::not_found::not_found_html_response;
|
use crate::html::page::not_found::not_found_html_response;
|
||||||
use crate::http_util::rpc_method_err;
|
use crate::http_util::rpc_method_err;
|
||||||
use crate::model::app_state::AppState;
|
use crate::model::app_state::AppState;
|
||||||
use crate::model::app_state::AppStateInner;
|
use crate::model::app_state::AppStateInner;
|
||||||
|
use axum::extract::State;
|
||||||
|
use axum::response::Html;
|
||||||
|
use axum::response::Response;
|
||||||
|
use html_escaper::Escape;
|
||||||
|
use neptune_cash::models::blockchain::block::block_height::BlockHeight;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tarpc::context;
|
||||||
|
|
||||||
#[axum::debug_handler]
|
#[axum::debug_handler]
|
||||||
pub async fn root(State(state_rw): State<Arc<AppState>>) -> Result<Html<String>, Response> {
|
pub async fn root(State(state_rw): State<Arc<AppState>>) -> Result<Html<String>, Response> {
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
use std::sync::Arc;
|
use crate::html::component::header::HeaderHtml;
|
||||||
|
use crate::html::page::not_found::not_found_html_response;
|
||||||
|
use crate::http_util::rpc_method_err;
|
||||||
|
use crate::model::app_state::AppState;
|
||||||
|
use crate::model::transparent_utxo_tuple::TransparentUtxoTuple;
|
||||||
use axum::extract::rejection::PathRejection;
|
use axum::extract::rejection::PathRejection;
|
||||||
use axum::extract::Path;
|
use axum::extract::Path;
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
@ -9,14 +12,9 @@ use html_escaper::Escape;
|
|||||||
use html_escaper::Trusted;
|
use html_escaper::Trusted;
|
||||||
use neptune_cash::api::export::Tip5;
|
use neptune_cash::api::export::Tip5;
|
||||||
use neptune_cash::prelude::tasm_lib::prelude::Digest;
|
use neptune_cash::prelude::tasm_lib::prelude::Digest;
|
||||||
|
use std::sync::Arc;
|
||||||
use tarpc::context;
|
use tarpc::context;
|
||||||
|
|
||||||
use crate::html::component::header::HeaderHtml;
|
|
||||||
use crate::html::page::not_found::not_found_html_response;
|
|
||||||
use crate::http_util::rpc_method_err;
|
|
||||||
use crate::model::app_state::AppState;
|
|
||||||
use crate::model::transparent_utxo_tuple::TransparentUtxoTuple;
|
|
||||||
|
|
||||||
#[axum::debug_handler]
|
#[axum::debug_handler]
|
||||||
pub async fn utxo_page(
|
pub async fn utxo_page(
|
||||||
index_maybe: Result<Path<u64>, PathRejection>,
|
index_maybe: Result<Path<u64>, PathRejection>,
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use axum::http::StatusCode;
|
|||||||
use axum::response::Html;
|
use axum::response::Html;
|
||||||
use axum::response::IntoResponse;
|
use axum::response::IntoResponse;
|
||||||
use axum::response::Response;
|
use axum::response::Response;
|
||||||
use neptune_cash::application::rpc::server::error::RpcError;
|
use neptune_cash::rpc_server::error::RpcError;
|
||||||
use tarpc::client::RpcError as TarpcError;
|
use tarpc::client::RpcError as TarpcError;
|
||||||
|
|
||||||
// note: http StatusCodes are defined at:
|
// note: http StatusCodes are defined at:
|
||||||
|
|||||||
@ -4,4 +4,3 @@ pub mod http_util;
|
|||||||
pub mod model;
|
pub mod model;
|
||||||
pub mod neptune_rpc;
|
pub mod neptune_rpc;
|
||||||
pub mod rpc;
|
pub mod rpc;
|
||||||
pub mod shared;
|
|
||||||
|
|||||||
@ -13,10 +13,8 @@ use neptune_explorer::model::app_state::AppState;
|
|||||||
use neptune_explorer::neptune_rpc;
|
use neptune_explorer::neptune_rpc;
|
||||||
use neptune_explorer::rpc::block_digest::block_digest;
|
use neptune_explorer::rpc::block_digest::block_digest;
|
||||||
use neptune_explorer::rpc::block_info::block_info;
|
use neptune_explorer::rpc::block_info::block_info;
|
||||||
use neptune_explorer::rpc::circulating_supply::circulating_supply;
|
|
||||||
use neptune_explorer::rpc::pow_puzzle::pow_puzzle;
|
use neptune_explorer::rpc::pow_puzzle::pow_puzzle;
|
||||||
use neptune_explorer::rpc::provide_pow_solution::provide_pow_solution;
|
use neptune_explorer::rpc::provide_pow_solution::provide_pow_solution;
|
||||||
use neptune_explorer::rpc::total_supply::total_supply;
|
|
||||||
use neptune_explorer::rpc::utxo_digest::utxo_digest;
|
use neptune_explorer::rpc::utxo_digest::utxo_digest;
|
||||||
use tower_http::services::ServeDir;
|
use tower_http::services::ServeDir;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
@ -60,8 +58,6 @@ pub fn setup_routes(app_state: AppState) -> Router {
|
|||||||
.route("/rpc/block_digest/*selector", get(block_digest))
|
.route("/rpc/block_digest/*selector", get(block_digest))
|
||||||
.route("/rpc/utxo_digest/:index", get(utxo_digest))
|
.route("/rpc/utxo_digest/:index", get(utxo_digest))
|
||||||
.route("/rpc/pow_puzzle/*address", get(pow_puzzle))
|
.route("/rpc/pow_puzzle/*address", get(pow_puzzle))
|
||||||
.route("/rpc/circulating_supply", get(circulating_supply))
|
|
||||||
.route("/rpc/total_supply", get(total_supply))
|
|
||||||
.route("/rpc/provide_pow_solution", post(provide_pow_solution))
|
.route("/rpc/provide_pow_solution", post(provide_pow_solution))
|
||||||
// -- Dynamic HTML pages --
|
// -- Dynamic HTML pages --
|
||||||
.route("/", get(root))
|
.route("/", get(root))
|
||||||
|
|||||||
@ -1,13 +1,11 @@
|
|||||||
use std::fmt::Display;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use neptune_cash::api::export::BlockHeight;
|
use neptune_cash::api::export::BlockHeight;
|
||||||
|
use neptune_cash::models::blockchain::block::block_selector::BlockSelector;
|
||||||
use neptune_cash::prelude::tasm_lib::prelude::Digest;
|
use neptune_cash::prelude::tasm_lib::prelude::Digest;
|
||||||
use neptune_cash::protocol::consensus::block::block_selector::BlockSelector;
|
|
||||||
use neptune_cash::protocol::consensus::block::block_selector::BlockSelectorLiteral;
|
|
||||||
use serde::de::Error;
|
use serde::de::Error;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Deserializer;
|
use serde::Deserializer;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
/// newtype for `BlockSelector` that provides ability to parse `height_or_digest/value`.
|
/// newtype for `BlockSelector` that provides ability to parse `height_or_digest/value`.
|
||||||
///
|
///
|
||||||
@ -60,11 +58,11 @@ impl FromStr for AnnouncementSelector {
|
|||||||
let (block_selector, index) = match parts.as_slice() {
|
let (block_selector, index) = match parts.as_slice() {
|
||||||
["tip", index] => {
|
["tip", index] => {
|
||||||
let index = index.parse::<u64>().map_err(Self::Err::TipIndex)?;
|
let index = index.parse::<u64>().map_err(Self::Err::TipIndex)?;
|
||||||
(BlockSelector::Special(BlockSelectorLiteral::Tip), index)
|
(BlockSelector::Tip, index)
|
||||||
}
|
}
|
||||||
["genesis", index] => index
|
["genesis", index] => index
|
||||||
.parse::<u64>()
|
.parse::<u64>()
|
||||||
.map(|i| (BlockSelector::Special(BlockSelectorLiteral::Genesis), i))
|
.map(|i| (BlockSelector::Genesis, i))
|
||||||
.map_err(Self::Err::GenesisIndex)?,
|
.map_err(Self::Err::GenesisIndex)?,
|
||||||
["height", number, index] => {
|
["height", number, index] => {
|
||||||
let height_as_u64 = number.parse::<u64>().map_err(Self::Err::BlockHeight)?;
|
let height_as_u64 = number.parse::<u64>().map_err(Self::Err::BlockHeight)?;
|
||||||
@ -130,10 +128,8 @@ impl Display for AnnouncementSelector {
|
|||||||
BlockSelector::Height(block_height) => {
|
BlockSelector::Height(block_height) => {
|
||||||
write!(f, "height/{}/{}", block_height, self.index)
|
write!(f, "height/{}/{}", block_height, self.index)
|
||||||
}
|
}
|
||||||
BlockSelector::Special(BlockSelectorLiteral::Genesis) => {
|
BlockSelector::Genesis => write!(f, "genesis/{}", self.index),
|
||||||
write!(f, "genesis/{}", self.index)
|
BlockSelector::Tip => write!(f, "tip/{}", self.index),
|
||||||
}
|
|
||||||
BlockSelector::Special(BlockSelectorLiteral::Tip) => write!(f, "tip/{}", self.index),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,18 +147,15 @@ impl<'de> Deserialize<'de> for AnnouncementSelector {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use arbitrary::Arbitrary;
|
|
||||||
use arbitrary::Unstructured;
|
|
||||||
use proptest::prop_assert;
|
|
||||||
use proptest::prop_assert_eq;
|
|
||||||
use proptest::string::string_regex;
|
|
||||||
use proptest_arbitrary_interop::arb;
|
|
||||||
use test_strategy::proptest;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use arbitrary::{Arbitrary, Unstructured};
|
||||||
|
use proptest::string::string_regex;
|
||||||
|
use proptest::{prop_assert, prop_assert_eq};
|
||||||
|
use proptest_arbitrary_interop::arb;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use test_strategy::proptest;
|
||||||
|
|
||||||
impl<'a> Arbitrary<'a> for AnnouncementSelector {
|
impl<'a> Arbitrary<'a> for AnnouncementSelector {
|
||||||
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||||
// Pick one of the variants randomly
|
// Pick one of the variants randomly
|
||||||
@ -192,7 +185,7 @@ mod tests {
|
|||||||
// Genesis selector
|
// Genesis selector
|
||||||
let index = u64::arbitrary(u)? as usize;
|
let index = u64::arbitrary(u)? as usize;
|
||||||
AnnouncementSelector {
|
AnnouncementSelector {
|
||||||
block_selector: BlockSelector::Special(BlockSelectorLiteral::Genesis),
|
block_selector: BlockSelector::Genesis,
|
||||||
index,
|
index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -200,7 +193,7 @@ mod tests {
|
|||||||
// Tip selector
|
// Tip selector
|
||||||
let index = u64::arbitrary(u)? as usize;
|
let index = u64::arbitrary(u)? as usize;
|
||||||
AnnouncementSelector {
|
AnnouncementSelector {
|
||||||
block_selector: BlockSelector::Special(BlockSelectorLiteral::Tip),
|
block_selector: BlockSelector::Tip,
|
||||||
index,
|
index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,15 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use anyhow::Context;
|
|
||||||
use arc_swap::ArcSwap;
|
|
||||||
use clap::Parser;
|
|
||||||
use neptune_cash::api::export::Network;
|
|
||||||
use neptune_cash::application::rpc::auth;
|
|
||||||
use neptune_cash::prelude::twenty_first::tip5::Digest;
|
|
||||||
use neptune_cash::protocol::consensus::block::block_selector::{
|
|
||||||
BlockSelector, BlockSelectorLiteral,
|
|
||||||
};
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
|
|
||||||
use crate::model::config::Config;
|
use crate::model::config::Config;
|
||||||
use crate::model::transparent_utxo_tuple::TransparentUtxoTuple;
|
use crate::model::transparent_utxo_tuple::TransparentUtxoTuple;
|
||||||
use crate::neptune_rpc;
|
use crate::neptune_rpc;
|
||||||
|
use anyhow::Context;
|
||||||
|
use arc_swap::ArcSwap;
|
||||||
|
use clap::Parser;
|
||||||
|
use neptune_cash::config_models::network::Network;
|
||||||
|
use neptune_cash::models::blockchain::block::block_selector::BlockSelector;
|
||||||
|
use neptune_cash::prelude::twenty_first::tip5::Digest;
|
||||||
|
use neptune_cash::rpc_auth;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AppStateInner {
|
pub struct AppStateInner {
|
||||||
@ -30,7 +26,7 @@ pub struct AppStateInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AppStateInner {
|
impl AppStateInner {
|
||||||
pub fn token(&self) -> auth::Token {
|
pub fn token(&self) -> rpc_auth::Token {
|
||||||
self.rpc_client.token
|
self.rpc_client.token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,7 +57,7 @@ impl AppState {
|
|||||||
.block_digest(
|
.block_digest(
|
||||||
tarpc::context::current(),
|
tarpc::context::current(),
|
||||||
rpc_client.token,
|
rpc_client.token,
|
||||||
BlockSelector::Special(BlockSelectorLiteral::Genesis),
|
BlockSelector::Genesis,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.with_context(|| "Failed calling neptune-core api: block_digest")?
|
.with_context(|| "Failed calling neptune-core api: block_digest")?
|
||||||
|
|||||||
@ -1,16 +1,10 @@
|
|||||||
use std::num::ParseIntError;
|
use super::height_or_digest::HeightOrDigest;
|
||||||
use std::str::FromStr;
|
use neptune_cash::models::blockchain::block::block_selector::BlockSelector;
|
||||||
|
use neptune_cash::models::blockchain::block::block_selector::BlockSelectorParseError;
|
||||||
use neptune_cash::api::export::BlockHeight;
|
|
||||||
use neptune_cash::api::export::Digest;
|
|
||||||
use neptune_cash::prelude::triton_vm::prelude::BFieldElement;
|
|
||||||
use neptune_cash::protocol::consensus::block::block_selector::BlockSelector;
|
|
||||||
use neptune_cash::protocol::consensus::block::block_selector::BlockSelectorParseError;
|
|
||||||
use serde::de::Error;
|
use serde::de::Error;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Deserializer;
|
use serde::Deserializer;
|
||||||
|
use std::str::FromStr;
|
||||||
use super::height_or_digest::HeightOrDigest;
|
|
||||||
|
|
||||||
/// newtype for `BlockSelector` that provides ability to parse `height_or_digest/value`.
|
/// newtype for `BlockSelector` that provides ability to parse `height_or_digest/value`.
|
||||||
///
|
///
|
||||||
@ -33,34 +27,17 @@ impl FromStr for BlockSelectorExtended {
|
|||||||
|
|
||||||
// note: this parses BlockSelector, plus height_or_digest/<value>
|
// note: this parses BlockSelector, plus height_or_digest/<value>
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
let res = match BlockSelector::from_str(s) {
|
match BlockSelector::from_str(s) {
|
||||||
Ok(bs) => Ok(Self::from(bs)),
|
Ok(bs) => Ok(Self::from(bs)),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let parts: Vec<_> = s.split('/').collect();
|
let parts: Vec<_> = s.split('/').collect();
|
||||||
if parts.len() == 2 {
|
if parts.len() == 2 && parts[0] == "height_or_digest" {
|
||||||
if parts[0] == "height_or_digest" {
|
Ok(Self::from(HeightOrDigest::from_str(parts[1])?))
|
||||||
Ok(Self::from(HeightOrDigest::from_str(parts[1])?))
|
|
||||||
} else if parts[0] == "digest" {
|
|
||||||
Ok(Self(BlockSelector::Digest(
|
|
||||||
Digest::try_from_hex(parts[1]).map_err(|tfhde| {
|
|
||||||
BlockSelectorParseError::InvalidSelector(tfhde.to_string())
|
|
||||||
})?,
|
|
||||||
)))
|
|
||||||
} else if parts[0] == "height" {
|
|
||||||
Ok(Self(BlockSelector::Height(BlockHeight::new(
|
|
||||||
BFieldElement::new(parts[1].parse().map_err(|e: ParseIntError| {
|
|
||||||
BlockSelectorParseError::InvalidSelector(e.to_string())
|
|
||||||
})?),
|
|
||||||
))))
|
|
||||||
} else {
|
|
||||||
Err(e)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Err(e)
|
Err(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
res
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
use std::str::FromStr;
|
use neptune_cash::models::blockchain::block::block_height::BlockHeight;
|
||||||
|
use neptune_cash::models::blockchain::block::block_selector::BlockSelector;
|
||||||
use neptune_cash::api::export::BlockHeight;
|
use neptune_cash::models::blockchain::block::block_selector::BlockSelectorParseError;
|
||||||
use neptune_cash::prelude::tasm_lib::prelude::Digest;
|
use neptune_cash::prelude::tasm_lib::prelude::Digest;
|
||||||
use neptune_cash::protocol::consensus::block::block_selector::BlockSelector;
|
use serde::{Deserialize, Serialize};
|
||||||
use neptune_cash::protocol::consensus::block::block_selector::BlockSelectorParseError;
|
use std::str::FromStr;
|
||||||
use serde::Deserialize;
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
/// represents either a block-height or a block digest
|
/// represents either a block-height or a block digest
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@ -34,11 +32,7 @@ impl FromStr for HeightOrDigest {
|
|||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
Ok(match s.parse::<u64>() {
|
Ok(match s.parse::<u64>() {
|
||||||
Ok(h) => Self::Height(h.into()),
|
Ok(h) => Self::Height(h.into()),
|
||||||
Err(_) => {
|
Err(_) => Self::Digest(Digest::try_from_hex(s)?),
|
||||||
let digest = Digest::try_from_hex(s)
|
|
||||||
.map_err(|_| BlockSelectorParseError::InvalidSelector(s.to_string()))?;
|
|
||||||
Self::Digest(digest)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,36 +1,33 @@
|
|||||||
use std::net::Ipv4Addr;
|
use crate::alert_email;
|
||||||
use std::net::SocketAddr;
|
use crate::model::app_state::AppState;
|
||||||
use std::sync::Arc;
|
use crate::model::config::Config;
|
||||||
|
use crate::model::transparent_utxo_tuple::TransparentUtxoTuple;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use chrono::TimeDelta;
|
use chrono::TimeDelta;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use neptune_cash::api::export::Announcement;
|
use neptune_cash::api::export::Announcement;
|
||||||
use neptune_cash::api::export::Network;
|
use neptune_cash::config_models::data_directory::DataDirectory;
|
||||||
use neptune_cash::application::config::data_directory::DataDirectory;
|
use neptune_cash::config_models::network::Network;
|
||||||
use neptune_cash::application::rpc::auth;
|
use neptune_cash::models::blockchain::block::block_height::BlockHeight;
|
||||||
use neptune_cash::application::rpc::server::error::RpcError;
|
use neptune_cash::models::blockchain::block::block_info::BlockInfo;
|
||||||
use neptune_cash::application::rpc::server::RPCClient;
|
use neptune_cash::models::blockchain::block::block_selector::BlockSelector;
|
||||||
use neptune_cash::application::rpc::server::RpcResult;
|
|
||||||
use neptune_cash::prelude::tasm_lib::prelude::Digest;
|
use neptune_cash::prelude::tasm_lib::prelude::Digest;
|
||||||
use neptune_cash::protocol::consensus::block::block_height::BlockHeight;
|
use neptune_cash::rpc_auth;
|
||||||
use neptune_cash::protocol::consensus::block::block_info::BlockInfo;
|
use neptune_cash::rpc_server::error::RpcError;
|
||||||
use neptune_cash::protocol::consensus::block::block_selector::BlockSelector;
|
use neptune_cash::rpc_server::RPCClient;
|
||||||
|
use neptune_cash::rpc_server::RpcResult;
|
||||||
use neptune_cash::util_types::mutator_set::addition_record::AdditionRecord;
|
use neptune_cash::util_types::mutator_set::addition_record::AdditionRecord;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::sync::Arc;
|
||||||
use tarpc::client;
|
use tarpc::client;
|
||||||
use tarpc::context;
|
use tarpc::context;
|
||||||
use tarpc::tokio_serde::formats::Json as RpcJson;
|
use tarpc::tokio_serde::formats::Json as RpcJson;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tracing::debug;
|
use tracing::{debug, info, warn};
|
||||||
use tracing::info;
|
|
||||||
use tracing::warn;
|
|
||||||
|
|
||||||
use crate::alert_email;
|
|
||||||
use crate::model::app_state::AppState;
|
|
||||||
use crate::model::config::Config;
|
|
||||||
use crate::model::transparent_utxo_tuple::TransparentUtxoTuple;
|
|
||||||
|
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
const MOCK_KEY: &str = "MOCK";
|
const MOCK_KEY: &str = "MOCK";
|
||||||
@ -38,7 +35,7 @@ const MOCK_KEY: &str = "MOCK";
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AuthenticatedClient {
|
pub struct AuthenticatedClient {
|
||||||
pub client: RPCClient,
|
pub client: RPCClient,
|
||||||
pub token: auth::Token,
|
pub token: rpc_auth::Token,
|
||||||
pub network: Network,
|
pub network: Network,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +52,7 @@ impl AuthenticatedClient {
|
|||||||
pub async fn block_info(
|
pub async fn block_info(
|
||||||
&self,
|
&self,
|
||||||
ctx: ::tarpc::context::Context,
|
ctx: ::tarpc::context::Context,
|
||||||
token: auth::Token,
|
token: rpc_auth::Token,
|
||||||
block_selector: BlockSelector,
|
block_selector: BlockSelector,
|
||||||
) -> ::core::result::Result<RpcResult<Option<BlockInfo>>, ::tarpc::client::RpcError> {
|
) -> ::core::result::Result<RpcResult<Option<BlockInfo>>, ::tarpc::client::RpcError> {
|
||||||
let rpc_result = self.client.block_info(ctx, token, block_selector).await;
|
let rpc_result = self.client.block_info(ctx, token, block_selector).await;
|
||||||
@ -98,7 +95,7 @@ impl AuthenticatedClient {
|
|||||||
pub async fn utxo_digest(
|
pub async fn utxo_digest(
|
||||||
&self,
|
&self,
|
||||||
ctx: ::tarpc::context::Context,
|
ctx: ::tarpc::context::Context,
|
||||||
token: auth::Token,
|
token: rpc_auth::Token,
|
||||||
leaf_index: u64,
|
leaf_index: u64,
|
||||||
_transparent_utxos_cache: Arc<Mutex<Vec<TransparentUtxoTuple>>>,
|
_transparent_utxos_cache: Arc<Mutex<Vec<TransparentUtxoTuple>>>,
|
||||||
) -> ::core::result::Result<RpcResult<Option<Digest>>, ::tarpc::client::RpcError> {
|
) -> ::core::result::Result<RpcResult<Option<Digest>>, ::tarpc::client::RpcError> {
|
||||||
@ -128,7 +125,7 @@ impl AuthenticatedClient {
|
|||||||
pub async fn announcements_in_block(
|
pub async fn announcements_in_block(
|
||||||
&self,
|
&self,
|
||||||
ctx: ::tarpc::context::Context,
|
ctx: ::tarpc::context::Context,
|
||||||
token: auth::Token,
|
token: rpc_auth::Token,
|
||||||
block_selector: BlockSelector,
|
block_selector: BlockSelector,
|
||||||
) -> Result<Result<Option<Vec<Announcement>>, RpcError>, ::tarpc::client::RpcError> {
|
) -> Result<Result<Option<Vec<Announcement>>, RpcError>, ::tarpc::client::RpcError> {
|
||||||
let rpc_result = self
|
let rpc_result = self
|
||||||
@ -194,11 +191,11 @@ impl AuthenticatedClient {
|
|||||||
pub async fn addition_record_indices_for_block(
|
pub async fn addition_record_indices_for_block(
|
||||||
&self,
|
&self,
|
||||||
ctx: ::tarpc::context::Context,
|
ctx: ::tarpc::context::Context,
|
||||||
token: auth::Token,
|
token: rpc_auth::Token,
|
||||||
block_selector: BlockSelector,
|
block_selector: BlockSelector,
|
||||||
_addition_records: &[AdditionRecord],
|
_addition_records: &[AdditionRecord],
|
||||||
) -> ::core::result::Result<
|
) -> ::core::result::Result<
|
||||||
RpcResult<Vec<(AdditionRecord, Option<u64>)>>,
|
RpcResult<Option<HashMap<AdditionRecord, Option<u64>>>>,
|
||||||
::tarpc::client::RpcError,
|
::tarpc::client::RpcError,
|
||||||
> {
|
> {
|
||||||
let rpc_result = self
|
let rpc_result = self
|
||||||
@ -207,7 +204,7 @@ impl AuthenticatedClient {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
// if the RPC call was successful, return that
|
// if the RPC call was successful, return that
|
||||||
if let Ok(Ok(_)) = rpc_result {
|
if let Ok(Ok(Some(_))) = rpc_result {
|
||||||
return rpc_result;
|
return rpc_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,8 +237,8 @@ impl AuthenticatedClient {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<Vec<(_, _)>>();
|
.collect::<HashMap<_, _>>();
|
||||||
return Ok(Ok(addition_record_indices));
|
return Ok(Ok(Some(addition_record_indices)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise, return the original error
|
// otherwise, return the original error
|
||||||
@ -253,12 +250,12 @@ impl AuthenticatedClient {
|
|||||||
pub async fn gen_authenticated_rpc_client() -> Result<AuthenticatedClient, anyhow::Error> {
|
pub async fn gen_authenticated_rpc_client() -> Result<AuthenticatedClient, anyhow::Error> {
|
||||||
let client = gen_rpc_client().await?;
|
let client = gen_rpc_client().await?;
|
||||||
|
|
||||||
let auth::CookieHint {
|
let rpc_auth::CookieHint {
|
||||||
data_directory,
|
data_directory,
|
||||||
network,
|
network,
|
||||||
} = get_cookie_hint(&client, &None).await?;
|
} = get_cookie_hint(&client, &None).await?;
|
||||||
|
|
||||||
let token: auth::Token = auth::Cookie::try_load(&data_directory).await?.into();
|
let token: rpc_auth::Token = rpc_auth::Cookie::try_load(&data_directory).await?.into();
|
||||||
|
|
||||||
Ok(AuthenticatedClient {
|
Ok(AuthenticatedClient {
|
||||||
client,
|
client,
|
||||||
@ -292,14 +289,14 @@ pub async fn gen_rpc_client() -> Result<RPCClient, anyhow::Error> {
|
|||||||
async fn get_cookie_hint(
|
async fn get_cookie_hint(
|
||||||
client: &RPCClient,
|
client: &RPCClient,
|
||||||
data_dir: &Option<std::path::PathBuf>,
|
data_dir: &Option<std::path::PathBuf>,
|
||||||
) -> anyhow::Result<auth::CookieHint> {
|
) -> anyhow::Result<rpc_auth::CookieHint> {
|
||||||
async fn fallback(
|
async fn fallback(
|
||||||
client: &RPCClient,
|
client: &RPCClient,
|
||||||
data_dir: &Option<std::path::PathBuf>,
|
data_dir: &Option<std::path::PathBuf>,
|
||||||
) -> anyhow::Result<auth::CookieHint> {
|
) -> anyhow::Result<rpc_auth::CookieHint> {
|
||||||
let network = client.network(context::current()).await??;
|
let network = client.network(context::current()).await??;
|
||||||
let data_directory = DataDirectory::get(data_dir.to_owned(), network)?;
|
let data_directory = DataDirectory::get(data_dir.to_owned(), network)?;
|
||||||
Ok(auth::CookieHint {
|
Ok(rpc_auth::CookieHint {
|
||||||
data_directory,
|
data_directory,
|
||||||
network,
|
network,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,17 +1,15 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use axum::extract::Path;
|
|
||||||
use axum::extract::State;
|
|
||||||
use axum::response::IntoResponse;
|
|
||||||
use axum::response::Json;
|
|
||||||
use neptune_cash::prelude::twenty_first::tip5::Digest;
|
|
||||||
use tarpc::context;
|
|
||||||
|
|
||||||
use crate::http_util::not_found_err;
|
use crate::http_util::not_found_err;
|
||||||
use crate::http_util::rpc_err;
|
use crate::http_util::rpc_err;
|
||||||
use crate::http_util::rpc_method_err;
|
use crate::http_util::rpc_method_err;
|
||||||
use crate::model::app_state::AppState;
|
use crate::model::app_state::AppState;
|
||||||
use crate::model::block_selector_extended::BlockSelectorExtended;
|
use crate::model::block_selector_extended::BlockSelectorExtended;
|
||||||
|
use axum::extract::Path;
|
||||||
|
use axum::extract::State;
|
||||||
|
use axum::response::IntoResponse;
|
||||||
|
use axum::response::Json;
|
||||||
|
use neptune_cash::prelude::twenty_first::tip5::Digest;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tarpc::context;
|
||||||
|
|
||||||
#[axum::debug_handler]
|
#[axum::debug_handler]
|
||||||
pub async fn block_digest(
|
pub async fn block_digest(
|
||||||
|
|||||||
@ -1,17 +1,15 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use axum::extract::Path;
|
|
||||||
use axum::extract::State;
|
|
||||||
use axum::response::Json;
|
|
||||||
use axum::response::Response;
|
|
||||||
use neptune_cash::protocol::consensus::block::block_info::BlockInfo;
|
|
||||||
use tarpc::context;
|
|
||||||
|
|
||||||
use crate::http_util::not_found_err;
|
use crate::http_util::not_found_err;
|
||||||
use crate::http_util::rpc_err;
|
use crate::http_util::rpc_err;
|
||||||
use crate::http_util::rpc_method_err;
|
use crate::http_util::rpc_method_err;
|
||||||
use crate::model::app_state::AppState;
|
use crate::model::app_state::AppState;
|
||||||
use crate::model::block_selector_extended::BlockSelectorExtended;
|
use crate::model::block_selector_extended::BlockSelectorExtended;
|
||||||
|
use axum::extract::Path;
|
||||||
|
use axum::extract::State;
|
||||||
|
use axum::response::Json;
|
||||||
|
use axum::response::Response;
|
||||||
|
use neptune_cash::models::blockchain::block::block_info::BlockInfo;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tarpc::context;
|
||||||
|
|
||||||
#[axum::debug_handler]
|
#[axum::debug_handler]
|
||||||
pub async fn block_info(
|
pub async fn block_info(
|
||||||
|
|||||||
@ -1,30 +0,0 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use axum::extract::State;
|
|
||||||
use axum::response::Json;
|
|
||||||
use axum::response::Response;
|
|
||||||
use tarpc::context;
|
|
||||||
|
|
||||||
use crate::http_util::rpc_err;
|
|
||||||
use crate::http_util::rpc_method_err;
|
|
||||||
use crate::model::app_state::AppState;
|
|
||||||
use crate::shared::monetary_supplies;
|
|
||||||
|
|
||||||
/// Return the current monetary amount that is liquid, assuming all redemptions
|
|
||||||
/// on the old chain have successfully been made. Returned unit is in number of
|
|
||||||
/// coins. To convert to number of nau, multiply by $4*10^{30}$/
|
|
||||||
#[axum::debug_handler]
|
|
||||||
pub async fn circulating_supply(State(state): State<Arc<AppState>>) -> Result<Json<i32>, Response> {
|
|
||||||
let s = state.load();
|
|
||||||
|
|
||||||
let block_height = s
|
|
||||||
.rpc_client
|
|
||||||
.block_height(context::current(), s.token())
|
|
||||||
.await
|
|
||||||
.map_err(rpc_err)?
|
|
||||||
.map_err(rpc_method_err)?;
|
|
||||||
|
|
||||||
let (liquid_supply, _) = monetary_supplies(block_height);
|
|
||||||
|
|
||||||
Ok(Json(liquid_supply.ceil_num_whole_coins()))
|
|
||||||
}
|
|
||||||
@ -1,7 +1,5 @@
|
|||||||
pub mod block_digest;
|
pub mod block_digest;
|
||||||
pub mod block_info;
|
pub mod block_info;
|
||||||
pub mod circulating_supply;
|
|
||||||
pub mod pow_puzzle;
|
pub mod pow_puzzle;
|
||||||
pub mod provide_pow_solution;
|
pub mod provide_pow_solution;
|
||||||
pub mod total_supply;
|
|
||||||
pub mod utxo_digest;
|
pub mod utxo_digest;
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use axum::extract::Path;
|
use axum::extract::Path;
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum::response::IntoResponse;
|
use axum::response::IntoResponse;
|
||||||
use axum::response::Json;
|
use axum::response::Json;
|
||||||
use neptune_cash::application::rpc::server::error::RpcError;
|
use neptune_cash::models::state::wallet::address::generation_address::GenerationReceivingAddress;
|
||||||
use neptune_cash::application::rpc::server::proof_of_work_puzzle::ProofOfWorkPuzzle;
|
use neptune_cash::rpc_server::error::RpcError;
|
||||||
use neptune_cash::state::wallet::address::generation_address::GenerationReceivingAddress;
|
use neptune_cash::rpc_server::ProofOfWorkPuzzle;
|
||||||
|
use std::sync::Arc;
|
||||||
use tarpc::context;
|
use tarpc::context;
|
||||||
|
|
||||||
use crate::http_util::not_found_err;
|
use crate::http_util::not_found_err;
|
||||||
|
|||||||
@ -1,17 +1,15 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use axum::extract::State;
|
|
||||||
use axum::response::Json;
|
|
||||||
use axum::response::Response;
|
|
||||||
use neptune_cash::prelude::twenty_first::tip5::Digest;
|
|
||||||
use neptune_cash::protocol::consensus::block::block_header::BlockPow;
|
|
||||||
use serde::Deserialize;
|
|
||||||
use serde::Serialize;
|
|
||||||
use tarpc::context;
|
|
||||||
|
|
||||||
use crate::http_util::rpc_err;
|
use crate::http_util::rpc_err;
|
||||||
use crate::http_util::rpc_method_err;
|
use crate::http_util::rpc_method_err;
|
||||||
use crate::model::app_state::AppState;
|
use crate::model::app_state::AppState;
|
||||||
|
use axum::extract::State;
|
||||||
|
use axum::response::Json;
|
||||||
|
use axum::response::Response;
|
||||||
|
use neptune_cash::models::blockchain::block::block_header::BlockPow;
|
||||||
|
use neptune_cash::prelude::twenty_first::tip5::Digest;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tarpc::context;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct PowSolution {
|
pub struct PowSolution {
|
||||||
|
|||||||
@ -1,31 +0,0 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use axum::extract::State;
|
|
||||||
use axum::response::Json;
|
|
||||||
use axum::response::Response;
|
|
||||||
use tarpc::context;
|
|
||||||
|
|
||||||
use crate::http_util::rpc_err;
|
|
||||||
use crate::http_util::rpc_method_err;
|
|
||||||
use crate::model::app_state::AppState;
|
|
||||||
use crate::shared::monetary_supplies;
|
|
||||||
|
|
||||||
/// Return the current total monetary supply, the sum of the timeloced and
|
|
||||||
/// liquid supply. Assumes all redemptions on the old chain have successfully
|
|
||||||
/// been made. Returned unit is in number of coins. To convert to number of
|
|
||||||
/// nau, multiply by $4*10^{30}$.
|
|
||||||
#[axum::debug_handler]
|
|
||||||
pub async fn total_supply(State(state): State<Arc<AppState>>) -> Result<Json<i32>, Response> {
|
|
||||||
let s = state.load();
|
|
||||||
|
|
||||||
let block_height = s
|
|
||||||
.rpc_client
|
|
||||||
.block_height(context::current(), s.token())
|
|
||||||
.await
|
|
||||||
.map_err(rpc_err)?
|
|
||||||
.map_err(rpc_method_err)?;
|
|
||||||
|
|
||||||
let (_, total_supply) = monetary_supplies(block_height);
|
|
||||||
|
|
||||||
Ok(Json(total_supply.ceil_num_whole_coins()))
|
|
||||||
}
|
|
||||||
@ -1,16 +1,16 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use axum::extract::Path;
|
use axum::extract::Path;
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum::response::IntoResponse;
|
use axum::response::IntoResponse;
|
||||||
use axum::response::Json;
|
use axum::response::Json;
|
||||||
use neptune_cash::prelude::twenty_first::tip5::Digest;
|
use neptune_cash::prelude::twenty_first::tip5::Digest;
|
||||||
|
use std::sync::Arc;
|
||||||
use tarpc::context;
|
use tarpc::context;
|
||||||
|
|
||||||
use crate::http_util::not_found_err;
|
|
||||||
use crate::http_util::rpc_err;
|
|
||||||
use crate::http_util::rpc_method_err;
|
use crate::http_util::rpc_method_err;
|
||||||
use crate::model::app_state::AppState;
|
use crate::{
|
||||||
|
http_util::{not_found_err, rpc_err},
|
||||||
|
model::app_state::AppState,
|
||||||
|
};
|
||||||
|
|
||||||
#[axum::debug_handler]
|
#[axum::debug_handler]
|
||||||
pub async fn utxo_digest(
|
pub async fn utxo_digest(
|
||||||
|
|||||||
@ -1,60 +0,0 @@
|
|||||||
use neptune_cash::api::export::BlockHeight;
|
|
||||||
use neptune_cash::api::export::NativeCurrencyAmount;
|
|
||||||
use neptune_cash::protocol::consensus::block::block_height::BLOCKS_PER_GENERATION;
|
|
||||||
use neptune_cash::protocol::consensus::block::block_height::NUM_BLOCKS_SKIPPED_BECAUSE_REBOOT;
|
|
||||||
use neptune_cash::protocol::consensus::block::Block;
|
|
||||||
use neptune_cash::protocol::consensus::block::PREMINE_MAX_SIZE;
|
|
||||||
|
|
||||||
/// Return the pair (liquid supply, total supply)
|
|
||||||
///
|
|
||||||
/// Assumes all redemption claims have been rewarded.
|
|
||||||
pub(crate) fn monetary_supplies(
|
|
||||||
block_height: BlockHeight,
|
|
||||||
) -> (NativeCurrencyAmount, NativeCurrencyAmount) {
|
|
||||||
let block_height: u64 = block_height.into();
|
|
||||||
let generation_0_subsidy = Block::block_subsidy(BlockHeight::genesis().next());
|
|
||||||
let effective_block_height = block_height + NUM_BLOCKS_SKIPPED_BECAUSE_REBOOT;
|
|
||||||
let (num_generations, num_blocks_in_curr_gen): (u64, u32) = (
|
|
||||||
effective_block_height / BLOCKS_PER_GENERATION,
|
|
||||||
(effective_block_height % BLOCKS_PER_GENERATION)
|
|
||||||
.try_into()
|
|
||||||
.expect("There are fewer than u32::MAX blocks per generation"),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut liquid_supply = PREMINE_MAX_SIZE;
|
|
||||||
let mut liquid_subsidy = generation_0_subsidy.half();
|
|
||||||
let mut total_supply = PREMINE_MAX_SIZE;
|
|
||||||
let blocks_per_generation: u32 = BLOCKS_PER_GENERATION
|
|
||||||
.try_into()
|
|
||||||
.expect("There are fewer than u32::MAX blocks per generation");
|
|
||||||
for _ in 0..num_generations {
|
|
||||||
liquid_supply += liquid_subsidy.scalar_mul(blocks_per_generation);
|
|
||||||
total_supply += liquid_subsidy.scalar_mul(2);
|
|
||||||
liquid_subsidy = liquid_subsidy.half();
|
|
||||||
}
|
|
||||||
|
|
||||||
let liquid_supply_current_generation = liquid_subsidy.scalar_mul(num_blocks_in_curr_gen);
|
|
||||||
liquid_supply += liquid_supply_current_generation;
|
|
||||||
total_supply += liquid_supply_current_generation.scalar_mul(2);
|
|
||||||
|
|
||||||
// How much of timelocked miner rewards have been unlocked? Assume that the
|
|
||||||
// timelock is exactly one generation long. In reality the timelock is
|
|
||||||
// is defined in relation to timestamp and not block heights, so this is
|
|
||||||
// only a (good) approximation.
|
|
||||||
|
|
||||||
let mut released_subsidy = generation_0_subsidy.half();
|
|
||||||
for _ in 1..num_generations {
|
|
||||||
liquid_supply += released_subsidy.scalar_mul(blocks_per_generation);
|
|
||||||
released_subsidy = released_subsidy.half();
|
|
||||||
}
|
|
||||||
|
|
||||||
if num_generations > 0 {
|
|
||||||
liquid_supply += released_subsidy.scalar_mul(num_blocks_in_curr_gen);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If you want correct results for anything but main net, and claims are
|
|
||||||
// only being refunded on main net, the size of the claims pool must be
|
|
||||||
// subtracted here, if that the network is not main net.
|
|
||||||
|
|
||||||
(liquid_supply, total_supply)
|
|
||||||
}
|
|
||||||
@ -147,10 +147,6 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer class="container" style="margin-top: 2em; font-size: 0.9em; text-align: center;">
|
|
||||||
<a href="https://github.com/Neptune-Crypto/neptune-explorer" target="_blank">Source code</a>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user