Skip to content

Commit

Permalink
Add functionality to handle JSON doc child inscriptions as special case
Browse files Browse the repository at this point in the history
Why:
- Some generative art collections have inscribed all art within a single
parent inscription to save considerable storage space on the
blockchain.
- The ord explorer is unable to currently support collections inscribed
  in this manner.  This commit adds this support.

What:
- In order to support these generative art collections, we need to be able
  to inscribe the metadata for children in an agreed upon JSON spec.
- These child inscriptions are JSON documents containing url parameters
  that instruct the parent inscription to
  render a specific item in the collection.

How:
- Since all art is contained in the parent, we added a simple redirect to
  render the parent inscription with included url parameters.
- There are 3 constraints which must be in the child inscription before
  the redirect can happen:
1. The child inscription must have a parent inscription.
2. The child inscription's body data must be valid JSON.
3. The child inscription's body JSON data must contain a field named
   "is_ord_pointer", with any value.

Proposed JSON standard:
```
{
   "is_pointer": 1,
   "url_params": ["tokenID=4969"],
}
```

Related Issue:
- https://github.com/casey/ord/issues/783

Co-authored-by: Clarke Plumo <[email protected]>
  • Loading branch information
ericatallah and clrke committed Apr 5, 2023
1 parent 0842994 commit 778f137
Showing 1 changed file with 71 additions and 0 deletions.
71 changes: 71 additions & 0 deletions src/subcommand/server.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use serde_json::{Value};

use {
self::{
deserialize_from_str::DeserializeFromStr,
Expand Down Expand Up @@ -734,6 +736,10 @@ impl Server {
.get_inscription_by_id(inscription_id)?
.ok_or_not_found(|| format!("inscription {inscription_id}"))?;

if let Some(response) = Self::get_content_response_if_child_pointer(&inscription) {
return response;
}

Ok(
Self::content_response(inscription)
.ok_or_not_found(|| format!("inscription {inscription_id} content"))?
Expand Down Expand Up @@ -764,6 +770,45 @@ impl Server {
Some((headers, inscription.into_body()?))
}

fn get_parent_url_params_if_child_pointer(inscription: &Inscription) -> Option<String> {
if !inscription.get_parent_id().is_some() {
return None;
}

if !Self::valid_json(inscription.body()) {
return None;
}

let json_result: Result<Value, serde_json::Error> = serde_json::from_slice(&inscription.body().unwrap());
let json: Value = json_result.unwrap();

if !json.as_object().unwrap().contains_key("is_pointer") {
return None;
}

let parent_url_params = Self::get_url_params_from_json_value(json);

Some(parent_url_params)
}

fn get_content_response_if_child_pointer(inscription: &Inscription) -> Option<ServerResult<Response>> {
let parent_url_params = Self::get_parent_url_params_if_child_pointer(inscription);
if let Some(url_params) = parent_url_params {
let redirect_uri = format!("/content/{}?{}", inscription.get_parent_id().unwrap(), url_params);
return Some(Ok(Redirect::temporary(&redirect_uri).into_response()));
}
None
}

fn get_preview_response_if_child_pointer(inscription: &Inscription) -> Option<ServerResult<Response>> {
let parent_url_params = Self::get_parent_url_params_if_child_pointer(inscription);
if let Some(url_params) = parent_url_params {
let redirect_uri = format!("/preview/{}?{}", inscription.get_parent_id().unwrap(), url_params);
return Some(Ok(Redirect::temporary(&redirect_uri).into_response()));
}
None
}

async fn preview(
Extension(index): Extension<Arc<Index>>,
Extension(config): Extension<Arc<Config>>,
Expand All @@ -777,6 +822,10 @@ impl Server {
.get_inscription_by_id(inscription_id)?
.ok_or_not_found(|| format!("inscription {inscription_id}"))?;

if let Some(response) = Self::get_preview_response_if_child_pointer(&inscription) {
return response;
}

match inscription.media() {
Media::Audio => Ok(PreviewAudioHtml { inscription_id }.into_response()),
Media::Iframe => Ok(
Expand Down Expand Up @@ -821,6 +870,28 @@ impl Server {
}
}

fn valid_json(data: Option<&[u8]>) -> bool {
match data {
Some(bytes) => serde_json::from_slice::<Value>(bytes).is_ok(),
None => false,
}
}

fn get_url_params_from_json_value(json: Value) -> String {
let mut params_str = String::new();
if let Some(url_params_field) = json.get("url_params") {
if let Some(url_params) = url_params_field.as_array() {
for param in url_params {
if !params_str.is_empty() {
params_str.push('&');
}
params_str.push_str(&param.as_str().unwrap());
}
}
}
params_str
}

async fn inscription(
Extension(page_config): Extension<Arc<PageConfig>>,
Extension(index): Extension<Arc<Index>>,
Expand Down

0 comments on commit 778f137

Please sign in to comment.