change(rpc): Adds `getmininginfo`, `getnetworksolps` and `getnetworkhashps` methods (#5808)
* adds type and stub * adds: - SolutionRate state request - getnetworksolps, getnetworkhashps, & getmininginfo RPCs - vectors tests * adds snapshot tests updates ReadRequest::SolutionRate doc link * removes random slash in doc comment moves snapshot tests up where it can use the populated state service * adds snapshots * updates doc comments * applies `num_blocks` default in RPC instead of `solution_rate` * adds # Correctness comment * Add testnet field to getmininginfo response * use PartialCumulativeWork instead of u128 * document why `solution_rate` takes an extra block * add comment explaining why the work for the last block in the iterator is not added to `total_work` * use `as_u128` method instead of deref for PartialCumulativeWork * Updates `chain` field of getmininginfo response * Updates snapshots Adds "arbitrary_precision" feature to serde_json in zebra-rpc
This commit is contained in:
parent
accc8ccbe5
commit
77b85cf767
|
@ -503,6 +503,13 @@ impl std::ops::Add for Work {
|
|||
/// Partial work used to track relative work in non-finalized chains
|
||||
pub struct PartialCumulativeWork(u128);
|
||||
|
||||
impl PartialCumulativeWork {
|
||||
/// Return the inner `u128` value.
|
||||
pub fn as_u128(self) -> u128 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Work> for PartialCumulativeWork {
|
||||
fn from(work: Work) -> Self {
|
||||
PartialCumulativeWork(work.0)
|
||||
|
|
|
@ -43,7 +43,7 @@ jsonrpc-http-server = "18.0.0"
|
|||
num_cpus = "1.14.0"
|
||||
|
||||
# zebra-rpc needs the preserve_order feature in serde_json, which is a dependency of jsonrpc-core
|
||||
serde_json = { version = "1.0.89", features = ["preserve_order"] }
|
||||
serde_json = { version = "1.0.89", features = ["preserve_order", "arbitrary_precision"] }
|
||||
indexmap = { version = "1.9.2", features = ["serde"] }
|
||||
|
||||
tokio = { version = "1.23.0", features = ["time", "rt-multi-thread", "macros", "tracing"] }
|
||||
|
|
|
@ -52,6 +52,11 @@ pub mod zip317;
|
|||
/// > and clock time varies between nodes.
|
||||
const MAX_ESTIMATED_DISTANCE_TO_NETWORK_CHAIN_TIP: i32 = 100;
|
||||
|
||||
/// The default window size specifying how many blocks to check when estimating the chain's solution rate.
|
||||
///
|
||||
/// Based on default value in zcashd.
|
||||
const DEFAULT_SOLUTION_RATE_WINDOW_SIZE: usize = 120;
|
||||
|
||||
/// The RPC error code used by `zcashd` for when it's still downloading initial blocks.
|
||||
///
|
||||
/// `s-nomp` mining pool expects error code `-10` when the node is not synced:
|
||||
|
@ -132,6 +137,38 @@ pub trait GetBlockTemplateRpc {
|
|||
hex_data: HexData,
|
||||
_options: Option<submit_block::JsonParameters>,
|
||||
) -> BoxFuture<Result<submit_block::Response>>;
|
||||
|
||||
/// Returns mining-related information.
|
||||
///
|
||||
/// zcashd reference: [`getmininginfo`](https://zcash.github.io/rpc/getmininginfo.html)
|
||||
#[rpc(name = "getmininginfo")]
|
||||
fn get_mining_info(&self) -> BoxFuture<Result<types::get_mining_info::Response>>;
|
||||
|
||||
/// Returns the estimated network solutions per second based on the last `num_blocks` before `height`.
|
||||
/// If `num_blocks` is not supplied, uses 120 blocks.
|
||||
/// If `height` is not supplied or is 0, uses the tip height.
|
||||
///
|
||||
/// zcashd reference: [`getnetworksolps`](https://zcash.github.io/rpc/getnetworksolps.html)
|
||||
#[rpc(name = "getnetworksolps")]
|
||||
fn get_network_sol_ps(
|
||||
&self,
|
||||
num_blocks: Option<usize>,
|
||||
height: Option<i32>,
|
||||
) -> BoxFuture<Result<u128>>;
|
||||
|
||||
/// Returns the estimated network solutions per second based on the last `num_blocks` before `height`.
|
||||
/// If `num_blocks` is not supplied, uses 120 blocks.
|
||||
/// If `height` is not supplied or is 0, uses the tip height.
|
||||
///
|
||||
/// zcashd reference: [`getnetworkhashps`](https://zcash.github.io/rpc/getnetworkhashps.html)
|
||||
#[rpc(name = "getnetworkhashps")]
|
||||
fn get_network_hash_ps(
|
||||
&self,
|
||||
num_blocks: Option<usize>,
|
||||
height: Option<i32>,
|
||||
) -> BoxFuture<Result<u128>> {
|
||||
self.get_network_sol_ps(num_blocks, height)
|
||||
}
|
||||
}
|
||||
|
||||
/// RPC method implementations.
|
||||
|
@ -531,6 +568,56 @@ where
|
|||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn get_mining_info(&self) -> BoxFuture<Result<types::get_mining_info::Response>> {
|
||||
let network = self.network;
|
||||
let solution_rate_fut = self.get_network_sol_ps(None, None);
|
||||
async move {
|
||||
Ok(types::get_mining_info::Response::new(
|
||||
network,
|
||||
solution_rate_fut.await?,
|
||||
))
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn get_network_sol_ps(
|
||||
&self,
|
||||
num_blocks: Option<usize>,
|
||||
height: Option<i32>,
|
||||
) -> BoxFuture<Result<u128>> {
|
||||
let num_blocks = num_blocks
|
||||
.map(|num_blocks| num_blocks.max(1))
|
||||
.unwrap_or(DEFAULT_SOLUTION_RATE_WINDOW_SIZE);
|
||||
let height = height.and_then(|height| (height > 1).then_some(Height(height as u32)));
|
||||
let mut state = self.state.clone();
|
||||
|
||||
async move {
|
||||
let request = ReadRequest::SolutionRate { num_blocks, height };
|
||||
|
||||
let response = state
|
||||
.ready()
|
||||
.and_then(|service| service.call(request))
|
||||
.await
|
||||
.map_err(|error| Error {
|
||||
code: ErrorCode::ServerError(0),
|
||||
message: error.to_string(),
|
||||
data: None,
|
||||
})?;
|
||||
|
||||
let solution_rate = match response {
|
||||
ReadResponse::SolutionRate(solution_rate) => solution_rate.ok_or(Error {
|
||||
code: ErrorCode::ServerError(0),
|
||||
message: "No blocks in state".to_string(),
|
||||
data: None,
|
||||
})?,
|
||||
_ => unreachable!("unmatched response to a solution rate request"),
|
||||
};
|
||||
|
||||
Ok(solution_rate)
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
// get_block_template support methods
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
pub mod default_roots;
|
||||
pub mod get_block_template;
|
||||
pub mod get_block_template_opts;
|
||||
pub mod get_mining_info;
|
||||
pub mod hex_data;
|
||||
pub mod submit_block;
|
||||
pub mod transaction;
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
//! Response type for the `getmininginfo` RPC.
|
||||
|
||||
use zebra_chain::parameters::Network;
|
||||
|
||||
/// Response to a `getmininginfo` RPC request.
|
||||
#[derive(Debug, PartialEq, Eq, serde::Serialize)]
|
||||
pub struct Response {
|
||||
/// The estimated network solution rate in Sol/s.
|
||||
networksolps: u128,
|
||||
|
||||
/// The estimated network solution rate in Sol/s.
|
||||
networkhashps: u128,
|
||||
|
||||
/// Current network name as defined in BIP70 (main, test, regtest)
|
||||
chain: String,
|
||||
|
||||
/// If using testnet or not
|
||||
testnet: bool,
|
||||
}
|
||||
|
||||
impl Response {
|
||||
/// Creates a new `getmininginfo` response
|
||||
pub fn new(network: Network, networksolps: u128) -> Self {
|
||||
Self {
|
||||
networksolps,
|
||||
networkhashps: networksolps,
|
||||
chain: network.bip70_network_name(),
|
||||
testnet: network == Network::Testnet,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,7 +29,9 @@ use zebra_test::mock_service::{MockService, PanicAssertion};
|
|||
use crate::methods::{
|
||||
get_block_template_rpcs::{
|
||||
self,
|
||||
types::{get_block_template::GetBlockTemplate, hex_data::HexData, submit_block},
|
||||
types::{
|
||||
get_block_template::GetBlockTemplate, get_mining_info, hex_data::HexData, submit_block,
|
||||
},
|
||||
},
|
||||
tests::utils::fake_history_tree,
|
||||
GetBlockHash, GetBlockTemplateRpc, GetBlockTemplateRpcImpl,
|
||||
|
@ -127,9 +129,22 @@ pub async fn test_responses<State, ReadState>(
|
|||
.get_block_hash(BLOCK_HEIGHT10)
|
||||
.await
|
||||
.expect("We should have a GetBlockHash struct");
|
||||
|
||||
snapshot_rpc_getblockhash(get_block_hash, &settings);
|
||||
|
||||
// `getmininginfo`
|
||||
let get_mining_info = get_block_template_rpc
|
||||
.get_mining_info()
|
||||
.await
|
||||
.expect("We should have a success response");
|
||||
snapshot_rpc_getmininginfo(get_mining_info, &settings);
|
||||
|
||||
// `getnetworksolps` (and `getnetworkhashps`)
|
||||
let get_network_sol_ps = get_block_template_rpc
|
||||
.get_network_sol_ps(None, None)
|
||||
.await
|
||||
.expect("We should have a success response");
|
||||
snapshot_rpc_getnetworksolps(get_network_sol_ps, &settings);
|
||||
|
||||
// get a new empty state
|
||||
let new_read_state = MockService::build().for_unit_tests();
|
||||
|
||||
|
@ -225,3 +240,16 @@ fn snapshot_rpc_submit_block_invalid(
|
|||
insta::assert_json_snapshot!("snapshot_rpc_submit_block_invalid", submit_block_response)
|
||||
});
|
||||
}
|
||||
|
||||
/// Snapshot `getmininginfo` response, using `cargo insta` and JSON serialization.
|
||||
fn snapshot_rpc_getmininginfo(
|
||||
get_mining_info: get_mining_info::Response,
|
||||
settings: &insta::Settings,
|
||||
) {
|
||||
settings.bind(|| insta::assert_json_snapshot!("get_mining_info", get_mining_info));
|
||||
}
|
||||
|
||||
/// Snapshot `getnetworksolps` response, using `cargo insta` and JSON serialization.
|
||||
fn snapshot_rpc_getnetworksolps(get_network_sol_ps: u128, settings: &insta::Settings) {
|
||||
settings.bind(|| insta::assert_json_snapshot!("get_network_sol_ps", get_network_sol_ps));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
|
||||
expression: get_mining_info
|
||||
---
|
||||
{
|
||||
"networksolps": 2,
|
||||
"networkhashps": 2,
|
||||
"chain": "main",
|
||||
"testnet": false
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
|
||||
expression: get_mining_info
|
||||
---
|
||||
{
|
||||
"networksolps": 0,
|
||||
"networkhashps": 0,
|
||||
"chain": "test",
|
||||
"testnet": true
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
|
||||
expression: get_network_sol_ps
|
||||
---
|
||||
2
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
|
||||
expression: get_network_sol_ps
|
||||
---
|
||||
0
|
|
@ -785,6 +785,95 @@ async fn rpc_getblockhash() {
|
|||
mempool.expect_no_requests().await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn rpc_getmininginfo() {
|
||||
let _init_guard = zebra_test::init();
|
||||
|
||||
// Create a continuous chain of mainnet blocks from genesis
|
||||
let blocks: Vec<Arc<Block>> = zebra_test::vectors::CONTINUOUS_MAINNET_BLOCKS
|
||||
.iter()
|
||||
.map(|(_height, block_bytes)| block_bytes.zcash_deserialize_into().unwrap())
|
||||
.collect();
|
||||
|
||||
// Create a populated state service
|
||||
let (_state, read_state, latest_chain_tip, _chain_tip_change) =
|
||||
zebra_state::populated_state(blocks.clone(), Mainnet).await;
|
||||
|
||||
// Init RPC
|
||||
let get_block_template_rpc = get_block_template_rpcs::GetBlockTemplateRpcImpl::new(
|
||||
Mainnet,
|
||||
Default::default(),
|
||||
Buffer::new(MockService::build().for_unit_tests(), 1),
|
||||
read_state,
|
||||
latest_chain_tip.clone(),
|
||||
MockService::build().for_unit_tests(),
|
||||
MockSyncStatus::default(),
|
||||
);
|
||||
|
||||
get_block_template_rpc
|
||||
.get_mining_info()
|
||||
.await
|
||||
.expect("get_mining_info call should succeed");
|
||||
}
|
||||
|
||||
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn rpc_getnetworksolps() {
|
||||
let _init_guard = zebra_test::init();
|
||||
|
||||
// Create a continuous chain of mainnet blocks from genesis
|
||||
let blocks: Vec<Arc<Block>> = zebra_test::vectors::CONTINUOUS_MAINNET_BLOCKS
|
||||
.iter()
|
||||
.map(|(_height, block_bytes)| block_bytes.zcash_deserialize_into().unwrap())
|
||||
.collect();
|
||||
|
||||
// Create a populated state service
|
||||
let (_state, read_state, latest_chain_tip, _chain_tip_change) =
|
||||
zebra_state::populated_state(blocks.clone(), Mainnet).await;
|
||||
|
||||
// Init RPC
|
||||
let get_block_template_rpc = get_block_template_rpcs::GetBlockTemplateRpcImpl::new(
|
||||
Mainnet,
|
||||
Default::default(),
|
||||
Buffer::new(MockService::build().for_unit_tests(), 1),
|
||||
read_state,
|
||||
latest_chain_tip.clone(),
|
||||
MockService::build().for_unit_tests(),
|
||||
MockSyncStatus::default(),
|
||||
);
|
||||
|
||||
let get_network_sol_ps_inputs = [
|
||||
(None, None),
|
||||
(Some(0), None),
|
||||
(Some(0), Some(0)),
|
||||
(Some(0), Some(-1)),
|
||||
(Some(0), Some(10)),
|
||||
(Some(0), Some(i32::MAX)),
|
||||
(Some(1), None),
|
||||
(Some(1), Some(0)),
|
||||
(Some(1), Some(-1)),
|
||||
(Some(1), Some(10)),
|
||||
(Some(1), Some(i32::MAX)),
|
||||
(Some(usize::MAX), None),
|
||||
(Some(usize::MAX), Some(0)),
|
||||
(Some(usize::MAX), Some(-1)),
|
||||
(Some(usize::MAX), Some(10)),
|
||||
(Some(usize::MAX), Some(i32::MAX)),
|
||||
];
|
||||
|
||||
for (num_blocks_input, height_input) in get_network_sol_ps_inputs {
|
||||
let get_network_sol_ps_result = get_block_template_rpc
|
||||
.get_network_sol_ps(num_blocks_input, height_input)
|
||||
.await;
|
||||
assert!(
|
||||
get_network_sol_ps_result
|
||||
.is_ok(),
|
||||
"get_network_sol_ps({num_blocks_input:?}, {height_input:?}) call with should be ok, got: {get_network_sol_ps_result:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn rpc_getblocktemplate() {
|
||||
|
|
|
@ -779,6 +779,17 @@ pub enum ReadRequest {
|
|||
/// [`zebra-state::GetBlockTemplateChainInfo`](zebra-state::GetBlockTemplateChainInfo)` structure containing
|
||||
/// best chain state information.
|
||||
ChainInfo,
|
||||
|
||||
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||
/// Get the average solution rate in the best chain.
|
||||
///
|
||||
/// Returns [`ReadResponse::SolutionRate`]
|
||||
SolutionRate {
|
||||
/// Specifies over difficulty averaging window.
|
||||
num_blocks: usize,
|
||||
/// Optionally estimate the network speed at the time when a certain block was found
|
||||
height: Option<block::Height>,
|
||||
},
|
||||
}
|
||||
|
||||
impl ReadRequest {
|
||||
|
@ -806,6 +817,8 @@ impl ReadRequest {
|
|||
ReadRequest::BestChainBlockHash(_) => "best_chain_block_hash",
|
||||
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||
ReadRequest::ChainInfo => "chain_info",
|
||||
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||
ReadRequest::SolutionRate { .. } => "solution_rate",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -133,6 +133,10 @@ pub enum ReadResponse {
|
|||
/// Response to [`ReadRequest::ChainInfo`](crate::ReadRequest::ChainInfo) with the state
|
||||
/// information needed by the `getblocktemplate` RPC method.
|
||||
ChainInfo(GetBlockTemplateChainInfo),
|
||||
|
||||
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||
/// Response to [`ReadRequest::SolutionRate`](crate::ReadRequest::SolutionRate)
|
||||
SolutionRate(Option<u128>),
|
||||
}
|
||||
|
||||
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||
|
@ -204,7 +208,7 @@ impl TryFrom<ReadResponse> for Response {
|
|||
Err("there is no corresponding Response for this ReadResponse")
|
||||
}
|
||||
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||
ReadResponse::ChainInfo(_) => {
|
||||
ReadResponse::ChainInfo(_) | ReadResponse::SolutionRate(_) => {
|
||||
Err("there is no corresponding Response for this ReadResponse")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1622,6 +1622,62 @@ impl Service<ReadRequest> for ReadStateService {
|
|||
.map(|join_result| join_result.expect("panic in ReadRequest::ChainInfo"))
|
||||
.boxed()
|
||||
}
|
||||
|
||||
// Used by getmininginfo, getnetworksolps, and getnetworkhashps RPCs.
|
||||
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||
ReadRequest::SolutionRate { num_blocks, height } => {
|
||||
let timer = CodeTimer::start();
|
||||
|
||||
let state = self.clone();
|
||||
|
||||
// # Performance
|
||||
//
|
||||
// Allow other async tasks to make progress while concurrently reading blocks from disk.
|
||||
let span = Span::current();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
span.in_scope(move || {
|
||||
let latest_non_finalized_state = state.latest_non_finalized_state();
|
||||
// # Correctness
|
||||
//
|
||||
// It is ok to do these lookups using multiple database calls. Finalized state updates
|
||||
// can only add overlapping blocks, and block hashes are unique across all chain forks.
|
||||
//
|
||||
// The worst that can happen here is that the default `start_hash` will be below
|
||||
// the chain tip.
|
||||
let (tip_height, tip_hash) =
|
||||
match read::tip(latest_non_finalized_state.best_chain(), &state.db) {
|
||||
Some(tip_hash) => tip_hash,
|
||||
None => return Ok(ReadResponse::SolutionRate(None)),
|
||||
};
|
||||
|
||||
let start_hash = match height {
|
||||
Some(height) if height < tip_height => read::hash_by_height(
|
||||
latest_non_finalized_state.best_chain(),
|
||||
&state.db,
|
||||
height,
|
||||
),
|
||||
// use the chain tip hash if height is above it or not provided.
|
||||
_ => Some(tip_hash),
|
||||
};
|
||||
|
||||
let solution_rate = start_hash.and_then(|start_hash| {
|
||||
read::difficulty::solution_rate(
|
||||
&latest_non_finalized_state,
|
||||
&state.db,
|
||||
num_blocks,
|
||||
start_hash,
|
||||
)
|
||||
});
|
||||
|
||||
// The work is done in the future.
|
||||
timer.finish(module_path!(), line!(), "ReadRequest::ChainInfo");
|
||||
|
||||
Ok(ReadResponse::SolutionRate(solution_rate))
|
||||
})
|
||||
})
|
||||
.map(|join_result| join_result.expect("panic in ReadRequest::ChainInfo"))
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@ use std::sync::Arc;
|
|||
use chrono::{DateTime, Duration, TimeZone, Utc};
|
||||
|
||||
use zebra_chain::{
|
||||
block::{self, Block, Height},
|
||||
block::{self, Block, Hash, Height},
|
||||
history_tree::HistoryTree,
|
||||
parameters::{Network, NetworkUpgrade, POST_BLOSSOM_POW_TARGET_SPACING},
|
||||
work::difficulty::CompactDifficulty,
|
||||
work::difficulty::{CompactDifficulty, PartialCumulativeWork},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -63,6 +63,58 @@ pub fn get_block_template_chain_info(
|
|||
))
|
||||
}
|
||||
|
||||
/// Accepts a `non_finalized_state`, [`ZebraDb`], `num_blocks`, and a block hash to start at.
|
||||
///
|
||||
/// Iterates over up to the last `num_blocks` blocks, summing up their total work.
|
||||
/// Divides that total by the number of seconds between the timestamp of the
|
||||
/// first block in the iteration and 1 block below the last block.
|
||||
///
|
||||
/// Returns the solution rate per second for the current best chain, or `None` if
|
||||
/// the `start_hash` and at least 1 block below it are not found in the chain.
|
||||
pub fn solution_rate(
|
||||
non_finalized_state: &NonFinalizedState,
|
||||
db: &ZebraDb,
|
||||
num_blocks: usize,
|
||||
start_hash: Hash,
|
||||
) -> Option<u128> {
|
||||
// Take 1 extra block for calculating the number of seconds between when mining on the first block likely started.
|
||||
// The work for the last block in this iterator is not added to `total_work`.
|
||||
let mut block_iter = any_ancestor_blocks(non_finalized_state, db, start_hash)
|
||||
.take(num_blocks.checked_add(1).unwrap_or(num_blocks))
|
||||
.peekable();
|
||||
|
||||
let get_work = |block: Arc<Block>| {
|
||||
block
|
||||
.header
|
||||
.difficulty_threshold
|
||||
.to_work()
|
||||
.expect("work has already been validated")
|
||||
};
|
||||
|
||||
let block = block_iter.next()?;
|
||||
let last_block_time = block.header.time;
|
||||
|
||||
let mut total_work: PartialCumulativeWork = get_work(block).into();
|
||||
|
||||
loop {
|
||||
// Return `None` if the iterator doesn't yield a second item.
|
||||
let block = block_iter.next()?;
|
||||
|
||||
if block_iter.peek().is_some() {
|
||||
// Add the block's work to `total_work` if it's not the last item in the iterator.
|
||||
// The last item in the iterator is only used to estimate when mining on the first block
|
||||
// in the window of `num_blocks` likely started.
|
||||
total_work += get_work(block);
|
||||
} else {
|
||||
let first_block_time = block.header.time;
|
||||
let duration_between_first_and_last_block = last_block_time - first_block_time;
|
||||
return Some(
|
||||
total_work.as_u128() / duration_between_first_and_last_block.num_seconds() as u128,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Do a consistency check by checking the finalized tip before and after all other database queries.
|
||||
/// Returns and error if the tip obtained before and after is not the same.
|
||||
///
|
||||
|
|
Loading…
Reference in New Issue