use super::*;
use node::database::rocksdb::MD_HASH_KEY;
use node::database::{Metadata, into_array};
pub async fn block_by_height(
ctx: &Context<'_>,
height: f64,
) -> OptResult<Block> {
let (db, _) = ctx.data::<DBContext>()?;
let block_hash = db.read().await.view(|t| {
if height >= 0f64 {
t.block_hash_by_height(height as u64)
} else {
Ok(t.op_read(MD_HASH_KEY)?.map(|hash| into_array(&hash[..])))
}
})?;
if let Some(hash) = block_hash {
return block_by_hash(ctx, hex::encode(hash)).await;
};
Ok(None)
}
pub async fn last_block(ctx: &Context<'_>) -> FieldResult<Block> {
let (db, _) = ctx.data::<DBContext>()?;
let block = db.read().await.view(|t| {
let hash = t.op_read(MD_HASH_KEY)?;
match hash {
None => Ok(None),
Some(hash) => t.light_block(&hash),
}
})?;
block
.map(Block::from)
.ok_or_else(|| FieldError::new("Cannot find last block"))
}
pub async fn block_by_hash(
ctx: &Context<'_>,
hash: String,
) -> OptResult<Block> {
let (db, _) = ctx.data::<DBContext>()?;
let hash = hex::decode(hash)?;
let header = db.read().await.view(|t| t.light_block(&hash))?;
Ok(header.map(Block::from))
}
pub async fn last_blocks(
ctx: &Context<'_>,
count: u64,
) -> FieldResult<Vec<Block>> {
if count < 1 {
return Err(FieldError::new("count must be positive"));
}
if count > MAX_GRAPHQL_BLOCKS_PER_QUERY {
return Err(FieldError::new(format!(
"count too large (max {MAX_GRAPHQL_BLOCKS_PER_QUERY})"
)));
}
let (db, _) = ctx.data::<DBContext>()?;
let last_block = last_block(ctx).await?;
let mut hash_to_search = last_block.header().prev_block_hash;
let blocks = db.read().await.view(|t| {
let mut blocks = vec![last_block];
let mut count = count - 1;
while count > 0 {
match t.light_block(&hash_to_search)? {
None => break,
Some(h) => {
hash_to_search = h.header.prev_block_hash;
blocks.push(Block::from(h));
count -= 1;
}
}
}
Ok::<_, anyhow::Error>(blocks)
})?;
Ok(blocks)
}
pub async fn blocks_range(
ctx: &Context<'_>,
from: u64,
to: u64,
) -> FieldResult<Vec<Block>> {
let span = to
.checked_sub(from)
.and_then(|diff| diff.checked_add(1))
.ok_or_else(|| FieldError::new("Invalid range"))?;
if span > MAX_GRAPHQL_BLOCKS_PER_QUERY {
return Err(FieldError::new(format!(
"range too large (max {MAX_GRAPHQL_BLOCKS_PER_QUERY} blocks)"
)));
}
let (db, _) = ctx.data::<DBContext>()?;
let mut blocks = db.read().await.view(|t| {
let mut blocks = vec![];
let mut hash_to_search = None;
for height in (from..=to).rev() {
if hash_to_search.is_none() {
hash_to_search = t.block_hash_by_height(height)?;
}
if let Some(hash) = hash_to_search {
let h = t.light_block(&hash)?.expect("Block to be found");
hash_to_search = h.header.prev_block_hash.into();
blocks.push(Block::from(h))
}
}
Ok::<_, anyhow::Error>(blocks)
})?;
blocks.reverse();
Ok(blocks)
}
pub(super) async fn check_block(
ctx: &Context<'_>,
block_height: u64,
hex_block_hash: String,
) -> FieldResult<bool> {
let (db, _) = ctx.data::<DBContext>()?;
let block_hash = hex::decode(hex_block_hash)?;
let block = db.read().await.view(|t| {
t.block_hash_by_height(block_height).map(|hash| {
if let Some(hash) = hash {
hash == block_hash[..]
} else {
false
}
})
})?;
Ok(block)
}