diff --git a/Cargo.lock b/Cargo.lock index 1ab5ffe0b4..0fd08f6cf0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -323,9 +323,9 @@ dependencies = [ [[package]] name = "atom_syndication" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a3a5ed3201df5658d1aa45060c5a57dc9dba8a8ada20d696d67cb0c479ee043" +checksum = "3ee79fb83c725eae67b55218870813d2fc39fd85e4f1583848ef9f4f823cfe7c" dependencies = [ "chrono", "derive_builder", @@ -669,9 +669,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22" dependencies = [ "memchr", "serde", @@ -703,9 +703,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.37" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" dependencies = [ "shlex", ] @@ -772,9 +772,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" dependencies = [ "clap_builder", "clap_derive", @@ -782,9 +782,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" dependencies = [ "anstream", "anstyle", @@ -806,9 +806,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" [[package]] name = "colorchoice" @@ -844,7 +844,7 @@ dependencies = [ "encode_unicode", "lazy_static", "libc", - "unicode-width", + "unicode-width 0.1.14", "windows-sys 0.52.0", ] @@ -1104,9 +1104,9 @@ dependencies = [ [[package]] name = "diligent-date-parser" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6cf7fe294274a222363f84bcb63cdea762979a0443b4cf1f4f8fd17c86b1182" +checksum = "c8ede7d79366f419921e2e2f67889c12125726692a313bffb474bd5f37a581e9" dependencies = [ "chrono", ] @@ -1253,9 +1253,9 @@ checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "flate2" -version = "1.0.34" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", @@ -1511,9 +1511,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", @@ -1713,14 +1713,14 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.6", + "h2 0.4.7", "http 1.1.0", "http-body 1.0.1", "httparse", @@ -1739,9 +1739,9 @@ checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http 1.1.0", - "hyper 1.5.0", + "hyper 1.5.1", "hyper-util", - "rustls 0.23.16", + "rustls 0.23.17", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -1769,7 +1769,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.5.0", + "hyper 1.5.1", "hyper-util", "native-tls", "tokio", @@ -1788,7 +1788,7 @@ dependencies = [ "futures-util", "http 1.1.0", "http-body 1.0.1", - "hyper 1.5.0", + "hyper 1.5.1", "pin-project-lite", "socket2 0.5.7", "tokio", @@ -1988,15 +1988,15 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.8" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281" dependencies = [ "console", - "instant", "number_prefix", "portable-atomic", - "unicode-width", + "unicode-width 0.2.0", + "web-time", ] [[package]] @@ -2053,9 +2053,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" [[package]] name = "js-sys" @@ -2156,9 +2156,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.162" +version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" [[package]] name = "libredox" @@ -2823,9 +2823,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.36.2" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +checksum = "f22f29bdff3987b4d8632ef95fd6424ec7e4e0a57e2f4fc63e489e75357f6a03" dependencies = [ "encoding_rs", "memchr", @@ -3034,11 +3034,11 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.4.6", + "h2 0.4.7", "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.0", + "hyper 1.5.1", "hyper-rustls", "hyper-tls 0.6.0", "hyper-util", @@ -3054,7 +3054,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "system-configuration 0.6.1", "tokio", "tokio-native-tls", @@ -3098,9 +3098,9 @@ dependencies = [ [[package]] name = "rss" -version = "2.0.9" +version = "2.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27e92048f840d98c6d6dd870af9101610ea9ff413f11f1bcebf4f4c31d96d957" +checksum = "554a62b3dd5450fcbb0435b3db809f9dd3c6e9f5726172408f7ad3b57ed59057" dependencies = [ "atom_syndication", "derive_builder", @@ -3173,9 +3173,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.40" +version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ "bitflags 2.6.0", "errno", @@ -3212,9 +3212,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.16" +version = "0.23.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" +checksum = "7f1a745511c54ba6d4465e8d5dfbd81b45791756de28d4981af70d6dca128f1e" dependencies = [ "once_cell", "rustls-pki-types", @@ -3320,9 +3320,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] @@ -3420,9 +3420,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "indexmap 2.6.0", "itoa", @@ -3670,9 +3670,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] @@ -3765,7 +3765,7 @@ dependencies = [ "cfg-if 1.0.0", "fastrand 2.2.0", "once_cell", - "rustix 0.38.40", + "rustix 0.38.41", "windows-sys 0.59.0", ] @@ -3928,7 +3928,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.16", + "rustls 0.23.17", "rustls-pki-types", "tokio", ] @@ -4072,9 +4072,9 @@ checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-normalization" @@ -4091,6 +4091,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -4289,6 +4295,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "0.25.4" diff --git a/crates/ordinals/src/rune.rs b/crates/ordinals/src/rune.rs index c95a215b96..ab052c045a 100644 --- a/crates/ordinals/src/rune.rs +++ b/crates/ordinals/src/rune.rs @@ -6,7 +6,7 @@ use super::*; pub struct Rune(pub u128); impl Rune { - const RESERVED: u128 = 6402364363415443603228541259936211926; + pub const RESERVED: u128 = 6402364363415443603228541259936211926; const UNLOCKED: usize = 12; diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 196e841501..38e501f4ed 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -10,7 +10,7 @@ use { InputHtml, InscriptionHtml, InscriptionsBlockHtml, InscriptionsHtml, OutputHtml, PageContent, PageHtml, ParentsHtml, PreviewAudioHtml, PreviewCodeHtml, PreviewFontHtml, PreviewImageHtml, PreviewMarkdownHtml, PreviewModelHtml, PreviewPdfHtml, PreviewTextHtml, PreviewUnknownHtml, - PreviewVideoHtml, RareTxt, RuneHtml, RunesHtml, SatHtml, TransactionHtml, + PreviewVideoHtml, RareTxt, RuneHtml, RuneNotFoundHtml, RunesHtml, SatHtml, TransactionHtml, }, axum::{ body, @@ -807,9 +807,21 @@ impl Server { .ok_or_not_found(|| format!("rune number {number}"))?, }; - let (id, entry, parent) = index - .rune(rune)? - .ok_or_not_found(|| format!("rune {rune}"))?; + let Some((id, entry, parent)) = index.rune(rune)? else { + return Ok(if accept_json { + StatusCode::NOT_FOUND.into_response() + } else { + ( + StatusCode::NOT_FOUND, + RuneNotFoundHtml { + rune, + unlock_height: rune.unlock_height(server_config.chain.network()), + } + .page(server_config), + ) + .into_response() + }); + }; let block_height = index.block_height()?.unwrap_or(Height(0)); @@ -2590,14 +2602,19 @@ mod tests { #[track_caller] fn assert_html(&self, path: impl AsRef, content: impl PageContent) { + self.assert_html_status(path, StatusCode::OK, content); + } + + #[track_caller] + fn assert_html_status( + &self, + path: impl AsRef, + status: StatusCode, + content: impl PageContent, + ) { let response = self.get(path); - assert_eq!( - response.status(), - StatusCode::OK, - "{}", - response.text().unwrap() - ); + assert_eq!(response.status(), status, "{}", response.text().unwrap()); let expected_response = PageHtml::new( content, @@ -3209,6 +3226,44 @@ mod tests { ); } + #[test] + fn rune_not_etched_shows_unlock_height() { + let server = TestServer::builder() + .chain(Chain::Regtest) + .index_runes() + .build(); + + server.mine_blocks(1); + + server.assert_html_status( + "/rune/A", + StatusCode::NOT_FOUND, + RuneNotFoundHtml { + rune: Rune(0), + unlock_height: Some(Height(209999)), + }, + ); + } + + #[test] + fn reserved_rune_not_etched_shows_reserved_status() { + let server = TestServer::builder() + .chain(Chain::Regtest) + .index_runes() + .build(); + + server.mine_blocks(1); + + server.assert_html_status( + format!("/rune/{}", Rune(Rune::RESERVED)), + StatusCode::NOT_FOUND, + RuneNotFoundHtml { + rune: Rune(Rune::RESERVED), + unlock_height: None, + }, + ); + } + #[test] fn runes_are_displayed_on_runes_page() { let server = TestServer::builder() diff --git a/src/templates.rs b/src/templates.rs index 7898d2a5f8..fa54b01725 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -20,6 +20,7 @@ pub(crate) use { PreviewModelHtml, PreviewPdfHtml, PreviewTextHtml, PreviewUnknownHtml, PreviewVideoHtml, }, rare::RareTxt, + rune_not_found::RuneNotFoundHtml, sat::SatHtml, }; @@ -46,6 +47,7 @@ mod parents; mod preview; mod rare; pub mod rune; +pub mod rune_not_found; pub mod runes; pub mod sat; pub mod status; diff --git a/src/templates/rune_not_found.rs b/src/templates/rune_not_found.rs new file mode 100644 index 0000000000..e050fa5c80 --- /dev/null +++ b/src/templates/rune_not_found.rs @@ -0,0 +1,54 @@ +use super::*; + +#[derive(Boilerplate, Debug, PartialEq, Serialize)] +pub struct RuneNotFoundHtml { + pub rune: Rune, + pub unlock_height: Option, +} + +impl PageContent for RuneNotFoundHtml { + fn title(&self) -> String { + format!("Rune {}", self.rune) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn display() { + assert_regex_match!( + RuneNotFoundHtml { + rune: Rune(u128::MAX), + unlock_height: Some(Height(111)), + }, + "

BCGDENLQRQWDSLRUGSNLBTMFIJAV

+
+
unlock height
+
111
+
reserved
+
false
+
+" + ); + } + + #[test] + fn display_reserved() { + assert_regex_match!( + RuneNotFoundHtml { + rune: Rune(Rune::RESERVED), + unlock_height: None, + }, + "

AAAAAAAAAAAAAAAAAAAAAAAAAAA

+
+
unlock height
+
none
+
reserved
+
true
+
+" + ); + } +} diff --git a/templates/rune-not-found.html b/templates/rune-not-found.html new file mode 100644 index 0000000000..ede0843104 --- /dev/null +++ b/templates/rune-not-found.html @@ -0,0 +1,14 @@ +

{{ self.rune }}

+
+%% if let Some(unlock_height) = self.unlock_height { +
unlock height
+
{{ unlock_height }}
+
reserved
+
false
+%% } else { +
unlock height
+
none
+
reserved
+
true
+%% } +