mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-08 12:21:09 +03:00
API endpoints to browse blocks (#416)
* Implement /block api endpoint displaying basic information * Add block inputs and kernels to the api output. * Add fields to BlockHeaderInfo and TxKernelPrintable * Add features debug string to TxKernelPrintable. * Return 400 and 404 statuses from the blocks api endpoint. * For the blocks api, return a 404 if a block is not found at the requested height * Add back hash to BlockHeader api output.
This commit is contained in:
parent
f5d24c5a9c
commit
72fdceb0d6
4 changed files with 160 additions and 16 deletions
|
@ -13,8 +13,10 @@ grin_util = { path = "../util" }
|
||||||
grin_p2p = { path = "../p2p" }
|
grin_p2p = { path = "../p2p" }
|
||||||
hyper = "~0.10.6"
|
hyper = "~0.10.6"
|
||||||
slog = { version = "^2.0.12", features = ["max_level_trace", "release_max_level_trace"] }
|
slog = { version = "^2.0.12", features = ["max_level_trace", "release_max_level_trace"] }
|
||||||
|
lazy_static = "1.0"
|
||||||
iron = "~0.5.1"
|
iron = "~0.5.1"
|
||||||
router = "~0.5.1"
|
router = "~0.5.1"
|
||||||
|
regex = "0.2"
|
||||||
mount = "~0.3.0"
|
mount = "~0.3.0"
|
||||||
urlencoded = "~0.5.0"
|
urlencoded = "~0.5.0"
|
||||||
serde = "~1.0.8"
|
serde = "~1.0.8"
|
||||||
|
|
|
@ -25,10 +25,12 @@ use serde_json;
|
||||||
|
|
||||||
use chain;
|
use chain;
|
||||||
use core::core::Transaction;
|
use core::core::Transaction;
|
||||||
|
use core::core::hash::Hash;
|
||||||
use core::core::hash::Hashed;
|
use core::core::hash::Hashed;
|
||||||
use core::ser;
|
use core::ser;
|
||||||
use pool;
|
use pool;
|
||||||
use p2p;
|
use p2p;
|
||||||
|
use regex::Regex;
|
||||||
use rest::*;
|
use rest::*;
|
||||||
use util::secp::pedersen::Commitment;
|
use util::secp::pedersen::Commitment;
|
||||||
use types::*;
|
use types::*;
|
||||||
|
@ -273,6 +275,53 @@ impl Handler for ChainHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gets block details given either a hex address or height.
|
||||||
|
// GET /v1/block/<address>
|
||||||
|
// GET /v1/block/<height>
|
||||||
|
pub struct BlockHandler {
|
||||||
|
pub chain: Arc<chain::Chain>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockHandler {
|
||||||
|
fn get_block(&self, h: &Hash) -> Result<BlockPrintable, Error> {
|
||||||
|
let block = self.chain.clone().get_block(h).map_err(|_| Error::NotFound)?;
|
||||||
|
Ok(BlockPrintable::from_block(&block))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to decode the string as a height or a hash address.
|
||||||
|
fn parse_input(&self, input: String) -> Result<Hash, Error> {
|
||||||
|
if let Ok(height) = input.parse() {
|
||||||
|
match self.chain.clone().get_header_by_height(height) {
|
||||||
|
Ok(header) => return Ok(header.hash()),
|
||||||
|
Err(_) => return Err(Error::NotFound),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lazy_static! {
|
||||||
|
static ref RE: Regex = Regex::new(r"[0-9a-fA-F]{64}").unwrap();
|
||||||
|
}
|
||||||
|
if !RE.is_match(&input) {
|
||||||
|
return Err(Error::Argument(
|
||||||
|
String::from("Not a valid hex address or height.")))
|
||||||
|
}
|
||||||
|
let vec = util::from_hex(input).unwrap();
|
||||||
|
Ok(Hash::from_vec(vec))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler for BlockHandler {
|
||||||
|
fn handle(&self, req: &mut Request) -> IronResult<Response> {
|
||||||
|
let url = req.url.clone();
|
||||||
|
let mut path_elems = url.path();
|
||||||
|
if *path_elems.last().unwrap() == "" {
|
||||||
|
path_elems.pop();
|
||||||
|
}
|
||||||
|
let el = *path_elems.last().unwrap();
|
||||||
|
let h = try!(self.parse_input(el.to_string()));
|
||||||
|
let b = try!(self.get_block(&h));
|
||||||
|
json_response(&b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get basic information about the transaction pool.
|
// Get basic information about the transaction pool.
|
||||||
struct PoolInfoHandler<T> {
|
struct PoolInfoHandler<T> {
|
||||||
tx_pool: Arc<RwLock<pool::TransactionPool<T>>>,
|
tx_pool: Arc<RwLock<pool::TransactionPool<T>>>,
|
||||||
|
@ -382,6 +431,9 @@ pub fn start_rest_apis<T>(
|
||||||
let utxo_handler = UtxoHandler {
|
let utxo_handler = UtxoHandler {
|
||||||
chain: chain.clone(),
|
chain: chain.clone(),
|
||||||
};
|
};
|
||||||
|
let block_handler = BlockHandler {
|
||||||
|
chain: chain.clone(),
|
||||||
|
};
|
||||||
let chain_tip_handler = ChainHandler {
|
let chain_tip_handler = ChainHandler {
|
||||||
chain: chain.clone(),
|
chain: chain.clone(),
|
||||||
};
|
};
|
||||||
|
@ -403,6 +455,7 @@ pub fn start_rest_apis<T>(
|
||||||
|
|
||||||
let route_list = vec!(
|
let route_list = vec!(
|
||||||
"get /".to_string(),
|
"get /".to_string(),
|
||||||
|
"get /blocks".to_string(),
|
||||||
"get /chain".to_string(),
|
"get /chain".to_string(),
|
||||||
"get /chain/utxos".to_string(),
|
"get /chain/utxos".to_string(),
|
||||||
"get /sumtrees/roots".to_string(),
|
"get /sumtrees/roots".to_string(),
|
||||||
|
@ -417,6 +470,7 @@ pub fn start_rest_apis<T>(
|
||||||
let index_handler = IndexHandler { list: route_list };
|
let index_handler = IndexHandler { list: route_list };
|
||||||
let router = router!(
|
let router = router!(
|
||||||
index: get "/" => index_handler,
|
index: get "/" => index_handler,
|
||||||
|
blocks: get "/blocks/*" => block_handler,
|
||||||
chain_tip: get "/chain" => chain_tip_handler,
|
chain_tip: get "/chain" => chain_tip_handler,
|
||||||
chain_utxos: get "/chain/utxos/*" => utxo_handler,
|
chain_utxos: get "/chain/utxos/*" => utxo_handler,
|
||||||
sumtree_roots: get "/sumtrees/*" => sumtree_handler,
|
sumtree_roots: get "/sumtrees/*" => sumtree_handler,
|
||||||
|
|
|
@ -20,8 +20,11 @@ extern crate grin_store as store;
|
||||||
extern crate grin_util as util;
|
extern crate grin_util as util;
|
||||||
|
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
extern crate iron;
|
extern crate iron;
|
||||||
extern crate mount;
|
extern crate mount;
|
||||||
|
extern crate regex;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate router;
|
extern crate router;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
|
|
113
api/src/types.rs
113
api/src/types.rs
|
@ -15,6 +15,7 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use core::{core, global};
|
use core::{core, global};
|
||||||
use core::core::hash::Hashed;
|
use core::core::hash::Hashed;
|
||||||
|
use core::core::target::Difficulty;
|
||||||
use chain;
|
use chain;
|
||||||
use util::secp::pedersen;
|
use util::secp::pedersen;
|
||||||
use rest::*;
|
use rest::*;
|
||||||
|
@ -239,26 +240,110 @@ impl OutputSwitch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Just the information required for wallet reconstruction
|
|
||||||
|
// Printable representation of a block
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct BlockHeaderInfo {
|
pub struct TxKernelPrintable {
|
||||||
/// Hash
|
pub features: String,
|
||||||
pub hash: String,
|
pub fee: u64,
|
||||||
/// Previous block hash
|
pub lock_height: u64,
|
||||||
pub previous: String,
|
pub excess: String,
|
||||||
/// Height
|
pub excess_sig: String,
|
||||||
pub height: u64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockHeaderInfo {
|
impl TxKernelPrintable {
|
||||||
pub fn from_header(block_header: &core::BlockHeader) -> BlockHeaderInfo{
|
pub fn from_txkernel(k: &core::TxKernel) -> TxKernelPrintable {
|
||||||
BlockHeaderInfo {
|
TxKernelPrintable {
|
||||||
hash: util::to_hex(block_header.hash().to_vec()),
|
features: format!("{:?}", k.features),
|
||||||
previous: util::to_hex(block_header.previous.to_vec()),
|
fee: k.fee,
|
||||||
height: block_header.height,
|
lock_height: k.lock_height,
|
||||||
|
excess: util::to_hex(k.excess.0.to_vec()),
|
||||||
|
excess_sig: util::to_hex(k.excess_sig.to_vec())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Just the information required for wallet reconstruction
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BlockHeaderInfo {
|
||||||
|
// Hash
|
||||||
|
pub hash: String,
|
||||||
|
/// Version of the block
|
||||||
|
pub version: u16,
|
||||||
|
/// Height of this block since the genesis block (height 0)
|
||||||
|
pub height: u64,
|
||||||
|
/// Hash of the block previous to this in the chain.
|
||||||
|
pub previous: String,
|
||||||
|
/// rfc3339 timestamp at which the block was built.
|
||||||
|
pub timestamp: String,
|
||||||
|
/// Merklish root of all the commitments in the UTXO set
|
||||||
|
pub utxo_root: String,
|
||||||
|
/// Merklish root of all range proofs in the UTXO set
|
||||||
|
pub range_proof_root: String,
|
||||||
|
/// Merklish root of all transaction kernels in the UTXO set
|
||||||
|
pub kernel_root: String,
|
||||||
|
/// Nonce increment used to mine this block.
|
||||||
|
pub nonce: u64,
|
||||||
|
/// Difficulty used to mine the block.
|
||||||
|
pub difficulty: Difficulty,
|
||||||
|
/// Total accumulated difficulty since genesis block
|
||||||
|
pub total_difficulty: Difficulty,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockHeaderInfo {
|
||||||
|
pub fn from_header(h: &core::BlockHeader) -> BlockHeaderInfo {
|
||||||
|
BlockHeaderInfo {
|
||||||
|
hash: util::to_hex(h.hash().to_vec()),
|
||||||
|
version: h.version,
|
||||||
|
height: h.height,
|
||||||
|
previous: util::to_hex(h.previous.to_vec()),
|
||||||
|
timestamp: h.timestamp.rfc3339().to_string(),
|
||||||
|
utxo_root: util::to_hex(h.utxo_root.to_vec()),
|
||||||
|
range_proof_root: util::to_hex(h.range_proof_root.to_vec()),
|
||||||
|
kernel_root: util::to_hex(h.kernel_root.to_vec()),
|
||||||
|
nonce: h.nonce,
|
||||||
|
difficulty: h.difficulty.clone(),
|
||||||
|
total_difficulty: h.total_difficulty.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printable representation of a block
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct BlockPrintable {
|
||||||
|
/// The block header
|
||||||
|
pub header: BlockHeaderInfo,
|
||||||
|
// Input transactions
|
||||||
|
pub inputs: Vec<String>,
|
||||||
|
/// A printable version of the outputs
|
||||||
|
pub outputs: Vec<OutputPrintable>,
|
||||||
|
/// A printable version of the transaction kernels
|
||||||
|
pub kernels: Vec<TxKernelPrintable>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockPrintable {
|
||||||
|
pub fn from_block(block: &core::Block) -> BlockPrintable {
|
||||||
|
let inputs = block.inputs
|
||||||
|
.iter()
|
||||||
|
.map(|input| util::to_hex((input.0).0.to_vec()))
|
||||||
|
.collect();
|
||||||
|
let outputs = block.outputs
|
||||||
|
.iter()
|
||||||
|
.map(|output| OutputPrintable::from_output(output, &block.header, true))
|
||||||
|
.collect();
|
||||||
|
let kernels = block.kernels
|
||||||
|
.iter()
|
||||||
|
.map(|kernel| TxKernelPrintable::from_txkernel(kernel))
|
||||||
|
.collect();
|
||||||
|
BlockPrintable {
|
||||||
|
header: BlockHeaderInfo::from_header(&block.header),
|
||||||
|
inputs: inputs,
|
||||||
|
outputs: outputs,
|
||||||
|
kernels: kernels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// For wallet reconstruction, include the header info along with the
|
// For wallet reconstruction, include the header info along with the
|
||||||
// transactions in the block
|
// transactions in the block
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
|
Loading…
Reference in a new issue