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

Recursive Endpoints #2175

Merged
merged 9 commits into from
Jun 15, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion docs/src/inscriptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,16 @@ This has a number of interesting use-cases:
- Publishing snippets of code, images, audio, or stylesheets as shared public
resources.

- Generative art collections where an algorithm is inscribed as JavaScript ,
- Generative art collections where an algorithm is inscribed as JavaScript,
and instantiated from multiple inscriptions with unique seeds.

- Generative profile picture collections where accessories and attributes are
inscribed as individual images, or in a shared texture atlas, and then
combined, collage-style, in unique combinations in multiple inscriptions.

A couple other endpoints that inscriptions may access are the following:

- `/blockheight`: latest block height.
- `/blockhash`: latest block hash.
- `/blockhash/<HEIGHT>`: block hash at given block height.
- `/blocktime`: UNIX time stamp of latest block.
6 changes: 6 additions & 0 deletions src/blocktime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ impl Blocktime {
}
}

pub(crate) fn unix_timestamp(self) -> i64 {
match self {
Self::Confirmed(timestamp) | Self::Expected(timestamp) => timestamp.timestamp(),
}
}

pub(crate) fn suffix(self) -> &'static str {
match self {
Self::Confirmed(_) => "",
Expand Down
18 changes: 11 additions & 7 deletions src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,14 +405,18 @@ impl Index {
.unwrap_or(0)
}

pub(crate) fn height(&self) -> Result<Option<Height>> {
self.begin_read()?.height()
}

pub(crate) fn block_count(&self) -> Result<u64> {
self.begin_read()?.block_count()
}

pub(crate) fn block_height(&self) -> Result<Option<Height>> {
self.begin_read()?.block_height()
}

pub(crate) fn block_hash(&self, height: Option<u64>) -> Result<Option<BlockHash>> {
self.begin_read()?.block_hash(height)
}

pub(crate) fn blocks(&self, take: usize) -> Result<Vec<(u64, BlockHash)>> {
let mut blocks = Vec::new();

Expand Down Expand Up @@ -1010,21 +1014,21 @@ mod tests {
{
let context = Context::builder().args(["--height-limit", "0"]).build();
context.mine_blocks(1);
assert_eq!(context.index.height().unwrap(), None);
assert_eq!(context.index.block_height().unwrap(), None);
assert_eq!(context.index.block_count().unwrap(), 0);
}

{
let context = Context::builder().args(["--height-limit", "1"]).build();
context.mine_blocks(1);
assert_eq!(context.index.height().unwrap(), Some(Height(0)));
assert_eq!(context.index.block_height().unwrap(), Some(Height(0)));
assert_eq!(context.index.block_count().unwrap(), 1);
}

{
let context = Context::builder().args(["--height-limit", "2"]).build();
context.mine_blocks(2);
assert_eq!(context.index.height().unwrap(), Some(Height(1)));
assert_eq!(context.index.block_height().unwrap(), Some(Height(1)));
assert_eq!(context.index.block_count().unwrap(), 2);
}
}
Expand Down
23 changes: 22 additions & 1 deletion src/index/rtx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use super::*;
pub(crate) struct Rtx<'a>(pub(crate) redb::ReadTransaction<'a>);
raphjaph marked this conversation as resolved.
Show resolved Hide resolved

impl Rtx<'_> {
pub(crate) fn height(&self) -> Result<Option<Height>> {
pub(crate) fn block_height(&self) -> Result<Option<Height>> {
Ok(
self
.0
Expand All @@ -27,4 +27,25 @@ impl Rtx<'_> {
.unwrap_or(0),
)
}

pub(crate) fn block_hash(&self, height: Option<u64>) -> Result<Option<BlockHash>> {
match height {
Some(height) => Ok(
self
.0
.open_table(HEIGHT_TO_BLOCK_HASH)?
.get(height)?
.map(|hash| BlockHash::load(*hash.value())),
),
None => Ok(
self
.0
.open_table(HEIGHT_TO_BLOCK_HASH)?
.range(0..)?
.rev()
.next()
.map(|(_height, hash)| BlockHash::load(*hash.value())),
),
}
}
}
96 changes: 91 additions & 5 deletions src/subcommand/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,12 @@ impl Server {

let router = Router::new()
.route("/", get(Self::home))
.route("/block-count", get(Self::block_count))
.route("/block/:query", get(Self::block))
.route("/blockcount", get(Self::block_count))
raphjaph marked this conversation as resolved.
Show resolved Hide resolved
.route("/blockheight", get(Self::block_height))
.route("/blockhash", get(Self::block_hash))
.route("/blockhash/:height", get(Self::block_hash_from_height))
.route("/blocktime", get(Self::block_time))
.route("/bounties", get(Self::bounties))
.route("/clock", get(Self::clock))
.route("/content/:inscription_id", get(Self::content))
Expand Down Expand Up @@ -352,7 +356,7 @@ impl Server {
}

fn index_height(index: &Index) -> ServerResult<Height> {
index.height()?.ok_or_not_found(|| "genesis block")
index.block_height()?.ok_or_not_found(|| "genesis block")
}

async fn clock(Extension(index): Extension<Arc<Index>>) -> ServerResult<Response> {
Expand Down Expand Up @@ -678,6 +682,35 @@ impl Server {
Ok(index.block_count()?.to_string())
}

async fn block_height(Extension(index): Extension<Arc<Index>>) -> ServerResult<String> {
let height = index.block_height()?.ok_or_not_found(|| "blockheight")?;
raphjaph marked this conversation as resolved.
Show resolved Hide resolved

Ok(height.to_string())
}

async fn block_hash(Extension(index): Extension<Arc<Index>>) -> ServerResult<String> {
let height = index.block_hash(None)?.ok_or_not_found(|| "blockheight")?;
raphjaph marked this conversation as resolved.
Show resolved Hide resolved

Ok(height.to_string())
raphjaph marked this conversation as resolved.
Show resolved Hide resolved
}

async fn block_hash_from_height(
Extension(index): Extension<Arc<Index>>,
Path(height): Path<u64>,
) -> ServerResult<String> {
let hash = index
.block_hash(Some(height))?
.ok_or_not_found(|| "blockhash")?;
raphjaph marked this conversation as resolved.
Show resolved Hide resolved

Ok(hash.to_string())
}

async fn block_time(Extension(index): Extension<Arc<Index>>) -> ServerResult<String> {
let height = index.block_height()?.ok_or_not_found(|| "blocktime")?;

raphjaph marked this conversation as resolved.
Show resolved Hide resolved
Ok(index.blocktime(height)?.unix_timestamp().to_string())
}

async fn input(
Extension(page_config): Extension<Arc<PageConfig>>,
Extension(index): Extension<Arc<Index>>,
Expand Down Expand Up @@ -749,7 +782,7 @@ impl Server {
);
headers.append(
header::CONTENT_SECURITY_POLICY,
HeaderValue::from_static("default-src *:*/content/ 'unsafe-eval' 'unsafe-inline' data:"),
HeaderValue::from_static("default-src *:*/content/ *:*/blockheight *:*/blockhash *:*/blocktime 'unsafe-eval' 'unsafe-inline' data:"),
raphjaph marked this conversation as resolved.
Show resolved Hide resolved
);
headers.insert(
header::CACHE_CONTROL,
Expand Down Expand Up @@ -1400,19 +1433,72 @@ mod tests {
fn block_count_endpoint() {
let test_server = TestServer::new();

let response = test_server.get("/block-count");
let response = test_server.get("/blockcount");

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(response.text().unwrap(), "1");

test_server.mine_blocks(1);

let response = test_server.get("/block-count");
let response = test_server.get("/blockcount");

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(response.text().unwrap(), "2");
}

#[test]
fn block_height_endpoint() {
let test_server = TestServer::new();

let response = test_server.get("/blockheight");

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(response.text().unwrap(), "0");

test_server.mine_blocks(2);

let response = test_server.get("/blockheight");

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(response.text().unwrap(), "2");
}

#[test]
fn block_hash_endpoint() {
let test_server = TestServer::new();

let response = test_server.get("/blockhash");

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(
response.text().unwrap(),
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
);
}

#[test]
fn block_hash_from_height_endpoint() {
let test_server = TestServer::new();

let response = test_server.get("/blockhash/0");

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(
response.text().unwrap(),
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
);
}

#[test]
fn block_time_endpoint() {
let test_server = TestServer::new();

let response = test_server.get("/blocktime");

assert_eq!(response.status(), StatusCode::OK);
assert_eq!(response.text().unwrap(), "1231006505");
}

#[test]
fn range_end_before_range_start_returns_400() {
TestServer::new().assert_response(
Expand Down
4 changes: 2 additions & 2 deletions tests/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ fn inscription_content() {
.collect::<Vec<&http::HeaderValue>>(),
&[
"default-src 'self' 'unsafe-eval' 'unsafe-inline' data:",
"default-src *:*/content/ 'unsafe-eval' 'unsafe-inline' data:"
"default-src *:*/content/ *:*/blockheight *:*/blockhash *:*/blocktime 'unsafe-eval' 'unsafe-inline' data:",
]
);
assert_eq!(response.bytes().unwrap(), "FOO");
Expand Down Expand Up @@ -346,7 +346,7 @@ fn server_runs_with_rpc_user_and_pass_as_env_vars() {

rpc_server.mine_blocks(1);

let response = reqwest::blocking::get(format!("http://127.0.0.1:{port}/block-count")).unwrap();
let response = reqwest::blocking::get(format!("http://127.0.0.1:{port}/blockcount")).unwrap();
assert_eq!(response.status(), StatusCode::OK);
assert_eq!(response.text().unwrap(), "2");

Expand Down
4 changes: 2 additions & 2 deletions tests/test_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl TestServer {
let chain_block_count = client.get_block_count().unwrap() + 1;

for i in 0.. {
let response = reqwest::blocking::get(self.url().join("/block-count").unwrap()).unwrap();
let response = reqwest::blocking::get(self.url().join("/blockcount").unwrap()).unwrap();
assert_eq!(response.status(), StatusCode::OK);
if response.text().unwrap().parse::<u64>().unwrap() == chain_block_count {
break;
Expand All @@ -84,7 +84,7 @@ impl TestServer {
let chain_block_count = client.get_block_count().unwrap() + 1;

for i in 0.. {
let response = reqwest::blocking::get(self.url().join("/block-count").unwrap()).unwrap();
let response = reqwest::blocking::get(self.url().join("/blockcount").unwrap()).unwrap();
assert_eq!(response.status(), StatusCode::OK);
if response.text().unwrap().parse::<u64>().unwrap() == chain_block_count {
break;
Expand Down