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

Add /r/inscriptions/<QUERY>/<PAGE> #3822

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
85 changes: 84 additions & 1 deletion docs/src/inscriptions/recursion.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,14 @@ The recursive endpoints are:
- `/r/children/<INSCRIPTION_ID>/inscriptions`: details of the first 100 child inscriptions.
- `/r/children/<INSCRIPTION_ID>/inscriptions/<PAGE>`: details of the set of 100 child inscriptions on `<PAGE>`.
- `/r/inscription/<INSCRIPTION_ID>`: information about an inscription
- `/r/inscriptions/<QUERY>/<PAGE>`: information about inscriptions in a block,`<QUERY>`. Paginated,`<PAGE>`.
- `/r/metadata/<INSCRIPTION_ID>`: JSON string containing the hex-encoded CBOR metadata.
- `/r/parents/<INSCRIPTION_ID>`: the first 100 parent inscription ids.
- `/r/parents/<INSCRIPTION_ID>/<PAGE>`: the set of 100 parent inscription ids on `<PAGE>`.
- `/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>`.
- `/r/sat/<SAT_NUMBER>/at/<INDEX>`: the inscription id at `<INDEX>` of all inscriptions on a sat. `<INDEX>` may be a negative number to index from the back. `0` being the first and `-1` being the most recent for example.

- `/r/txs/:query`: transaction input and outputs in a block.
Note: `<SAT_NUMBER>` only allows the actual number of a sat no other sat
notations like degree, percentile or decimal. We may expand to allow those in
the future.
Expand Down Expand Up @@ -196,6 +197,22 @@ percentile in sats/vB.
}
```

- `/r/inscriptions/800000/0`

```json
{
"ids": [
"965f866bf8623bbf956c1b2aeec1efc1ad162fd428ab7fb89f128a0754ebbc32i0",
"1d11c135b165b5d9fe07f1b015ed21536aa1c235ff75cd7da5e73746d464cc97i0",
...
"0981a67c05405eb30810d2e1fcc3b383ede360a7cfed7b901e663fa2d902e2dbi0",
"366469b47dd78d49c9c62cffc0d3ff999b32b7e6139b6b3b18ed3ae28769fbe9i0"
],
"more": true,
"page_index": 0
}
```

- `/r/metadata/35b66389b44535861c44b2b18ed602997ee11db9a30d384ae89630c9fc6f011fi3`:

```json
Expand All @@ -221,3 +238,69 @@ percentile in sats/vB.
"id":"17541f6adf6eb160d52bc6eb0a3546c7c1d2adfe607b1a3cddc72cc0619526adi0"
}
```
`/r/txs/:query`:
```json
{
"best_height": 828677,
"hash": "00000000000000000002a7c4c1e48d76c5a37902165a270156b7a8d72728a054",
"height": 800000,
"target": "0000000000000000000538940000000000000000000000000000000000000000",
"parsed_transactions": [
{
"txid": "b75ca3106ed100521aa50e3ec267a06431c6319538898b25e1b757a5736f5fb4",
"input": [
{
"previous_output": "0000000000000000000000000000000000000000000000000000000000000000:4294967295",
"sequence": 4294967295
}
],
"output": [
{
"value": 638687680,
"script_pubkey": "a914c3f8f898ae5cab4f4c1d597ecb0f3a81a9b146c387"
},
{
"value": 0,
"script_pubkey": "6a24aa21a9ed9fbe517a588ccaca585a868f3cf19cb6897e3c26f3351361fb28ac8509e69a7e"
}
]
},
{
"txid": "d41f5de48325e79070ccd3a23005f7a3b405f3ce1faa4df09f6d71770497e9d5",
"input": [
{
"previous_output": "a992dbddbeb7382e3defc6914f970ea769ef813e69a923afa336976f2cbf0465:1",
"sequence": 4294967295
}
],
"output": [
{
"value": 143332,
"script_pubkey": "51202d618c1f73d5133fdc97d545bfbf55b4cba2ab2a9d41e4596b1df6b8ea9d9348"
},
{
"value": 291851,
"script_pubkey": "001464dbbc84f12f32699ca5010faa618d6a25559b6f"
}
]
},
...
{
"txid": "b2088e443cf4b28ade8873cc6b3f6a67557f104ec4dc5b5e293c12973ab8b6b8",
"input": [
{
"previous_output": "856bce86ad1f0039ccd9eabe49e58e640e3f05952bd0a26d6687710d3de9a5ad:0",
"sequence": 0
}
],
"output": [
{
"value": 499441,
"script_pubkey": "00149549a8a78144db492bedbbe57b99343d034bfafb"
}
]
}
]
}
```

49 changes: 49 additions & 0 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,55 @@ impl Block {
}
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct BlockTxs {
pub best_height: u32,
pub hash: BlockHash,
pub height: u32,
pub target: BlockHash,
pub parsed_transactions: Vec<ParsedTransaction>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct ParsedTransaction {
pub txid: bitcoin::Txid,
input: Vec<ParsedInput>,
output: Vec<bitcoin::TxOut>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct ParsedInput {
previous_output: bitcoin::OutPoint,
sequence: Sequence,
}

impl BlockTxs {
pub(crate) fn new(
block: bitcoin::Block,
height: Height,
best_height: Height,
) -> Self {
let parsed_transactions: Vec<ParsedTransaction> = block.txdata.iter().map(|tx| {
let pruned_inputs: Vec<ParsedInput> = tx.input.iter().map(|input| {
ParsedInput {
previous_output: input.previous_output,
sequence: input.sequence,
}
}).collect();
ParsedTransaction {
txid: tx.txid(),
input: pruned_inputs,
output: tx.output.clone(), // Clone outputs
}
}).collect();
Self {
hash: block.header.block_hash(),
target: target_as_block_hash(block.header.target()),
height: height.0,
best_height: best_height.0,
parsed_transactions,
}
}
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct BlockInfo {
pub average_fee: u64,
Expand Down
98 changes: 98 additions & 0 deletions src/subcommand/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ 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/inscriptions/:height/:page", get(Self::inscriptions_in_block_paginated_json))
.route(
"/r/inscription/:inscription_id",
get(Self::inscription_recursive),
Expand Down Expand Up @@ -268,6 +269,8 @@ impl Server {
"/r/sat/:sat_number/at/:index",
get(Self::sat_inscription_at_index),
)
.route("/r/tx/:txid", get(Self::transaction_json))
.route("/r/txs/:query", get(Self::transactions_in_block_json))
.route("/range/:start/:end", get(Self::range))
.route("/rare.txt", get(Self::rare_txt))
.route("/rune/:rune", get(Self::rune))
Expand Down Expand Up @@ -970,6 +973,31 @@ impl Server {
})
}

async fn transaction_json(
Extension(server_config): Extension<Arc<ServerConfig>>,
Extension(index): Extension<Arc<Index>>,
Path(txid): Path<Txid>,
) -> ServerResult {
task::block_in_place(|| {
let transaction = index
.get_transaction(txid)?
.ok_or_not_found(|| format!("transaction {txid}"))?;

let inscription_count = index.inscription_count(txid)?;

Ok(Json(api::Transaction {
chain: server_config.chain,
etching: index.get_etching(txid)?,
inscription_count,
transaction,
txid,
})
.into_response()
)
})
}


async fn decode(
Extension(index): Extension<Arc<Index>>,
Path(txid): Path<Txid>,
Expand Down Expand Up @@ -2001,6 +2029,76 @@ impl Server {
})
}

async fn inscriptions_in_block_paginated_json(
Extension(index): Extension<Arc<Index>>,
Path((block_height, page_index)): Path<(u32, u32)>,
) -> ServerResult {
task::block_in_place(|| {
let page_size = 100;

let page_index_usize = usize::try_from(page_index).unwrap_or(usize::MAX);
let page_size_usize = usize::try_from(page_size).unwrap_or(usize::MAX);

let mut inscriptions = index
.get_inscriptions_in_block(block_height)?
.into_iter()
.skip(page_index_usize.saturating_mul(page_size_usize))
.take(page_size_usize.saturating_add(1))
.collect::<Vec<InscriptionId>>();

let more = inscriptions.len() > page_size_usize;

if more {
inscriptions.pop();
}

Ok(Json(api::Inscriptions {
ids: inscriptions,
page_index,
more,
})
.into_response())

})

}

async fn transactions_in_block_json(
Extension(index): Extension<Arc<Index>>,
Path(DeserializeFromStr(query)): Path<DeserializeFromStr<query::Block>>,
) -> ServerResult {
task::block_in_place(|| {
let (block, height) = match query {
query::Block::Height(height) => {
let block = index
.get_block_by_height(height)?
.ok_or_not_found(|| format!("block {height}"))?;

(block, height)
}
query::Block::Hash(hash) => {
let info = index
.block_header_info(hash)?
.ok_or_not_found(|| format!("block {hash}"))?;

let block = index
.get_block_by_hash(hash)?
.ok_or_not_found(|| format!("block {hash}"))?;

(block, u32::try_from(info.height).unwrap())
}
};
Ok(Json(api::BlockTxs::new(
block,
Height(height),
Self::index_height(&index)?,
))
.into_response())
})


}

async fn parents(
Extension(server_config): Extension<Arc<ServerConfig>>,
Extension(index): Extension<Arc<Index>>,
Expand Down