From b563b49ed50c57e908dd37e27b0cd42cbc911b62 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Fri, 29 May 2020 12:50:25 -0600 Subject: [PATCH] Improve Rpc inflation tooling (#10309) automerge --- Cargo.lock | 1 + client/src/rpc_client.rs | 9 ++- client/src/rpc_config.rs | 10 ++- client/src/rpc_request.rs | 10 ++- client/src/rpc_response.rs | 32 ++++++++++ core/Cargo.toml | 3 +- core/src/rpc.rs | 117 ++++++++++++++++++++++++++++++----- docs/src/apps/jsonrpc-api.md | 43 ++++++++++--- genesis-programs/src/lib.rs | 32 +++++----- runtime/src/bank.rs | 10 +-- sdk/src/inflation.rs | 8 +-- 11 files changed, 217 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b382f174d..05dab7f3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3971,6 +3971,7 @@ dependencies = [ "solana-clap-utils", "solana-client", "solana-faucet", + "solana-genesis-programs", "solana-ledger", "solana-logger", "solana-measure", diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 65f287500..9e2ac41da 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -22,7 +22,6 @@ use solana_sdk::{ epoch_schedule::EpochSchedule, fee_calculator::{FeeCalculator, FeeRateGovernor}, hash::Hash, - inflation::Inflation, pubkey::Pubkey, signature::Signature, signers::Signers, @@ -346,8 +345,12 @@ impl RpcClient { }) } - pub fn get_inflation(&self) -> ClientResult { - self.send(RpcRequest::GetInflation, Value::Null) + pub fn get_inflation_governor(&self) -> ClientResult { + self.send(RpcRequest::GetInflationGovernor, Value::Null) + } + + pub fn get_inflation_rate(&self) -> ClientResult { + self.send(RpcRequest::GetInflationRate, Value::Null) } pub fn get_version(&self) -> ClientResult { diff --git a/client/src/rpc_config.rs b/client/src/rpc_config.rs index e31bdd9e4..9773bb7a1 100644 --- a/client/src/rpc_config.rs +++ b/client/src/rpc_config.rs @@ -1,4 +1,4 @@ -use solana_sdk::commitment_config::CommitmentConfig; +use solana_sdk::{clock::Epoch, commitment_config::CommitmentConfig}; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -26,3 +26,11 @@ pub struct RpcLargestAccountsConfig { pub commitment: Option, pub filter: Option, } + +#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcInflationConfig { + pub epoch: Option, + #[serde(flatten)] + pub commitment: Option, +} diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index e20fa4de1..253b5aef5 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -21,7 +21,8 @@ pub enum RpcRequest { GetFees, GetGenesisHash, GetIdentity, - GetInflation, + GetInflationGovernor, + GetInflationRate, GetLargestAccounts, GetLeaderSchedule, GetMinimumBalanceForRentExemption, @@ -67,7 +68,8 @@ impl fmt::Display for RpcRequest { RpcRequest::GetFees => "getFees", RpcRequest::GetGenesisHash => "getGenesisHash", RpcRequest::GetIdentity => "getIdentity", - RpcRequest::GetInflation => "getInflation", + RpcRequest::GetInflationGovernor => "getInflationGovernor", + RpcRequest::GetInflationRate => "getInflationRate", RpcRequest::GetLargestAccounts => "getLargestAccounts", RpcRequest::GetLeaderSchedule => "getLeaderSchedule", RpcRequest::GetMinimumBalanceForRentExemption => "getMinimumBalanceForRentExemption", @@ -146,10 +148,6 @@ mod tests { let request = test_request.build_request_json(1, Value::Null); assert_eq!(request["method"], "getEpochInfo"); - let test_request = RpcRequest::GetInflation; - let request = test_request.build_request_json(1, Value::Null); - assert_eq!(request["method"], "getInflation"); - let test_request = RpcRequest::GetRecentBlockhash; let request = test_request.build_request_json(1, Value::Null); assert_eq!(request["method"], "getRecentBlockhash"); diff --git a/client/src/rpc_response.rs b/client/src/rpc_response.rs index e3267a750..90a139136 100644 --- a/client/src/rpc_response.rs +++ b/client/src/rpc_response.rs @@ -3,6 +3,7 @@ use solana_sdk::{ account::Account, clock::{Epoch, Slot}, fee_calculator::{FeeCalculator, FeeRateGovernor}, + inflation::Inflation, pubkey::Pubkey, transaction::{Result, TransactionError}, }; @@ -55,6 +56,37 @@ pub struct RpcFeeRateGovernor { pub fee_rate_governor: FeeRateGovernor, } +#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct RpcInflationGovernor { + pub initial: f64, + pub terminal: f64, + pub taper: f64, + pub foundation: f64, + pub foundation_term: f64, +} + +impl From for RpcInflationGovernor { + fn from(inflation: Inflation) -> Self { + Self { + initial: inflation.initial, + terminal: inflation.terminal, + taper: inflation.taper, + foundation: inflation.foundation, + foundation_term: inflation.foundation_term, + } + } +} + +#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct RpcInflationRate { + pub total: f64, + pub validator: f64, + pub foundation: f64, + pub epoch: Epoch, +} + #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "camelCase")] pub struct RpcKeyedAccount { diff --git a/core/Cargo.toml b/core/Cargo.toml index 94cf8ec2a..9b556a458 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -21,6 +21,7 @@ byteorder = "1.3.4" chrono = { version = "0.4.11", features = ["serde"] } core_affinity = "0.5.10" crossbeam-channel = "0.4" +ed25519-dalek = "=1.0.0-pre.3" fs_extra = "1.1.0" flate2 = "1.0" indexmap = "1.3" @@ -47,7 +48,7 @@ solana-clap-utils = { path = "../clap-utils", version = "1.3.0" } solana-client = { path = "../client", version = "1.3.0" } solana-transaction-status = { path = "../transaction-status", version = "1.3.0" } solana-faucet = { path = "../faucet", version = "1.3.0" } -ed25519-dalek = "=1.0.0-pre.3" +solana-genesis-programs = { path = "../genesis-programs", version = "1.3.0" } solana-ledger = { path = "../ledger", version = "1.3.0" } solana-logger = { path = "../logger", version = "1.3.0" } solana-merkle-tree = { path = "../merkle-tree", version = "1.3.0" } diff --git a/core/src/rpc.rs b/core/src/rpc.rs index 116fed565..8ffeec9f7 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -26,12 +26,11 @@ use solana_ledger::{ use solana_perf::packet::PACKET_DATA_SIZE; use solana_runtime::{accounts::AccountAddressFilter, bank::Bank}; use solana_sdk::{ - clock::{Slot, UnixTimestamp}, + clock::{Epoch, Slot, UnixTimestamp}, commitment_config::{CommitmentConfig, CommitmentLevel}, epoch_info::EpochInfo, epoch_schedule::EpochSchedule, hash::Hash, - inflation::Inflation, pubkey::Pubkey, signature::Signature, timing::slot_duration_from_slots_per_year, @@ -173,8 +172,27 @@ impl JsonRpcRequestProcessor { .collect()) } - pub fn get_inflation(&self, commitment: Option) -> Result { - Ok(self.bank(commitment)?.inflation()) + pub fn get_inflation_governor( + &self, + commitment: Option, + ) -> Result { + Ok(self.bank(commitment)?.inflation().into()) + } + + pub fn get_inflation_rate(&self, epoch: Option) -> Result { + let bank = self.bank(None)?; + let operating_mode = bank.operating_mode(); + let epoch = epoch.unwrap_or_else(|| bank.epoch()); + let inflation = solana_genesis_programs::get_inflation_for_epoch(operating_mode, epoch); + let year = + (bank.epoch_schedule().get_last_slot_in_epoch(epoch)) as f64 / bank.slots_per_year(); + + Ok(RpcInflationRate { + total: inflation.total(year), + validator: inflation.validator(year), + foundation: inflation.foundation(year), + epoch, + }) } pub fn get_epoch_schedule(&self) -> Result { @@ -757,12 +775,19 @@ pub trait RpcSol { commitment: Option, ) -> Result; - #[rpc(meta, name = "getInflation")] - fn get_inflation( + #[rpc(meta, name = "getInflationGovernor")] + fn get_inflation_governor( &self, meta: Self::Metadata, commitment: Option, - ) -> Result; + ) -> Result; + + #[rpc(meta, name = "getInflationRate")] + fn get_inflation_rate( + &self, + meta: Self::Metadata, + epoch: Option, + ) -> Result; #[rpc(meta, name = "getEpochSchedule")] fn get_epoch_schedule(&self, meta: Self::Metadata) -> Result; @@ -1022,16 +1047,28 @@ impl RpcSol for RpcSolImpl { .get_program_accounts(&program_id, commitment) } - fn get_inflation( + fn get_inflation_governor( &self, meta: Self::Metadata, commitment: Option, - ) -> Result { - debug!("get_inflation rpc request received"); + ) -> Result { + debug!("get_inflation_governor rpc request received"); meta.request_processor .read() .unwrap() - .get_inflation(commitment) + .get_inflation_governor(commitment) + } + + fn get_inflation_rate( + &self, + meta: Self::Metadata, + epoch: Option, + ) -> Result { + debug!("get_inflation_rate rpc request received"); + meta.request_processor + .read() + .unwrap() + .get_inflation_rate(epoch) } fn get_epoch_schedule(&self, meta: Self::Metadata) -> Result { @@ -2066,11 +2103,11 @@ pub mod tests { let bob_pubkey = Pubkey::new_rand(); let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getInflation"}"#; - let rep = io.handle_request_sync(&req, meta); + let req = r#"{"jsonrpc":"2.0","id":1,"method":"getInflationGovernor"}"#; + let rep = io.handle_request_sync(&req, meta.clone()); let res: Response = serde_json::from_str(&rep.expect("actual response")) .expect("actual response deserialization"); - let inflation: Inflation = if let Response::Single(res) = res { + let inflation_governor: RpcInflationGovernor = if let Response::Single(res) = res { if let Output::Success(res) = res { serde_json::from_value(res.result).unwrap() } else { @@ -2079,7 +2116,57 @@ pub mod tests { } else { panic!("Expected single response"); }; - assert_eq!(inflation, bank.inflation()); + let expected_inflation_governor: RpcInflationGovernor = bank.inflation().into(); + assert_eq!(inflation_governor, expected_inflation_governor); + + let req = r#"{"jsonrpc":"2.0","id":1,"method":"getInflationRate"}"#; // Queries current epoch by default + let rep = io.handle_request_sync(&req, meta.clone()); + let res: Response = serde_json::from_str(&rep.expect("actual response")) + .expect("actual response deserialization"); + let inflation_rate: RpcInflationRate = if let Response::Single(res) = res { + if let Output::Success(res) = res { + serde_json::from_value(res.result).unwrap() + } else { + panic!("Expected success"); + } + } else { + panic!("Expected single response"); + }; + let operating_mode = bank.operating_mode(); + let inflation = solana_genesis_programs::get_inflation_for_epoch(operating_mode, 0); + let year = (bank.epoch_schedule().get_last_slot_in_epoch(0)) as f64 / bank.slots_per_year(); + let expected_inflation_rate = RpcInflationRate { + total: inflation.total(year), + validator: inflation.validator(year), + foundation: inflation.foundation(year), + epoch: 0, + }; + assert_eq!(inflation_rate, expected_inflation_rate); + + let epoch = 40_000_000; // After default foundation term + let req = format!( + r#"{{"jsonrpc":"2.0","id":1,"method":"getInflationRate","params":[{}]}}"#, + epoch + ); // Queries current epoch by default + let rep = io.handle_request_sync(&req, meta); + let res: Response = serde_json::from_str(&rep.expect("actual response")) + .expect("actual response deserialization"); + let inflation_rate: RpcInflationRate = if let Response::Single(res) = res { + if let Output::Success(res) = res { + serde_json::from_value(res.result).unwrap() + } else { + panic!("Expected success"); + } + } else { + panic!("Expected single response"); + }; + let expected_inflation_rate = RpcInflationRate { + total: 0.015, + validator: 0.015, + foundation: 0.0, + epoch, + }; + assert_eq!(inflation_rate, expected_inflation_rate); } #[test] diff --git a/docs/src/apps/jsonrpc-api.md b/docs/src/apps/jsonrpc-api.md index 77c8f1be9..1a92877f6 100644 --- a/docs/src/apps/jsonrpc-api.md +++ b/docs/src/apps/jsonrpc-api.md @@ -31,7 +31,8 @@ To interact with a Solana node inside a JavaScript application, use the [solana- * [getFirstAvailableBlock](jsonrpc-api.md#getfirstavailableblock) * [getGenesisHash](jsonrpc-api.md#getgenesishash) * [getIdentity](jsonrpc-api.md#getidentity) -* [getInflation](jsonrpc-api.md#getinflation) +* [getInflationGovernor](jsonrpc-api.md#getinflationgovernor) +* [getInflationRate](jsonrpc-api.md#getinflationrate) * [getLargestAccounts](jsonrpc-api.md#getlargestaccounts) * [getLeaderSchedule](jsonrpc-api.md#getleaderschedule) * [getMinimumBalanceForRentExemption](jsonrpc-api.md#getminimumbalanceforrentexemption) @@ -635,33 +636,59 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m {"jsonrpc":"2.0","result":{"identity": "2r1F4iWqVcb8M1DbAjQuFpebkQHY9hcVU4WuW2DJBppN"},"id":1} ``` -### getInflation +### getInflationGovernor -Returns the inflation configuration of the cluster +Returns the current inflation governor #### Parameters: -None +* `` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Results: -The result field will be an Inflation object with the following fields: +The result field will be a JSON object with the following fields: * `initial: `, the initial inflation percentage from time 0 * `terminal: `, terminal inflation percentage * `taper: `, rate per year at which inflation is lowered * `foundation: `, percentage of total inflation allocated to the foundation * `foundationTerm: `, duration of foundation pool inflation in years -* `storage: `, percentage of total inflation allocated to storage rewards #### Example: ```bash // Request -curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getInflation"}' http://localhost:8899 +curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getInflationGovernor"}' http://localhost:8899 // Result -{"jsonrpc":"2.0","result":{"foundation":0.05,"foundationTerm":7.0,"initial":0.15,"storage":0.1,"taper":0.15,"terminal":0.015},"id":1} +{"jsonrpc":"2.0","result":{"foundation":0.05,"foundationTerm":7.0,"initial":0.15,"taper":0.15,"terminal":0.015},"id":1} +``` + +### getInflationRate + +Returns the specific inflation values for a particular epoch + +#### Parameters: + +* `` - (optional) Epoch, default is the current epoch + +#### Results: + +The result field will be a JSON object with the following fields: + +* `total: `, total inflation +* `validator: `, inflation allocated to validators +* `foundation: `, inflation allocated to the foundation +* `epoch: `, epoch for which these values are valid + +#### Example: + +```bash +// Request +curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getInflationRate"}' http://localhost:8899 + +// Result +{"jsonrpc":"2.0","result":{"epoch":100,"foundation":0.001,"total":0.149,"validator":0.148},"id":1} ``` ### getLargestAccounts diff --git a/genesis-programs/src/lib.rs b/genesis-programs/src/lib.rs index da598e8a5..a99df0c92 100644 --- a/genesis-programs/src/lib.rs +++ b/genesis-programs/src/lib.rs @@ -16,26 +16,28 @@ use log::*; use solana_runtime::bank::{Bank, EnteredEpochCallback}; pub fn get_inflation(operating_mode: OperatingMode, epoch: Epoch) -> Option { + let past_epoch_inflation = get_inflation_for_epoch(operating_mode, epoch.saturating_sub(1)); + let epoch_inflation = get_inflation_for_epoch(operating_mode, epoch); + + if epoch_inflation != past_epoch_inflation || epoch == 0 { + Some(epoch_inflation) + } else { + None + } +} + +pub fn get_inflation_for_epoch(operating_mode: OperatingMode, epoch: Epoch) -> Inflation { match operating_mode { - OperatingMode::Development => { - if epoch == 0 { - Some(Inflation::default()) - } else { - None - } - } + OperatingMode::Development => Inflation::default(), OperatingMode::Stable | OperatingMode::Preview => { - if epoch == 0 { - // No inflation at epoch 0 - Some(Inflation::new_disabled()) - } else if epoch == std::u64::MAX { + if epoch == std::u64::MAX { // Inflation starts - // - // The epoch of std::u64::MAX - 1 is a placeholder and is expected to be reduced in + // The epoch of std::u64::MAX is a placeholder and is expected to be reduced in // a future hard fork. - Some(Inflation::default()) + Inflation::default() } else { - None + // No inflation from epoch 0 + Inflation::new_disabled() } } } diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 7e7f1999c..721fca827 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -1917,7 +1917,7 @@ impl Bank { self.get_slots_in_epoch(self.first_normal_epoch()) } - fn operating_mode(&self) -> OperatingMode { + pub fn operating_mode(&self) -> OperatingMode { // unwrap is safe; self.operating_mode is ensured to be Some() always... // we only using Option here for ABI compatibility... self.operating_mode.unwrap() @@ -6891,25 +6891,25 @@ mod tests { if bank.slot == 0 { assert_eq!( bank.hash().to_string(), - "7MKHH6P7J5aQNN29Cr6aZQbEpQcXe8KTgchd4Suk9NCG" + "DX3Jk7ae6VdogRb73iC1zdyrYN5UzinLcSFES2FQx8dY" ); } if bank.slot == 32 { assert_eq!( bank.hash().to_string(), - "3AxuV6GGcoqRi6pksN6btNEmeJCTesLbjgA88QZt9a8Q" + "FqLpq1gmTdzEmdEPa2JttEFDqRtuKwkKFKuLuqNSiYwH" ); } if bank.slot == 64 { assert_eq!( bank.hash().to_string(), - "B32ZLAzeCW5FueeauiGYnujh8Efmxvpeac74W9JU68oB" + "rBbDCyHuCBWQrLY37zjj2zwsEnzNERWHwoH3W2NpRYe" ); } if bank.slot == 128 { assert_eq!( bank.hash().to_string(), - "A2tCz2EqryRZ7tHpw9H2918RZLCbqnSGzRWUqbnnESGz" + "E9DThiAPbheGeLWBDsts8uzuwF3bJQzDWh5uo8ovtYiy" ); break; } diff --git a/sdk/src/inflation.rs b/sdk/src/inflation.rs index 5c6e44a82..56b58cc81 100644 --- a/sdk/src/inflation.rs +++ b/sdk/src/inflation.rs @@ -18,7 +18,7 @@ pub struct Inflation { /// Duration of foundation pool inflation, in years pub foundation_term: f64, - /// Percentage of total inflation allocated to storage rewards + /// DEPRECATED, this field is currently unused pub storage: f64, } @@ -27,7 +27,7 @@ const DEFAULT_TERMINAL: f64 = 0.015; const DEFAULT_TAPER: f64 = 0.15; const DEFAULT_FOUNDATION: f64 = 0.05; const DEFAULT_FOUNDATION_TERM: f64 = 7.0; -const DEFAULT_STORAGE: f64 = 0.10; +const DEFAULT_STORAGE: f64 = 0.0; impl Default for Inflation { fn default() -> Self { @@ -69,8 +69,8 @@ impl Inflation { self.total(year) - self.storage(year) - self.foundation(year) } - /// portion of total that goes to storage mining - pub fn storage(&self, year: f64) -> f64 { + /// DEPRECATED + fn storage(&self, year: f64) -> f64 { self.total(year) * self.storage }