Sum tree and improved chain API Endpoints (#214)
* adding more useful handlers * added method to return last n leaf nodes inserted into the sum tree * endpoints in place for getting last n sumtree nodes
This commit is contained in:
committed by
Ignotus Peverell
parent
8b324f7429
commit
7f8d307cc8
+5
-28
@@ -20,38 +20,12 @@ use chain;
|
||||
use core::core::Transaction;
|
||||
use core::ser;
|
||||
use pool;
|
||||
use handlers::UtxoHandler;
|
||||
use handlers::{UtxoHandler, ChainHandler, SumTreeHandler};
|
||||
use rest::*;
|
||||
use types::*;
|
||||
use util;
|
||||
use util::LOGGER;
|
||||
|
||||
/// ApiEndpoint implementation for the blockchain. Exposes the current chain
|
||||
/// state as a simple JSON object.
|
||||
#[derive(Clone)]
|
||||
pub struct ChainApi {
|
||||
/// data store access
|
||||
chain: Arc<chain::Chain>,
|
||||
}
|
||||
|
||||
impl ApiEndpoint for ChainApi {
|
||||
type ID = String;
|
||||
type T = Tip;
|
||||
type OP_IN = ();
|
||||
type OP_OUT = ();
|
||||
|
||||
fn operations(&self) -> Vec<Operation> {
|
||||
vec![Operation::Get]
|
||||
}
|
||||
|
||||
fn get(&self, _: String) -> ApiResult<Tip> {
|
||||
match self.chain.head() {
|
||||
Ok(tip) => Ok(Tip::from_tip(tip)),
|
||||
Err(e) => Err(Error::Internal(format!("{:?}", e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ApiEndpoint implementation for the transaction pool, to check its status
|
||||
/// and size as well as push new transactions.
|
||||
#[derive(Clone)]
|
||||
@@ -132,14 +106,17 @@ pub fn start_rest_apis<T>(
|
||||
|
||||
thread::spawn(move || {
|
||||
let mut apis = ApiServer::new("/v1".to_string());
|
||||
apis.register_endpoint("/chain".to_string(), ChainApi {chain: chain.clone()});
|
||||
apis.register_endpoint("/pool".to_string(), PoolApi {tx_pool: tx_pool});
|
||||
|
||||
// register a nested router at "/v2" for flexibility
|
||||
// so we can experiment with raw iron handlers
|
||||
let utxo_handler = UtxoHandler {chain: chain.clone()};
|
||||
let chain_tip_handler = ChainHandler {chain: chain.clone()};
|
||||
let sumtree_handler = SumTreeHandler {chain: chain.clone()};
|
||||
let router = router!(
|
||||
chain_tip: get "/chain" => chain_tip_handler,
|
||||
chain_utxos: get "/chain/utxos" => utxo_handler,
|
||||
sumtree_roots: get "/sumtrees/*" => sumtree_handler,
|
||||
);
|
||||
apis.register_handler("/v2", router);
|
||||
|
||||
|
||||
@@ -85,3 +85,108 @@ impl Handler for UtxoHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sum tree handler
|
||||
|
||||
pub struct SumTreeHandler {
|
||||
pub chain: Arc<chain::Chain>,
|
||||
}
|
||||
|
||||
impl SumTreeHandler {
|
||||
//gets roots
|
||||
fn get_roots(&self) -> SumTrees {
|
||||
SumTrees::from_head(self.chain.clone())
|
||||
}
|
||||
|
||||
// gets last n utxos inserted in to the tree
|
||||
fn get_last_n_utxo(&self, distance:u64) -> Vec<SumTreeNode> {
|
||||
SumTreeNode::get_last_n_utxo(self.chain.clone(), distance)
|
||||
}
|
||||
|
||||
// gets last n utxos inserted in to the tree
|
||||
fn get_last_n_rangeproof(&self, distance:u64) -> Vec<SumTreeNode> {
|
||||
SumTreeNode::get_last_n_rangeproof(self.chain.clone(), distance)
|
||||
}
|
||||
|
||||
// gets last n utxos inserted in to the tree
|
||||
fn get_last_n_kernel(&self, distance:u64) -> Vec<SumTreeNode> {
|
||||
SumTreeNode::get_last_n_kernel(self.chain.clone(), distance)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// Retrieve the roots:
|
||||
// GET /v2/sumtrees/roots
|
||||
//
|
||||
// Last inserted nodes::
|
||||
// GET /v2/sumtrees/lastutxos (gets last 10)
|
||||
// GET /v2/sumtrees/lastutxos?n=5
|
||||
// GET /v2/sumtrees/lastrangeproofs
|
||||
// GET /v2/sumtrees/lastkernels
|
||||
//
|
||||
|
||||
impl Handler for SumTreeHandler {
|
||||
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();
|
||||
}
|
||||
//TODO: probably need to set a reasonable max limit here
|
||||
let mut last_n=10;
|
||||
if let Ok(params) = req.get_ref::<UrlEncodedQuery>() {
|
||||
if let Some(nums) = params.get("n") {
|
||||
for num in nums {
|
||||
if let Ok(n) = str::parse(num) {
|
||||
last_n=n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
match *path_elems.last().unwrap(){
|
||||
"roots" => match serde_json::to_string_pretty(&self.get_roots()) {
|
||||
Ok(json) => Ok(Response::with((status::Ok, json))),
|
||||
Err(_) => Ok(Response::with((status::BadRequest, ""))),
|
||||
},
|
||||
"lastutxos" => match serde_json::to_string_pretty(&self.get_last_n_utxo(last_n)) {
|
||||
Ok(json) => Ok(Response::with((status::Ok, json))),
|
||||
Err(_) => Ok(Response::with((status::BadRequest, ""))),
|
||||
},
|
||||
"lastrangeproofs" => match serde_json::to_string_pretty(&self.get_last_n_rangeproof(last_n)) {
|
||||
Ok(json) => Ok(Response::with((status::Ok, json))),
|
||||
Err(_) => Ok(Response::with((status::BadRequest, ""))),
|
||||
},
|
||||
"lastkernels" => match serde_json::to_string_pretty(&self.get_last_n_kernel(last_n)) {
|
||||
Ok(json) => Ok(Response::with((status::Ok, json))),
|
||||
Err(_) => Ok(Response::with((status::BadRequest, ""))),
|
||||
},_ => Ok(Response::with((status::BadRequest, "")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Chain Handler
|
||||
|
||||
pub struct ChainHandler {
|
||||
pub chain: Arc<chain::Chain>,
|
||||
}
|
||||
|
||||
impl ChainHandler {
|
||||
fn get_tip(&self) -> Tip {
|
||||
Tip::from_tip(self.chain.head().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Get the head details
|
||||
// GET /v2/chain
|
||||
//
|
||||
|
||||
impl Handler for ChainHandler {
|
||||
fn handle(&self, _req: &mut Request) -> IronResult<Response> {
|
||||
match serde_json::to_string_pretty(&self.get_tip()) {
|
||||
Ok(json) => Ok(Response::with((status::Ok, json))),
|
||||
Err(_) => Ok(Response::with((status::BadRequest, ""))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+132
-4
@@ -12,25 +12,117 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::sync::Arc;
|
||||
use core::{core, global};
|
||||
use core::core::hash::Hashed;
|
||||
use chain;
|
||||
use secp::pedersen;
|
||||
use rest::*;
|
||||
use util;
|
||||
|
||||
/// The state of the current fork tip
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct Tip {
|
||||
/// Height of the tip (max height of the fork)
|
||||
pub height: u64,
|
||||
// Last block pushed to the fork
|
||||
// pub last_block_h: Hash,
|
||||
pub last_block_pushed: String,
|
||||
// Block previous to last
|
||||
// pub prev_block_h: Hash,
|
||||
pub prev_block_to_last: String,
|
||||
// Total difficulty accumulated on that fork
|
||||
// pub total_difficulty: Difficulty,
|
||||
pub total_difficulty: u64,
|
||||
}
|
||||
|
||||
impl Tip {
|
||||
pub fn from_tip(tip: chain::Tip) -> Tip {
|
||||
Tip { height: tip.height }
|
||||
Tip {
|
||||
height: tip.height,
|
||||
last_block_pushed: util::to_hex(tip.last_block_h.to_vec()),
|
||||
prev_block_to_last: util::to_hex(tip.prev_block_h.to_vec()),
|
||||
total_difficulty: tip.total_difficulty.into_num(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sumtrees
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct SumTrees {
|
||||
/// UTXO Root Hash
|
||||
pub utxo_root_hash: String,
|
||||
// UTXO Root Sum
|
||||
pub utxo_root_sum: String,
|
||||
// Rangeproof root hash
|
||||
pub range_proof_root_hash: String,
|
||||
// Kernel set root hash
|
||||
pub kernel_root_hash: String,
|
||||
}
|
||||
|
||||
impl SumTrees {
|
||||
pub fn from_head(head: Arc<chain::Chain>) -> SumTrees {
|
||||
let roots=head.get_sumtree_roots();
|
||||
SumTrees {
|
||||
utxo_root_hash: util::to_hex(roots.0.hash.to_vec()),
|
||||
utxo_root_sum: util::to_hex(roots.0.sum.commit.0.to_vec()),
|
||||
range_proof_root_hash: util::to_hex(roots.1.hash.to_vec()),
|
||||
kernel_root_hash: util::to_hex(roots.2.hash.to_vec()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around a list of sumtree nodes, so it can be
|
||||
/// presented properly via json
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct SumTreeNode {
|
||||
// The hash
|
||||
pub hash: String,
|
||||
// Output (if included)
|
||||
pub output: Option<OutputPrintable>,
|
||||
}
|
||||
|
||||
impl SumTreeNode {
|
||||
|
||||
pub fn get_last_n_utxo(chain: Arc<chain::Chain>, distance:u64) -> Vec<SumTreeNode> {
|
||||
let mut return_vec = Vec::new();
|
||||
let last_n = chain.get_last_n_utxo(distance);
|
||||
for elem_output in last_n {
|
||||
let header = chain
|
||||
.get_block_header_by_output_commit(&elem_output.1.commit)
|
||||
.map_err(|_| Error::NotFound);
|
||||
// Need to call further method to check if output is spent
|
||||
let mut output = OutputPrintable::from_output(&elem_output.1, &header.unwrap());
|
||||
if let Ok(_) = chain.get_unspent(&elem_output.1.commit) {
|
||||
output.spent = false;
|
||||
}
|
||||
return_vec.push(SumTreeNode {
|
||||
hash: util::to_hex(elem_output.0.to_vec()),
|
||||
output: Some(output),
|
||||
});
|
||||
}
|
||||
return_vec
|
||||
}
|
||||
|
||||
pub fn get_last_n_rangeproof(head: Arc<chain::Chain>, distance:u64) -> Vec<SumTreeNode> {
|
||||
let mut return_vec = Vec::new();
|
||||
let last_n = head.get_last_n_rangeproof(distance);
|
||||
for elem in last_n {
|
||||
return_vec.push(SumTreeNode {
|
||||
hash: util::to_hex(elem.hash.to_vec()),
|
||||
output: None,
|
||||
});
|
||||
}
|
||||
return_vec
|
||||
}
|
||||
|
||||
pub fn get_last_n_kernel(head: Arc<chain::Chain>, distance:u64) -> Vec<SumTreeNode> {
|
||||
let mut return_vec = Vec::new();
|
||||
let last_n = head.get_last_n_kernel(distance);
|
||||
for elem in last_n {
|
||||
return_vec.push(SumTreeNode {
|
||||
hash: util::to_hex(elem.hash.to_vec()),
|
||||
output: None,
|
||||
});
|
||||
}
|
||||
return_vec
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +165,42 @@ impl Output {
|
||||
}
|
||||
}
|
||||
|
||||
//As above, except formatted a bit better for human viewing
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct OutputPrintable {
|
||||
/// The type of output Coinbase|Transaction
|
||||
pub output_type: OutputType,
|
||||
/// The homomorphic commitment representing the output's amount (as hex string)
|
||||
pub commit: String,
|
||||
/// The height of the block creating this output
|
||||
pub height: u64,
|
||||
/// The lock height (earliest block this output can be spent)
|
||||
pub lock_height: u64,
|
||||
/// Whether the output has been spent
|
||||
pub spent: bool,
|
||||
/// Rangeproof hash (as hex string)
|
||||
pub proof_hash: String,
|
||||
}
|
||||
|
||||
impl OutputPrintable {
|
||||
pub fn from_output(output: &core::Output, block_header: &core::BlockHeader) -> OutputPrintable {
|
||||
let (output_type, lock_height) = match output.features {
|
||||
x if x.contains(core::transaction::COINBASE_OUTPUT) => {
|
||||
(OutputType::Coinbase, block_header.height + global::coinbase_maturity())
|
||||
}
|
||||
_ => (OutputType::Transaction, 0),
|
||||
};
|
||||
OutputPrintable {
|
||||
output_type: output_type,
|
||||
commit: util::to_hex(output.commit.0.to_vec()),
|
||||
height: block_header.height,
|
||||
lock_height: lock_height,
|
||||
spent: true,
|
||||
proof_hash: util::to_hex(output.proof.hash().to_vec()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct PoolInfo {
|
||||
/// Size of the pool
|
||||
|
||||
Reference in New Issue
Block a user