From 62fb3d82fbe23983e2b772691f9d1f9cd4b2c1da Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 13 Nov 2023 12:01:13 -0800 Subject: [PATCH 1/4] Don't display protocol inscriptions on homepage --- src/index.rs | 15 ++++ src/index/updater.rs | 47 ++++++---- src/index/updater/inscription_updater.rs | 110 ++++++++--------------- src/inscription.rs | 68 ++++++++++++++ src/media.rs | 69 +++++++------- src/subcommand/server.rs | 96 ++++++++++++++++---- src/templates.rs | 2 + src/templates/blocks.rs | 89 ++++++++++++++++++ src/templates/home.rs | 69 +++----------- static/index.css | 19 ++++ templates/blocks.html | 19 ++++ templates/home.html | 24 ++--- 12 files changed, 411 insertions(+), 216 deletions(-) create mode 100644 src/templates/blocks.rs create mode 100644 templates/blocks.html diff --git a/src/index.rs b/src/index.rs index 9f56aea601..950c24ce0e 100644 --- a/src/index.rs +++ b/src/index.rs @@ -55,6 +55,7 @@ define_multimap_table! { SATPOINT_TO_INSCRIPTION_ID, &SatPointValue, &Inscriptio define_multimap_table! { SAT_TO_INSCRIPTION_ID, u64, &InscriptionIdValue } define_table! { HEIGHT_TO_BLOCK_HASH, u64, &BlockHashValue } define_table! { HEIGHT_TO_LAST_SEQUENCE_NUMBER, u64, u64 } +define_table! { HOME_INSCRIPTIONS, u64, &InscriptionIdValue } define_table! { INSCRIPTION_ID_TO_INSCRIPTION_ENTRY, &InscriptionIdValue, InscriptionEntryValue } define_table! { INSCRIPTION_ID_TO_RUNE, &InscriptionIdValue, u128 } define_table! { INSCRIPTION_ID_TO_SATPOINT, &InscriptionIdValue, &SatPointValue } @@ -274,6 +275,7 @@ impl Index { tx.open_multimap_table(SAT_TO_INSCRIPTION_ID)?; tx.open_table(HEIGHT_TO_BLOCK_HASH)?; tx.open_table(HEIGHT_TO_LAST_SEQUENCE_NUMBER)?; + tx.open_table(HOME_INSCRIPTIONS)?; tx.open_table(INSCRIPTION_ID_TO_INSCRIPTION_ENTRY)?; tx.open_table(INSCRIPTION_ID_TO_RUNE)?; tx.open_table(INSCRIPTION_ID_TO_SATPOINT)?; @@ -1308,6 +1310,19 @@ impl Index { )) } + pub(crate) fn get_home_inscriptions(&self) -> Result> { + Ok( + self + .database + .begin_read()? + .open_table(HOME_INSCRIPTIONS)? + .iter()? + .rev() + .flat_map(|result| result.map(|(_number, id)| InscriptionId::load(*id.value()))) + .collect(), + ) + } + pub(crate) fn get_feed_inscriptions(&self, n: usize) -> Result> { Ok( self diff --git a/src/index/updater.rs b/src/index/updater.rs index 240ad58059..d7a6695e37 100644 --- a/src/index/updater.rs +++ b/src/index/updater.rs @@ -378,13 +378,14 @@ impl<'index> Updater<'_> { let mut height_to_block_hash = wtx.open_table(HEIGHT_TO_BLOCK_HASH)?; let mut height_to_last_sequence_number = wtx.open_table(HEIGHT_TO_LAST_SEQUENCE_NUMBER)?; + let mut home_inscriptions = wtx.open_table(HOME_INSCRIPTIONS)?; + let mut inscription_id_to_children = wtx.open_multimap_table(INSCRIPTION_ID_TO_CHILDREN)?; let mut inscription_id_to_inscription_entry = wtx.open_table(INSCRIPTION_ID_TO_INSCRIPTION_ENTRY)?; let mut inscription_id_to_satpoint = wtx.open_table(INSCRIPTION_ID_TO_SATPOINT)?; let mut inscription_number_to_inscription_id = wtx.open_table(INSCRIPTION_NUMBER_TO_INSCRIPTION_ID)?; let mut sat_to_inscription_id = wtx.open_multimap_table(SAT_TO_INSCRIPTION_ID)?; - let mut inscription_id_to_children = wtx.open_multimap_table(INSCRIPTION_ID_TO_CHILDREN)?; let mut satpoint_to_inscription_id = wtx.open_multimap_table(SATPOINT_TO_INSCRIPTION_ID)?; let mut sequence_number_to_inscription_id = wtx.open_table(SEQUENCE_NUMBER_TO_INSCRIPTION_ID)?; @@ -410,24 +411,38 @@ impl<'index> Updater<'_> { .map(|unbound_inscriptions| unbound_inscriptions.value()) .unwrap_or(0); - let mut inscription_updater = InscriptionUpdater::new( - self.height, - &mut inscription_id_to_children, - &mut inscription_id_to_satpoint, - value_receiver, - &mut inscription_id_to_inscription_entry, - lost_sats, - &mut inscription_number_to_inscription_id, - cursed_inscription_count, + let next_sequence_number = sequence_number_to_inscription_id + .iter()? + .next_back() + .and_then(|result| result.ok()) + .map(|(number, _id)| number.value() + 1) + .unwrap_or(0); + + let home_inscription_count = home_inscriptions.len()?; + + let mut inscription_updater = InscriptionUpdater { blessed_inscription_count, - &mut sequence_number_to_inscription_id, - &mut outpoint_to_value, - &mut sat_to_inscription_id, - &mut satpoint_to_inscription_id, - block.header.time, + cursed_inscription_count, + flotsam: Vec::new(), + height: self.height, + home_inscription_count, + home_inscriptions: &mut home_inscriptions, + id_to_children: &mut inscription_id_to_children, + id_to_entry: &mut inscription_id_to_inscription_entry, + id_to_satpoint: &mut inscription_id_to_satpoint, + inscription_number_to_id: &mut inscription_number_to_inscription_id, + lost_sats, + next_sequence_number, + outpoint_to_value: &mut outpoint_to_value, + reward: Height(self.height).subsidy(), + sat_to_inscription_id: &mut sat_to_inscription_id, + satpoint_to_id: &mut satpoint_to_inscription_id, + sequence_number_to_id: &mut sequence_number_to_inscription_id, + timestamp: block.header.time, unbound_inscriptions, value_cache, - )?; + value_receiver, + }; if self.index.index_sats { let mut sat_to_satpoint = wtx.open_table(SAT_TO_SATPOINT)?; diff --git a/src/index/updater/inscription_updater.rs b/src/index/updater/inscription_updater.rs index edd0ef74ba..0b20a9ebec 100644 --- a/src/index/updater/inscription_updater.rs +++ b/src/index/updater/inscription_updater.rs @@ -12,6 +12,7 @@ enum Origin { New { cursed: bool, fee: u64, + hidden: bool, parent: Option, pointer: Option, unbound: bool, @@ -22,88 +23,35 @@ enum Origin { } pub(super) struct InscriptionUpdater<'a, 'db, 'tx> { - flotsam: Vec, - height: u64, - id_to_children: + pub(super) flotsam: Vec, + pub(super) height: u64, + pub(super) home_inscription_count: u64, + pub(super) home_inscriptions: &'a mut Table<'db, 'tx, u64, &'static InscriptionIdValue>, + pub(super) id_to_children: &'a mut MultimapTable<'db, 'tx, &'static InscriptionIdValue, &'static InscriptionIdValue>, - id_to_satpoint: &'a mut Table<'db, 'tx, &'static InscriptionIdValue, &'static SatPointValue>, - value_receiver: &'a mut Receiver, - id_to_entry: &'a mut Table<'db, 'tx, &'static InscriptionIdValue, InscriptionEntryValue>, + pub(super) id_to_satpoint: + &'a mut Table<'db, 'tx, &'static InscriptionIdValue, &'static SatPointValue>, + pub(super) value_receiver: &'a mut Receiver, + pub(super) id_to_entry: + &'a mut Table<'db, 'tx, &'static InscriptionIdValue, InscriptionEntryValue>, pub(super) lost_sats: u64, pub(super) cursed_inscription_count: u64, pub(super) blessed_inscription_count: u64, pub(super) next_sequence_number: u64, - inscription_number_to_id: &'a mut Table<'db, 'tx, i64, &'static InscriptionIdValue>, - sequence_number_to_id: &'a mut Table<'db, 'tx, u64, &'static InscriptionIdValue>, - outpoint_to_value: &'a mut Table<'db, 'tx, &'static OutPointValue, u64>, - reward: u64, - sat_to_inscription_id: &'a mut MultimapTable<'db, 'tx, u64, &'static InscriptionIdValue>, - satpoint_to_id: + pub(super) inscription_number_to_id: &'a mut Table<'db, 'tx, i64, &'static InscriptionIdValue>, + pub(super) sequence_number_to_id: &'a mut Table<'db, 'tx, u64, &'static InscriptionIdValue>, + pub(super) outpoint_to_value: &'a mut Table<'db, 'tx, &'static OutPointValue, u64>, + pub(super) reward: u64, + pub(super) sat_to_inscription_id: + &'a mut MultimapTable<'db, 'tx, u64, &'static InscriptionIdValue>, + pub(super) satpoint_to_id: &'a mut MultimapTable<'db, 'tx, &'static SatPointValue, &'static InscriptionIdValue>, - timestamp: u32, + pub(super) timestamp: u32, pub(super) unbound_inscriptions: u64, - value_cache: &'a mut HashMap, + pub(super) value_cache: &'a mut HashMap, } impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { - pub(super) fn new( - height: u64, - id_to_children: &'a mut MultimapTable< - 'db, - 'tx, - &'static InscriptionIdValue, - &'static InscriptionIdValue, - >, - id_to_satpoint: &'a mut Table<'db, 'tx, &'static InscriptionIdValue, &'static SatPointValue>, - value_receiver: &'a mut Receiver, - id_to_entry: &'a mut Table<'db, 'tx, &'static InscriptionIdValue, InscriptionEntryValue>, - lost_sats: u64, - inscription_number_to_id: &'a mut Table<'db, 'tx, i64, &'static InscriptionIdValue>, - cursed_inscription_count: u64, - blessed_inscription_count: u64, - sequence_number_to_id: &'a mut Table<'db, 'tx, u64, &'static InscriptionIdValue>, - outpoint_to_value: &'a mut Table<'db, 'tx, &'static OutPointValue, u64>, - sat_to_inscription_id: &'a mut MultimapTable<'db, 'tx, u64, &'static InscriptionIdValue>, - satpoint_to_id: &'a mut MultimapTable< - 'db, - 'tx, - &'static SatPointValue, - &'static InscriptionIdValue, - >, - timestamp: u32, - unbound_inscriptions: u64, - value_cache: &'a mut HashMap, - ) -> Result { - let next_sequence_number = sequence_number_to_id - .iter()? - .next_back() - .and_then(|result| result.ok()) - .map(|(number, _id)| number.value() + 1) - .unwrap_or(0); - - Ok(Self { - flotsam: Vec::new(), - height, - id_to_children, - id_to_satpoint, - value_receiver, - id_to_entry, - lost_sats, - cursed_inscription_count, - blessed_inscription_count, - next_sequence_number, - sequence_number_to_id, - inscription_number_to_id, - outpoint_to_value, - reward: Height(height).subsidy(), - sat_to_inscription_id, - satpoint_to_id, - timestamp, - unbound_inscriptions, - value_cache, - }) - } - pub(super) fn index_envelopes( &mut self, tx: &Transaction, @@ -248,6 +196,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { origin: Origin::New { cursed, fee: 0, + hidden: inscription.payload.hidden(), parent: inscription.payload.parent(), pointer: inscription.payload.pointer(), unbound, @@ -290,6 +239,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { Origin::New { cursed, fee: _, + hidden, parent, pointer, unbound, @@ -300,8 +250,9 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { inscription_id, offset, origin: Origin::New { - fee: (total_input_value - total_output_value) / u64::from(id_counter), cursed, + fee: (total_input_value - total_output_value) / u64::from(id_counter), + hidden, parent, pointer, unbound, @@ -444,6 +395,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { fee, parent, unbound, + hidden, .. } => { let inscription_number = if cursed { @@ -500,6 +452,18 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> { .insert(&parent.store(), &inscription_id)?; } + if !hidden { + self + .home_inscriptions + .insert(&sequence_number, &inscription_id)?; + + if self.home_inscription_count == 100 { + self.home_inscriptions.pop_first()?; + } else { + self.home_inscription_count += 1; + } + } + unbound } }; diff --git a/src/inscription.rs b/src/inscription.rs index 4cd4b9598d..c3fc3f0f4a 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -266,6 +266,40 @@ impl Inscription { witness } + + pub(crate) fn hidden(&self) -> bool { + let Some(content_type) = self.content_type() else { + return false; + }; + + if content_type != "text/plain" && content_type != "text/plain;charset=utf-8" { + return false; + } + + let Some(body) = &self.body else { + return false; + }; + + let Ok(text) = str::from_utf8(body) else { + return false; + }; + + let trimmed = text.trim(); + + if trimmed.starts_with('{') && trimmed.ends_with('}') { + return true; + } + + if trimmed.starts_with("gib bc1") { + return true; + } + + if trimmed.ends_with(".bitmap") { + return true; + } + + false + } } #[cfg(test)] @@ -689,4 +723,38 @@ mod tests { assert_eq!(inscription.pointer, Some(vec![0, 1])); } + + #[test] + fn hidden() { + #[track_caller] + fn case(content_type: Option<&str>, body: Option<&str>, expected: bool) { + assert_eq!( + Inscription { + content_type: content_type.map(|content_type| content_type.as_bytes().into()), + body: body.map(|content_type| content_type.as_bytes().into()), + ..Default::default() + } + .hidden(), + expected + ); + } + + case(None, None, false); + case(Some("foo"), None, false); + case(Some("foo"), Some("{}"), false); + case(Some("text/plain"), None, false); + case(Some("text/plain"), Some("foo{}bar"), false); + + case(Some("text/plain"), Some("foo.bitmap"), true); + case(Some("text/plain"), Some("gib bc1"), true); + case(Some("text/plain"), Some("{}"), true); + case(Some("text/plain;charset=utf-8"), Some("foo.bitmap"), true); + + assert!(!Inscription { + content_type: Some("text/plain".as_bytes().into()), + body: Some(b"{\xc3\x28}".as_slice().into()), + ..Default::default() + } + .hidden()); + } } diff --git a/src/media.rs b/src/media.rs index 34867403dc..c30c39aa42 100644 --- a/src/media.rs +++ b/src/media.rs @@ -19,41 +19,42 @@ pub(crate) enum Media { } impl Media { + #[rustfmt::skip] const TABLE: &'static [(&'static str, Media, &'static [&'static str])] = &[ - ("application/cbor", Media::Unknown, &["cbor"]), - ("application/json", Media::Code, &["json"]), - ("application/pdf", Media::Pdf, &["pdf"]), - ("application/pgp-signature", Media::Text, &["asc"]), - ("application/protobuf", Media::Unknown, &["binpb"]), - ("application/yaml", Media::Code, &["yaml", "yml"]), - ("audio/flac", Media::Audio, &["flac"]), - ("audio/mpeg", Media::Audio, &["mp3"]), - ("audio/wav", Media::Audio, &["wav"]), - ("font/otf", Media::Unknown, &["otf"]), - ("font/ttf", Media::Unknown, &["ttf"]), - ("font/woff", Media::Unknown, &["woff"]), - ("font/woff2", Media::Unknown, &["woff2"]), - ("image/apng", Media::Image, &["apng"]), - ("image/avif", Media::Image, &[]), - ("image/gif", Media::Image, &["gif"]), - ("image/jpeg", Media::Image, &["jpg", "jpeg"]), - ("image/png", Media::Image, &["png"]), - ("image/svg+xml", Media::Iframe, &["svg"]), - ("image/webp", Media::Image, &["webp"]), - ("model/gltf+json", Media::Model, &["gltf"]), - ("model/gltf-binary", Media::Model, &["glb"]), - ("model/stl", Media::Unknown, &["stl"]), - ("text/css", Media::Code, &["css"]), - ("text/html", Media::Iframe, &[]), - ("text/html;charset=utf-8", Media::Iframe, &["html"]), - ("text/javascript", Media::Code, &["js"]), - ("text/markdown", Media::Markdown, &[]), - ("text/markdown;charset=utf-8", Media::Markdown, &["md"]), - ("text/plain", Media::Text, &[]), - ("text/plain;charset=utf-8", Media::Text, &["txt"]), - ("text/x-python", Media::Code, &["py"]), - ("video/mp4", Media::Video, &["mp4"]), - ("video/webm", Media::Video, &["webm"]), + ("application/cbor", Media::Unknown, &["cbor"]), + ("application/json", Media::Code, &["json"]), + ("application/pdf", Media::Pdf, &["pdf"]), + ("application/pgp-signature", Media::Text, &["asc"]), + ("application/protobuf", Media::Unknown, &["binpb"]), + ("application/yaml", Media::Code, &["yaml", "yml"]), + ("audio/flac", Media::Audio, &["flac"]), + ("audio/mpeg", Media::Audio, &["mp3"]), + ("audio/wav", Media::Audio, &["wav"]), + ("font/otf", Media::Unknown, &["otf"]), + ("font/ttf", Media::Unknown, &["ttf"]), + ("font/woff", Media::Unknown, &["woff"]), + ("font/woff2", Media::Unknown, &["woff2"]), + ("image/apng", Media::Image, &["apng"]), + ("image/avif", Media::Image, &[]), + ("image/gif", Media::Image, &["gif"]), + ("image/jpeg", Media::Image, &["jpg", "jpeg"]), + ("image/png", Media::Image, &["png"]), + ("image/svg+xml", Media::Iframe, &["svg"]), + ("image/webp", Media::Image, &["webp"]), + ("model/gltf+json", Media::Model, &["gltf"]), + ("model/gltf-binary", Media::Model, &["glb"]), + ("model/stl", Media::Unknown, &["stl"]), + ("text/css", Media::Code, &["css"]), + ("text/html", Media::Iframe, &[]), + ("text/html;charset=utf-8", Media::Iframe, &["html"]), + ("text/javascript", Media::Code, &["js"]), + ("text/markdown", Media::Markdown, &[]), + ("text/markdown;charset=utf-8", Media::Markdown, &["md"]), + ("text/plain", Media::Text, &[]), + ("text/plain;charset=utf-8", Media::Text, &["txt"]), + ("text/x-python", Media::Code, &["py"]), + ("video/mp4", Media::Video, &["mp4"]), + ("video/webm", Media::Video, &["webm"]), ]; pub(crate) fn content_type_for_path(path: &Path) -> Result<&'static str, Error> { diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 83f80b6d7a..e2c9890ed9 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -9,11 +9,12 @@ use { page_config::PageConfig, runes::Rune, templates::{ - BlockHtml, BlockJson, ChildrenHtml, ClockSvg, HomeHtml, InputHtml, InscriptionHtml, - InscriptionJson, InscriptionsBlockHtml, InscriptionsHtml, InscriptionsJson, OutputHtml, - OutputJson, PageContent, PageHtml, PreviewAudioHtml, PreviewCodeHtml, PreviewImageHtml, - PreviewMarkdownHtml, PreviewModelHtml, PreviewPdfHtml, PreviewTextHtml, PreviewUnknownHtml, - PreviewVideoHtml, RangeHtml, RareTxt, RuneHtml, RunesHtml, SatHtml, SatJson, TransactionHtml, + BlockHtml, BlockJson, BlocksHtml, ChildrenHtml, ClockSvg, HomeHtml, InputHtml, + InscriptionHtml, InscriptionJson, InscriptionsBlockHtml, InscriptionsHtml, InscriptionsJson, + OutputHtml, OutputJson, PageContent, PageHtml, PreviewAudioHtml, PreviewCodeHtml, + PreviewImageHtml, PreviewMarkdownHtml, PreviewModelHtml, PreviewPdfHtml, PreviewTextHtml, + PreviewUnknownHtml, PreviewVideoHtml, RangeHtml, RareTxt, RuneHtml, RunesHtml, SatHtml, + SatJson, TransactionHtml, }, }, axum::{ @@ -190,8 +191,14 @@ impl Server { .route("/blockhash", get(Self::block_hash)) .route("/blockhash/:height", get(Self::block_hash_from_height)) .route("/blockheight", get(Self::block_height)) + .route("/blocks", get(Self::blocks)) .route("/blocktime", get(Self::block_time)) .route("/bounties", get(Self::bounties)) + .route("/children/:inscription_id", get(Self::children)) + .route( + "/children/:inscription_id/:page", + get(Self::children_paginated), + ) .route("/clock", get(Self::clock)) .route("/content/:inscription_id", get(Self::content)) .route("/faq", get(Self::faq)) @@ -199,11 +206,6 @@ impl Server { .route("/feed.xml", get(Self::feed)) .route("/input/:block/:transaction/:input", get(Self::input)) .route("/inscription/:inscription_query", get(Self::inscription)) - .route("/children/:inscription_id", get(Self::children)) - .route( - "/children/:inscription_id/:page", - get(Self::children_paginated), - ) .route("/inscriptions", get(Self::inscriptions)) .route("/inscriptions/:from", get(Self::inscriptions_from)) .route("/inscriptions/:from/:n", get(Self::inscriptions_from_n)) @@ -609,6 +611,18 @@ impl Server { Extension(page_config): Extension>, Extension(index): Extension>, ) -> ServerResult> { + Ok( + HomeHtml { + inscriptions: index.get_home_inscriptions()?, + } + .page(page_config), + ) + } + + async fn blocks( + Extension(page_config): Extension>, + Extension(index): Extension>, + ) -> ServerResult> { let blocks = index.blocks(100)?; let mut featured_blocks = BTreeMap::new(); for (height, hash) in blocks.iter().take(5) { @@ -618,7 +632,7 @@ impl Server { featured_blocks.insert(*hash, inscriptions); } - Ok(HomeHtml::new(blocks, featured_blocks).page(page_config)) + Ok(BlocksHtml::new(blocks, featured_blocks).page(page_config)) } async fn install_script() -> Redirect { @@ -2249,14 +2263,60 @@ mod tests { #[test] fn home() { + let server = TestServer::new_with_regtest(); + + server.mine_blocks(1); + + server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(1, 0, 0, inscription("text/plain", "{}").to_witness())], + ..Default::default() + }); + + server.mine_blocks(1); + + let mut ids = Vec::new(); + + for i in 0..101 { + let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(i + 1, 0, 0, inscription("text/plain", "hello").to_witness())], + ..Default::default() + }); + ids.push(InscriptionId { txid, index: 0 }); + server.mine_blocks(1); + } + + server.assert_response_regex( + "/", + StatusCode::OK, + format!( + r".*Ordinals.* +
+ Inscriptions + \| + Blocks +
+
+ .* + (.*\s*){{99}} +
+.* +", + ids[100] + ), + ); + } + + #[test] + fn blocks() { let test_server = TestServer::new(); test_server.mine_blocks(1); test_server.assert_response_regex( - "/", + "/blocks", StatusCode::OK, - ".*Ordinals.* + ".*Blocks.* +

Blocks

Block 1

@@ -2281,16 +2341,16 @@ mod tests { } #[test] - fn home_block_limit() { + fn blocks_block_limit() { let test_server = TestServer::new(); test_server.mine_blocks(101); test_server.assert_response_regex( - "/", - StatusCode::OK, - ".*
    \n(
  1. [[:xdigit:]]{64}
  2. \n){95}
.*" - ); + "/blocks", + StatusCode::OK, + ".*
    \n(
  1. [[:xdigit:]]{64}
  2. \n){95}
.*" + ); } #[test] diff --git a/src/templates.rs b/src/templates.rs index 582e2a07c9..af6bf2df28 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -2,6 +2,7 @@ use {super::*, boilerplate::Boilerplate}; pub(crate) use { block::{BlockHtml, BlockJson}, + blocks::BlocksHtml, children::ChildrenHtml, clock::ClockSvg, home::HomeHtml, @@ -26,6 +27,7 @@ pub(crate) use { }; pub mod block; +mod blocks; mod children; mod clock; mod home; diff --git a/src/templates/blocks.rs b/src/templates/blocks.rs new file mode 100644 index 0000000000..5e790f4f01 --- /dev/null +++ b/src/templates/blocks.rs @@ -0,0 +1,89 @@ +use super::*; + +#[derive(Boilerplate)] +pub(crate) struct BlocksHtml { + last: u64, + blocks: Vec, + featured_blocks: BTreeMap>, +} + +impl BlocksHtml { + pub(crate) fn new( + blocks: Vec<(u64, BlockHash)>, + featured_blocks: BTreeMap>, + ) -> Self { + Self { + last: blocks + .get(0) + .map(|(height, _)| height) + .cloned() + .unwrap_or(0), + blocks: blocks.into_iter().map(|(_, hash)| hash).collect(), + featured_blocks, + } + } +} + +impl PageContent for BlocksHtml { + fn title(&self) -> String { + "Blocks".to_string() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn html() { + let mut feature_blocks = BTreeMap::new(); + feature_blocks.insert( + "2222222222222222222222222222222222222222222222222222222222222222" + .parse() + .unwrap(), + vec![inscription_id(1), inscription_id(2)], + ); + + assert_regex_match!( + &BlocksHtml::new( + vec![ + ( + 1260002, + "2222222222222222222222222222222222222222222222222222222222222222" + .parse() + .unwrap() + ), + ( + 1260001, + "1111111111111111111111111111111111111111111111111111111111111111" + .parse() + .unwrap() + ), + ( + 1260000, + "0000000000000000000000000000000000000000000000000000000000000000" + .parse() + .unwrap() + ) + ], + feature_blocks, + ) + .to_string() + .unindent(), + "

Blocks

+
+

Block 1260002

+
+ + +
+
+
    +
  1. 1{64}
  2. +
  3. 0{64}
  4. +
+ " + .unindent(), + ); + } +} diff --git a/src/templates/home.rs b/src/templates/home.rs index 137415cde5..8b276dfe14 100644 --- a/src/templates/home.rs +++ b/src/templates/home.rs @@ -2,26 +2,7 @@ use super::*; #[derive(Boilerplate)] pub(crate) struct HomeHtml { - last: u64, - blocks: Vec, - featured_blocks: BTreeMap>, -} - -impl HomeHtml { - pub(crate) fn new( - blocks: Vec<(u64, BlockHash)>, - featured_blocks: BTreeMap>, - ) -> Self { - Self { - last: blocks - .get(0) - .map(|(height, _)| height) - .cloned() - .unwrap_or(0), - blocks: blocks.into_iter().map(|(_, hash)| hash).collect(), - featured_blocks, - } - } + pub(crate) inscriptions: Vec, } impl PageContent for HomeHtml { @@ -36,51 +17,21 @@ mod tests { #[test] fn html() { - let mut feature_blocks = BTreeMap::new(); - feature_blocks.insert( - "2222222222222222222222222222222222222222222222222222222222222222" - .parse() - .unwrap(), - vec![inscription_id(1), inscription_id(2)], - ); - assert_regex_match!( - &HomeHtml::new( - vec![ - ( - 1260002, - "2222222222222222222222222222222222222222222222222222222222222222" - .parse() - .unwrap() - ), - ( - 1260001, - "1111111111111111111111111111111111111111111111111111111111111111" - .parse() - .unwrap() - ), - ( - 1260000, - "0000000000000000000000000000000000000000000000000000000000000000" - .parse() - .unwrap() - ) - ], - feature_blocks, - ) + HomeHtml { + inscriptions: vec![inscription_id(1), inscription_id(2)], + } .to_string() .unindent(), - "
-

Block 1260002

-
+ "
+ Inscriptions + | + Blocks +
+
-
-
    -
  1. 1{64}
  2. -
  3. 0{64}
  4. -
" .unindent(), ); diff --git a/static/index.css b/static/index.css index ff4056ba43..bf679be6d7 100644 --- a/static/index.css +++ b/static/index.css @@ -234,3 +234,22 @@ a.mythic { .inscription > a > iframe { width: 100%; } + +.tabs { + align-items: center; + display: flex; +} + +.tabs > a { + flex: 1; + padding: 1rem; + color: var(--light-fg); +} + +.tabs > *:nth-child(1) { + text-align: right; +} + +.tabs > *:nth-child(3) { + text-align: left; +} diff --git a/templates/blocks.html b/templates/blocks.html new file mode 100644 index 0000000000..60631be4e9 --- /dev/null +++ b/templates/blocks.html @@ -0,0 +1,19 @@ +

Blocks

+%% for (i, hash) in self.blocks.iter().enumerate() { +%% if let Some(inscription_ids) = &self.featured_blocks.get(hash) { +
+

Block {{ self.last - i as u64 }}

+
+%% for id in *inscription_ids { + {{ Iframe::thumbnail(*id) }} +%% } +
+
+%% } else { +%% if i == self.featured_blocks.len() { +
    +%% } +
  1. {{ hash }}
  2. +%% } +%% } +
diff --git a/templates/home.html b/templates/home.html index 328e2ffa14..8c8ccfcacf 100644 --- a/templates/home.html +++ b/templates/home.html @@ -1,18 +1,10 @@ -%% for (i, hash) in self.blocks.iter().enumerate() { -%% if let Some(inscription_ids) = &self.featured_blocks.get(hash) { -
-

Block {{ self.last - i as u64 }}

-
-%% for id in *inscription_ids { - {{ Iframe::thumbnail(*id) }} -%% } -
+ -%% } else { -%% if i == self.featured_blocks.len() { -
    -%% } -
  1. {{ hash }}
  2. +
    +%% for inscription in &self.inscriptions { + {{ Iframe::thumbnail(*inscription) }} %% } -%% } -
+
From 65d390b803a02bb53795c895015e72b8793492a9 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 15 Nov 2023 11:22:16 -0800 Subject: [PATCH 2/4] Test trimming --- src/inscription.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/inscription.rs b/src/inscription.rs index c3fc3f0f4a..ce6c072628 100644 --- a/src/inscription.rs +++ b/src/inscription.rs @@ -748,6 +748,7 @@ mod tests { case(Some("text/plain"), Some("foo.bitmap"), true); case(Some("text/plain"), Some("gib bc1"), true); case(Some("text/plain"), Some("{}"), true); + case(Some("text/plain"), Some(" {} "), true); case(Some("text/plain;charset=utf-8"), Some("foo.bitmap"), true); assert!(!Inscription { From 9cb5a79e786ce4c0cc283dc7520c2b101439dd79 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 15 Nov 2023 11:25:28 -0800 Subject: [PATCH 3/4] Fix test --- src/subcommand/server.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index e2c9890ed9..a898c943e5 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -2267,13 +2267,6 @@ mod tests { server.mine_blocks(1); - server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { - inputs: &[(1, 0, 0, inscription("text/plain", "{}").to_witness())], - ..Default::default() - }); - - server.mine_blocks(1); - let mut ids = Vec::new(); for i in 0..101 { @@ -2285,6 +2278,13 @@ mod tests { server.mine_blocks(1); } + server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate { + inputs: &[(1, 0, 0, inscription("text/plain", "{}").to_witness())], + ..Default::default() + }); + + server.mine_blocks(1); + server.assert_response_regex( "/", StatusCode::OK, From 3348bfe1274a5fe0a949a79a5776e700abd0df8f Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Wed, 15 Nov 2023 11:34:13 -0800 Subject: [PATCH 4/4] Use nav links --- src/subcommand/server.rs | 4 ++-- src/templates.rs | 8 ++++---- src/templates/home.rs | 4 ++-- static/index.css | 6 +++--- templates/home.html | 4 ++-- templates/page.html | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index a898c943e5..8242f27245 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -2290,11 +2290,11 @@ mod tests { StatusCode::OK, format!( r".*Ordinals.* - +
.* (.*\s*){{99}} diff --git a/src/templates.rs b/src/templates.rs index af6bf2df28..23a26520dd 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -135,7 +135,7 @@ mod tests {
-