Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support tx, blocks json response #3004

Merged
merged 11 commits into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1486,22 +1486,6 @@ impl Index {
self.client.get_raw_transaction(&txid, None).into_option()
}

pub(crate) fn get_transaction_blockhash(&self, txid: Txid) -> Result<Option<BlockHash>> {
Ok(
self
.client
.get_raw_transaction_info(&txid, None)
.into_option()?
.and_then(|info| {
if info.in_active_chain.unwrap_or_default() {
info.blockhash
} else {
None
}
}),
)
}

pub(crate) fn find(&self, sat: Sat) -> Result<Option<SatPoint>> {
let sat = sat.0;
let rtx = self.begin_read()?;
Expand Down
47 changes: 31 additions & 16 deletions src/subcommand/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ use {
crate::{
server_config::ServerConfig,
templates::{
BlockHtml, BlockJson, BlocksHtml, ChildrenHtml, ChildrenJson, ClockSvg, CollectionsHtml,
HomeHtml, InputHtml, InscriptionHtml, InscriptionJson, InscriptionsBlockHtml,
InscriptionsHtml, InscriptionsJson, OutputHtml, OutputJson, PageContent, PageHtml,
PreviewAudioHtml, PreviewCodeHtml, PreviewFontHtml, PreviewImageHtml, PreviewMarkdownHtml,
PreviewModelHtml, PreviewPdfHtml, PreviewTextHtml, PreviewUnknownHtml, PreviewVideoHtml,
RangeHtml, RareTxt, RuneHtml, RuneJson, RunesHtml, RunesJson, SatHtml, SatInscriptionJson,
SatInscriptionsJson, SatJson, TransactionHtml,
BlockHtml, BlockJson, BlocksHtml, BlocksJson, ChildrenHtml, ChildrenJson, ClockSvg,
CollectionsHtml, HomeHtml, InputHtml, InscriptionHtml, InscriptionJson,
InscriptionsBlockHtml, InscriptionsHtml, InscriptionsJson, OutputHtml, OutputJson,
PageContent, PageHtml, PreviewAudioHtml, PreviewCodeHtml, PreviewFontHtml, PreviewImageHtml,
PreviewMarkdownHtml, PreviewModelHtml, PreviewPdfHtml, PreviewTextHtml, PreviewUnknownHtml,
PreviewVideoHtml, RangeHtml, RareTxt, RuneHtml, RuneJson, RunesHtml, RunesJson, SatHtml,
SatInscriptionJson, SatInscriptionsJson, SatJson, TransactionHtml, TransactionJson,
},
},
axum::{
Expand Down Expand Up @@ -698,7 +698,8 @@ impl Server {
async fn blocks(
Extension(server_config): Extension<Arc<ServerConfig>>,
Extension(index): Extension<Arc<Index>>,
) -> ServerResult<PageHtml<BlocksHtml>> {
AcceptJson(accept_json): AcceptJson,
) -> ServerResult<Response> {
task::block_in_place(|| {
let blocks = index.blocks(100)?;
let mut featured_blocks = BTreeMap::new();
Expand All @@ -709,7 +710,13 @@ impl Server {
featured_blocks.insert(*hash, inscriptions);
}

Ok(BlocksHtml::new(blocks, featured_blocks).page(server_config))
Ok(if accept_json {
Json(BlocksJson::new(blocks, featured_blocks)).into_response()
} else {
BlocksHtml::new(blocks, featured_blocks)
.page(server_config)
.into_response()
})
})
}

Expand Down Expand Up @@ -774,27 +781,35 @@ impl Server {
Extension(server_config): Extension<Arc<ServerConfig>>,
Extension(index): Extension<Arc<Index>>,
Path(txid): Path<Txid>,
) -> ServerResult<PageHtml<TransactionHtml>> {
AcceptJson(accept_json): AcceptJson,
) -> ServerResult<Response> {
task::block_in_place(|| {
let transaction = index
.get_transaction(txid)?
.ok_or_not_found(|| format!("transaction {txid}"))?;

let inscription_count = index.inscription_count(txid)?;

let blockhash = index.get_transaction_blockhash(txid)?;

Ok(
Ok(if accept_json {
Json(TransactionJson {
chain: server_config.chain,
etching: index.get_etching(txid)?,
inscription_count,
transaction,
txid,
})
.into_response()
} else {
TransactionHtml {
blockhash,
transaction,
txid,
inscription_count,
chain: server_config.chain,
etching: index.get_etching(txid)?,
}
.page(server_config),
)
.page(server_config)
.into_response()
})
})
}

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

pub(crate) use {
block::{BlockHtml, BlockJson},
blocks::BlocksHtml,
blocks::{BlocksHtml, BlocksJson},
children::{ChildrenHtml, ChildrenJson},
clock::ClockSvg,
collections::CollectionsHtml,
Expand All @@ -25,11 +25,11 @@ pub(crate) use {
sat::{SatHtml, SatInscriptionJson, SatInscriptionsJson, SatJson},
server_config::ServerConfig,
status::StatusHtml,
transaction::TransactionHtml,
transaction::{TransactionHtml, TransactionJson},
};

pub mod block;
mod blocks;
pub mod blocks;
mod children;
mod clock;
pub mod collections;
Expand All @@ -48,7 +48,7 @@ pub mod rune;
pub mod runes;
pub mod sat;
pub mod status;
mod transaction;
pub mod transaction;

#[derive(Boilerplate)]
pub(crate) struct PageHtml<T: PageContent> {
Expand Down
12 changes: 7 additions & 5 deletions src/templates/blocks.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use super::*;

#[derive(Boilerplate)]
pub(crate) struct BlocksHtml {
last: u32,
blocks: Vec<BlockHash>,
featured_blocks: BTreeMap<BlockHash, Vec<InscriptionId>>,
pub type BlocksJson = BlocksHtml;

#[derive(Boilerplate, Debug, PartialEq, Serialize, Deserialize)]
pub struct BlocksHtml {
pub last: u32,
pub blocks: Vec<BlockHash>,
pub featured_blocks: BTreeMap<BlockHash, Vec<InscriptionId>>,
}

impl BlocksHtml {
Expand Down
58 changes: 9 additions & 49 deletions src/templates/transaction.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use super::*;

#[derive(Boilerplate)]
pub(crate) struct TransactionHtml {
pub(crate) blockhash: Option<BlockHash>,
pub(crate) chain: Chain,
pub(crate) etching: Option<SpacedRune>,
pub(crate) inscription_count: u32,
pub(crate) transaction: Transaction,
pub(crate) txid: Txid,
pub type TransactionJson = TransactionHtml;

#[derive(Boilerplate, Debug, PartialEq, Serialize, Deserialize)]
pub struct TransactionHtml {
pub chain: Chain,
pub etching: Option<SpacedRune>,
pub inscription_count: u32,
pub transaction: Transaction,
pub txid: Txid,
}

impl PageContent for TransactionHtml {
Expand Down Expand Up @@ -47,7 +48,6 @@ mod tests {

pretty_assert_eq!(
TransactionHtml {
blockhash: None,
chain: Chain::Mainnet,
etching: None,
inscription_count: 0,
Expand Down Expand Up @@ -89,44 +89,4 @@ mod tests {
.unindent()
);
}

#[test]
fn with_blockhash() {
let transaction = Transaction {
version: 2,
lock_time: LockTime::ZERO,
input: Vec::new(),
output: vec![
TxOut {
value: 50 * COIN_VALUE,
script_pubkey: script::Builder::new().push_int(0).into_script(),
},
TxOut {
value: 50 * COIN_VALUE,
script_pubkey: script::Builder::new().push_int(1).into_script(),
},
],
};

assert_regex_match!(
TransactionHtml {
blockhash: Some(blockhash(0)),
chain: Chain::Mainnet,
etching: None,
inscription_count: 0,
txid: transaction.txid(),
transaction,
}
.to_string(),
"
<h1>Transaction <span class=monospace>[[:xdigit:]]{64}</span></h1>
<dl>
<dt>block</dt>
<dd><a href=/block/0{64} class=monospace>0{64}</a></dd>
</dl>
.*
"
.unindent()
);
}
}
10 changes: 0 additions & 10 deletions src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,6 @@ macro_rules! assert_matches {
}
}

pub(crate) fn blockhash(n: u64) -> BlockHash {
let hex = format!("{n:x}");

if hex.is_empty() || hex.len() > 1 {
panic!();
}

hex.repeat(64).parse().unwrap()
}

pub(crate) fn txid(n: u64) -> Txid {
let hex = format!("{n:x}");

Expand Down
4 changes: 0 additions & 4 deletions templates/transaction.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ <h2>Inscription Geneses</h2>
</div>
%% }
<dl>
%% if let Some(blockhash) = self.blockhash {
<dt>block</dt>
<dd><a href=/block/{{ blockhash }} class=monospace>{{ blockhash }}</a></dd>
%% }
%% if let Some(rune) = self.etching {
<dt>etching</dt>
<dd><a href=/rune/{{ rune }}>{{ rune }}</a></dd>
Expand Down
58 changes: 58 additions & 0 deletions tests/json_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,64 @@ fn get_block() {
);
}

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

let blocks: Vec<BlockHash> = rpc_server
.mine_blocks(101)
.iter()
.rev()
.take(100)
.map(|block| block.block_hash())
.collect();

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

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

let blocks_json: BlocksJson = serde_json::from_str(&response.text().unwrap()).unwrap();

pretty_assert_eq!(
blocks_json,
BlocksJson {
last: 101,
blocks: blocks.clone(),
featured_blocks: blocks
.into_iter()
.take(5)
.map(|block_hash| (block_hash, Vec::new()))
.collect(),
}
);
}

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

let transaction = rpc_server.mine_blocks(1)[0].txdata[0].clone();

let txid = transaction.txid();

let response = TestServer::spawn_with_server_args(&rpc_server, &[], &["--enable-json-api"])
.json_request(format!("/tx/{txid}"));

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

assert_eq!(
serde_json::from_str::<TransactionJson>(&response.text().unwrap()).unwrap(),
TransactionJson {
chain: Chain::Mainnet,
etching: None,
inscription_count: 0,
transaction,
txid,
}
);
}

#[test]
fn get_status() {
let rpc_server = test_bitcoincore_rpc::builder()
Expand Down
5 changes: 3 additions & 2 deletions tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ use {
rarity::Rarity,
subcommand::runes::RuneInfo,
templates::{
block::BlockJson, inscription::InscriptionJson, inscriptions::InscriptionsJson,
output::OutputJson, rune::RuneJson, runes::RunesJson, sat::SatJson, status::StatusHtml,
block::BlockJson, blocks::BlocksJson, inscription::InscriptionJson,
inscriptions::InscriptionsJson, output::OutputJson, rune::RuneJson, runes::RunesJson,
sat::SatJson, status::StatusHtml, transaction::TransactionJson,
},
Edict, InscriptionId, Rune, RuneEntry, RuneId, Runestone, SatPoint,
},
Expand Down
Loading