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

Recursive endpoint for retrieving details about an inscription #2628

Merged
merged 22 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
66 changes: 61 additions & 5 deletions src/subcommand/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, ChildrenHtml, ClockSvg, HomeHtml, InputHtml, InscriptionDetailsJson,
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::{
Expand Down Expand Up @@ -226,6 +227,10 @@ impl Server {
)
.route("/r/blockheight", get(Self::block_height))
.route("/r/blocktime", get(Self::block_time))
.route(
"/r/inscription/:inscription_id",
get(Self::inscription_details),
)
.route("/r/metadata/:inscription_id", get(Self::metadata))
.route("/range/:start/:end", get(Self::range))
.route("/rare.txt", get(Self::rare_txt))
Expand Down Expand Up @@ -712,6 +717,57 @@ impl Server {
Ok(Json(hex::encode(metadata)))
}

async fn inscription_details(
lifofifoX marked this conversation as resolved.
Show resolved Hide resolved
Extension(page_config): Extension<Arc<PageConfig>>,
Extension(index): Extension<Arc<Index>>,
Path(inscription_id): Path<InscriptionId>,
) -> ServerResult<Response> {
lifofifoX marked this conversation as resolved.
Show resolved Hide resolved
let inscription = index
.get_inscription_by_id(inscription_id)?
.ok_or_not_found(|| format!("inscription {inscription_id}"))?;

let entry = index
.get_inscription_entry(inscription_id)?
.ok_or_not_found(|| format!("inscription {inscription_id}"))?;
lifofifoX marked this conversation as resolved.
Show resolved Hide resolved

let satpoint = index
.get_inscription_satpoint_by_id(inscription_id)?
.ok_or_not_found(|| format!("inscription {inscription_id}"))?;
lifofifoX marked this conversation as resolved.
Show resolved Hide resolved

let output = if satpoint.outpoint == unbound_outpoint() {
None
} else {
Some(
index
.get_transaction(satpoint.outpoint.txid)?
.ok_or_not_found(|| format!("inscription {inscription_id} current transaction"))?
.output
.into_iter()
.nth(satpoint.outpoint.vout.try_into().unwrap())
.ok_or_not_found(|| format!("inscription {inscription_id} current transaction output"))?,
)
};

let address = output
.as_ref()
.and_then(|o| page_config.chain.address_from_script(&o.script_pubkey).ok())
lifofifoX marked this conversation as resolved.
Show resolved Hide resolved
.map(|address| address.to_string());

Ok(
Json(InscriptionDetailsJson {
address,
raphjaph marked this conversation as resolved.
Show resolved Hide resolved
inscription_number: entry.inscription_number,
lifofifoX marked this conversation as resolved.
Show resolved Hide resolved
content_type: inscription.content_type().map(|s| s.to_string()),
lifofifoX marked this conversation as resolved.
Show resolved Hide resolved
raphjaph marked this conversation as resolved.
Show resolved Hide resolved
content_length: inscription.content_length(),
raphjaph marked this conversation as resolved.
Show resolved Hide resolved
genesis_fee: entry.fee,
lifofifoX marked this conversation as resolved.
Show resolved Hide resolved
genesis_height: entry.height,
lifofifoX marked this conversation as resolved.
Show resolved Hide resolved
satpoint,
lifofifoX marked this conversation as resolved.
Show resolved Hide resolved
timestamp: timestamp(entry.timestamp).timestamp(),
})
.into_response(),
)
}

async fn status(Extension(index): Extension<Arc<Index>>) -> (StatusCode, &'static str) {
if index.is_unrecoverably_reorged() {
(
Expand Down
2 changes: 1 addition & 1 deletion src/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub(crate) use {
home::HomeHtml,
iframe::Iframe,
input::InputHtml,
inscription::{InscriptionHtml, InscriptionJson},
inscription::{InscriptionDetailsJson, InscriptionHtml, InscriptionJson},
inscriptions::{InscriptionsHtml, InscriptionsJson},
inscriptions_block::InscriptionsBlockHtml,
metadata::MetadataHtml,
Expand Down
12 changes: 12 additions & 0 deletions src/templates/inscription.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ pub(crate) struct InscriptionHtml {
pub(crate) timestamp: DateTime<Utc>,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct InscriptionDetailsJson {
lifofifoX marked this conversation as resolved.
Show resolved Hide resolved
pub address: Option<String>,
raphjaph marked this conversation as resolved.
Show resolved Hide resolved
lifofifoX marked this conversation as resolved.
Show resolved Hide resolved
pub inscription_number: i64,
pub content_type: Option<String>,
pub content_length: Option<usize>,
pub genesis_fee: u64,
pub genesis_height: u64,
pub satpoint: SatPoint,
pub timestamp: i64,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct InscriptionJson {
pub address: Option<String>,
Expand Down
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::{
block::BlockJson, inscription::InscriptionJson, inscriptions::InscriptionsJson,
output::OutputJson, sat::SatJson,
block::BlockJson, inscription::InscriptionDetailsJson, inscription::InscriptionJson,
inscriptions::InscriptionsJson, output::OutputJson, sat::SatJson,
},
SatPoint,
},
Expand Down
48 changes: 48 additions & 0 deletions tests/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,54 @@ fn inscription_metadata() {
);
}

#[test]
fn recursive_inscription_metadata() {
lifofifoX marked this conversation as resolved.
Show resolved Hide resolved
let rpc_server = test_bitcoincore_rpc::spawn();
create_wallet(&rpc_server);

rpc_server.mine_blocks(1);

let output = CommandBuilder::new("wallet inscribe --fee-rate 1 --file wizards.txt")
.write("wizards.txt", "THEWIZARDSOFORD")
lifofifoX marked this conversation as resolved.
Show resolved Hide resolved
.rpc_server(&rpc_server)
.run_and_deserialize_output::<Inscribe>();

let inscription = output.inscriptions.get(0).unwrap();

rpc_server.mine_blocks(1);

let response = TestServer::spawn_with_args(&rpc_server, &[])
.request(format!("/r/inscription/{}", inscription.id));

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(
response.headers().get("content-type").unwrap(),
"application/json"
);

let mut inscription_metadata_json: InscriptionDetailsJson =
serde_json::from_str(&response.text().unwrap()).unwrap();
assert_regex_match!(inscription_metadata_json.address.unwrap(), r"bc1p.*");
inscription_metadata_json.address = None;

pretty_assert_eq!(
inscription_metadata_json,
InscriptionDetailsJson {
address: None,
inscription_number: 0,
content_type: Some("text/plain;charset=utf-8".to_string()),
content_length: Some(15),
genesis_fee: 141,
genesis_height: 2,
satpoint: SatPoint {
outpoint: inscription.location.outpoint,
offset: 0,
},
timestamp: 2,
}
)
}

#[test]
fn inscriptions_page() {
let rpc_server = test_bitcoincore_rpc::spawn();
Expand Down
Loading