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 neptune_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::pow_puzzle::pow_puzzle;
|
||||
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 tower_http::services::ServeDir;
|
||||
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/pow_puzzle/*address", get(pow_puzzle))
|
||||
.route("/rpc/circulating_supply", get(circulating_supply))
|
||||
.route("/rpc/total_supply", get(total_supply))
|
||||
.route("/rpc/provide_pow_solution", post(provide_pow_solution))
|
||||
// -- Dynamic HTML pages --
|
||||
.route("/", get(root))
|
||||
|
||||
@ -3,67 +3,28 @@ use std::sync::Arc;
|
||||
use axum::extract::State;
|
||||
use axum::response::Json;
|
||||
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 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 number of coins that are liquid, assuming all redemptions on the
|
||||
/// old chain have successfully been made.
|
||||
/// Return the monetary amount that is liquid, assuming 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 circulating_supply(State(state): State<Arc<AppState>>) -> Result<Json<f64>, Response> {
|
||||
let s = state.load();
|
||||
|
||||
// TODO: Remove this local declaration once version of neptune-core with
|
||||
// this value public is released.
|
||||
let generation_0_subsidy = Block::block_subsidy(BlockHeight::genesis().next());
|
||||
let block_height: u64 = s
|
||||
let block_height = s
|
||||
.rpc_client
|
||||
.block_height(context::current(), s.token())
|
||||
.await
|
||||
.map_err(rpc_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"),
|
||||
);
|
||||
.map_err(rpc_method_err)?;
|
||||
|
||||
let mut liquid_supply = PREMINE_MAX_SIZE;
|
||||
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);
|
||||
}
|
||||
let (liquid_supply, _) = monetary_supplies(block_height);
|
||||
|
||||
Ok(Json(liquid_supply.to_nau_f64()))
|
||||
}
|
||||
|
||||
@ -3,4 +3,5 @@ pub mod block_info;
|
||||
pub mod circulating_supply;
|
||||
pub mod pow_puzzle;
|
||||
pub mod provide_pow_solution;
|
||||
pub mod total_supply;
|
||||
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