Skip to content

Commit 46cb627

Browse files
authored
Add /status page (ordinals#2819)
1 parent 9f6cb92 commit 46cb627

File tree

10 files changed

+215
-27
lines changed

10 files changed

+215
-27
lines changed

Diff for: Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ futures = "0.3.21"
3939
hex = "0.4.3"
4040
html-escaper = "0.2.0"
4141
http = "0.2.6"
42+
humantime = "2.1.0"
4243
hyper = { version = "0.14.24", features = ["client", "http2"] }
4344
indicatif = "0.17.1"
4445
lazy_static = "1.4.0"

Diff for: build.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use std::{process::Command, str};
2+
3+
fn git_branch() -> Option<String> {
4+
str::from_utf8(
5+
&Command::new("git")
6+
.args(["rev-parse", "--abbrev-ref", "HEAD"])
7+
.output()
8+
.ok()?
9+
.stdout,
10+
)
11+
.ok()
12+
.map(|branch| branch.into())
13+
}
14+
15+
fn git_commit() -> Option<String> {
16+
str::from_utf8(
17+
&Command::new("git")
18+
.args(["rev-parse", "--verify", "HEAD"])
19+
.output()
20+
.ok()?
21+
.stdout,
22+
)
23+
.ok()
24+
.map(|branch| branch.into())
25+
}
26+
27+
fn main() {
28+
println!(
29+
"cargo:rustc-env=GIT_BRANCH={}",
30+
git_branch().unwrap_or_default()
31+
);
32+
println!(
33+
"cargo:rustc-env=GIT_COMMIT={}",
34+
git_commit().unwrap_or_default()
35+
);
36+
}

Diff for: src/index.rs

+48-8
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use {
99
updater::Updater,
1010
},
1111
super::*,
12-
crate::subcommand::find::FindRangeOutput,
1312
crate::wallet::Wallet,
13+
crate::{subcommand::find::FindRangeOutput, templates::StatusHtml},
1414
bitcoin::block::Header,
1515
bitcoincore_rpc::{json::GetBlockHeaderResult, Client},
1616
chrono::SubsecRound,
@@ -181,6 +181,7 @@ pub(crate) struct Index {
181181
index_sats: bool,
182182
options: Options,
183183
path: PathBuf,
184+
started: DateTime<Utc>,
184185
unrecoverably_reorged: AtomicBool,
185186
}
186187

@@ -342,10 +343,11 @@ impl Index {
342343
first_inscription_height: options.first_inscription_height(),
343344
genesis_block_coinbase_transaction,
344345
height_limit: options.height_limit,
345-
options: options.clone(),
346346
index_runes,
347347
index_sats,
348+
options: options.clone(),
348349
path,
350+
started: Utc::now(),
349351
unrecoverably_reorged: AtomicBool::new(false),
350352
})
351353
}
@@ -436,6 +438,48 @@ impl Index {
436438
self.index_sats
437439
}
438440

441+
pub(crate) fn status(&self) -> Result<StatusHtml> {
442+
let rtx = self.database.begin_read()?;
443+
444+
let statistic_to_count = rtx.open_table(STATISTIC_TO_COUNT)?;
445+
446+
let statistic = |statistic: Statistic| -> Result<u64> {
447+
Ok(
448+
statistic_to_count
449+
.get(statistic.key())?
450+
.map(|guard| guard.value())
451+
.unwrap_or_default(),
452+
)
453+
};
454+
455+
let height = rtx
456+
.open_table(HEIGHT_TO_BLOCK_HASH)?
457+
.range(0..)?
458+
.next_back()
459+
.transpose()?
460+
.map(|(height, _hash)| height.value());
461+
462+
let next_height = height.map(|height| height + 1).unwrap_or(0);
463+
464+
let blessed_inscriptions = statistic(Statistic::BlessedInscriptions)?;
465+
let cursed_inscriptions = statistic(Statistic::CursedInscriptions)?;
466+
467+
Ok(StatusHtml {
468+
blessed_inscriptions,
469+
cursed_inscriptions,
470+
height,
471+
inscriptions: blessed_inscriptions + cursed_inscriptions,
472+
lost_sats: statistic(Statistic::LostSats)?,
473+
minimum_rune_for_next_block: Rune::minimum_at_height(Height(next_height)),
474+
rune_index: statistic(Statistic::IndexRunes)? != 0,
475+
runes: statistic(Statistic::Runes)?,
476+
sat_index: statistic(Statistic::IndexSats)? != 0,
477+
started: self.started,
478+
unrecoverably_reorged: self.unrecoverably_reorged.load(atomic::Ordering::Relaxed),
479+
uptime: (Utc::now() - self.started).to_std()?,
480+
})
481+
}
482+
439483
pub(crate) fn info(&self) -> Result<Info> {
440484
fn insert_table_info<K: RedbKey + 'static, V: RedbValue + 'static>(
441485
tables: &mut BTreeMap<String, TableInfo>,
@@ -709,10 +753,6 @@ impl Index {
709753
Ok(())
710754
}
711755

712-
pub(crate) fn is_unrecoverably_reorged(&self) -> bool {
713-
self.unrecoverably_reorged.load(atomic::Ordering::Relaxed)
714-
}
715-
716756
fn begin_read(&self) -> Result<rtx::Rtx> {
717757
Ok(rtx::Rtx(self.database.begin_read()?))
718758
}
@@ -728,7 +768,7 @@ impl Index {
728768
let value = statistic_to_count
729769
.get(&(statistic.key()))?
730770
.map(|x| x.value())
731-
.unwrap_or(0)
771+
.unwrap_or_default()
732772
+ n;
733773
statistic_to_count.insert(&statistic.key(), &value)?;
734774
Ok(())
@@ -745,7 +785,7 @@ impl Index {
745785
.get(&statistic.key())
746786
.unwrap()
747787
.map(|x| x.value())
748-
.unwrap_or(0)
788+
.unwrap_or_default()
749789
}
750790

751791
pub(crate) fn block_count(&self) -> Result<u32> {

Diff for: src/subcommand/server.rs

+61-17
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use {
1616
PreviewAudioHtml, PreviewCodeHtml, PreviewFontHtml, PreviewImageHtml, PreviewMarkdownHtml,
1717
PreviewModelHtml, PreviewPdfHtml, PreviewTextHtml, PreviewUnknownHtml, PreviewVideoHtml,
1818
RangeHtml, RareTxt, RuneHtml, RunesHtml, SatHtml, SatInscriptionJson, SatInscriptionsJson,
19-
SatJson, TransactionHtml,
19+
SatJson, StatusHtml, TransactionHtml,
2020
},
2121
},
2222
axum::{
@@ -750,18 +750,11 @@ impl Server {
750750
Ok(Json(hex::encode(metadata)))
751751
}
752752

753-
async fn status(Extension(index): Extension<Arc<Index>>) -> (StatusCode, &'static str) {
754-
if index.is_unrecoverably_reorged() {
755-
(
756-
StatusCode::OK,
757-
"unrecoverable reorg detected, please rebuild the database.",
758-
)
759-
} else {
760-
(
761-
StatusCode::OK,
762-
StatusCode::OK.canonical_reason().unwrap_or_default(),
763-
)
764-
}
753+
async fn status(
754+
Extension(page_config): Extension<Arc<PageConfig>>,
755+
Extension(index): Extension<Arc<Index>>,
756+
) -> ServerResult<PageHtml<StatusHtml>> {
757+
Ok(index.status()?.page(page_config))
765758
}
766759

767760
async fn search_by_query(
@@ -973,7 +966,7 @@ impl Server {
973966
Extension(page_config): Extension<Arc<PageConfig>>,
974967
Extension(index): Extension<Arc<Index>>,
975968
Path(path): Path<(u32, usize, usize)>,
976-
) -> Result<PageHtml<InputHtml>, ServerError> {
969+
) -> ServerResult<PageHtml<InputHtml>> {
977970
let not_found = || format!("input /{}/{}/{}", path.0, path.1, path.2);
978971

979972
let block = index
@@ -2458,7 +2451,50 @@ mod tests {
24582451

24592452
#[test]
24602453
fn status() {
2461-
TestServer::new().assert_response("/status", StatusCode::OK, "OK");
2454+
let test_server = TestServer::new();
2455+
2456+
test_server.assert_response_regex(
2457+
"/status",
2458+
StatusCode::OK,
2459+
".*<h1>Status</h1>
2460+
<dl>
2461+
<dt>height</dt>
2462+
<dd>0</dd>
2463+
<dt>inscriptions</dt>
2464+
<dd>0</dd>
2465+
<dt>blessed inscriptions</dt>
2466+
<dd>0</dd>
2467+
<dt>cursed inscriptions</dt>
2468+
<dd>0</dd>
2469+
<dt>runes</dt>
2470+
<dd>0</dd>
2471+
<dt>lost sats</dt>
2472+
<dd>.*</dd>
2473+
<dt>started</dt>
2474+
<dd>.*</dd>
2475+
<dt>uptime</dt>
2476+
<dd>.*</dd>
2477+
<dt>minimum rune for next block</dt>
2478+
<dd>AAAAAAAAAAAAA</dd>
2479+
<dt>version</dt>
2480+
<dd>.*</dd>
2481+
<dt>unrecoverably reorged</dt>
2482+
<dd>false</dd>
2483+
<dt>sat index</dt>
2484+
<dd>false</dd>
2485+
<dt>rune index</dt>
2486+
<dd>false</dd>
2487+
<dt>git branch</dt>
2488+
<dd>.*</dd>
2489+
<dt>git commit</dt>
2490+
<dd>
2491+
<a href=https://github.com/ordinals/ord/commit/[[:xdigit:]]{40}>
2492+
[[:xdigit:]]{40}
2493+
</a>
2494+
</dd>
2495+
</dl>
2496+
.*",
2497+
);
24622498
}
24632499

24642500
#[test]
@@ -2994,15 +3030,23 @@ mod tests {
29943030

29953031
test_server.mine_blocks(21);
29963032

2997-
test_server.assert_response("/status", StatusCode::OK, "OK");
3033+
test_server.assert_response_regex(
3034+
"/status",
3035+
StatusCode::OK,
3036+
".*<dt>unrecoverably reorged</dt>\n <dd>false</dd>.*",
3037+
);
29983038

29993039
for _ in 0..15 {
30003040
test_server.bitcoin_rpc_server.invalidate_tip();
30013041
}
30023042

30033043
test_server.bitcoin_rpc_server.mine_blocks(21);
30043044

3005-
test_server.assert_response_regex("/status", StatusCode::OK, "unrecoverable reorg detected.*");
3045+
test_server.assert_response_regex(
3046+
"/status",
3047+
StatusCode::OK,
3048+
".*<dt>unrecoverably reorged</dt>\n <dd>true</dd>.*",
3049+
);
30063050
}
30073051

30083052
#[test]

Diff for: src/templates.rs

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub(crate) use {
2424
rune::RuneHtml,
2525
runes::RunesHtml,
2626
sat::{SatHtml, SatInscriptionJson, SatInscriptionsJson, SatJson},
27+
status::StatusHtml,
2728
transaction::TransactionHtml,
2829
};
2930

@@ -46,6 +47,7 @@ mod rare;
4647
mod rune;
4748
mod runes;
4849
pub mod sat;
50+
mod status;
4951
mod transaction;
5052

5153
#[derive(Boilerplate)]

Diff for: src/templates/status.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use super::*;
2+
3+
#[derive(Boilerplate)]
4+
pub(crate) struct StatusHtml {
5+
pub(crate) blessed_inscriptions: u64,
6+
pub(crate) cursed_inscriptions: u64,
7+
pub(crate) height: Option<u32>,
8+
pub(crate) inscriptions: u64,
9+
pub(crate) lost_sats: u64,
10+
pub(crate) minimum_rune_for_next_block: Rune,
11+
pub(crate) rune_index: bool,
12+
pub(crate) runes: u64,
13+
pub(crate) sat_index: bool,
14+
pub(crate) started: DateTime<Utc>,
15+
pub(crate) unrecoverably_reorged: bool,
16+
pub(crate) uptime: Duration,
17+
}
18+
19+
impl PageContent for StatusHtml {
20+
fn title(&self) -> String {
21+
"Status".into()
22+
}
23+
}

Diff for: templates/status.html

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<h1>Status</h1>
2+
<dl>
3+
%% if let Some(height) = self.height {
4+
<dt>height</dt>
5+
<dd>{{ height }}</dd>
6+
%% }
7+
<dt>inscriptions</dt>
8+
<dd>{{ self.inscriptions }}</dd>
9+
<dt>blessed inscriptions</dt>
10+
<dd>{{ self.blessed_inscriptions }}</dd>
11+
<dt>cursed inscriptions</dt>
12+
<dd>{{ self.cursed_inscriptions }}</dd>
13+
<dt>runes</dt>
14+
<dd>{{ self.runes }}</dd>
15+
<dt>lost sats</dt>
16+
<dd>{{ self.lost_sats }}</dd>
17+
<dt>started</dt>
18+
<dd>{{ self.started }}</dd>
19+
<dt>uptime</dt>
20+
<dd>{{ humantime::format_duration(self.uptime) }}</dd>
21+
<dt>minimum rune for next block</dt>
22+
<dd>{{ self.minimum_rune_for_next_block }}</dd>
23+
<dt>version</dt>
24+
<dd>{{ env!("CARGO_PKG_VERSION") }}</dd>
25+
<dt>unrecoverably reorged</dt>
26+
<dd>{{ self.unrecoverably_reorged }}</dd>
27+
<dt>sat index</dt>
28+
<dd>{{ self.sat_index }}</dd>
29+
<dt>rune index</dt>
30+
<dd>{{ self.rune_index }}</dd>
31+
%% if !env!("GIT_BRANCH").is_empty() {
32+
<dt>git branch</dt>
33+
<dd>{{ env!("GIT_BRANCH") }}</dd>
34+
%% }
35+
%% if !env!("GIT_COMMIT").is_empty() {
36+
<dt>git commit</dt>
37+
<dd>
38+
<a href=https://github.com/ordinals/ord/commit/{{ env!("GIT_COMMIT") }}>
39+
{{ env!("GIT_COMMIT") }}
40+
</a>
41+
</dd>
42+
%% }
43+
</dl>

Diff for: tests/core.rs

-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ fn preview() {
4343
for attempt in 0.. {
4444
if let Ok(response) = reqwest::blocking::get(format!("http://127.0.0.1:{port}/status")) {
4545
if response.status() == 200 {
46-
assert_eq!(response.text().unwrap(), "OK");
4746
break;
4847
}
4948
}

Diff for: tests/server.rs

-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ fn run() {
2323
for attempt in 0.. {
2424
if let Ok(response) = reqwest::blocking::get(format!("http://localhost:{port}/status")) {
2525
if response.status() == 200 {
26-
assert_eq!(response.text().unwrap(), "OK");
2726
break;
2827
}
2928
}

0 commit comments

Comments
 (0)