diff --git a/src/api.rs b/src/api.rs index 0cd03d7e1a..9efdf1e48d 100644 --- a/src/api.rs +++ b/src/api.rs @@ -3,9 +3,12 @@ use { serde_hex::{SerHex, Strict}, }; -pub use crate::templates::{ - BlocksHtml as Blocks, RuneHtml as Rune, RunesHtml as Runes, StatusHtml as Status, - TransactionHtml as Transaction, +pub use crate::{ + subcommand::decode::RawOutput as Decode, + templates::{ + BlocksHtml as Blocks, RuneHtml as Rune, RunesHtml as Runes, StatusHtml as Status, + TransactionHtml as Transaction, + }, }; #[derive(Debug, PartialEq, Serialize, Deserialize)] diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 954264a79b..2a0c3706f6 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -262,6 +262,7 @@ impl Server { .route("/static/*path", get(Self::static_asset)) .route("/status", get(Self::status)) .route("/tx/:txid", get(Self::transaction)) + .route("/decode/:txid", get(Self::decode)) .route("/update", get(Self::update)) .fallback(Self::fallback) .layer(Extension(index)) @@ -914,6 +915,31 @@ impl Server { }) } + async fn decode( + Extension(index): Extension>, + Path(txid): Path, + AcceptJson(accept_json): AcceptJson, + ) -> ServerResult { + task::block_in_place(|| { + let transaction = index + .get_transaction(txid)? + .ok_or_not_found(|| format!("transaction {txid}"))?; + + let inscriptions = ParsedEnvelope::from_transaction(&transaction); + let runestone = Runestone::decipher(&transaction); + + Ok(if accept_json { + Json(api::Decode { + inscriptions, + runestone, + }) + .into_response() + } else { + StatusCode::NOT_FOUND.into_response() + }) + }) + } + async fn update( Extension(settings): Extension>, Extension(index): Extension>, diff --git a/tests/json_api.rs b/tests/json_api.rs index 2b0df4628d..94b8dcaab3 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -1,4 +1,8 @@ -use {super::*, bitcoin::BlockHash}; +use { + super::*, + bitcoin::BlockHash, + ord::{Envelope, Inscription}, +}; #[test] fn get_sat_without_sat_index() { @@ -705,3 +709,46 @@ fn get_runes_balances() { pretty_assert_eq!(runes_balance_json, rune_balances); } + +#[test] +fn get_decode_tx() { + let core = mockcore::builder().network(Network::Regtest).build(); + + let ord = TestServer::spawn_with_server_args(&core, &["--index-runes", "--regtest"], &[]); + + create_wallet(&core, &ord); + core.mine_blocks(3); + + let envelope = envelope(&[b"ord", &[1], b"text/plain;charset=utf-8", &[], b"bar"]); + + let txid = core.broadcast_tx(TransactionTemplate { + inputs: &[(1, 0, 0, envelope.clone())], + ..default() + }); + + let transaction = core.mine_blocks(1)[0].txdata[0].clone(); + + let inscriptions = vec![Envelope { + payload: Inscription { + body: Some(vec![98, 97, 114]), + content_type: Some(b"text/plain;charset=utf-8".into()), + ..default() + }, + input: 0, + offset: 0, + pushnum: false, + stutter: false, + }]; + let runestone = Runestone::decipher(&transaction); + let response = ord.json_request(format!("/decode/{txid}")); + + assert_eq!(response.status(), StatusCode::OK); + + assert_eq!( + serde_json::from_str::(&response.text().unwrap()).unwrap(), + api::Decode { + inscriptions, + runestone, + } + ); +}