Skip to content

Commit

Permalink
Add /block/:query JSON API endpoint (ordinals#2423)
Browse files Browse the repository at this point in the history
  • Loading branch information
terror authored Oct 26, 2023
1 parent 231bdda commit 219a9cb
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 19 deletions.
26 changes: 18 additions & 8 deletions src/subcommand/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use {
page_config::PageConfig,
runes::Rune,
templates::{
BlockHtml, ClockSvg, HomeHtml, InputHtml, InscriptionHtml, InscriptionJson,
BlockHtml, BlockJson, ClockSvg, HomeHtml, InputHtml, InscriptionHtml, InscriptionJson,
InscriptionsBlockHtml, InscriptionsHtml, InscriptionsJson, OutputHtml, OutputJson,
PageContent, PageHtml, PreviewAudioHtml, PreviewCodeHtml, PreviewImageHtml,
PreviewMarkdownHtml, PreviewModelHtml, PreviewPdfHtml, PreviewTextHtml, PreviewUnknownHtml,
Expand Down Expand Up @@ -614,7 +614,8 @@ impl Server {
Extension(page_config): Extension<Arc<PageConfig>>,
Extension(index): Extension<Arc<Index>>,
Path(DeserializeFromStr(query)): Path<DeserializeFromStr<BlockQuery>>,
) -> ServerResult<PageHtml<BlockHtml>> {
accept_json: AcceptJson,
) -> ServerResult<Response> {
let (block, height) = match query {
BlockQuery::Height(height) => {
let block = index
Expand All @@ -636,19 +637,28 @@ impl Server {
}
};

let (featured_inscriptions, total_num) =
index.get_highest_paying_inscriptions_in_block(height, 8)?;

Ok(
Ok(if accept_json.0 {
let inscriptions = index.get_inscriptions_in_block(height)?;
Json(BlockJson::new(
block,
Height(height),
Self::index_height(&index)?,
inscriptions,
))
.into_response()
} else {
let (featured_inscriptions, total_num) =
index.get_highest_paying_inscriptions_in_block(height, 8)?;
BlockHtml::new(
block,
Height(height),
Self::index_height(&index)?,
total_num,
featured_inscriptions,
)
.page(page_config),
)
.page(page_config)
.into_response()
})
}

async fn transaction(
Expand Down
4 changes: 2 additions & 2 deletions src/templates.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use {super::*, boilerplate::Boilerplate};

pub(crate) use {
block::BlockHtml,
block::{BlockHtml, BlockJson},
clock::ClockSvg,
home::HomeHtml,
iframe::Iframe,
Expand All @@ -24,7 +24,7 @@ pub(crate) use {
transaction::TransactionHtml,
};

mod block;
pub mod block;
mod clock;
mod home;
mod iframe;
Expand Down
46 changes: 42 additions & 4 deletions src/templates/block.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
use super::*;

fn target_as_block_hash(target: bitcoin::Target) -> BlockHash {
BlockHash::from_raw_hash(Hash::from_byte_array(target.to_le_bytes()))
}

#[derive(Boilerplate)]
pub(crate) struct BlockHtml {
hash: BlockHash,
target: BlockHash,
best_height: Height,
block: Block,
height: Height,
total_num_inscriptions: usize,
inscription_count: usize,
featured_inscriptions: Vec<InscriptionId>,
}

Expand All @@ -16,21 +20,47 @@ impl BlockHtml {
block: Block,
height: Height,
best_height: Height,
total_num_inscriptions: usize,
inscription_count: usize,
featured_inscriptions: Vec<InscriptionId>,
) -> Self {
Self {
hash: block.header.block_hash(),
target: BlockHash::from_raw_hash(Hash::from_byte_array(block.header.target().to_be_bytes())),
target: target_as_block_hash(block.header.target()),
block,
height,
best_height,
total_num_inscriptions,
inscription_count,
featured_inscriptions,
}
}
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct BlockJson {
pub hash: BlockHash,
pub target: BlockHash,
pub best_height: u64,
pub height: u64,
pub inscriptions: Vec<InscriptionId>,
}

impl BlockJson {
pub(crate) fn new(
block: Block,
height: Height,
best_height: Height,
inscriptions: Vec<InscriptionId>,
) -> Self {
Self {
hash: block.header.block_hash(),
target: target_as_block_hash(block.header.target()),
height: height.0,
best_height: best_height.0,
inscriptions,
}
}
}

impl PageContent for BlockHtml {
fn title(&self) -> String {
format!("Block {}", self.height)
Expand Down Expand Up @@ -103,4 +133,12 @@ mod tests {
r"<h1>Block 1</h1>.*<a class=prev href=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/block/0>prev</a>\s*next.*",
);
}

#[test]
fn block_hash_serializes_as_hex_string() {
assert_eq!(
serde_json::to_string(&BlockHash::all_zeros()).unwrap(),
"\"0000000000000000000000000000000000000000000000000000000000000000\""
);
}
}
4 changes: 2 additions & 2 deletions templates/block.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ <h1>Block {{ self.height }}</h1>
next
%% }
</div>
<h2>{{"Inscription".tally(self.total_num_inscriptions)}}</h2>
<h2>{{"Inscription".tally(self.inscription_count)}}</h2>
<div class=thumbnails>
%% for id in &self.featured_inscriptions {
{{ Iframe::thumbnail(*id) }}
%% }
</div>
%% if &self.total_num_inscriptions > &self.featured_inscriptions.len() {
%% if &self.inscription_count > &self.featured_inscriptions.len() {
<div class=center>
<a href="/inscriptions/block/{{ &self.height }}">more</a>
</div>
Expand Down
31 changes: 30 additions & 1 deletion tests/json_api.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::*;
use {super::*, bitcoin::BlockHash};

#[test]
fn get_sat_without_sat_index() {
Expand Down Expand Up @@ -392,3 +392,32 @@ fn json_request_fails_when_not_enabled() {

assert_eq!(response.status(), StatusCode::NOT_ACCEPTABLE);
}

#[test]
fn get_block() {
let rpc_server = test_bitcoincore_rpc::spawn();

rpc_server.mine_blocks(1);

let response =
TestServer::spawn_with_args(&rpc_server, &["--enable-json-api"]).json_request("/block/0");

assert_eq!(response.status(), StatusCode::OK);

let block_json: BlockJson = serde_json::from_str(&response.text().unwrap()).unwrap();

assert_eq!(
block_json,
BlockJson {
hash: "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
.parse::<BlockHash>()
.unwrap(),
target: "00000000ffff0000000000000000000000000000000000000000000000000000"
.parse::<BlockHash>()
.unwrap(),
best_height: 1,
height: 0,
inscriptions: vec![],
}
);
}
4 changes: 2 additions & 2 deletions tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use {
inscription_id::InscriptionId,
rarity::Rarity,
templates::{
inscription::InscriptionJson, inscriptions::InscriptionsJson, output::OutputJson,
sat::SatJson,
block::BlockJson, inscription::InscriptionJson, inscriptions::InscriptionsJson,
output::OutputJson, sat::SatJson,
},
SatPoint,
},
Expand Down

0 comments on commit 219a9cb

Please sign in to comment.