mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 03:21:08 +03:00
Kernel lookup api method (#3000)
* Kernel lookup api * Min and max height parameters, scan backwards * Return null instead of 404 if kernel not found * Return TxKernel instead of TxKernelPrintable * Update description of KernelHandler
This commit is contained in:
parent
d36a0b29ef
commit
30156cdcf9
5 changed files with 173 additions and 3 deletions
|
@ -26,6 +26,7 @@ use self::blocks_api::HeaderHandler;
|
||||||
use self::chain_api::ChainCompactHandler;
|
use self::chain_api::ChainCompactHandler;
|
||||||
use self::chain_api::ChainHandler;
|
use self::chain_api::ChainHandler;
|
||||||
use self::chain_api::ChainValidationHandler;
|
use self::chain_api::ChainValidationHandler;
|
||||||
|
use self::chain_api::KernelHandler;
|
||||||
use self::chain_api::OutputHandler;
|
use self::chain_api::OutputHandler;
|
||||||
use self::peers_api::PeerHandler;
|
use self::peers_api::PeerHandler;
|
||||||
use self::peers_api::PeersAllHandler;
|
use self::peers_api::PeersAllHandler;
|
||||||
|
@ -119,7 +120,9 @@ pub fn build_router(
|
||||||
let output_handler = OutputHandler {
|
let output_handler = OutputHandler {
|
||||||
chain: Arc::downgrade(&chain),
|
chain: Arc::downgrade(&chain),
|
||||||
};
|
};
|
||||||
|
let kernel_handler = KernelHandler {
|
||||||
|
chain: Arc::downgrade(&chain),
|
||||||
|
};
|
||||||
let block_handler = BlockHandler {
|
let block_handler = BlockHandler {
|
||||||
chain: Arc::downgrade(&chain),
|
chain: Arc::downgrade(&chain),
|
||||||
};
|
};
|
||||||
|
@ -171,6 +174,7 @@ pub fn build_router(
|
||||||
router.add_route("/v1/headers/*", Arc::new(header_handler))?;
|
router.add_route("/v1/headers/*", Arc::new(header_handler))?;
|
||||||
router.add_route("/v1/chain", Arc::new(chain_tip_handler))?;
|
router.add_route("/v1/chain", Arc::new(chain_tip_handler))?;
|
||||||
router.add_route("/v1/chain/outputs/*", Arc::new(output_handler))?;
|
router.add_route("/v1/chain/outputs/*", Arc::new(output_handler))?;
|
||||||
|
router.add_route("/v1/chain/kernels/*", Arc::new(kernel_handler))?;
|
||||||
router.add_route("/v1/chain/compact", Arc::new(chain_compact_handler))?;
|
router.add_route("/v1/chain/compact", Arc::new(chain_compact_handler))?;
|
||||||
router.add_route("/v1/chain/validate", Arc::new(chain_validation_handler))?;
|
router.add_route("/v1/chain/validate", Arc::new(chain_validation_handler))?;
|
||||||
router.add_route("/v1/txhashset/*", Arc::new(txhashset_handler))?;
|
router.add_route("/v1/txhashset/*", Arc::new(txhashset_handler))?;
|
||||||
|
|
|
@ -197,3 +197,72 @@ impl Handler for OutputHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Kernel handler, search for a kernel by excess commitment
|
||||||
|
/// GET /v1/chain/kernels/XXX?min_height=YYY&max_height=ZZZ
|
||||||
|
/// The `min_height` and `max_height` parameters are optional
|
||||||
|
pub struct KernelHandler {
|
||||||
|
pub chain: Weak<chain::Chain>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KernelHandler {
|
||||||
|
fn get_kernel(&self, req: Request<Body>) -> Result<Option<LocatedTxKernel>, Error> {
|
||||||
|
let excess = req
|
||||||
|
.uri()
|
||||||
|
.path()
|
||||||
|
.trim_end_matches('/')
|
||||||
|
.rsplit('/')
|
||||||
|
.next()
|
||||||
|
.ok_or(ErrorKind::RequestError("missing excess".into()))?;
|
||||||
|
let excess = util::from_hex(excess.to_owned())
|
||||||
|
.map_err(|_| ErrorKind::RequestError("invalid excess hex".into()))?;
|
||||||
|
if excess.len() != 33 {
|
||||||
|
return Err(ErrorKind::RequestError("invalid excess length".into()).into());
|
||||||
|
}
|
||||||
|
let excess = Commitment::from_vec(excess);
|
||||||
|
|
||||||
|
let chain = w(&self.chain)?;
|
||||||
|
|
||||||
|
let mut min_height: Option<u64> = None;
|
||||||
|
let mut max_height: Option<u64> = None;
|
||||||
|
|
||||||
|
// Check query parameters for minimum and maximum search height
|
||||||
|
if let Some(q) = req.uri().query() {
|
||||||
|
let params = QueryParams::from(q);
|
||||||
|
if let Some(h) = params.get("min_height") {
|
||||||
|
let h = h
|
||||||
|
.parse()
|
||||||
|
.map_err(|_| ErrorKind::RequestError("invalid minimum height".into()))?;
|
||||||
|
// Default is genesis
|
||||||
|
min_height = if h == 0 { None } else { Some(h) };
|
||||||
|
}
|
||||||
|
if let Some(h) = params.get("max_height") {
|
||||||
|
let h = h
|
||||||
|
.parse()
|
||||||
|
.map_err(|_| ErrorKind::RequestError("invalid maximum height".into()))?;
|
||||||
|
// Default is current head
|
||||||
|
let head_height = chain
|
||||||
|
.head()
|
||||||
|
.map_err(|e| ErrorKind::Internal(format!("{}", e)))?
|
||||||
|
.height;
|
||||||
|
max_height = if h >= head_height { None } else { Some(h) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let kernel = chain
|
||||||
|
.get_kernel_height(&excess, min_height, max_height)
|
||||||
|
.map_err(|e| ErrorKind::Internal(format!("{}", e)))?
|
||||||
|
.map(|(tx_kernel, height, mmr_index)| LocatedTxKernel {
|
||||||
|
tx_kernel,
|
||||||
|
height,
|
||||||
|
mmr_index,
|
||||||
|
});
|
||||||
|
Ok(kernel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler for KernelHandler {
|
||||||
|
fn get(&self, req: Request<Body>) -> ResponseFuture {
|
||||||
|
result_to_response(self.get_kernel(req))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ use std::sync::Arc;
|
||||||
use crate::chain;
|
use crate::chain;
|
||||||
use crate::core::core::hash::Hashed;
|
use crate::core::core::hash::Hashed;
|
||||||
use crate::core::core::merkle_proof::MerkleProof;
|
use crate::core::core::merkle_proof::MerkleProof;
|
||||||
use crate::core::core::KernelFeatures;
|
use crate::core::core::{KernelFeatures, TxKernel};
|
||||||
use crate::core::{core, ser};
|
use crate::core::{core, ser};
|
||||||
use crate::p2p;
|
use crate::p2p;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
@ -699,6 +699,13 @@ pub struct OutputListing {
|
||||||
pub outputs: Vec<OutputPrintable>,
|
pub outputs: Vec<OutputPrintable>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct LocatedTxKernel {
|
||||||
|
pub tx_kernel: TxKernel,
|
||||||
|
pub height: u64,
|
||||||
|
pub mmr_index: u64,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct PoolInfo {
|
pub struct PoolInfo {
|
||||||
/// Size of the pool
|
/// Size of the pool
|
||||||
|
|
|
@ -19,7 +19,8 @@ use crate::core::core::hash::{Hash, Hashed, ZERO_HASH};
|
||||||
use crate::core::core::merkle_proof::MerkleProof;
|
use crate::core::core::merkle_proof::MerkleProof;
|
||||||
use crate::core::core::verifier_cache::VerifierCache;
|
use crate::core::core::verifier_cache::VerifierCache;
|
||||||
use crate::core::core::{
|
use crate::core::core::{
|
||||||
Block, BlockHeader, BlockSums, Committed, Output, OutputIdentifier, Transaction, TxKernelEntry,
|
Block, BlockHeader, BlockSums, Committed, Output, OutputIdentifier, Transaction, TxKernel,
|
||||||
|
TxKernelEntry,
|
||||||
};
|
};
|
||||||
use crate::core::global;
|
use crate::core::global;
|
||||||
use crate::core::pow;
|
use crate::core::pow;
|
||||||
|
@ -1281,6 +1282,72 @@ impl Chain {
|
||||||
Ok(txhashset.get_header_by_height(output_pos.height)?)
|
Ok(txhashset.get_header_by_height(output_pos.height)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the kernel with a given excess and the block height it is included in.
|
||||||
|
pub fn get_kernel_height(
|
||||||
|
&self,
|
||||||
|
excess: &Commitment,
|
||||||
|
min_height: Option<u64>,
|
||||||
|
max_height: Option<u64>,
|
||||||
|
) -> Result<Option<(TxKernel, u64, u64)>, Error> {
|
||||||
|
let min_index = match min_height {
|
||||||
|
Some(h) => Some(self.get_header_by_height(h - 1)?.kernel_mmr_size + 1),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let max_index = match max_height {
|
||||||
|
Some(h) => Some(self.get_header_by_height(h)?.kernel_mmr_size),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (kernel, mmr_index) = match self
|
||||||
|
.txhashset
|
||||||
|
.read()
|
||||||
|
.find_kernel(&excess, min_index, max_index)
|
||||||
|
{
|
||||||
|
Some(k) => k,
|
||||||
|
None => return Ok(None),
|
||||||
|
};
|
||||||
|
|
||||||
|
let header = self.get_header_for_kernel_index(mmr_index, min_height, max_height)?;
|
||||||
|
|
||||||
|
Ok(Some((kernel, header.height, mmr_index)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the block header in which a given kernel mmr index appears in the txhashset.
|
||||||
|
pub fn get_header_for_kernel_index(
|
||||||
|
&self,
|
||||||
|
kernel_mmr_index: u64,
|
||||||
|
min_height: Option<u64>,
|
||||||
|
max_height: Option<u64>,
|
||||||
|
) -> Result<BlockHeader, Error> {
|
||||||
|
let txhashset = self.txhashset.read();
|
||||||
|
|
||||||
|
let mut min = min_height.unwrap_or(0).saturating_sub(1);
|
||||||
|
let mut max = match max_height {
|
||||||
|
Some(h) => h,
|
||||||
|
None => self.head()?.height,
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let search_height = max - (max - min) / 2;
|
||||||
|
let h = txhashset.get_header_by_height(search_height)?;
|
||||||
|
if search_height == 0 {
|
||||||
|
return Ok(h);
|
||||||
|
}
|
||||||
|
let h_prev = txhashset.get_header_by_height(search_height - 1)?;
|
||||||
|
if kernel_mmr_index > h.kernel_mmr_size {
|
||||||
|
min = search_height;
|
||||||
|
} else if kernel_mmr_index < h_prev.kernel_mmr_size {
|
||||||
|
max = search_height;
|
||||||
|
} else {
|
||||||
|
if kernel_mmr_index == h_prev.kernel_mmr_size {
|
||||||
|
return Ok(h_prev);
|
||||||
|
}
|
||||||
|
return Ok(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Verifies the given block header is actually on the current chain.
|
/// Verifies the given block header is actually on the current chain.
|
||||||
/// Checks the header_by_height index to verify the header is where we say
|
/// Checks the header_by_height index to verify the header is where we say
|
||||||
/// it is
|
/// it is
|
||||||
|
|
|
@ -269,6 +269,29 @@ impl TxHashSet {
|
||||||
.elements_from_insertion_index(start_index, max_count)
|
.elements_from_insertion_index(start_index, max_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find a kernel with a given excess. Work backwards from `max_index` to `min_index`
|
||||||
|
pub fn find_kernel(
|
||||||
|
&self,
|
||||||
|
excess: &Commitment,
|
||||||
|
min_index: Option<u64>,
|
||||||
|
max_index: Option<u64>,
|
||||||
|
) -> Option<(TxKernel, u64)> {
|
||||||
|
let min_index = min_index.unwrap_or(1);
|
||||||
|
let max_index = max_index.unwrap_or(self.kernel_pmmr_h.last_pos);
|
||||||
|
|
||||||
|
let pmmr = ReadonlyPMMR::at(&self.kernel_pmmr_h.backend, self.kernel_pmmr_h.last_pos);
|
||||||
|
let mut index = max_index + 1;
|
||||||
|
while index > min_index {
|
||||||
|
index -= 1;
|
||||||
|
if let Some(t) = pmmr.get_data(index) {
|
||||||
|
if &t.kernel.excess == excess {
|
||||||
|
return Some((t.kernel, index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Get MMR roots.
|
/// Get MMR roots.
|
||||||
pub fn roots(&self) -> TxHashSetRoots {
|
pub fn roots(&self) -> TxHashSetRoots {
|
||||||
let header_pmmr =
|
let header_pmmr =
|
||||||
|
|
Loading…
Reference in a new issue