-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add block-info command to neptune-cli
This command makes it easy to query and view block details at a glance. Supporting changes: * impl Display for BlockInfo * impl Display for BlockSelector * impl FromStr for BlockSelector * add BlockSelectorParseError * add dep on thiserror * moved BlockInfo and BlockSelector into their own files
- Loading branch information
Showing
7 changed files
with
213 additions
and
72 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
//! BlockInfo is a concise summary of a block intended for human | ||
//! consumption/reporting in block explorers, cli, dashboard, etc. | ||
|
||
use super::block_header::TARGET_DIFFICULTY_U32_SIZE; | ||
use crate::models::blockchain::block::block_height::BlockHeight; | ||
use crate::models::blockchain::block::Block; | ||
use crate::models::blockchain::type_scripts::neptune_coins::NeptuneCoins; | ||
use crate::models::consensus::timestamp::Timestamp; | ||
use crate::prelude::twenty_first; | ||
use serde::{Deserialize, Serialize}; | ||
use twenty_first::math::digest::Digest; | ||
use twenty_first::prelude::U32s; | ||
|
||
/// Provides summary information about a Block | ||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] | ||
pub struct BlockInfo { | ||
pub height: BlockHeight, | ||
pub digest: Digest, | ||
pub timestamp: Timestamp, | ||
pub difficulty: U32s<TARGET_DIFFICULTY_U32_SIZE>, | ||
pub num_inputs: usize, | ||
pub num_outputs: usize, | ||
pub num_uncle_blocks: usize, | ||
pub mining_reward: NeptuneCoins, | ||
pub fee: NeptuneCoins, | ||
pub is_genesis: bool, | ||
pub is_tip: bool, | ||
} | ||
|
||
// note: this is used by neptune-cli block-info command. | ||
impl std::fmt::Display for BlockInfo { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
let buf = String::new() | ||
+ &format!("height: {}\n", self.height) | ||
+ &format!("digest: {}\n", self.digest.to_hex()) | ||
+ &format!("timestamp: {}\n", self.timestamp.standard_format()) | ||
+ &format!("difficulty: {}\n", self.difficulty) | ||
+ &format!("num_inputs: {}\n", self.num_inputs) | ||
+ &format!("num_outputs: {}\n", self.num_outputs) | ||
+ &format!("num_uncle_blocks: {}\n", self.num_uncle_blocks) | ||
+ &format!("mining_reward: {}\n", self.mining_reward) | ||
+ &format!("fee: {}\n", self.fee) | ||
+ &format!("is_genesis: {}\n", self.is_genesis) | ||
+ &format!("is_tip: {}\n", self.is_tip); | ||
|
||
write!(f, "{}", buf) | ||
} | ||
} | ||
|
||
impl BlockInfo { | ||
pub fn from_block_and_digests( | ||
block: &Block, | ||
genesis_digest: Digest, | ||
tip_digest: Digest, | ||
) -> Self { | ||
let body = block.body(); | ||
let header = block.header(); | ||
let digest = block.hash(); | ||
Self { | ||
digest, | ||
height: header.height, | ||
timestamp: header.timestamp, | ||
difficulty: header.difficulty, | ||
num_inputs: body.transaction.kernel.inputs.len(), | ||
num_outputs: body.transaction.kernel.outputs.len(), | ||
num_uncle_blocks: body.uncle_blocks.len(), | ||
fee: body.transaction.kernel.fee, | ||
mining_reward: crate::Block::get_mining_reward(header.height), | ||
is_genesis: digest == genesis_digest, | ||
is_tip: digest == tip_digest, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
//! BlockSelector is a helper for querying blocks. | ||
//! | ||
//! The idea is to instantiate a BlockSelector using any of the following as | ||
//! identifier: | ||
//! * A Digest | ||
//! * A BlockHeight | ||
//! * Genesis | ||
//! * Tip | ||
//! | ||
//! Then call BlockSelector::to_digest() to obtain the block's Digest, if it | ||
//! exists. | ||
//! | ||
//! Public API's such as RPCs should accept a BlockSelector rather than a Digest | ||
//! or Height. | ||
|
||
use super::block_height::BlockHeight; | ||
use crate::models::state::GlobalState; | ||
use crate::twenty_first::error::FromHexDigestError; | ||
use crate::twenty_first::math::digest::Digest; | ||
use serde::{Deserialize, Serialize}; | ||
use std::num::ParseIntError; | ||
use std::str::FromStr; | ||
use thiserror::Error; | ||
|
||
/// Provides alternatives for looking up a block. | ||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] | ||
pub enum BlockSelector { | ||
Digest(Digest), // Identifies block by Digest (hash) | ||
Height(BlockHeight), // Identifies block by Height (count from genesis) | ||
Genesis, // Indicates the genesis block | ||
Tip, // Indicates the latest canonical block | ||
} | ||
|
||
/// BlockSelector can be written out as any of: | ||
/// genesis | ||
/// tip | ||
/// height/<N> | ||
/// digest/<hex> | ||
/// | ||
/// This is intended to be easy for humans to read and also input, ie suitable | ||
/// for use as CLI argument. | ||
impl std::fmt::Display for BlockSelector { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
Self::Digest(d) => write!(f, "digest/{}", d), | ||
Self::Height(h) => write!(f, "height/{}", h), | ||
Self::Genesis => write!(f, "genesis"), | ||
Self::Tip => write!(f, "tip"), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, Error)] | ||
pub enum BlockSelectorParseError { | ||
#[error("Invalid selector {0}. Try genesis or tip")] | ||
InvalidSelector(String), | ||
|
||
#[error("Invalid pair selector {0}. Try height/<N> or digest/<hex>")] | ||
InvalidPairSelector(String), | ||
|
||
#[error("Wrong selector length {0}. (too many or too few '/')")] | ||
WrongSelectorLength(usize), | ||
|
||
#[error("Bad Digest")] | ||
BadDigest(#[from] FromHexDigestError), | ||
|
||
#[error("Bad Height")] | ||
BadHeight(#[from] ParseIntError), | ||
} | ||
|
||
impl FromStr for BlockSelector { | ||
type Err = BlockSelectorParseError; | ||
|
||
// note: this parses the output of impl Display for BlockSelector | ||
// note: this is used by clap parser in neptune-cli for block-info command | ||
// and probably future commands as well. | ||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
let parts: Vec<&str> = s.split('/').collect(); | ||
if parts.len() == 1 { | ||
match parts[0] { | ||
"genesis" => Ok(Self::Genesis), | ||
"tip" => Ok(Self::Tip), | ||
other => Err(BlockSelectorParseError::InvalidSelector(other.to_string())), | ||
} | ||
} else if parts.len() == 2 { | ||
match parts[0] { | ||
"digest" => Ok(Self::Digest(Digest::try_from_hex(parts[1])?)), | ||
"height" => Ok(Self::Height(parts[1].parse::<u64>()?.into())), | ||
other => Err(BlockSelectorParseError::InvalidPairSelector( | ||
other.to_string(), | ||
)), | ||
} | ||
} else { | ||
Err(BlockSelectorParseError::WrongSelectorLength(parts.len())) | ||
} | ||
} | ||
} | ||
|
||
impl BlockSelector { | ||
/// returns Digest for this selector, if it exists. | ||
pub async fn as_digest(&self, state: &GlobalState) -> Option<Digest> { | ||
match self { | ||
BlockSelector::Digest(d) => Some(*d), | ||
BlockSelector::Height(h) => { | ||
state | ||
.chain | ||
.archival_state() | ||
.block_height_to_canonical_block_digest(*h, state.chain.light_state().hash()) | ||
.await | ||
} | ||
BlockSelector::Tip => Some(state.chain.light_state().hash()), | ||
BlockSelector::Genesis => Some(state.chain.archival_state().genesis_block().hash()), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters