From 324cfd40f0ac857f571e5c8c52ab3063a1c1fa0c Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Wed, 20 May 2020 20:04:07 -0700 Subject: [PATCH] Add v0 REST APIs for circulating and total supply (#10102) --- core/src/non_circulating_supply.rs | 8 +-- core/src/rpc.rs | 4 +- core/src/rpc_service.rs | 112 +++++++++++++++++++++++------ 3 files changed, 98 insertions(+), 26 deletions(-) diff --git a/core/src/non_circulating_supply.rs b/core/src/non_circulating_supply.rs index 428739567c9cbe..1c74555cf06c16 100644 --- a/core/src/non_circulating_supply.rs +++ b/core/src/non_circulating_supply.rs @@ -8,7 +8,7 @@ pub struct NonCirculatingSupply { pub accounts: Vec, } -pub fn calculate_non_circulating_supply(bank: Arc) -> NonCirculatingSupply { +pub fn calculate_non_circulating_supply(bank: &Arc) -> NonCirculatingSupply { debug!("Updating Bank supply, epoch: {}", bank.epoch()); let mut non_circulating_accounts_set: HashSet = HashSet::new(); @@ -150,7 +150,7 @@ mod tests { (num_genesis_accounts + num_non_circulating_accounts + num_stake_accounts) * balance ); - let non_circulating_supply = calculate_non_circulating_supply(bank.clone()); + let non_circulating_supply = calculate_non_circulating_supply(&bank); assert_eq!( non_circulating_supply.lamports, (num_non_circulating_accounts + num_stake_accounts) * balance @@ -165,7 +165,7 @@ mod tests { for key in non_circulating_accounts { bank.store_account(&key, &Account::new(new_balance, 0, &Pubkey::default())); } - let non_circulating_supply = calculate_non_circulating_supply(bank.clone()); + let non_circulating_supply = calculate_non_circulating_supply(&bank); assert_eq!( non_circulating_supply.lamports, (num_non_circulating_accounts * new_balance) + (num_stake_accounts * balance) @@ -180,7 +180,7 @@ mod tests { bank = Arc::new(new_from_parent(&bank)); } assert_eq!(bank.epoch(), 1); - let non_circulating_supply = calculate_non_circulating_supply(bank); + let non_circulating_supply = calculate_non_circulating_supply(&bank); assert_eq!( non_circulating_supply.lamports, num_non_circulating_accounts * new_balance diff --git a/core/src/rpc.rs b/core/src/rpc.rs index 96fefb86519f6b..e1fc60127bf820 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -291,7 +291,7 @@ impl JsonRpcRequestProcessor { let config = config.unwrap_or_default(); let bank = self.bank(config.commitment)?; let (addresses, address_filter) = if let Some(filter) = config.filter { - let non_circulating_supply = calculate_non_circulating_supply(bank.clone()); + let non_circulating_supply = calculate_non_circulating_supply(&bank); let addresses = non_circulating_supply.accounts.into_iter().collect(); let address_filter = match filter { RpcLargestAccountsFilter::Circulating => AccountAddressFilter::Exclude, @@ -315,7 +315,7 @@ impl JsonRpcRequestProcessor { fn get_supply(&self, commitment: Option) -> RpcResponse { let bank = self.bank(commitment)?; - let non_circulating_supply = calculate_non_circulating_supply(bank.clone()); + let non_circulating_supply = calculate_non_circulating_supply(&bank); let total_supply = bank.capitalization(); new_response( &bank, diff --git a/core/src/rpc_service.rs b/core/src/rpc_service.rs index 9218e2791e812c..e67a47c30183a7 100644 --- a/core/src/rpc_service.rs +++ b/core/src/rpc_service.rs @@ -43,6 +43,7 @@ struct RpcRequestMiddleware { snapshot_config: Option, cluster_info: Arc, trusted_validators: Option>, + bank_forks: Arc>, } impl RpcRequestMiddleware { @@ -51,6 +52,7 @@ impl RpcRequestMiddleware { snapshot_config: Option, cluster_info: Arc, trusted_validators: Option>, + bank_forks: Arc>, ) -> Self { Self { ledger_path, @@ -61,6 +63,7 @@ impl RpcRequestMiddleware { snapshot_config, cluster_info, trusted_validators, + bank_forks, } } @@ -86,7 +89,7 @@ impl RpcRequestMiddleware { .unwrap() } - fn is_get_path(&self, path: &str) -> bool { + fn is_file_get_path(&self, path: &str) -> bool { match path { "/genesis.tar.bz2" => true, _ => { @@ -99,7 +102,7 @@ impl RpcRequestMiddleware { } } - fn get(&self, path: &str) -> RequestMiddlewareAction { + fn process_file_get(&self, path: &str) -> RequestMiddlewareAction { let stem = path.split_at(1).1; // Drop leading '/' from path let filename = { match path { @@ -218,8 +221,19 @@ impl RequestMiddleware for RpcRequestMiddleware { }; } } - if self.is_get_path(request.uri().path()) { - self.get(request.uri().path()) + + if let Some(result) = process_rest(&self.bank_forks, request.uri().path()) { + RequestMiddlewareAction::Respond { + should_validate_hosts: true, + response: Box::new(jsonrpc_core::futures::future::ok( + hyper::Response::builder() + .status(hyper::StatusCode::OK) + .body(hyper::Body::from(result)) + .unwrap(), + )), + } + } else if self.is_file_get_path(request.uri().path()) { + self.process_file_get(request.uri().path()) } else if request.uri().path() == "/health" { RequestMiddlewareAction::Respond { should_validate_hosts: true, @@ -239,6 +253,26 @@ impl RequestMiddleware for RpcRequestMiddleware { } } +fn process_rest(bank_forks: &Arc>, path: &str) -> Option { + match path { + "/v0/circulating-supply" => { + let r_bank_forks = bank_forks.read().unwrap(); + let bank = r_bank_forks.root_bank(); + let total_supply = bank.capitalization(); + let non_circulating_supply = + crate::non_circulating_supply::calculate_non_circulating_supply(&bank).lamports; + Some(format!("{}", total_supply - non_circulating_supply)) + } + "/v0/total-supply" => { + let r_bank_forks = bank_forks.read().unwrap(); + let bank = r_bank_forks.root_bank(); + let total_supply = bank.capitalization(); + Some(format!("{}", total_supply)) + } + _ => None, + } +} + impl JsonRpcService { #[allow(clippy::too_many_arguments)] pub fn new( @@ -258,7 +292,7 @@ impl JsonRpcService { info!("rpc configuration: {:?}", config); let request_processor = Arc::new(RwLock::new(JsonRpcRequestProcessor::new( config, - bank_forks, + bank_forks.clone(), block_commitment_cache, blockstore, validator_exit.clone(), @@ -282,6 +316,7 @@ impl JsonRpcService { snapshot_config, cluster_info.clone(), trusted_validators, + bank_forks.clone(), ); let server = ServerBuilder::with_meta_extractor( io, @@ -411,11 +446,39 @@ mod tests { rpc_service.join().unwrap(); } + fn create_bank_forks() -> Arc> { + let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000); + let bank = Bank::new(&genesis_config); + Arc::new(RwLock::new(BankForks::new(bank.slot(), bank))) + } + #[test] - fn test_is_get_path() { + fn test_process_rest_api() { + let bank_forks = create_bank_forks(); + + assert_eq!(None, process_rest(&bank_forks, "not-a-supported-rest-api")); + assert_eq!( + Some("10127".to_string()), + process_rest(&bank_forks, "/v0/circulating-supply") + ); + assert_eq!( + Some("10127".to_string()), + process_rest(&bank_forks, "/v0/total-supply") + ); + } + + #[test] + fn test_is_file_get_path() { let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(ContactInfo::default())); + let bank_forks = create_bank_forks(); - let rrm = RpcRequestMiddleware::new(PathBuf::from("/"), None, cluster_info.clone(), None); + let rrm = RpcRequestMiddleware::new( + PathBuf::from("/"), + None, + cluster_info.clone(), + None, + bank_forks.clone(), + ); let rrm_with_snapshot_config = RpcRequestMiddleware::new( PathBuf::from("/"), Some(SnapshotConfig { @@ -426,33 +489,41 @@ mod tests { }), cluster_info, None, + bank_forks, ); - assert!(rrm.is_get_path("/genesis.tar.bz2")); - assert!(!rrm.is_get_path("genesis.tar.bz2")); + assert!(rrm.is_file_get_path("/genesis.tar.bz2")); + assert!(!rrm.is_file_get_path("genesis.tar.bz2")); - assert!(!rrm.is_get_path("/snapshot.tar.bz2")); // This is a redirect + assert!(!rrm.is_file_get_path("/snapshot.tar.bz2")); // This is a redirect - assert!( - !rrm.is_get_path("/snapshot-100-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar.bz2") - ); - assert!(rrm_with_snapshot_config - .is_get_path("/snapshot-100-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar.bz2")); + assert!(!rrm.is_file_get_path( + "/snapshot-100-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar.bz2" + )); + assert!(rrm_with_snapshot_config.is_file_get_path( + "/snapshot-100-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar.bz2" + )); - assert!(!rrm.is_get_path( + assert!(!rrm.is_file_get_path( "/snapshot-notaslotnumber-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar.bz2" )); - assert!(!rrm.is_get_path("/")); - assert!(!rrm.is_get_path("..")); - assert!(!rrm.is_get_path("🎣")); + assert!(!rrm.is_file_get_path("/")); + assert!(!rrm.is_file_get_path("..")); + assert!(!rrm.is_file_get_path("🎣")); } #[test] fn test_health_check_with_no_trusted_validators() { let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(ContactInfo::default())); - let rm = RpcRequestMiddleware::new(PathBuf::from("/"), None, cluster_info, None); + let rm = RpcRequestMiddleware::new( + PathBuf::from("/"), + None, + cluster_info, + None, + create_bank_forks(), + ); assert_eq!(rm.health_check(), "ok"); } @@ -466,6 +537,7 @@ mod tests { None, cluster_info.clone(), Some(trusted_validators.clone().into_iter().collect()), + create_bank_forks(), ); // No account hashes for this node or any trusted validators == "behind"