Skip to content

Commit

Permalink
Add /r/inscription endpoint for getting inscription details (#2628)
Browse files Browse the repository at this point in the history
  • Loading branch information
lifofifoX authored Feb 21, 2024
1 parent 4100dce commit 3eb92cd
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 33 deletions.
83 changes: 59 additions & 24 deletions docs/src/inscriptions/recursion.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ The recursive endpoints are:
- `/r/blocktime`: UNIX time stamp of latest block.
- `/r/children/<INSCRIPTION_ID>`: the first 100 child inscription ids.
- `/r/children/<INSCRIPTION_ID>/<PAGE>`: the set of 100 child inscription ids on `<PAGE>`.
- `/r/inscription/:inscription_id`: information about an inscription
- `/r/metadata/<INSCRIPTION_ID>`: JSON string containing the hex-encoded CBOR metadata.
- `/r/sat/<SAT_NUMBER>`: the first 100 inscription ids on a sat.
- `/r/sat/<SAT_NUMBER>/<PAGE>`: the set of 100 inscription ids on `<PAGE>`.
Expand All @@ -50,16 +51,38 @@ plain-text responses.
Examples
--------

- `/r/blockhash/0`:

```json
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
```

- `/r/blockheight`:

```json
777000
```

- `/r/blockhash/0`:
- `/r/blockinfo/0`:

```json
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
{
"bits": 486604799,
"chainwork": 0,
"confirmations": 0,
"difficulty": 0.0,
"hash": "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
"height": 0,
"median_time": null,
"merkle_root": "0000000000000000000000000000000000000000000000000000000000000000",
"next_block": null,
"nonce": 0,
"previous_block": null,
"target": "00000000ffff0000000000000000000000000000000000000000000000000000",
"timestamp": 0,
"transaction_count": 0,
"version": 1
}
```

- `/r/blocktime`:
Expand All @@ -68,6 +91,40 @@ Examples
1700770905
```

- `/r/children/60bcf821240064a9c55225c4f01711b0ebbcab39aa3fafeefe4299ab158536fai0/49`:

```json
{
"ids":[
"7cd66b8e3a63dcd2fada917119830286bca0637267709d6df1ca78d98a1b4487i4900",
"7cd66b8e3a63dcd2fada917119830286bca0637267709d6df1ca78d98a1b4487i4901",
...
"7cd66b8e3a63dcd2fada917119830286bca0637267709d6df1ca78d98a1b4487i4935",
"7cd66b8e3a63dcd2fada917119830286bca0637267709d6df1ca78d98a1b4487i4936"
],
"more":false,
"page":49
}
```

- `r/inscription/3bd72a7ef68776c9429961e43043ff65efa7fb2d8bb407386a9e3b19f149bc36i0`

```json
{
"charms": [],
"content_type": "image/png",
"content_length": 144037,
"fee": 36352,
"height": 209,
"number": 2,
"output": "3bd72a7ef68776c9429961e43043ff65efa7fb2d8bb407386a9e3b19f149bc36:0",
"sat": null,
"satpoint": "3bd72a7ef68776c9429961e43043ff65efa7fb2d8bb407386a9e3b19f149bc36:0:0",
"timestamp": 1708312562,
"value": 10000
}
```

- `/r/metadata/35b66389b44535861c44b2b18ed602997ee11db9a30d384ae89630c9fc6f011fi3`:

```json
Expand Down Expand Up @@ -109,25 +166,3 @@ Examples
"page":49
}
```

- `/r/blockinfo/0`:

```json
{
"bits": 486604799,
"chainwork": 0,
"confirmations": 0,
"difficulty": 0.0,
"hash": "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
"height": 0,
"median_time": null,
"merkle_root": "0000000000000000000000000000000000000000000000000000000000000000",
"next_block": null,
"nonce": 0,
"previous_block": null,
"target": "00000000ffff0000000000000000000000000000000000000000000000000000",
"timestamp": 0,
"transaction_count": 0,
"version": 1
}
```
75 changes: 69 additions & 6 deletions src/subcommand/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ use {
templates::{
BlockHtml, BlockInfoJson, 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, RuneBalancesHtml, RuneHtml, RuneJson, RunesHtml,
RunesJson, SatHtml, SatInscriptionJson, SatInscriptionsJson, SatJson, TransactionHtml,
TransactionJson,
InscriptionRecursiveJson, InscriptionsBlockHtml, InscriptionsHtml, InscriptionsJson,
OutputHtml, OutputJson, PageContent, PageHtml, PreviewAudioHtml, PreviewCodeHtml,
PreviewFontHtml, PreviewImageHtml, PreviewMarkdownHtml, PreviewModelHtml, PreviewPdfHtml,
PreviewTextHtml, PreviewUnknownHtml, PreviewVideoHtml, RangeHtml, RareTxt, RuneBalancesHtml,
RuneHtml, RuneJson, RunesHtml, RunesJson, SatHtml, SatInscriptionJson, SatInscriptionsJson,
SatJson, TransactionHtml, TransactionJson,
},
},
axum::{
Expand Down Expand Up @@ -272,6 +272,10 @@ impl Server {
.route("/r/blockheight", get(Self::block_height))
.route("/r/blocktime", get(Self::block_time))
.route("/r/blockinfo/:query", get(Self::block_info))
.route(
"/r/inscription/:inscription_id",
get(Self::inscription_recursive),
)
.route("/r/children/:inscription_id", get(Self::children_recursive))
.route(
"/r/children/:inscription_id/:page",
Expand Down Expand Up @@ -873,6 +877,65 @@ impl Server {
})
}

async fn inscription_recursive(
Extension(index): Extension<Arc<Index>>,
Path(inscription_id): Path<InscriptionId>,
) -> ServerResult<Response> {
task::block_in_place(|| {
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)
.unwrap()
.unwrap();

let satpoint = index
.get_inscription_satpoint_by_id(inscription_id)
.ok()
.flatten()
.unwrap();

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")
})?,
)
};

Ok(
Json(InscriptionRecursiveJson {
charms: Charm::ALL
.iter()
.filter(|charm| charm.is_set(entry.charms))
.map(|charm| charm.title().into())
.collect(),
content_type: inscription.content_type().map(|s| s.to_string()),
content_length: inscription.content_length(),
fee: entry.fee,
height: entry.height,
number: entry.inscription_number,
output: satpoint.outpoint,
value: output.as_ref().map(|o| o.value),
sat: entry.sat,
satpoint,
timestamp: timestamp(entry.timestamp).timestamp(),
})
.into_response(),
)
})
}

async fn status(
Extension(server_config): Extension<Arc<ServerConfig>>,
Extension(index): Extension<Arc<Index>>,
Expand Down
2 changes: 1 addition & 1 deletion src/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub(crate) use {
home::HomeHtml,
iframe::Iframe,
input::InputHtml,
inscription::{InscriptionHtml, InscriptionJson},
inscription::{InscriptionHtml, InscriptionJson, InscriptionRecursiveJson},
inscriptions::{InscriptionsHtml, InscriptionsJson},
inscriptions_block::InscriptionsBlockHtml,
metadata::MetadataHtml,
Expand Down
15 changes: 15 additions & 0 deletions src/templates/inscription.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@ pub(crate) struct InscriptionHtml {
pub(crate) charms: u16,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct InscriptionRecursiveJson {
pub charms: Vec<String>,
pub content_type: Option<String>,
pub content_length: Option<usize>,
pub fee: u64,
pub height: u32,
pub number: i32,
pub output: OutPoint,
pub sat: Option<Sat>,
pub satpoint: SatPoint,
pub timestamp: i64,
pub value: Option<u64>,
}

#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct InscriptionJson {
pub address: Option<String>,
Expand Down
5 changes: 3 additions & 2 deletions tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ use {
subcommand::runes::RuneInfo,
templates::{
block::BlockJson, blocks::BlocksJson, inscription::InscriptionJson,
inscriptions::InscriptionsJson, output::OutputJson, rune::RuneJson, runes::RunesJson,
sat::SatJson, status::StatusJson, transaction::TransactionJson,
inscription::InscriptionRecursiveJson, inscriptions::InscriptionsJson, output::OutputJson,
rune::RuneJson, runes::RunesJson, sat::SatJson, status::StatusJson,
transaction::TransactionJson,
},
Edict, InscriptionId, Rune, RuneEntry, RuneId, Runestone,
},
Expand Down
51 changes: 51 additions & 0 deletions tests/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,57 @@ fn inscription_metadata() {
);
}

#[test]
fn recursive_inscription_endpoint() {
let bitcoin_rpc_server = test_bitcoincore_rpc::spawn();
let ord_rpc_server =
TestServer::spawn_with_server_args(&bitcoin_rpc_server, &["--index-sats"], &[]);

create_wallet(&bitcoin_rpc_server, &ord_rpc_server);

bitcoin_rpc_server.mine_blocks(1);

let output = CommandBuilder::new("wallet inscribe --fee-rate 1 --file foo.txt")
.write("foo.txt", "FOO")
.bitcoin_rpc_server(&bitcoin_rpc_server)
.ord_rpc_server(&ord_rpc_server)
.run_and_deserialize_output::<Inscribe>();

bitcoin_rpc_server.mine_blocks(1);

let inscription = output.inscriptions.first().unwrap();
let response = ord_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 inscription_recursive_json: InscriptionRecursiveJson =
serde_json::from_str(&response.text().unwrap()).unwrap();

pretty_assert_eq!(
inscription_recursive_json,
InscriptionRecursiveJson {
charms: vec!["coin".into(), "uncommon".into()],
content_type: Some("text/plain;charset=utf-8".to_string()),
content_length: Some(3),
fee: 138,
height: 2,
number: 0,
output: inscription.location.outpoint,
sat: Some(Sat(50 * COIN_VALUE)),
satpoint: SatPoint {
outpoint: inscription.location.outpoint,
offset: 0,
},
timestamp: 2,
value: Some(10000),
}
)
}

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

0 comments on commit 3eb92cd

Please sign in to comment.