feat: Add endpoint for total supply
Total supply is defined as liquid supply plus timelocked supply.
This commit is contained in:
parent
d22fe50de6
commit
21bb64a276
@ -4,3 +4,4 @@ pub mod http_util;
|
|||||||
pub mod model;
|
pub mod model;
|
||||||
pub mod neptune_rpc;
|
pub mod neptune_rpc;
|
||||||
pub mod rpc;
|
pub mod rpc;
|
||||||
|
pub mod shared;
|
||||||
|
|||||||
@ -16,6 +16,7 @@ use neptune_explorer::rpc::block_info::block_info;
|
|||||||
use neptune_explorer::rpc::circulating_supply::circulating_supply;
|
use neptune_explorer::rpc::circulating_supply::circulating_supply;
|
||||||
use neptune_explorer::rpc::pow_puzzle::pow_puzzle;
|
use neptune_explorer::rpc::pow_puzzle::pow_puzzle;
|
||||||
use neptune_explorer::rpc::provide_pow_solution::provide_pow_solution;
|
use neptune_explorer::rpc::provide_pow_solution::provide_pow_solution;
|
||||||
|
use neptune_explorer::rpc::total_supply::total_supply;
|
||||||
use neptune_explorer::rpc::utxo_digest::utxo_digest;
|
use neptune_explorer::rpc::utxo_digest::utxo_digest;
|
||||||
use tower_http::services::ServeDir;
|
use tower_http::services::ServeDir;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
@ -60,6 +61,7 @@ pub fn setup_routes(app_state: AppState) -> Router {
|
|||||||
.route("/rpc/utxo_digest/:index", get(utxo_digest))
|
.route("/rpc/utxo_digest/:index", get(utxo_digest))
|
||||||
.route("/rpc/pow_puzzle/*address", get(pow_puzzle))
|
.route("/rpc/pow_puzzle/*address", get(pow_puzzle))
|
||||||
.route("/rpc/circulating_supply", get(circulating_supply))
|
.route("/rpc/circulating_supply", get(circulating_supply))
|
||||||
|
.route("/rpc/total_supply", get(total_supply))
|
||||||
.route("/rpc/provide_pow_solution", post(provide_pow_solution))
|
.route("/rpc/provide_pow_solution", post(provide_pow_solution))
|
||||||
// -- Dynamic HTML pages --
|
// -- Dynamic HTML pages --
|
||||||
.route("/", get(root))
|
.route("/", get(root))
|
||||||
|
|||||||
@ -3,67 +3,28 @@ use std::sync::Arc;
|
|||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum::response::Json;
|
use axum::response::Json;
|
||||||
use axum::response::Response;
|
use axum::response::Response;
|
||||||
use neptune_cash::api::export::BlockHeight;
|
|
||||||
use neptune_cash::protocol::consensus::block::block_height::BLOCKS_PER_GENERATION;
|
|
||||||
use neptune_cash::protocol::consensus::block::block_height::NUM_BLOCKS_SKIPPED_BECAUSE_REBOOT;
|
|
||||||
use neptune_cash::protocol::consensus::block::Block;
|
|
||||||
use neptune_cash::protocol::consensus::block::PREMINE_MAX_SIZE;
|
|
||||||
use tarpc::context;
|
use tarpc::context;
|
||||||
|
|
||||||
use crate::http_util::rpc_err;
|
use crate::http_util::rpc_err;
|
||||||
use crate::http_util::rpc_method_err;
|
use crate::http_util::rpc_method_err;
|
||||||
use crate::model::app_state::AppState;
|
use crate::model::app_state::AppState;
|
||||||
|
use crate::shared::monetary_supplies;
|
||||||
|
|
||||||
/// Return the number of coins that are liquid, assuming all redemptions on the
|
/// Return the monetary amount that is liquid, assuming all redemptions on the
|
||||||
/// old chain have successfully been made.
|
/// old chain have successfully been made. Returned unit is nau, Neptune Atomic
|
||||||
|
/// Units. To convert to number of coins, divide by $4*10^{30}$.
|
||||||
#[axum::debug_handler]
|
#[axum::debug_handler]
|
||||||
pub async fn circulating_supply(State(state): State<Arc<AppState>>) -> Result<Json<f64>, Response> {
|
pub async fn circulating_supply(State(state): State<Arc<AppState>>) -> Result<Json<f64>, Response> {
|
||||||
let s = state.load();
|
let s = state.load();
|
||||||
|
|
||||||
// TODO: Remove this local declaration once version of neptune-core with
|
let block_height = s
|
||||||
// this value public is released.
|
|
||||||
let generation_0_subsidy = Block::block_subsidy(BlockHeight::genesis().next());
|
|
||||||
let block_height: u64 = s
|
|
||||||
.rpc_client
|
.rpc_client
|
||||||
.block_height(context::current(), s.token())
|
.block_height(context::current(), s.token())
|
||||||
.await
|
.await
|
||||||
.map_err(rpc_err)?
|
.map_err(rpc_err)?
|
||||||
.map_err(rpc_method_err)?
|
.map_err(rpc_method_err)?;
|
||||||
.into();
|
|
||||||
let effective_block_height = block_height + NUM_BLOCKS_SKIPPED_BECAUSE_REBOOT;
|
|
||||||
let (num_generations, num_blocks_in_generation): (u64, u32) = (
|
|
||||||
effective_block_height / BLOCKS_PER_GENERATION,
|
|
||||||
(effective_block_height % BLOCKS_PER_GENERATION)
|
|
||||||
.try_into()
|
|
||||||
.expect("There are fewer than u32::MAX blocks per generation"),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut liquid_supply = PREMINE_MAX_SIZE;
|
let (liquid_supply, _) = monetary_supplies(block_height);
|
||||||
let mut liquid_subsidy = generation_0_subsidy.half();
|
|
||||||
let blocks_per_generation: u32 = BLOCKS_PER_GENERATION
|
|
||||||
.try_into()
|
|
||||||
.expect("There are fewer than u32::MAX blocks per generation");
|
|
||||||
for _ in 0..num_generations {
|
|
||||||
liquid_supply += liquid_subsidy.scalar_mul(blocks_per_generation);
|
|
||||||
liquid_subsidy = liquid_subsidy.half();
|
|
||||||
}
|
|
||||||
|
|
||||||
liquid_supply += liquid_subsidy.scalar_mul(num_blocks_in_generation);
|
|
||||||
|
|
||||||
// How much of timelocked miner rewards have been unlocked? Assume that the
|
|
||||||
// timelock is exactly one generation long. In reality the timelock is
|
|
||||||
// is defined in relation to timestamp and not block heights, so this is
|
|
||||||
// only a (pretty good) approximation.
|
|
||||||
|
|
||||||
let mut released_subsidy = generation_0_subsidy.half();
|
|
||||||
for _ in 1..num_generations {
|
|
||||||
liquid_supply += released_subsidy.scalar_mul(blocks_per_generation);
|
|
||||||
released_subsidy = released_subsidy.half();
|
|
||||||
}
|
|
||||||
|
|
||||||
if num_generations > 0 {
|
|
||||||
liquid_supply += released_subsidy.scalar_mul(num_blocks_in_generation);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Json(liquid_supply.to_nau_f64()))
|
Ok(Json(liquid_supply.to_nau_f64()))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,4 +3,5 @@ pub mod block_info;
|
|||||||
pub mod circulating_supply;
|
pub mod circulating_supply;
|
||||||
pub mod pow_puzzle;
|
pub mod pow_puzzle;
|
||||||
pub mod provide_pow_solution;
|
pub mod provide_pow_solution;
|
||||||
|
pub mod total_supply;
|
||||||
pub mod utxo_digest;
|
pub mod utxo_digest;
|
||||||
|
|||||||
31
src/rpc/total_supply.rs
Normal file
31
src/rpc/total_supply.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
|
use axum::response::Json;
|
||||||
|
use axum::response::Response;
|
||||||
|
use tarpc::context;
|
||||||
|
|
||||||
|
use crate::http_util::rpc_err;
|
||||||
|
use crate::http_util::rpc_method_err;
|
||||||
|
use crate::model::app_state::AppState;
|
||||||
|
use crate::shared::monetary_supplies;
|
||||||
|
|
||||||
|
/// Return the total monetary supply, the sum of the timeloced and liquid
|
||||||
|
/// supply. Assumes all redemptions on the old chain have successfully been
|
||||||
|
/// made. Returned unit is nau, Neptune Atomic Units. To convert to number of
|
||||||
|
/// coins, divide by $4*10^{30}$.
|
||||||
|
#[axum::debug_handler]
|
||||||
|
pub async fn total_supply(State(state): State<Arc<AppState>>) -> Result<Json<f64>, Response> {
|
||||||
|
let s = state.load();
|
||||||
|
|
||||||
|
let block_height = s
|
||||||
|
.rpc_client
|
||||||
|
.block_height(context::current(), s.token())
|
||||||
|
.await
|
||||||
|
.map_err(rpc_err)?
|
||||||
|
.map_err(rpc_method_err)?;
|
||||||
|
|
||||||
|
let (_, total_supply) = monetary_supplies(block_height);
|
||||||
|
|
||||||
|
Ok(Json(total_supply.to_nau_f64()))
|
||||||
|
}
|
||||||
60
src/shared.rs
Normal file
60
src/shared.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
use neptune_cash::api::export::BlockHeight;
|
||||||
|
use neptune_cash::api::export::NativeCurrencyAmount;
|
||||||
|
use neptune_cash::protocol::consensus::block::block_height::BLOCKS_PER_GENERATION;
|
||||||
|
use neptune_cash::protocol::consensus::block::block_height::NUM_BLOCKS_SKIPPED_BECAUSE_REBOOT;
|
||||||
|
use neptune_cash::protocol::consensus::block::Block;
|
||||||
|
use neptune_cash::protocol::consensus::block::PREMINE_MAX_SIZE;
|
||||||
|
|
||||||
|
/// Return the pair (liquid supply, total supply)
|
||||||
|
///
|
||||||
|
/// Assumes all redemption claims have been rewarded.
|
||||||
|
pub(crate) fn monetary_supplies(
|
||||||
|
block_height: BlockHeight,
|
||||||
|
) -> (NativeCurrencyAmount, NativeCurrencyAmount) {
|
||||||
|
let block_height: u64 = block_height.into();
|
||||||
|
let generation_0_subsidy = Block::block_subsidy(BlockHeight::genesis().next());
|
||||||
|
let effective_block_height = block_height + NUM_BLOCKS_SKIPPED_BECAUSE_REBOOT;
|
||||||
|
let (num_generations, num_blocks_in_curr_gen): (u64, u32) = (
|
||||||
|
effective_block_height / BLOCKS_PER_GENERATION,
|
||||||
|
(effective_block_height % BLOCKS_PER_GENERATION)
|
||||||
|
.try_into()
|
||||||
|
.expect("There are fewer than u32::MAX blocks per generation"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut liquid_supply = PREMINE_MAX_SIZE;
|
||||||
|
let mut liquid_subsidy = generation_0_subsidy.half();
|
||||||
|
let mut total_supply = PREMINE_MAX_SIZE;
|
||||||
|
let blocks_per_generation: u32 = BLOCKS_PER_GENERATION
|
||||||
|
.try_into()
|
||||||
|
.expect("There are fewer than u32::MAX blocks per generation");
|
||||||
|
for _ in 0..num_generations {
|
||||||
|
liquid_supply += liquid_subsidy.scalar_mul(blocks_per_generation);
|
||||||
|
total_supply += liquid_subsidy.scalar_mul(2);
|
||||||
|
liquid_subsidy = liquid_subsidy.half();
|
||||||
|
}
|
||||||
|
|
||||||
|
let liquid_supply_current_generation = liquid_subsidy.scalar_mul(num_blocks_in_curr_gen);
|
||||||
|
liquid_supply += liquid_supply_current_generation;
|
||||||
|
total_supply += liquid_supply_current_generation.scalar_mul(2);
|
||||||
|
|
||||||
|
// How much of timelocked miner rewards have been unlocked? Assume that the
|
||||||
|
// timelock is exactly one generation long. In reality the timelock is
|
||||||
|
// is defined in relation to timestamp and not block heights, so this is
|
||||||
|
// only a (good) approximation.
|
||||||
|
|
||||||
|
let mut released_subsidy = generation_0_subsidy.half();
|
||||||
|
for _ in 1..num_generations {
|
||||||
|
liquid_supply += released_subsidy.scalar_mul(blocks_per_generation);
|
||||||
|
released_subsidy = released_subsidy.half();
|
||||||
|
}
|
||||||
|
|
||||||
|
if num_generations > 0 {
|
||||||
|
liquid_supply += released_subsidy.scalar_mul(num_blocks_in_curr_gen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If you want correct results for anything but main net, and claims are
|
||||||
|
// only being refunded on main net, the size of the claims pool must be
|
||||||
|
// subtracted here, if that the network is not main net.
|
||||||
|
|
||||||
|
(liquid_supply, total_supply)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user