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/parents/:inscription_id/inscriptions endpoint. #4088

Merged
merged 17 commits into from
Feb 17, 2025
Merged
4 changes: 1 addition & 3 deletions docs/src/guides/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5127,6 +5127,4 @@ curl -s -H "Accept: application/json" \

## Recursive Endpoints

See [Recursion](../inscriptions/recursion.md) for an explanation of these.

{{#include ../inscriptions/recursion.md:35:3483}}
See [Recursion](../inscriptions/recursion.md).
122 changes: 122 additions & 0 deletions docs/src/inscriptions/recursion.md
Original file line number Diff line number Diff line change
Expand Up @@ -3277,6 +3277,128 @@ curl -s \
```
</details>

<details>
<summary>
<code>GET</code>
<code><b>/r/parents/&lt;INSCRIPTION_ID&gt;/inscriptions</b></code>
</summary>

### Description

Details of the first 100 parent inscriptions.

### Example

```bash
curl -s -H "Accept: application/json" \
http://0.0.0.0:80/r/parents/4a86d375a70a4ecc7ffcd910e05f5e0771ae6a50133543f1bf6b5651adbf0019i0/inscriptions
```

```json
{
"parents": [
{
"charms": [],
"fee": 21730,
"height": 775167,
"id": "92c409fb749b1005fe9a1482d3a74a8e73936a72644f4979df8184aba473841di0",
"number": 4573,
"output": "4a86d375a70a4ecc7ffcd910e05f5e0771ae6a50133543f1bf6b5651adbf0019:13",
"sat": null,
"satpoint": "4a86d375a70a4ecc7ffcd910e05f5e0771ae6a50133543f1bf6b5651adbf0019:13:0",
"timestamp": 1675607405
},
{
"charms": [],
"fee": 14977,
"height": 775167,
"id": "c689cbcb8e31858c5e1476d04af4e7e7cedd1fb4fb9cae5bb62036936a08282di0",
"number": 4576,
"output": "4a86d375a70a4ecc7ffcd910e05f5e0771ae6a50133543f1bf6b5651adbf0019:14",
"sat": null,
"satpoint": "4a86d375a70a4ecc7ffcd910e05f5e0771ae6a50133543f1bf6b5651adbf0019:14:0",
"timestamp": 1675607405
},
{
"charms": [],
"fee": 12533,
"height": 775167,
"id": "982d15f6b3510307ef845f1cb3352b27e2b048616b7c0642367ebc05bbd36d3ai0",
"number": 4578,
"output": "4a86d375a70a4ecc7ffcd910e05f5e0771ae6a50133543f1bf6b5651adbf0019:12",
"sat": null,
"satpoint": "4a86d375a70a4ecc7ffcd910e05f5e0771ae6a50133543f1bf6b5651adbf0019:12:0",
"timestamp": 1675607405
}
...
],
"more": true,
"page": 0
}
```
</details>

<details>
<summary>
<code>GET</code>
<code><b>/r/parents/&lt;INSCRIPTION_ID&gt;/inscriptions/&lt;PAGE&gt;</b></code>
</summary>

### Description

Details of the set of 100 parent inscriptions on &lt;PAGE&gt;.

### Example

```bash
curl -s -H "Accept: application/json" \
http://0.0.0.0:80/r/parents/4a86d375a70a4ecc7ffcd910e05f5e0771ae6a50133543f1bf6b5651adbf0019i0/inscriptions/1
```

```json
{
"parents": [
{
"charms": [],
"fee": 65049,
"height": 775443,
"id": "972994a55c338e8458bfd156642f4aa56bdab54c68658d6b64d932fedef3c81fi0",
"number": 10804,
"output": "4a86d375a70a4ecc7ffcd910e05f5e0771ae6a50133543f1bf6b5651adbf0019:102",
"sat": null,
"satpoint": "4a86d375a70a4ecc7ffcd910e05f5e0771ae6a50133543f1bf6b5651adbf0019:102:0",
"timestamp": 1675780989
},
{
"charms": [],
"fee": 60111,
"height": 775443,
"id": "dbc21f2d3323df24a378fef3bdbe4e79c4947ce7da54968affcdefa7eda80d21i0",
"number": 10805,
"output": "4a86d375a70a4ecc7ffcd910e05f5e0771ae6a50133543f1bf6b5651adbf0019:110",
"sat": null,
"satpoint": "4a86d375a70a4ecc7ffcd910e05f5e0771ae6a50133543f1bf6b5651adbf0019:110:0",
"timestamp": 1675780989
},
{
"charms": [],
"fee": 49881,
"height": 775443,
"id": "97870f7cf65992a66d0413a7e6773190e686f185500f78c30f989f2d1f1ba922i0",
"number": 10806,
"output": "4a86d375a70a4ecc7ffcd910e05f5e0771ae6a50133543f1bf6b5651adbf0019:101",
"sat": null,
"satpoint": "4a86d375a70a4ecc7ffcd910e05f5e0771ae6a50133543f1bf6b5651adbf0019:101:0",
"timestamp": 1675780989
}
...
],
"more": false,
"page": 1
}
```
</details>

<details>
<summary>
<code>GET</code>
Expand Down
11 changes: 9 additions & 2 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,14 @@ pub struct Children {

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct ChildInscriptions {
pub children: Vec<ChildInscriptionRecursive>,
pub children: Vec<RelativeInscriptionRecursive>,
pub more: bool,
pub page: usize,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct ParentInscriptions {
pub parents: Vec<RelativeInscriptionRecursive>,
pub more: bool,
pub page: usize,
}
Expand Down Expand Up @@ -132,7 +139,7 @@ pub struct InscriptionRecursive {
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct ChildInscriptionRecursive {
pub struct RelativeInscriptionRecursive {
pub charms: Vec<Charm>,
pub fee: u64,
pub height: u32,
Expand Down
8 changes: 4 additions & 4 deletions src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1308,17 +1308,17 @@ impl Index {
pub fn get_parents_by_sequence_number_paginated(
&self,
parent_sequence_numbers: Vec<u32>,
page_size: usize,
page_index: usize,
) -> Result<(Vec<InscriptionId>, bool)> {
const PAGE_SIZE: usize = 100;
let rtx = self.database.begin_read()?;

let sequence_number_to_entry = rtx.open_table(SEQUENCE_NUMBER_TO_INSCRIPTION_ENTRY)?;

let mut parents = parent_sequence_numbers
.iter()
.skip(page_index * PAGE_SIZE)
.take(PAGE_SIZE.saturating_add(1))
.skip(page_index * page_size)
.take(page_size.saturating_add(1))
.map(|sequence_number| {
sequence_number_to_entry
.get(sequence_number)
Expand All @@ -1327,7 +1327,7 @@ impl Index {
})
.collect::<Result<Vec<InscriptionId>>>()?;

let more_parents = parents.len() > PAGE_SIZE;
let more_parents = parents.len() > page_size;

if more_parents {
parents.pop();
Expand Down
134 changes: 133 additions & 1 deletion src/subcommand/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,14 @@ impl Server {
"/r/parents/{inscription_id}/{page}",
get(r::parents_paginated),
)
.route(
"/r/parents/{inscription_id}/inscriptions",
get(r::parent_inscriptions),
)
.route(
"/r/parents/{inscription_id}/inscriptions/{page}",
get(r::parent_inscriptions_paginated),
)
.route("/r/sat/{sat_number}", get(r::sat))
.route("/r/sat/{sat_number}/at/{index}", get(r::sat_at_index))
.route("/r/sat/{sat_number}/{page}", get(r::sat_paginated))
Expand Down Expand Up @@ -1825,7 +1833,8 @@ impl Server {
.get_inscription_entry(id)?
.ok_or_not_found(|| format!("inscription {id}"))?;

let (parents, more) = index.get_parents_by_sequence_number_paginated(child.parents, page)?;
let (parents, more) =
index.get_parents_by_sequence_number_paginated(child.parents, 100, page)?;

let prev_page = page.checked_sub(1);

Expand Down Expand Up @@ -6445,6 +6454,129 @@ next
assert_eq!(child_inscriptions_json.page, 1);
}

#[test]
fn parent_inscriptions_recursive_endpoint() {
let server = TestServer::builder().chain(Chain::Regtest).build();
server.mine_blocks(1);

let mut builder = script::Builder::new();
for _ in 0..111 {
builder = Inscription {
content_type: Some("text/plain".into()),
body: Some("hello".into()),
unrecognized_even_field: false,
..default()
}
.append_reveal_script_to_builder(builder);
}

let witness = Witness::from_slice(&[builder.into_bytes(), Vec::new()]);

let parents_txid = server.core.broadcast_tx(TransactionTemplate {
inputs: &[(1, 0, 0, witness)],
..default()
});

server.mine_blocks(1);

let mut builder = script::Builder::new();
builder = Inscription {
content_type: Some("text/plain".into()),
body: Some("hello".into()),
parents: (0..111)
.map(|i| {
InscriptionId {
txid: parents_txid,
index: i,
}
.value()
})
.collect(),
unrecognized_even_field: false,
..default()
}
.append_reveal_script_to_builder(builder);

let witness = Witness::from_slice(&[builder.into_bytes(), Vec::new()]);

let child_txid = server.core.broadcast_tx(TransactionTemplate {
inputs: &[(2, 0, 0, witness), (2, 1, 0, Default::default())],
..default()
});

let child_inscription_id = InscriptionId {
txid: child_txid,
index: 0,
};

server.assert_response(
format!("/r/parents/{child_inscription_id}/inscriptions"),
StatusCode::NOT_FOUND,
&format!("inscription {child_inscription_id} not found"),
);

server.mine_blocks(1);

let first_parent_inscription_id = InscriptionId {
txid: parents_txid,
index: 0,
};
let hundredth_parent_inscription_id = InscriptionId {
txid: parents_txid,
index: 99,
};
let hundred_first_parent_inscription_id = InscriptionId {
txid: parents_txid,
index: 100,
};
let hundred_eleventh_parent_inscription_id = InscriptionId {
txid: parents_txid,
index: 110,
};

let parent_inscriptions_json = server.get_json::<api::ParentInscriptions>(format!(
"/r/parents/{child_inscription_id}/inscriptions"
));

assert_eq!(parent_inscriptions_json.parents.len(), 100);

assert_eq!(
parent_inscriptions_json.parents[0].id,
first_parent_inscription_id
);
assert_eq!(parent_inscriptions_json.parents[0].number, 0); // parents are #0 and -1 to -110, child is #1

assert_eq!(
parent_inscriptions_json.parents[99].id,
hundredth_parent_inscription_id
);
assert_eq!(parent_inscriptions_json.parents[99].number, -99); // all but 1st parent are cursed

assert!(parent_inscriptions_json.more);
assert_eq!(parent_inscriptions_json.page, 0);

let parent_inscriptions_json = server.get_json::<api::ParentInscriptions>(format!(
"/r/parents/{child_inscription_id}/inscriptions/1"
));

assert_eq!(parent_inscriptions_json.parents.len(), 11);

assert_eq!(
parent_inscriptions_json.parents[0].id,
hundred_first_parent_inscription_id
);
assert_eq!(parent_inscriptions_json.parents[0].number, -100);

assert_eq!(
parent_inscriptions_json.parents[10].id,
hundred_eleventh_parent_inscription_id
);
assert_eq!(parent_inscriptions_json.parents[10].number, -110);

assert!(!parent_inscriptions_json.more);
assert_eq!(parent_inscriptions_json.page, 1);
}

#[test]
fn inscriptions_in_block_page() {
let server = TestServer::builder()
Expand Down
Loading
Loading