2019-02-17 09:29:08 -08:00
|
|
|
//! The `rpc_service` module implements the Solana JSON RPC service.
|
|
|
|
|
2019-10-14 15:24:10 -07:00
|
|
|
use crate::{
|
2019-11-13 10:12:09 -08:00
|
|
|
cluster_info::ClusterInfo, commitment::BlockCommitmentCache, rpc::*,
|
2019-10-20 08:54:38 -07:00
|
|
|
storage_stage::StorageState, validator::ValidatorExit,
|
2019-10-14 15:24:10 -07:00
|
|
|
};
|
2019-02-17 09:29:08 -08:00
|
|
|
use jsonrpc_core::MetaIoHandler;
|
2019-07-30 22:43:47 -07:00
|
|
|
use jsonrpc_http_server::{
|
2019-10-14 15:24:10 -07:00
|
|
|
hyper, AccessControlAllowOrigin, CloseHandle, DomainsValidation, RequestMiddleware,
|
|
|
|
RequestMiddlewareAction, ServerBuilder,
|
2019-07-30 22:43:47 -07:00
|
|
|
};
|
2020-02-24 12:37:14 -08:00
|
|
|
use regex::Regex;
|
2020-02-28 16:57:50 -08:00
|
|
|
use solana_ledger::{
|
|
|
|
bank_forks::{BankForks, SnapshotConfig},
|
|
|
|
blockstore::Blockstore,
|
|
|
|
snapshot_utils,
|
|
|
|
};
|
2020-04-14 13:22:58 -07:00
|
|
|
use solana_sdk::{hash::Hash, pubkey::Pubkey};
|
2019-10-14 15:24:10 -07:00
|
|
|
use std::{
|
2020-04-14 13:22:58 -07:00
|
|
|
collections::HashSet,
|
2019-10-14 15:24:10 -07:00
|
|
|
net::SocketAddr,
|
|
|
|
path::{Path, PathBuf},
|
2019-11-11 10:18:34 -08:00
|
|
|
sync::{mpsc::channel, Arc, RwLock},
|
2019-10-14 15:24:10 -07:00
|
|
|
thread::{self, Builder, JoinHandle},
|
|
|
|
};
|
2019-07-30 22:43:47 -07:00
|
|
|
use tokio::prelude::Future;
|
2019-02-17 09:29:08 -08:00
|
|
|
|
2020-04-14 13:22:58 -07:00
|
|
|
// If trusted validators are specified, consider this validator healthy if its latest account hash
|
|
|
|
// is no further behind than this distance from the latest trusted validator account hash
|
|
|
|
const HEALTH_CHECK_SLOT_DISTANCE: u64 = 150;
|
|
|
|
|
2019-02-17 09:29:08 -08:00
|
|
|
pub struct JsonRpcService {
|
|
|
|
thread_hdl: JoinHandle<()>,
|
2019-04-23 14:46:41 -07:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
pub request_processor: Arc<RwLock<JsonRpcRequestProcessor>>, // Used only by test_rpc_new()...
|
2019-08-20 23:59:31 -07:00
|
|
|
|
|
|
|
close_handle: Option<CloseHandle>,
|
2019-02-17 09:29:08 -08:00
|
|
|
}
|
|
|
|
|
2019-07-30 22:43:47 -07:00
|
|
|
struct RpcRequestMiddleware {
|
|
|
|
ledger_path: PathBuf,
|
2020-02-24 12:37:14 -08:00
|
|
|
snapshot_archive_path_regex: Regex,
|
2020-02-28 16:57:50 -08:00
|
|
|
snapshot_config: Option<SnapshotConfig>,
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info: Arc<ClusterInfo>,
|
2020-04-14 13:22:58 -07:00
|
|
|
trusted_validators: Option<HashSet<Pubkey>>,
|
2019-07-30 22:43:47 -07:00
|
|
|
}
|
2020-02-28 16:57:50 -08:00
|
|
|
|
2019-07-30 22:43:47 -07:00
|
|
|
impl RpcRequestMiddleware {
|
2020-04-14 13:22:58 -07:00
|
|
|
pub fn new(
|
|
|
|
ledger_path: PathBuf,
|
|
|
|
snapshot_config: Option<SnapshotConfig>,
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info: Arc<ClusterInfo>,
|
2020-04-14 13:22:58 -07:00
|
|
|
trusted_validators: Option<HashSet<Pubkey>>,
|
|
|
|
) -> Self {
|
2020-02-24 12:37:14 -08:00
|
|
|
Self {
|
|
|
|
ledger_path,
|
2020-04-03 13:13:49 -07:00
|
|
|
snapshot_archive_path_regex: Regex::new(
|
|
|
|
r"/snapshot-\d+-[[:alnum:]]+\.tar\.(bz2|zst|gz)$",
|
|
|
|
)
|
|
|
|
.unwrap(),
|
2020-02-28 16:57:50 -08:00
|
|
|
snapshot_config,
|
2020-04-14 13:22:58 -07:00
|
|
|
cluster_info,
|
|
|
|
trusted_validators,
|
2020-02-24 12:37:14 -08:00
|
|
|
}
|
2019-07-30 22:43:47 -07:00
|
|
|
}
|
|
|
|
|
2020-02-28 16:57:50 -08:00
|
|
|
fn redirect(location: &str) -> hyper::Response<hyper::Body> {
|
|
|
|
hyper::Response::builder()
|
|
|
|
.status(hyper::StatusCode::SEE_OTHER)
|
|
|
|
.header(hyper::header::LOCATION, location)
|
|
|
|
.body(hyper::Body::from(String::from(location)))
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
2019-07-30 22:43:47 -07:00
|
|
|
fn not_found() -> hyper::Response<hyper::Body> {
|
|
|
|
hyper::Response::builder()
|
|
|
|
.status(hyper::StatusCode::NOT_FOUND)
|
|
|
|
.body(hyper::Body::empty())
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn internal_server_error() -> hyper::Response<hyper::Body> {
|
|
|
|
hyper::Response::builder()
|
|
|
|
.status(hyper::StatusCode::INTERNAL_SERVER_ERROR)
|
|
|
|
.body(hyper::Body::empty())
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
2020-02-24 12:37:14 -08:00
|
|
|
fn is_get_path(&self, path: &str) -> bool {
|
|
|
|
match path {
|
|
|
|
"/genesis.tar.bz2" => true,
|
2020-02-28 16:57:50 -08:00
|
|
|
_ => {
|
|
|
|
if self.snapshot_config.is_some() {
|
|
|
|
self.snapshot_archive_path_regex.is_match(path)
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
2020-02-24 12:37:14 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get(&self, path: &str) -> RequestMiddlewareAction {
|
2020-03-07 07:08:01 -08:00
|
|
|
let stem = path.split_at(1).1; // Drop leading '/' from path
|
|
|
|
let filename = {
|
|
|
|
match path {
|
|
|
|
"/genesis.tar.bz2" => self.ledger_path.join(stem),
|
|
|
|
_ => self
|
|
|
|
.snapshot_config
|
|
|
|
.as_ref()
|
|
|
|
.unwrap()
|
|
|
|
.snapshot_package_output_path
|
|
|
|
.join(stem),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-02-24 12:37:14 -08:00
|
|
|
info!("get {} -> {:?}", path, filename);
|
|
|
|
|
2019-07-30 22:43:47 -07:00
|
|
|
RequestMiddlewareAction::Respond {
|
|
|
|
should_validate_hosts: true,
|
|
|
|
response: Box::new(
|
|
|
|
tokio_fs::file::File::open(filename)
|
|
|
|
.and_then(|file| {
|
|
|
|
let buf: Vec<u8> = Vec::new();
|
|
|
|
tokio_io::io::read_to_end(file, buf)
|
|
|
|
.and_then(|item| Ok(hyper::Response::new(item.1.into())))
|
|
|
|
.or_else(|_| Ok(RpcRequestMiddleware::internal_server_error()))
|
|
|
|
})
|
|
|
|
.or_else(|_| Ok(RpcRequestMiddleware::not_found())),
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
2020-04-14 13:22:58 -07:00
|
|
|
|
|
|
|
fn health_check(&self) -> &'static str {
|
|
|
|
let response = if let Some(trusted_validators) = &self.trusted_validators {
|
|
|
|
let (latest_account_hash_slot, latest_trusted_validator_account_hash_slot) = {
|
|
|
|
(
|
2020-04-21 12:54:45 -07:00
|
|
|
self.cluster_info
|
|
|
|
.get_accounts_hash_for_node(&self.cluster_info.id(), |hashes| {
|
|
|
|
hashes
|
|
|
|
.iter()
|
|
|
|
.max_by(|a, b| a.0.cmp(&b.0))
|
|
|
|
.map(|slot_hash| slot_hash.0)
|
|
|
|
})
|
2020-04-14 13:22:58 -07:00
|
|
|
.flatten()
|
|
|
|
.unwrap_or(0),
|
|
|
|
trusted_validators
|
|
|
|
.iter()
|
|
|
|
.map(|trusted_validator| {
|
2020-04-21 12:54:45 -07:00
|
|
|
self.cluster_info
|
|
|
|
.get_accounts_hash_for_node(&trusted_validator, |hashes| {
|
|
|
|
hashes
|
|
|
|
.iter()
|
|
|
|
.max_by(|a, b| a.0.cmp(&b.0))
|
|
|
|
.map(|slot_hash| slot_hash.0)
|
|
|
|
})
|
2020-04-14 13:22:58 -07:00
|
|
|
.flatten()
|
|
|
|
.unwrap_or(0)
|
|
|
|
})
|
|
|
|
.max()
|
|
|
|
.unwrap_or(0),
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
// This validator is considered healthy if its latest account hash slot is within
|
|
|
|
// `HEALTH_CHECK_SLOT_DISTANCE` of the latest trusted validator's account hash slot
|
|
|
|
if latest_account_hash_slot > 0
|
|
|
|
&& latest_trusted_validator_account_hash_slot > 0
|
|
|
|
&& latest_account_hash_slot
|
|
|
|
> latest_trusted_validator_account_hash_slot
|
|
|
|
.saturating_sub(HEALTH_CHECK_SLOT_DISTANCE)
|
|
|
|
{
|
|
|
|
"ok"
|
|
|
|
} else {
|
|
|
|
warn!(
|
|
|
|
"health check: me={}, latest trusted_validator={}",
|
|
|
|
latest_account_hash_slot, latest_trusted_validator_account_hash_slot
|
|
|
|
);
|
|
|
|
"behind"
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// No trusted validator point of reference available, so this validator is healthy
|
|
|
|
// because it's running
|
|
|
|
"ok"
|
|
|
|
};
|
|
|
|
|
|
|
|
info!("health check: {}", response);
|
|
|
|
response
|
|
|
|
}
|
2019-07-30 22:43:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl RequestMiddleware for RpcRequestMiddleware {
|
|
|
|
fn on_request(&self, request: hyper::Request<hyper::Body>) -> RequestMiddlewareAction {
|
|
|
|
trace!("request uri: {}", request.uri());
|
2020-02-24 12:37:14 -08:00
|
|
|
|
2020-02-28 16:57:50 -08:00
|
|
|
if let Some(ref snapshot_config) = self.snapshot_config {
|
|
|
|
if request.uri().path() == "/snapshot.tar.bz2" {
|
|
|
|
// Convenience redirect to the latest snapshot
|
|
|
|
return RequestMiddlewareAction::Respond {
|
|
|
|
should_validate_hosts: true,
|
|
|
|
response: Box::new(jsonrpc_core::futures::future::ok(
|
|
|
|
if let Some((snapshot_archive, _)) =
|
|
|
|
snapshot_utils::get_highest_snapshot_archive_path(
|
|
|
|
&snapshot_config.snapshot_package_output_path,
|
|
|
|
)
|
|
|
|
{
|
|
|
|
RpcRequestMiddleware::redirect(&format!(
|
|
|
|
"/{}",
|
|
|
|
snapshot_archive
|
|
|
|
.file_name()
|
|
|
|
.unwrap_or_else(|| std::ffi::OsStr::new(""))
|
|
|
|
.to_str()
|
|
|
|
.unwrap_or(&"")
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
RpcRequestMiddleware::not_found()
|
|
|
|
},
|
|
|
|
)),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2020-02-24 12:37:14 -08:00
|
|
|
if self.is_get_path(request.uri().path()) {
|
|
|
|
self.get(request.uri().path())
|
2020-04-14 13:22:58 -07:00
|
|
|
} else if request.uri().path() == "/health" {
|
|
|
|
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(self.health_check()))
|
|
|
|
.unwrap(),
|
|
|
|
)),
|
|
|
|
}
|
2020-02-24 12:37:14 -08:00
|
|
|
} else {
|
|
|
|
RequestMiddlewareAction::Proceed {
|
2019-07-30 22:43:47 -07:00
|
|
|
should_continue_on_invalid_cors: false,
|
|
|
|
request,
|
2020-02-24 12:37:14 -08:00
|
|
|
}
|
2019-07-30 22:43:47 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-17 09:29:08 -08:00
|
|
|
impl JsonRpcService {
|
2019-11-11 10:18:34 -08:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2019-02-17 09:29:08 -08:00
|
|
|
pub fn new(
|
|
|
|
rpc_addr: SocketAddr,
|
2019-03-03 22:01:09 -08:00
|
|
|
config: JsonRpcConfig,
|
2020-02-28 16:57:50 -08:00
|
|
|
snapshot_config: Option<SnapshotConfig>,
|
2019-03-18 14:18:43 -07:00
|
|
|
bank_forks: Arc<RwLock<BankForks>>,
|
2019-11-04 15:44:27 -08:00
|
|
|
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
|
2020-01-13 13:13:52 -08:00
|
|
|
blockstore: Arc<Blockstore>,
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info: Arc<ClusterInfo>,
|
2019-11-08 20:56:57 -08:00
|
|
|
genesis_hash: Hash,
|
2019-11-11 10:18:34 -08:00
|
|
|
ledger_path: &Path,
|
|
|
|
storage_state: StorageState,
|
2019-12-19 23:27:54 -08:00
|
|
|
validator_exit: Arc<RwLock<Option<ValidatorExit>>>,
|
2020-04-14 13:22:58 -07:00
|
|
|
trusted_validators: Option<HashSet<Pubkey>>,
|
2019-02-17 09:29:08 -08:00
|
|
|
) -> Self {
|
|
|
|
info!("rpc bound to {:?}", rpc_addr);
|
2019-03-06 09:26:12 -08:00
|
|
|
info!("rpc configuration: {:?}", config);
|
2019-03-03 22:01:09 -08:00
|
|
|
let request_processor = Arc::new(RwLock::new(JsonRpcRequestProcessor::new(
|
|
|
|
config,
|
2019-03-18 14:18:43 -07:00
|
|
|
bank_forks,
|
2019-11-04 15:44:27 -08:00
|
|
|
block_commitment_cache,
|
2020-01-13 13:13:52 -08:00
|
|
|
blockstore,
|
2019-11-11 10:18:34 -08:00
|
|
|
storage_state,
|
2019-12-19 23:27:54 -08:00
|
|
|
validator_exit.clone(),
|
2019-03-03 22:01:09 -08:00
|
|
|
)));
|
2019-02-17 09:29:08 -08:00
|
|
|
|
2019-12-19 23:27:54 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
let test_request_processor = request_processor.clone();
|
|
|
|
|
2019-07-30 22:43:47 -07:00
|
|
|
let ledger_path = ledger_path.to_path_buf();
|
2019-02-17 09:29:08 -08:00
|
|
|
|
2019-08-20 23:59:31 -07:00
|
|
|
let (close_handle_sender, close_handle_receiver) = channel();
|
2019-02-17 09:29:08 -08:00
|
|
|
let thread_hdl = Builder::new()
|
|
|
|
.name("solana-jsonrpc".to_string())
|
|
|
|
.spawn(move || {
|
|
|
|
let mut io = MetaIoHandler::default();
|
|
|
|
let rpc = RpcSolImpl;
|
|
|
|
io.extend_with(rpc.to_delegate());
|
|
|
|
|
2020-04-14 13:22:58 -07:00
|
|
|
let request_middleware = RpcRequestMiddleware::new(
|
|
|
|
ledger_path,
|
|
|
|
snapshot_config,
|
|
|
|
cluster_info.clone(),
|
|
|
|
trusted_validators,
|
|
|
|
);
|
|
|
|
let server = ServerBuilder::with_meta_extractor(
|
|
|
|
io,
|
|
|
|
move |_req: &hyper::Request<hyper::Body>| Meta {
|
2019-12-19 23:27:54 -08:00
|
|
|
request_processor: request_processor.clone(),
|
2019-04-23 14:46:41 -07:00
|
|
|
cluster_info: cluster_info.clone(),
|
2020-04-14 13:22:58 -07:00
|
|
|
genesis_hash,
|
|
|
|
},
|
|
|
|
)
|
2020-04-17 12:37:33 -07:00
|
|
|
.threads(num_cpus::get())
|
2020-04-14 13:22:58 -07:00
|
|
|
.cors(DomainsValidation::AllowOnly(vec![
|
|
|
|
AccessControlAllowOrigin::Any,
|
|
|
|
]))
|
|
|
|
.cors_max_age(86400)
|
|
|
|
.request_middleware(request_middleware)
|
|
|
|
.start_http(&rpc_addr);
|
|
|
|
|
2019-02-17 09:29:08 -08:00
|
|
|
if let Err(e) = server {
|
2020-04-14 13:22:58 -07:00
|
|
|
warn!(
|
|
|
|
"JSON RPC service unavailable error: {:?}. \n\
|
|
|
|
Also, check that port {} is not already in use by another application",
|
|
|
|
e,
|
|
|
|
rpc_addr.port()
|
|
|
|
);
|
2019-02-17 09:29:08 -08:00
|
|
|
return;
|
|
|
|
}
|
2019-07-30 22:43:47 -07:00
|
|
|
|
2019-08-20 23:59:31 -07:00
|
|
|
let server = server.unwrap();
|
|
|
|
close_handle_sender.send(server.close_handle()).unwrap();
|
|
|
|
server.wait();
|
2019-02-17 09:29:08 -08:00
|
|
|
})
|
|
|
|
.unwrap();
|
2019-08-20 23:59:31 -07:00
|
|
|
|
|
|
|
let close_handle = close_handle_receiver.recv().unwrap();
|
|
|
|
let close_handle_ = close_handle.clone();
|
|
|
|
let mut validator_exit_write = validator_exit.write().unwrap();
|
|
|
|
validator_exit_write
|
|
|
|
.as_mut()
|
|
|
|
.unwrap()
|
|
|
|
.register_exit(Box::new(move || close_handle_.close()));
|
2019-02-17 09:29:08 -08:00
|
|
|
Self {
|
|
|
|
thread_hdl,
|
2019-04-23 14:46:41 -07:00
|
|
|
#[cfg(test)]
|
2019-12-19 23:27:54 -08:00
|
|
|
request_processor: test_request_processor,
|
2019-08-20 23:59:31 -07:00
|
|
|
close_handle: Some(close_handle),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn exit(&mut self) {
|
|
|
|
if let Some(c) = self.close_handle.take() {
|
|
|
|
c.close()
|
2019-02-17 09:29:08 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-13 10:12:09 -08:00
|
|
|
pub fn join(self) -> thread::Result<()> {
|
2019-02-17 09:29:08 -08:00
|
|
|
self.thread_hdl.join()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2020-04-14 13:22:58 -07:00
|
|
|
use crate::{
|
|
|
|
contact_info::ContactInfo,
|
|
|
|
crds_value::{CrdsData, CrdsValue, SnapshotHash},
|
|
|
|
rpc::tests::create_validator_exit,
|
|
|
|
};
|
2020-03-21 10:54:40 -07:00
|
|
|
use solana_ledger::{
|
2020-04-14 13:22:58 -07:00
|
|
|
bank_forks::CompressionType,
|
2019-11-11 10:18:34 -08:00
|
|
|
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
2020-03-21 10:54:40 -07:00
|
|
|
get_tmp_ledger_path,
|
2019-11-11 10:18:34 -08:00
|
|
|
};
|
2019-02-20 21:23:44 -08:00
|
|
|
use solana_runtime::bank::Bank;
|
2020-02-20 13:28:55 -08:00
|
|
|
use solana_sdk::signature::Signer;
|
2019-02-17 09:29:08 -08:00
|
|
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
2019-08-20 23:59:31 -07:00
|
|
|
use std::sync::atomic::AtomicBool;
|
2019-02-17 09:29:08 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_rpc_new() {
|
2019-11-08 20:56:57 -08:00
|
|
|
let GenesisConfigInfo {
|
|
|
|
genesis_config,
|
2019-05-22 20:39:00 -07:00
|
|
|
mint_keypair,
|
|
|
|
..
|
2019-11-08 20:56:57 -08:00
|
|
|
} = create_genesis_config(10_000);
|
2019-03-03 22:01:09 -08:00
|
|
|
let exit = Arc::new(AtomicBool::new(false));
|
2019-08-20 23:59:31 -07:00
|
|
|
let validator_exit = create_validator_exit(&exit);
|
2019-11-08 20:56:57 -08:00
|
|
|
let bank = Bank::new(&genesis_config);
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(ContactInfo::default()));
|
2020-03-04 21:46:43 -08:00
|
|
|
let ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
|
2019-02-17 09:29:08 -08:00
|
|
|
let rpc_addr = SocketAddr::new(
|
2020-03-04 21:46:43 -08:00
|
|
|
ip_addr,
|
|
|
|
solana_net_utils::find_available_port_in_range(ip_addr, (10000, 65535)).unwrap(),
|
2019-02-17 09:29:08 -08:00
|
|
|
);
|
2019-03-18 14:18:43 -07:00
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank.slot(), bank)));
|
2019-11-11 10:18:34 -08:00
|
|
|
let ledger_path = get_tmp_ledger_path!();
|
2020-04-22 11:22:09 -07:00
|
|
|
let blockstore = Arc::new(Blockstore::open(&ledger_path).unwrap());
|
|
|
|
let block_commitment_cache = Arc::new(RwLock::new(
|
|
|
|
BlockCommitmentCache::default_with_blockstore(blockstore.clone()),
|
|
|
|
));
|
2019-08-20 23:59:31 -07:00
|
|
|
let mut rpc_service = JsonRpcService::new(
|
2019-03-03 22:01:09 -08:00
|
|
|
rpc_addr,
|
|
|
|
JsonRpcConfig::default(),
|
2020-02-28 16:57:50 -08:00
|
|
|
None,
|
2019-03-18 14:18:43 -07:00
|
|
|
bank_forks,
|
2019-11-04 15:44:27 -08:00
|
|
|
block_commitment_cache,
|
2020-04-22 11:22:09 -07:00
|
|
|
blockstore,
|
2019-12-19 23:27:54 -08:00
|
|
|
cluster_info,
|
2019-08-21 18:16:40 -07:00
|
|
|
Hash::default(),
|
2019-11-11 10:18:34 -08:00
|
|
|
&PathBuf::from("farf"),
|
|
|
|
StorageState::default(),
|
2019-12-19 23:27:54 -08:00
|
|
|
validator_exit,
|
2020-04-14 13:22:58 -07:00
|
|
|
None,
|
2019-03-03 22:01:09 -08:00
|
|
|
);
|
2019-02-17 09:29:08 -08:00
|
|
|
let thread = rpc_service.thread_hdl.thread();
|
|
|
|
assert_eq!(thread.name().unwrap(), "solana-jsonrpc");
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
10_000,
|
|
|
|
rpc_service
|
|
|
|
.request_processor
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
2019-11-12 11:49:41 -08:00
|
|
|
.get_balance(Ok(mint_keypair.pubkey()), None)
|
|
|
|
.unwrap()
|
|
|
|
.value
|
2019-02-17 09:29:08 -08:00
|
|
|
);
|
2019-08-20 23:59:31 -07:00
|
|
|
rpc_service.exit();
|
2019-03-04 16:21:33 -08:00
|
|
|
rpc_service.join().unwrap();
|
2019-02-17 09:29:08 -08:00
|
|
|
}
|
2020-02-24 12:37:14 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_is_get_path() {
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(ContactInfo::default()));
|
2020-04-14 13:22:58 -07:00
|
|
|
|
|
|
|
let rrm = RpcRequestMiddleware::new(PathBuf::from("/"), None, cluster_info.clone(), None);
|
2020-02-28 16:57:50 -08:00
|
|
|
let rrm_with_snapshot_config = RpcRequestMiddleware::new(
|
|
|
|
PathBuf::from("/"),
|
|
|
|
Some(SnapshotConfig {
|
|
|
|
snapshot_interval_slots: 0,
|
|
|
|
snapshot_package_output_path: PathBuf::from("/"),
|
|
|
|
snapshot_path: PathBuf::from("/"),
|
2020-04-03 13:13:49 -07:00
|
|
|
compression: CompressionType::Bzip2,
|
2020-02-28 16:57:50 -08:00
|
|
|
}),
|
2020-04-14 13:22:58 -07:00
|
|
|
cluster_info,
|
|
|
|
None,
|
2020-02-28 16:57:50 -08:00
|
|
|
);
|
2020-02-24 12:37:14 -08:00
|
|
|
|
|
|
|
assert!(rrm.is_get_path("/genesis.tar.bz2"));
|
|
|
|
assert!(!rrm.is_get_path("genesis.tar.bz2"));
|
|
|
|
|
2020-02-28 16:57:50 -08:00
|
|
|
assert!(!rrm.is_get_path("/snapshot.tar.bz2")); // This is a redirect
|
2020-02-24 12:37:14 -08:00
|
|
|
|
|
|
|
assert!(
|
2020-02-28 16:57:50 -08:00
|
|
|
!rrm.is_get_path("/snapshot-100-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar.bz2")
|
2020-02-24 12:37:14 -08:00
|
|
|
);
|
2020-02-28 16:57:50 -08:00
|
|
|
assert!(rrm_with_snapshot_config
|
|
|
|
.is_get_path("/snapshot-100-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar.bz2"));
|
|
|
|
|
2020-02-24 12:37:14 -08:00
|
|
|
assert!(!rrm.is_get_path(
|
|
|
|
"/snapshot-notaslotnumber-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar.bz2"
|
|
|
|
));
|
|
|
|
|
|
|
|
assert!(!rrm.is_get_path("/"));
|
|
|
|
assert!(!rrm.is_get_path(".."));
|
|
|
|
assert!(!rrm.is_get_path("🎣"));
|
|
|
|
}
|
2020-04-14 13:22:58 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_health_check_with_no_trusted_validators() {
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(ContactInfo::default()));
|
2020-04-14 13:22:58 -07:00
|
|
|
|
|
|
|
let rm = RpcRequestMiddleware::new(PathBuf::from("/"), None, cluster_info.clone(), None);
|
|
|
|
assert_eq!(rm.health_check(), "ok");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_health_check_with_trusted_validators() {
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(ContactInfo::default()));
|
2020-04-14 13:22:58 -07:00
|
|
|
|
|
|
|
let trusted_validators = vec![Pubkey::new_rand(), Pubkey::new_rand(), Pubkey::new_rand()];
|
|
|
|
let rm = RpcRequestMiddleware::new(
|
|
|
|
PathBuf::from("/"),
|
|
|
|
None,
|
|
|
|
cluster_info.clone(),
|
|
|
|
Some(trusted_validators.clone().into_iter().collect()),
|
|
|
|
);
|
|
|
|
|
|
|
|
// No account hashes for this node or any trusted validators == "behind"
|
|
|
|
assert_eq!(rm.health_check(), "behind");
|
|
|
|
|
|
|
|
// No account hashes for any trusted validators == "behind"
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info.push_accounts_hashes(vec![(1000, Hash::default()), (900, Hash::default())]);
|
2020-04-14 13:22:58 -07:00
|
|
|
assert_eq!(rm.health_check(), "behind");
|
|
|
|
|
|
|
|
// This node is ahead of the trusted validators == "ok"
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info
|
|
|
|
.gossip
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.crds
|
|
|
|
.insert(
|
|
|
|
CrdsValue::new_unsigned(CrdsData::AccountsHashes(SnapshotHash::new(
|
|
|
|
trusted_validators[0].clone(),
|
|
|
|
vec![
|
|
|
|
(1, Hash::default()),
|
|
|
|
(1001, Hash::default()),
|
|
|
|
(2, Hash::default()),
|
|
|
|
],
|
|
|
|
))),
|
|
|
|
1,
|
|
|
|
)
|
|
|
|
.unwrap();
|
2020-04-14 13:22:58 -07:00
|
|
|
assert_eq!(rm.health_check(), "ok");
|
|
|
|
|
|
|
|
// Node is slightly behind the trusted validators == "ok"
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info
|
|
|
|
.gossip
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.crds
|
|
|
|
.insert(
|
|
|
|
CrdsValue::new_unsigned(CrdsData::AccountsHashes(SnapshotHash::new(
|
|
|
|
trusted_validators[1].clone(),
|
|
|
|
vec![(1000 + HEALTH_CHECK_SLOT_DISTANCE - 1, Hash::default())],
|
|
|
|
))),
|
|
|
|
1,
|
|
|
|
)
|
|
|
|
.unwrap();
|
2020-04-14 13:22:58 -07:00
|
|
|
assert_eq!(rm.health_check(), "ok");
|
|
|
|
|
|
|
|
// Node is far behind the trusted validators == "behind"
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info
|
|
|
|
.gossip
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.crds
|
|
|
|
.insert(
|
|
|
|
CrdsValue::new_unsigned(CrdsData::AccountsHashes(SnapshotHash::new(
|
|
|
|
trusted_validators[2].clone(),
|
|
|
|
vec![(1000 + HEALTH_CHECK_SLOT_DISTANCE, Hash::default())],
|
|
|
|
))),
|
|
|
|
1,
|
|
|
|
)
|
|
|
|
.unwrap();
|
2020-04-14 13:22:58 -07:00
|
|
|
assert_eq!(rm.health_check(), "behind");
|
|
|
|
}
|
2019-02-17 09:29:08 -08:00
|
|
|
}
|