diff --git a/data_structures/src/chain/mod.rs b/data_structures/src/chain/mod.rs index 393402af7..883f4a628 100644 --- a/data_structures/src/chain/mod.rs +++ b/data_structures/src/chain/mod.rs @@ -1742,20 +1742,10 @@ pub struct SupplyInfo { pub blocks_minted: u32, /// WIT minted through block creation pub blocks_minted_reward: u64, - /// Number of blocks missing - pub blocks_missing: u32, - /// WIT missing because a block was not created - pub blocks_missing_reward: u64, - /// Amount of in-flight data requests - pub in_flight_requests: u32, - /// Supply currently locked in data requests - pub locked_wits_by_requests: u64, - /// Current unlocked supply - pub current_unlocked_supply: u64, - /// Current locked supply - pub current_locked_supply: u64, - /// Maximum supply: the number of nanowits that will ever exist - pub maximum_supply: u64, + /// Current staked supply + pub current_staked_supply: u64, + /// Genesis supply + pub genesis_supply: u64, } /// Information about the total supply after V1_8 activation diff --git a/node/src/actors/chain_manager/handlers.rs b/node/src/actors/chain_manager/handlers.rs index 988832038..63f31e184 100644 --- a/node/src/actors/chain_manager/handlers.rs +++ b/node/src/actors/chain_manager/handlers.rs @@ -34,8 +34,7 @@ use witnet_data_structures::{ }; use witnet_util::timestamp::get_timestamp; use witnet_validations::validations::{ - block_reward, total_block_reward, validate_rad_request, validate_stake_transaction, - validate_unstake_transaction, + validate_rad_request, validate_stake_transaction, validate_unstake_transaction, }; use crate::{ @@ -1996,69 +1995,54 @@ impl Handler for ChainManager { } let chain_info = self.chain_state.chain_info.as_ref().unwrap(); - let halving_period = chain_info.consensus_constants.halving_period; - let initial_block_reward = chain_info.consensus_constants.initial_block_reward; - let collateral_minimum = chain_info.consensus_constants.collateral_minimum; - let current_epoch = self.current_epoch.unwrap(); let current_time = u64::try_from(get_timestamp()).unwrap(); + let current_staked_supply = self.chain_state.stakes.total_staked().nanowits(); - let mut current_unlocked_supply = 0; - let mut current_locked_supply = 0; - for (_output_pointer, value_transfer_output) in self.chain_state.unspent_outputs_pool.iter() - { - if value_transfer_output.0.time_lock <= current_time { - current_unlocked_supply += value_transfer_output.0.value; - } else { - current_locked_supply += value_transfer_output.0.value; - } - } + let wit1_block_reward = chain_info.consensus_constants.initial_block_reward; + let wit2_activation_epoch = get_protocol_version_activation_epoch(ProtocolVersion::V2_0); + let wit2_block_reward = + ConsensusConstantsWit2::default().get_validator_block_reward(current_epoch); - let in_flight_requests = self - .chain_state - .data_request_pool - .data_request_pool - .len() - .try_into() - .unwrap(); - let locked_wits_by_requests = self - .chain_state - .data_request_pool - .locked_wits_by_requests(collateral_minimum); + let (mut blocks_minted, mut blocks_minted_reward) = + (self.last_blocks_minted, self.last_blocks_minted_reward); - let (mut blocks_minted, mut blocks_minted_reward) = (0, 0); - let (mut blocks_missing, mut blocks_missing_reward) = (0, 0); - for epoch in 1..current_epoch { - let block_reward = block_reward(epoch, initial_block_reward, halving_period); - // If the blockchain contains an epoch, a block was minted in that epoch, add the reward to blocks_minted_reward - if self.chain_state.block_chain.contains_key(&epoch) { - blocks_minted += 1; - blocks_minted_reward += block_reward; - // Otherwise, a block was rolled back or no block was proposed, add the reward to blocks_missing_reward - } else { - blocks_missing += 1; - blocks_missing_reward += block_reward; + if self.last_supply_info_epoch < wit2_activation_epoch { + for epoch in self.last_supply_info_epoch..wit2_activation_epoch { + // If the blockchain contains an epoch, a block was minted in that epoch, add the reward to blocks_minted_reward + if self.chain_state.block_chain.contains_key(&epoch) { + blocks_minted += 1; + blocks_minted_reward += wit1_block_reward; + } + } + for epoch in wit2_activation_epoch..current_epoch { + // If the blockchain contains an epoch, a block was minted in that epoch, add the reward to blocks_minted_reward + if self.chain_state.block_chain.contains_key(&epoch) { + blocks_minted += 1; + blocks_minted_reward += wit2_block_reward; + } + } + } else { + for epoch in self.last_supply_info_epoch..current_epoch { + // If the blockchain contains an epoch, a block was minted in that epoch, add the reward to blocks_minted_reward + if self.chain_state.block_chain.contains_key(&epoch) { + blocks_minted += 1; + blocks_minted_reward += wit2_block_reward; + } } } - let genesis_amount = - current_locked_supply + current_unlocked_supply + locked_wits_by_requests - - blocks_minted_reward; - let maximum_block_reward = total_block_reward(initial_block_reward, halving_period); - let maximum_supply = genesis_amount + maximum_block_reward; + self.last_supply_info_epoch = current_epoch; + self.last_blocks_minted = blocks_minted; + self.last_blocks_minted_reward = blocks_minted_reward; Ok(SupplyInfo { epoch: current_epoch, current_time, blocks_minted, blocks_minted_reward, - blocks_missing, - blocks_missing_reward, - in_flight_requests, - locked_wits_by_requests, - current_unlocked_supply, - current_locked_supply, - maximum_supply, + current_staked_supply, + genesis_supply: self.initial_supply, }) } } @@ -2080,7 +2064,6 @@ impl Handler for ChainManager { let current_staked_supply = self.chain_state.stakes.total_staked().nanowits(); let wit1_block_reward = chain_info.consensus_constants.initial_block_reward; - let wit2_activated = get_protocol_version(None) == ProtocolVersion::V2_0; let wit2_activation_epoch = get_protocol_version_activation_epoch(ProtocolVersion::V2_0); let wit2_block_reward = ConsensusConstantsWit2::default().get_validator_block_reward(current_epoch); @@ -2103,19 +2086,38 @@ impl Handler for ChainManager { } } - let (mut blocks_minted, mut blocks_minted_reward) = (0, 0); - for epoch in 1..current_epoch { - // If the blockchain contains an epoch, a block was minted in that epoch, add the reward to blocks_minted_reward - if self.chain_state.block_chain.contains_key(&epoch) { - blocks_minted += 1; - blocks_minted_reward += if wit2_activated && epoch >= wit2_activation_epoch { - wit2_block_reward - } else { - wit1_block_reward + let (mut blocks_minted, mut blocks_minted_reward) = + (self.last_blocks_minted, self.last_blocks_minted_reward); + + if self.last_supply_info_epoch < wit2_activation_epoch { + for epoch in self.last_supply_info_epoch..wit2_activation_epoch { + // If the blockchain contains an epoch, a block was minted in that epoch, add the reward to blocks_minted_reward + if self.chain_state.block_chain.contains_key(&epoch) { + blocks_minted += 1; + blocks_minted_reward += wit1_block_reward; + } + } + for epoch in wit2_activation_epoch..current_epoch { + // If the blockchain contains an epoch, a block was minted in that epoch, add the reward to blocks_minted_reward + if self.chain_state.block_chain.contains_key(&epoch) { + blocks_minted += 1; + blocks_minted_reward += wit2_block_reward; + } + } + } else { + for epoch in self.last_supply_info_epoch..current_epoch { + // If the blockchain contains an epoch, a block was minted in that epoch, add the reward to blocks_minted_reward + if self.chain_state.block_chain.contains_key(&epoch) { + blocks_minted += 1; + blocks_minted_reward += wit2_block_reward; } } } + self.last_supply_info_epoch = current_epoch; + self.last_blocks_minted = blocks_minted; + self.last_blocks_minted_reward = blocks_minted_reward; + let burnt_supply = self .initial_supply .saturating_add(blocks_minted_reward) diff --git a/node/src/actors/chain_manager/mod.rs b/node/src/actors/chain_manager/mod.rs index de66ff301..5888cf39c 100644 --- a/node/src/actors/chain_manager/mod.rs +++ b/node/src/actors/chain_manager/mod.rs @@ -262,6 +262,12 @@ pub struct ChainManager { initial_supply: u64, /// Populate RAD hashes index rad_hashes_index: bool, + /// Epoch when getSupplyInfo2 was last resolved. + last_supply_info_epoch: u32, + /// Count of minted blocks the last time getSupplyInfo2 was resolved. + last_blocks_minted: u32, + /// Counf of minted rewards the last time getSupplyInfo2 was resolved. + last_blocks_minted_reward: u64, } impl ChainManager { diff --git a/node/src/actors/json_rpc/api.rs b/node/src/actors/json_rpc/api.rs index 0ba89e414..e103e382d 100644 --- a/node/src/actors/json_rpc/api.rs +++ b/node/src/actors/json_rpc/api.rs @@ -132,9 +132,6 @@ pub fn attach_regular_methods( server.add_actix_method(system, "getSupplyInfo", |_params: Params| { Box::pin(get_supply_info()) }); - server.add_actix_method(system, "getSupplyInfo2", |_params: Params| { - Box::pin(get_supply_info_2()) - }); server.add_actix_method(system, "peers", |_params: Params| Box::pin(peers())); server.add_actix_method(system, "knownPeers", |_params: Params| { Box::pin(known_peers()) @@ -300,7 +297,6 @@ pub fn attach_sensitive_methods( |params| stake(params.parse()), )) }); - server.add_actix_method(system, "authorizeStake", move |params: Params| { Box::pin(if_authorized( enable_sensitive_methods, @@ -309,7 +305,6 @@ pub fn attach_sensitive_methods( |params| authorize_stake(params.parse()), )) }); - server.add_actix_method(system, "unstake", move |params| { Box::pin(if_authorized( enable_sensitive_methods, @@ -318,6 +313,14 @@ pub fn attach_sensitive_methods( |params| unstake(params.parse()), )) }); + server.add_actix_method(system, "getSupplyInfo2", move |params| { + Box::pin(if_authorized( + enable_sensitive_methods, + "getSupplyInfo2", + params, + |_params| get_supply_info_2(), + )) + }); } fn extract_topic_and_params(params: Params) -> Result<(String, Value), Error> { diff --git a/src/cli/node/json_rpc_client.rs b/src/cli/node/json_rpc_client.rs index 8d1d1aece..23e563d8f 100644 --- a/src/cli/node/json_rpc_client.rs +++ b/src/cli/node/json_rpc_client.rs @@ -29,7 +29,7 @@ use witnet_data_structures::{ chain::{ Block, ConsensusConstants, DataRequestInfo, DataRequestOutput, Environment, Epoch, Hashable, KeyedSignature, NodeStats, OutputPointer, PublicKey, PublicKeyHash, StateMachine, - SupplyInfo, SyncStatus, ValueTransferOutput, + SupplyInfo, SupplyInfo2, SyncStatus, ValueTransferOutput, priority::{PrioritiesEstimate, Priority, PriorityEstimate, TimeToBlock}, tapi::{ActiveWips, current_active_wips}, }, @@ -130,40 +130,109 @@ pub fn get_supply_info(addr: SocketAddr) -> Result<(), anyhow::Error> { ); let block_rewards_wit = whole_wits(supply_info.blocks_minted_reward); - let block_rewards_missing_wit = whole_wits(supply_info.blocks_missing_reward); - let collateralized_data_requests_total_wit = whole_wits(supply_info.locked_wits_by_requests); - let current_supply = - whole_wits(supply_info.current_unlocked_supply + supply_info.locked_wits_by_requests); - let locked_supply = whole_wits(supply_info.current_locked_supply); - let total_supply = whole_wits(supply_info.maximum_supply - supply_info.blocks_missing_reward); - let expected_total_supply = whole_wits(supply_info.maximum_supply); + let staked_supply = whole_wits(supply_info.current_staked_supply); + let initial_supply = whole_wits(supply_info.genesis_supply); let mut supply_table = Table::new(); supply_table.set_format(*prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR); supply_table.set_titles(row!["Supply type", r->"Total WITs"]); + // supply_table.add_row(row![ + // "Temporarily locked in data requests".to_string(), + // r->collateralized_data_requests_total_wit.to_formatted_string(&Locale::en) + // ]); supply_table.add_row(row![ - "Temporarily locked in data requests".to_string(), - r->collateralized_data_requests_total_wit.to_formatted_string(&Locale::en) + "Block rewards".to_string(), + r->block_rewards_wit.to_formatted_string(&Locale::en) ]); supply_table.add_row(row![ - "Unlocked supply".to_string(), - r->current_supply.to_formatted_string(&Locale::en) + "Staked supply".to_string(), + r->staked_supply.to_formatted_string(&Locale::en) ]); supply_table.add_row(row![ - "Locked supply".to_string(), - r->locked_supply.to_formatted_string(&Locale::en) + "Total supply".to_string(), + r->(block_rewards_wit + initial_supply).to_formatted_string(&Locale::en) ]); supply_table.add_row(row![ - "Circulating supply".to_string(), - r->(current_supply + locked_supply).to_formatted_string(&Locale::en) + "Initial supply".to_string(), + r->initial_supply.to_formatted_string(&Locale::en) + ]); + supply_table.printstd(); + println!(); + + let mut blocks_table = Table::new(); + blocks_table.set_format(*prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR); + blocks_table.set_titles(row!["Blocks", r->"Amount", r->"Total WITs"]); + blocks_table.add_row(row![ + "Minted".to_string(), + r->supply_info.blocks_minted.to_formatted_string(&Locale::en), + r->block_rewards_wit.to_formatted_string(&Locale::en) ]); + blocks_table.printstd(); + + println!(); + println!( + "{}% of all blocks so far have been reverted.", + (f64::from(supply_info.epoch - supply_info.blocks_minted) / f64::from(supply_info.epoch) + * 100.0) + .round() as u8 + ); + println!( + "For more information about block rewards and staking, see:\nhttps://github.com/witnet/WIPs/blob/master/wip-0028.md" + ); + + Ok(()) +} + +#[allow( + clippy::cast_possible_wrap, + clippy::cast_precision_loss, + clippy::cast_sign_loss, + clippy::cast_possible_truncation +)] +pub fn get_supply_info_2(addr: SocketAddr) -> Result<(), anyhow::Error> { + let mut stream = start_client(addr)?; + + let request = r#"{"jsonrpc": "2.0","method": "getSupplyInfo2", "id": "1"}"#; + let response = send_request(&mut stream, request)?; + let supply_info = parse_response::(&response)?; + + log::info!("{supply_info:?}"); + + println!( + "\nSupply info at {} (epoch {}):\n", + pretty_print(supply_info.current_time as i64, 0), + supply_info.epoch + ); + + let block_rewards_wit = whole_wits(supply_info.blocks_minted_reward); + let locked_supply = whole_wits(supply_info.current_locked_supply); + let staked_supply = whole_wits(supply_info.current_staked_supply); + let unlocked_supply = whole_wits(supply_info.current_unlocked_supply); + let burnt_supply = whole_wits(supply_info.burnt_supply); + let initial_supply = whole_wits(supply_info.initial_supply); + + let mut supply_table = Table::new(); + supply_table.set_format(*prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR); + supply_table.set_titles(row!["Supply type", r->"Total WITs"]); + // supply_table.add_row(row![ + // "Temporarily locked in data requests".to_string(), + // r->collateralized_data_requests_total_wit.to_formatted_string(&Locale::en) + // ]); supply_table.add_row(row![ - "Actual maximum supply".to_string(), - r->total_supply.to_formatted_string(&Locale::en) + "Burnt supply".to_string(), + r->burnt_supply.to_formatted_string(&Locale::en) ]); supply_table.add_row(row![ - "Expected maximum supply".to_string(), - r->expected_total_supply.to_formatted_string(&Locale::en) + "Staked supply".to_string(), + r->staked_supply.to_formatted_string(&Locale::en) + ]); + supply_table.add_row(row![ + "Circulating supply".to_string(), + r->(unlocked_supply + locked_supply).to_formatted_string(&Locale::en) + ]); + supply_table.add_row(row![ + "Initial supply".to_string(), + r->initial_supply.to_formatted_string(&Locale::en) ]); supply_table.printstd(); println!(); @@ -176,32 +245,21 @@ pub fn get_supply_info(addr: SocketAddr) -> Result<(), anyhow::Error> { r->supply_info.blocks_minted.to_formatted_string(&Locale::en), r->block_rewards_wit.to_formatted_string(&Locale::en) ]); - blocks_table.add_row(row![ - "Reverted".to_string(), - r->supply_info.blocks_missing.to_formatted_string(&Locale::en), - r->block_rewards_missing_wit.to_formatted_string(&Locale::en) - ]); - blocks_table.add_row(row![ - "Expected".to_string(), - r->(supply_info.blocks_minted + supply_info.blocks_missing).to_formatted_string(&Locale::en), - r->(block_rewards_wit + block_rewards_missing_wit).to_formatted_string(&Locale::en) - ]); blocks_table.printstd(); println!(); println!( "{}% of circulating supply is locked.", - ((locked_supply as f64 / (current_supply + locked_supply) as f64) * 100.0).round() as u8 + ((locked_supply as f64 / (unlocked_supply + locked_supply) as f64) * 100.0).round() as u8 ); println!( "{}% of all blocks so far have been reverted.", - ((block_rewards_missing_wit as f64 - / (block_rewards_wit + block_rewards_missing_wit) as f64) + (f64::from(supply_info.epoch - supply_info.blocks_minted) / f64::from(supply_info.epoch) * 100.0) .round() as u8 ); println!( - "For more information about block rewards and halvings, see:\nhttps://github.com/witnet/WIPs/blob/master/wip-0003.md" + "For more information about block rewards and staking, see:\nhttps://github.com/witnet/WIPs/blob/master/wip-0028.md" ); Ok(()) diff --git a/src/cli/node/with_node.rs b/src/cli/node/with_node.rs index d06791a15..517cb8b65 100644 --- a/src/cli/node/with_node.rs +++ b/src/cli/node/with_node.rs @@ -68,6 +68,7 @@ pub fn exec_cmd( rpc::get_balance(node.unwrap_or(default_jsonrpc), target, simple) } Command::GetSupplyInfo { node } => rpc::get_supply_info(node.unwrap_or(default_jsonrpc)), + Command::GetSupplyInfo2 { node } => rpc::get_supply_info_2(node.unwrap_or(default_jsonrpc)), Command::GetAddress { node } => rpc::get_pkh(node.unwrap_or(default_jsonrpc)), Command::GetUtxoInfo { node, long, pkh } => { let pkh = pkh.map(|x| x.parse()).transpose()?; @@ -458,6 +459,17 @@ pub enum Command { #[structopt(short = "n", long = "node")] node: Option, }, + #[structopt( + name = "supply2", + alias = "getSupply2", + alias = "getSupplyInfo2", + about = "Get detailed supply information of witnet tokens (requires sensitive methods on)" + )] + GetSupplyInfo2 { + /// Socket address of the Witnet node to query + #[structopt(short = "n", long = "node")] + node: Option, + }, #[structopt( name = "address", alias = "getAddress",