diff --git a/core/src/non_circulating_supply.rs b/core/src/non_circulating_supply.rs index e137ffe50b..69740ca266 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(); @@ -149,7 +149,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 @@ -164,7 +164,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) @@ -179,7 +179,7 @@ mod tests { bank = Arc::new(new_from_parent(&bank)); } assert_eq!(bank.epoch(), 1); - 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 diff --git a/core/src/rpc.rs b/core/src/rpc.rs index 33d0bba6c1..07376ba1d5 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -294,7 +294,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, @@ -318,7 +318,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 436f5747bb..4f76a3987b 100644 --- a/core/src/rpc_service.rs +++ b/core/src/rpc_service.rs @@ -44,6 +44,7 @@ struct RpcRequestMiddleware { snapshot_config: Option, cluster_info: Arc, trusted_validators: Option>, + bank_forks: Arc>, } impl RpcRequestMiddleware { @@ -52,6 +53,7 @@ impl RpcRequestMiddleware { snapshot_config: Option, cluster_info: Arc, trusted_validators: Option>, + bank_forks: Arc>, ) -> Self { Self { ledger_path, @@ -60,6 +62,7 @@ impl RpcRequestMiddleware { snapshot_config, cluster_info, trusted_validators, + bank_forks, } } @@ -85,7 +88,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, _ => { @@ -98,7 +101,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 { @@ -217,8 +220,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, @@ -238,6 +252,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, storage_state, @@ -283,6 +317,7 @@ impl JsonRpcService { snapshot_config, cluster_info.clone(), trusted_validators, + bank_forks.clone(), ); let server = ServerBuilder::with_meta_extractor( io, @@ -412,11 +447,39 @@ mod tests { rpc_service.join().unwrap(); } - #[test] - fn test_is_get_path() { - let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(ContactInfo::default())); + 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))) + } - let rrm = RpcRequestMiddleware::new(PathBuf::from("/"), None, cluster_info.clone(), None); + #[test] + 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, + 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.clone(), None); + let rm = RpcRequestMiddleware::new( + PathBuf::from("/"), + None, + cluster_info.clone(), + 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"