Initial solana-test-validator command-line program
This commit is contained in:
parent
13db3eca9f
commit
0a9ff1dc9d
|
@ -4014,6 +4014,7 @@ dependencies = [
|
|||
"solana-metrics",
|
||||
"solana-net-utils",
|
||||
"solana-perf",
|
||||
"solana-program-test",
|
||||
"solana-rayon-threadlimit",
|
||||
"solana-runtime",
|
||||
"solana-sdk",
|
||||
|
@ -5071,12 +5072,14 @@ dependencies = [
|
|||
"chrono",
|
||||
"clap",
|
||||
"console",
|
||||
"indicatif",
|
||||
"libc",
|
||||
"log 0.4.8",
|
||||
"rand 0.7.3",
|
||||
"serde_json",
|
||||
"signal-hook",
|
||||
"solana-clap-utils",
|
||||
"solana-cli-config",
|
||||
"solana-client",
|
||||
"solana-core",
|
||||
"solana-download-utils",
|
||||
|
@ -5090,7 +5093,7 @@ dependencies = [
|
|||
"solana-sdk",
|
||||
"solana-version",
|
||||
"solana-vote-program",
|
||||
"solana-vote-signer",
|
||||
"symlink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -21,10 +21,11 @@ fn test_cli_deploy_program() {
|
|||
pathbuf.push("noop");
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
|
|
|
@ -22,13 +22,21 @@ use std::sync::mpsc::channel;
|
|||
|
||||
#[test]
|
||||
fn test_nonce() {
|
||||
full_battery_tests(TestValidator::with_no_fees(), None, false);
|
||||
let mint_keypair = Keypair::new();
|
||||
full_battery_tests(
|
||||
TestValidator::with_no_fees(mint_keypair.pubkey()),
|
||||
mint_keypair,
|
||||
None,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonce_with_seed() {
|
||||
let mint_keypair = Keypair::new();
|
||||
full_battery_tests(
|
||||
TestValidator::with_no_fees(),
|
||||
TestValidator::with_no_fees(mint_keypair.pubkey()),
|
||||
mint_keypair,
|
||||
Some(String::from("seed")),
|
||||
false,
|
||||
);
|
||||
|
@ -36,16 +44,23 @@ fn test_nonce_with_seed() {
|
|||
|
||||
#[test]
|
||||
fn test_nonce_with_authority() {
|
||||
full_battery_tests(TestValidator::with_no_fees(), None, true);
|
||||
let mint_keypair = Keypair::new();
|
||||
full_battery_tests(
|
||||
TestValidator::with_no_fees(mint_keypair.pubkey()),
|
||||
mint_keypair,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
fn full_battery_tests(
|
||||
test_validator: TestValidator,
|
||||
mint_keypair: Keypair,
|
||||
seed: Option<String>,
|
||||
use_nonce_authority: bool,
|
||||
) {
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
|
@ -202,10 +217,11 @@ fn full_battery_tests(
|
|||
#[test]
|
||||
fn test_create_account_with_seed() {
|
||||
solana_logger::setup();
|
||||
let test_validator = TestValidator::with_custom_fees(1);
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let offline_nonce_authority_signer = keypair_from_seed(&[1u8; 32]).unwrap();
|
||||
|
|
|
@ -2,15 +2,19 @@ use solana_cli::cli::{process_command, CliCommand, CliConfig};
|
|||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::test_validator::TestValidator;
|
||||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
#[test]
|
||||
fn test_cli_request_airdrop() {
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let mut bob_config = CliConfig::recent_for_tests();
|
||||
|
|
|
@ -26,9 +26,10 @@ use std::sync::mpsc::channel;
|
|||
|
||||
#[test]
|
||||
fn test_stake_delegation_force() {
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
|
@ -115,9 +116,10 @@ fn test_stake_delegation_force() {
|
|||
fn test_seed_stake_delegation_and_deactivation() {
|
||||
solana_logger::setup();
|
||||
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
|
@ -195,9 +197,10 @@ fn test_seed_stake_delegation_and_deactivation() {
|
|||
fn test_stake_delegation_and_deactivation() {
|
||||
solana_logger::setup();
|
||||
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
|
@ -271,9 +274,10 @@ fn test_stake_delegation_and_deactivation() {
|
|||
fn test_offline_stake_delegation_and_deactivation() {
|
||||
solana_logger::setup();
|
||||
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
|
@ -404,9 +408,10 @@ fn test_offline_stake_delegation_and_deactivation() {
|
|||
fn test_nonced_stake_delegation_and_deactivation() {
|
||||
solana_logger::setup();
|
||||
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
|
@ -519,9 +524,10 @@ fn test_nonced_stake_delegation_and_deactivation() {
|
|||
fn test_stake_authorize() {
|
||||
solana_logger::setup();
|
||||
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
|
@ -792,9 +798,10 @@ fn test_stake_authorize_with_fee_payer() {
|
|||
solana_logger::setup();
|
||||
const SIG_FEE: u64 = 42;
|
||||
|
||||
let test_validator = TestValidator::with_custom_fees(SIG_FEE);
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), SIG_FEE);
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
|
@ -917,9 +924,10 @@ fn test_stake_authorize_with_fee_payer() {
|
|||
fn test_stake_split() {
|
||||
solana_logger::setup();
|
||||
|
||||
let test_validator = TestValidator::with_custom_fees(1);
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
|
@ -1061,9 +1069,10 @@ fn test_stake_split() {
|
|||
fn test_stake_set_lockup() {
|
||||
solana_logger::setup();
|
||||
|
||||
let test_validator = TestValidator::with_custom_fees(1);
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
|
@ -1323,9 +1332,10 @@ fn test_stake_set_lockup() {
|
|||
fn test_offline_nonced_create_stake_account_and_withdraw() {
|
||||
solana_logger::setup();
|
||||
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
|
|
|
@ -22,10 +22,11 @@ use std::sync::mpsc::channel;
|
|||
#[test]
|
||||
fn test_transfer() {
|
||||
solana_logger::setup();
|
||||
let test_validator = TestValidator::with_custom_fees(1);
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
|
@ -243,10 +244,11 @@ fn test_transfer() {
|
|||
#[test]
|
||||
fn test_transfer_multisession_signing() {
|
||||
solana_logger::setup();
|
||||
let test_validator = TestValidator::with_custom_fees(1);
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let to_pubkey = Pubkey::new(&[1u8; 32]);
|
||||
|
@ -363,10 +365,11 @@ fn test_transfer_multisession_signing() {
|
|||
#[test]
|
||||
fn test_transfer_all() {
|
||||
solana_logger::setup();
|
||||
let test_validator = TestValidator::with_custom_fees(1);
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_custom_fees(mint_keypair.pubkey(), 1);
|
||||
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
|
|
|
@ -19,9 +19,10 @@ use std::sync::mpsc::channel;
|
|||
|
||||
#[test]
|
||||
fn test_vote_authorize_and_withdraw() {
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(test_validator.mint_keypair(), sender, None);
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
|
|
|
@ -58,6 +58,7 @@ solana-metrics = { path = "../metrics", version = "1.5.0" }
|
|||
solana-measure = { path = "../measure", version = "1.5.0" }
|
||||
solana-net-utils = { path = "../net-utils", version = "1.5.0" }
|
||||
solana-perf = { path = "../perf", version = "1.5.0" }
|
||||
solana-program-test = { path = "../program-test", version = "1.5.0" }
|
||||
solana-runtime = { path = "../runtime", version = "1.5.0" }
|
||||
solana-sdk = { path = "../sdk", version = "1.5.0" }
|
||||
solana-frozen-abi = { path = "../frozen-abi", version = "1.5.0" }
|
||||
|
|
|
@ -2,188 +2,279 @@ use {
|
|||
crate::{
|
||||
cluster_info::Node,
|
||||
gossip_service::discover_cluster,
|
||||
rpc::JsonRpcConfig,
|
||||
validator::{Validator, ValidatorConfig},
|
||||
},
|
||||
solana_ledger::create_new_tmp_ledger,
|
||||
solana_ledger::{blockstore::create_new_ledger, create_new_tmp_ledger},
|
||||
solana_runtime::{
|
||||
bank_forks::{CompressionType, SnapshotConfig, SnapshotVersion},
|
||||
genesis_utils::create_genesis_config_with_leader_ex,
|
||||
hardened_unpack::MAX_GENESIS_ARCHIVE_UNPACKED_SIZE,
|
||||
},
|
||||
solana_sdk::{
|
||||
fee_calculator::FeeRateGovernor,
|
||||
hash::Hash,
|
||||
native_token::sol_to_lamports,
|
||||
pubkey::Pubkey,
|
||||
rent::Rent,
|
||||
signature::{Keypair, Signer},
|
||||
signature::{read_keypair_file, write_keypair_file, Keypair, Signer},
|
||||
},
|
||||
std::{
|
||||
fs::remove_dir_all,
|
||||
net::SocketAddr,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
},
|
||||
std::{fs::remove_dir_all, net::SocketAddr, path::PathBuf, sync::Arc},
|
||||
};
|
||||
|
||||
pub struct TestValidatorConfig {
|
||||
pub struct TestValidatorGenesisConfig {
|
||||
pub fee_rate_governor: FeeRateGovernor,
|
||||
pub mint_lamports: u64,
|
||||
pub mint_address: Pubkey,
|
||||
pub rent: Rent,
|
||||
pub validator_identity_keypair: Keypair,
|
||||
pub validator_identity_lamports: u64,
|
||||
pub validator_stake_lamports: u64,
|
||||
}
|
||||
|
||||
impl Default for TestValidatorConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
fee_rate_governor: FeeRateGovernor::default(),
|
||||
mint_lamports: sol_to_lamports(500_000_000.),
|
||||
rent: Rent::default(),
|
||||
validator_identity_keypair: Keypair::new(),
|
||||
validator_identity_lamports: sol_to_lamports(500.),
|
||||
validator_stake_lamports: sol_to_lamports(1.),
|
||||
}
|
||||
}
|
||||
#[derive(Default)]
|
||||
pub struct TestValidatorStartConfig {
|
||||
pub rpc_config: JsonRpcConfig,
|
||||
pub rpc_ports: Option<(u16, u16)>, // (JsonRpc, JsonRpcPubSub), None == random ports
|
||||
}
|
||||
|
||||
pub struct TestValidator {
|
||||
validator: Validator,
|
||||
ledger_path: PathBuf,
|
||||
preserve_ledger: bool,
|
||||
|
||||
genesis_hash: Hash,
|
||||
mint_keypair: Keypair,
|
||||
vote_account_address: Pubkey,
|
||||
|
||||
tpu: SocketAddr,
|
||||
rpc_url: String,
|
||||
rpc_pubsub_url: String,
|
||||
}
|
||||
|
||||
impl Default for TestValidator {
|
||||
fn default() -> Self {
|
||||
Self::new(TestValidatorConfig::default())
|
||||
}
|
||||
rpc_url: String,
|
||||
tpu: SocketAddr,
|
||||
gossip: SocketAddr,
|
||||
validator: Validator,
|
||||
vote_account_address: Pubkey,
|
||||
}
|
||||
|
||||
impl TestValidator {
|
||||
pub fn with_no_fees() -> Self {
|
||||
Self::new(TestValidatorConfig {
|
||||
fee_rate_governor: FeeRateGovernor::new(0, 0),
|
||||
rent: Rent {
|
||||
lamports_per_byte_year: 1,
|
||||
exemption_threshold: 1.0,
|
||||
..Rent::default()
|
||||
/// The default test validator is intended to be generically suitable for unit testing.
|
||||
///
|
||||
/// It uses a unique temporary ledger that is deleted on `close` and randomly assigned ports.
|
||||
/// All test tokens will be minted into `mint_address`
|
||||
///
|
||||
/// This function panics on initialization failure.
|
||||
pub fn new(mint_address: Pubkey) -> Self {
|
||||
let ledger_path = Self::initialize_ledger(
|
||||
None,
|
||||
TestValidatorGenesisConfig {
|
||||
fee_rate_governor: FeeRateGovernor::default(),
|
||||
mint_address,
|
||||
rent: Rent::default(),
|
||||
},
|
||||
..TestValidatorConfig::default()
|
||||
})
|
||||
)
|
||||
.unwrap();
|
||||
Self::start(&ledger_path, TestValidatorStartConfig::default()).unwrap()
|
||||
}
|
||||
|
||||
pub fn with_custom_fees(target_lamports_per_signature: u64) -> Self {
|
||||
Self::new(TestValidatorConfig {
|
||||
fee_rate_governor: FeeRateGovernor::new(target_lamports_per_signature, 0),
|
||||
rent: Rent {
|
||||
lamports_per_byte_year: 1,
|
||||
exemption_threshold: 1.0,
|
||||
..Rent::default()
|
||||
/// Create a `TestValidator` with no transaction fees and minimal rent.
|
||||
///
|
||||
/// This function panics on initialization failure.
|
||||
pub fn with_no_fees(mint_address: Pubkey) -> Self {
|
||||
let ledger_path = Self::initialize_ledger(
|
||||
None,
|
||||
TestValidatorGenesisConfig {
|
||||
fee_rate_governor: FeeRateGovernor::new(0, 0),
|
||||
mint_address,
|
||||
rent: Rent {
|
||||
lamports_per_byte_year: 1,
|
||||
exemption_threshold: 1.0,
|
||||
..Rent::default()
|
||||
},
|
||||
},
|
||||
..TestValidatorConfig::default()
|
||||
})
|
||||
)
|
||||
.unwrap();
|
||||
Self::start(&ledger_path, TestValidatorStartConfig::default()).unwrap()
|
||||
}
|
||||
|
||||
pub fn new(config: TestValidatorConfig) -> Self {
|
||||
use solana_ledger::genesis_utils::{
|
||||
create_genesis_config_with_leader_ex, GenesisConfigInfo,
|
||||
};
|
||||
/// Create a `TestValidator` with custom transaction fees and minimal rent.
|
||||
///
|
||||
/// This function panics on initialization failure.
|
||||
pub fn with_custom_fees(mint_address: Pubkey, target_lamports_per_signature: u64) -> Self {
|
||||
let ledger_path = Self::initialize_ledger(
|
||||
None,
|
||||
TestValidatorGenesisConfig {
|
||||
fee_rate_governor: FeeRateGovernor::new(target_lamports_per_signature, 0),
|
||||
mint_address,
|
||||
rent: Rent {
|
||||
lamports_per_byte_year: 1,
|
||||
exemption_threshold: 1.0,
|
||||
..Rent::default()
|
||||
},
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
Self::start(&ledger_path, TestValidatorStartConfig::default()).unwrap()
|
||||
}
|
||||
|
||||
let TestValidatorConfig {
|
||||
/// Initialize the test validator's ledger directory
|
||||
///
|
||||
/// If `ledger_path` is `None`, a temporary ledger will be created. Otherwise the ledger will
|
||||
/// be initialized in the provided directory.
|
||||
///
|
||||
/// Returns the path to the ledger directory.
|
||||
pub fn initialize_ledger(
|
||||
ledger_path: Option<&Path>,
|
||||
config: TestValidatorGenesisConfig,
|
||||
) -> Result<PathBuf, Box<dyn std::error::Error>> {
|
||||
let TestValidatorGenesisConfig {
|
||||
fee_rate_governor,
|
||||
mint_lamports,
|
||||
mint_address,
|
||||
rent,
|
||||
validator_identity_keypair,
|
||||
validator_identity_lamports,
|
||||
validator_stake_lamports,
|
||||
} = config;
|
||||
let validator_identity_keypair = Arc::new(validator_identity_keypair);
|
||||
|
||||
let node = Node::new_localhost_with_pubkey(&validator_identity_keypair.pubkey());
|
||||
let validator_identity_keypair = Keypair::new();
|
||||
let validator_vote_account = Keypair::new();
|
||||
let validator_stake_account = Keypair::new();
|
||||
let validator_identity_lamports = sol_to_lamports(500.);
|
||||
let validator_stake_lamports = sol_to_lamports(1_000_000.);
|
||||
let mint_lamports = sol_to_lamports(500_000_000.);
|
||||
|
||||
let GenesisConfigInfo {
|
||||
mut genesis_config,
|
||||
mint_keypair,
|
||||
voting_keypair: vote_account_keypair,
|
||||
} = create_genesis_config_with_leader_ex(
|
||||
let initial_accounts = solana_program_test::programs::spl_programs(&rent);
|
||||
let genesis_config = create_genesis_config_with_leader_ex(
|
||||
mint_lamports,
|
||||
&node.info.id,
|
||||
&Keypair::new(),
|
||||
&Keypair::new().pubkey(),
|
||||
&mint_address,
|
||||
&validator_identity_keypair.pubkey(),
|
||||
&validator_vote_account.pubkey(),
|
||||
&validator_stake_account.pubkey(),
|
||||
validator_stake_lamports,
|
||||
validator_identity_lamports,
|
||||
fee_rate_governor,
|
||||
rent,
|
||||
solana_sdk::genesis_config::ClusterType::Development,
|
||||
initial_accounts,
|
||||
);
|
||||
|
||||
genesis_config.rent = rent;
|
||||
genesis_config.fee_rate_governor = fee_rate_governor;
|
||||
|
||||
let (ledger_path, blockhash) = create_new_tmp_ledger!(&genesis_config);
|
||||
|
||||
let config = ValidatorConfig {
|
||||
rpc_addrs: Some((node.info.rpc, node.info.rpc_pubsub)),
|
||||
..ValidatorConfig::default()
|
||||
let ledger_path = match ledger_path {
|
||||
None => create_new_tmp_ledger!(&genesis_config).0,
|
||||
Some(ledger_path) => {
|
||||
let _ = create_new_ledger(
|
||||
ledger_path,
|
||||
&genesis_config,
|
||||
MAX_GENESIS_ARCHIVE_UNPACKED_SIZE,
|
||||
solana_ledger::blockstore_db::AccessType::PrimaryOnly,
|
||||
)
|
||||
.map_err(|err| {
|
||||
format!(
|
||||
"Failed to create ledger at {}: {}",
|
||||
ledger_path.display(),
|
||||
err
|
||||
)
|
||||
})?;
|
||||
ledger_path.to_path_buf()
|
||||
}
|
||||
};
|
||||
|
||||
let vote_account_address = vote_account_keypair.pubkey();
|
||||
write_keypair_file(
|
||||
&validator_identity_keypair,
|
||||
ledger_path.join("validator-keypair.json").to_str().unwrap(),
|
||||
)?;
|
||||
write_keypair_file(
|
||||
&validator_vote_account,
|
||||
ledger_path
|
||||
.join("vote-account-keypair.json")
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
)?;
|
||||
|
||||
Ok(ledger_path)
|
||||
}
|
||||
|
||||
/// Starts a TestValidator at the provided ledger directory
|
||||
pub fn start(
|
||||
ledger_path: &Path,
|
||||
config: TestValidatorStartConfig,
|
||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let validator_identity_keypair =
|
||||
read_keypair_file(ledger_path.join("validator-keypair.json").to_str().unwrap())?;
|
||||
let validator_vote_account = read_keypair_file(
|
||||
ledger_path
|
||||
.join("vote-account-keypair.json")
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
)?;
|
||||
|
||||
let mut node = Node::new_localhost_with_pubkey(&validator_identity_keypair.pubkey());
|
||||
if let Some((rpc, rpc_pubsub)) = config.rpc_ports {
|
||||
node.info.rpc = SocketAddr::new(node.info.gossip.ip(), rpc);
|
||||
node.info.rpc_pubsub = SocketAddr::new(node.info.gossip.ip(), rpc_pubsub);
|
||||
}
|
||||
|
||||
let vote_account_address = validator_vote_account.pubkey();
|
||||
let rpc_url = format!("http://{}:{}", node.info.rpc.ip(), node.info.rpc.port());
|
||||
let rpc_pubsub_url = format!("ws://{}/", node.info.rpc_pubsub);
|
||||
let tpu = node.info.tpu;
|
||||
let gossip = node.info.gossip;
|
||||
|
||||
let validator_config = ValidatorConfig {
|
||||
rpc_addrs: Some((node.info.rpc, node.info.rpc_pubsub)),
|
||||
rpc_config: config.rpc_config,
|
||||
accounts_hash_interval_slots: 100,
|
||||
account_paths: vec![ledger_path.join("accounts")],
|
||||
poh_verify: false, // Skip PoH verification of ledger on startup for speed
|
||||
snapshot_config: Some(SnapshotConfig {
|
||||
snapshot_interval_slots: 100,
|
||||
snapshot_path: ledger_path.join("snapshot"),
|
||||
snapshot_package_output_path: ledger_path.to_path_buf(),
|
||||
compression: CompressionType::NoCompression,
|
||||
snapshot_version: SnapshotVersion::default(),
|
||||
}),
|
||||
..ValidatorConfig::default()
|
||||
};
|
||||
|
||||
let validator = Validator::new(
|
||||
node,
|
||||
&validator_identity_keypair,
|
||||
&Arc::new(validator_identity_keypair),
|
||||
&ledger_path,
|
||||
&vote_account_keypair.pubkey(),
|
||||
vec![Arc::new(vote_account_keypair)],
|
||||
&validator_vote_account.pubkey(),
|
||||
vec![Arc::new(validator_vote_account)],
|
||||
None,
|
||||
&config,
|
||||
&validator_config,
|
||||
);
|
||||
|
||||
// Needed to avoid panics in `solana-responder-gossip` in tests that create a number of
|
||||
// test validators concurrently...
|
||||
discover_cluster(&gossip, 1).expect("TestValidator startup failed");
|
||||
|
||||
TestValidator {
|
||||
Ok(TestValidator {
|
||||
ledger_path: ledger_path.to_path_buf(),
|
||||
rpc_pubsub_url,
|
||||
rpc_url,
|
||||
gossip,
|
||||
tpu,
|
||||
validator,
|
||||
vote_account_address,
|
||||
mint_keypair,
|
||||
ledger_path,
|
||||
genesis_hash: blockhash,
|
||||
tpu,
|
||||
rpc_url,
|
||||
rpc_pubsub_url,
|
||||
preserve_ledger: false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Stop the test validator and delete its ledger directory
|
||||
pub fn close(self) {
|
||||
self.validator.close().unwrap();
|
||||
if !self.preserve_ledger {
|
||||
remove_dir_all(&self.ledger_path).unwrap();
|
||||
}
|
||||
remove_dir_all(&self.ledger_path).unwrap();
|
||||
}
|
||||
|
||||
/// Return the test validator's TPU address
|
||||
pub fn tpu(&self) -> &SocketAddr {
|
||||
&self.tpu
|
||||
}
|
||||
|
||||
pub fn mint_keypair(&self) -> Keypair {
|
||||
Keypair::from_bytes(&self.mint_keypair.to_bytes()).unwrap()
|
||||
/// Return the test validator's Gossip address
|
||||
pub fn gossip(&self) -> &SocketAddr {
|
||||
&self.gossip
|
||||
}
|
||||
|
||||
/// Return the test validator's JSON RPC URL
|
||||
pub fn rpc_url(&self) -> String {
|
||||
self.rpc_url.clone()
|
||||
}
|
||||
|
||||
/// Return the test validator's JSON RPC PubSub URL
|
||||
pub fn rpc_pubsub_url(&self) -> String {
|
||||
self.rpc_pubsub_url.clone()
|
||||
}
|
||||
|
||||
pub fn genesis_hash(&self) -> Hash {
|
||||
self.genesis_hash
|
||||
}
|
||||
|
||||
/// Return the vote account address of the validator
|
||||
pub fn vote_account_address(&self) -> Pubkey {
|
||||
self.vote_account_address
|
||||
}
|
||||
|
|
|
@ -490,6 +490,13 @@ impl Validator {
|
|||
|
||||
let (snapshot_packager_service, snapshot_config_and_package_sender) =
|
||||
if let Some(snapshot_config) = config.snapshot_config.clone() {
|
||||
if is_snapshot_config_invalid(
|
||||
snapshot_config.snapshot_interval_slots,
|
||||
config.accounts_hash_interval_slots,
|
||||
) {
|
||||
error!("Snapshot config is invalid");
|
||||
}
|
||||
|
||||
// Start a snapshot packaging service
|
||||
let (sender, receiver) = channel();
|
||||
let snapshot_packager_service =
|
||||
|
@ -1218,6 +1225,15 @@ fn cleanup_accounts_path(account_path: &std::path::Path) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_snapshot_config_invalid(
|
||||
snapshot_interval_slots: u64,
|
||||
accounts_hash_interval_slots: u64,
|
||||
) -> bool {
|
||||
snapshot_interval_slots != 0
|
||||
&& (snapshot_interval_slots < accounts_hash_interval_slots
|
||||
|| snapshot_interval_slots % accounts_hash_interval_slots != 0)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -1388,4 +1404,13 @@ mod tests {
|
|||
rpc_override_health_check
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interval_check() {
|
||||
assert!(!is_snapshot_config_invalid(0, 100));
|
||||
assert!(is_snapshot_config_invalid(1, 100));
|
||||
assert!(is_snapshot_config_invalid(230, 100));
|
||||
assert!(!is_snapshot_config_invalid(500, 100));
|
||||
assert!(!is_snapshot_config_invalid(5, 5));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,11 @@ use solana_runtime::{
|
|||
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||
};
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig, native_token::sol_to_lamports, rpc_port,
|
||||
signature::Signer, system_transaction,
|
||||
commitment_config::CommitmentConfig,
|
||||
native_token::sol_to_lamports,
|
||||
rpc_port,
|
||||
signature::{Keypair, Signer},
|
||||
system_transaction,
|
||||
};
|
||||
use std::{
|
||||
net::{IpAddr, SocketAddr},
|
||||
|
@ -30,8 +33,8 @@ use systemstat::Ipv4Addr;
|
|||
fn test_rpc_client() {
|
||||
solana_logger::setup();
|
||||
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let alice = test_validator.mint_keypair();
|
||||
let alice = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(alice.pubkey());
|
||||
|
||||
let bob_pubkey = solana_sdk::pubkey::new_rand();
|
||||
|
||||
|
|
|
@ -14,7 +14,10 @@ use solana_client::{
|
|||
};
|
||||
use solana_core::{rpc_pubsub::gen_client::Client as PubsubClient, test_validator::TestValidator};
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig, hash::Hash, signature::Signer, system_transaction,
|
||||
commitment_config::CommitmentConfig,
|
||||
hash::Hash,
|
||||
signature::{Keypair, Signer},
|
||||
system_transaction,
|
||||
transaction::Transaction,
|
||||
};
|
||||
use std::{
|
||||
|
@ -52,8 +55,8 @@ fn post_rpc(request: Value, rpc_url: &str) -> Value {
|
|||
fn test_rpc_send_tx() {
|
||||
solana_logger::setup();
|
||||
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let alice = test_validator.mint_keypair();
|
||||
let alice = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(alice.pubkey());
|
||||
let rpc_url = test_validator.rpc_url();
|
||||
|
||||
let bob_pubkey = solana_sdk::pubkey::new_rand();
|
||||
|
@ -113,7 +116,8 @@ fn test_rpc_send_tx() {
|
|||
fn test_rpc_invalid_requests() {
|
||||
solana_logger::setup();
|
||||
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let alice = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(alice.pubkey());
|
||||
let rpc_url = test_validator.rpc_url();
|
||||
|
||||
let bob_pubkey = solana_sdk::pubkey::new_rand();
|
||||
|
@ -145,12 +149,15 @@ fn test_rpc_invalid_requests() {
|
|||
fn test_rpc_subscriptions() {
|
||||
solana_logger::setup();
|
||||
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let alice = test_validator.mint_keypair();
|
||||
let alice = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(alice.pubkey());
|
||||
|
||||
let transactions_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||
transactions_socket.connect(test_validator.tpu()).unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
let recent_blockhash = rpc_client.get_recent_blockhash().unwrap().0;
|
||||
|
||||
// Create transaction signatures to subscribe to
|
||||
let transactions: Vec<Transaction> = (0..1000)
|
||||
.map(|_| {
|
||||
|
@ -158,7 +165,7 @@ fn test_rpc_subscriptions() {
|
|||
&alice,
|
||||
&solana_sdk::pubkey::new_rand(),
|
||||
1,
|
||||
test_validator.genesis_hash(),
|
||||
recent_blockhash,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
pub use solana_runtime::genesis_utils::{
|
||||
bootstrap_validator_stake_lamports, create_genesis_config_with_leader,
|
||||
create_genesis_config_with_leader_ex, GenesisConfigInfo,
|
||||
bootstrap_validator_stake_lamports, create_genesis_config_with_leader, GenesisConfigInfo,
|
||||
};
|
||||
|
||||
// same as genesis_config::create_genesis_config, but with bootstrap_validator staking logic
|
||||
|
|
|
@ -39,6 +39,7 @@ use {
|
|||
|
||||
// Export types so test clients can limit their solana crate dependencies
|
||||
pub use solana_banks_client::BanksClient;
|
||||
pub mod programs;
|
||||
|
||||
#[macro_use]
|
||||
extern crate solana_bpf_loader_program;
|
||||
|
@ -358,24 +359,6 @@ fn read_file<P: AsRef<Path>>(path: P) -> Vec<u8> {
|
|||
file_data
|
||||
}
|
||||
|
||||
mod spl_token {
|
||||
solana_sdk::declare_id!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
|
||||
}
|
||||
mod spl_memo {
|
||||
solana_sdk::declare_id!("Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo");
|
||||
}
|
||||
mod spl_associated_token_account {
|
||||
solana_sdk::declare_id!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
|
||||
}
|
||||
static SPL_PROGRAMS: &[(Pubkey, &[u8])] = &[
|
||||
(spl_token::ID, include_bytes!("programs/spl_token-2.0.6.so")),
|
||||
(spl_memo::ID, include_bytes!("programs/spl_memo-1.0.0.so")),
|
||||
(
|
||||
spl_associated_token_account::ID,
|
||||
include_bytes!("programs/spl_associated-token-account-1.0.1.so"),
|
||||
),
|
||||
];
|
||||
|
||||
pub struct ProgramTest {
|
||||
accounts: Vec<(Pubkey, Account)>,
|
||||
builtins: Vec<Builtin>,
|
||||
|
@ -614,17 +597,8 @@ impl ProgramTest {
|
|||
}
|
||||
|
||||
// Add commonly-used SPL programs as a convenience to the user
|
||||
for (program_id, elf) in SPL_PROGRAMS.iter() {
|
||||
bank.store_account(
|
||||
program_id,
|
||||
&Account {
|
||||
lamports: Rent::default().minimum_balance(elf.len()).min(1),
|
||||
data: elf.to_vec(),
|
||||
owner: solana_program::bpf_loader::id(),
|
||||
executable: true,
|
||||
rent_epoch: 0,
|
||||
},
|
||||
)
|
||||
for (program_id, account) in programs::spl_programs(&Rent::default()).iter() {
|
||||
bank.store_account(program_id, &account);
|
||||
}
|
||||
|
||||
// User-supplied additional builtins
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
use solana_sdk::{account::Account, pubkey::Pubkey, rent::Rent};
|
||||
|
||||
mod spl_token {
|
||||
solana_sdk::declare_id!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
|
||||
}
|
||||
mod spl_memo {
|
||||
solana_sdk::declare_id!("Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo");
|
||||
}
|
||||
mod spl_associated_token_account {
|
||||
solana_sdk::declare_id!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
|
||||
}
|
||||
|
||||
static SPL_PROGRAMS: &[(Pubkey, &[u8])] = &[
|
||||
(spl_token::ID, include_bytes!("programs/spl_token-2.0.6.so")),
|
||||
(spl_memo::ID, include_bytes!("programs/spl_memo-1.0.0.so")),
|
||||
(
|
||||
spl_associated_token_account::ID,
|
||||
include_bytes!("programs/spl_associated-token-account-1.0.1.so"),
|
||||
),
|
||||
];
|
||||
|
||||
pub fn spl_programs(rent: &Rent) -> Vec<(Pubkey, Account)> {
|
||||
SPL_PROGRAMS
|
||||
.iter()
|
||||
.map(|(program_id, elf)| {
|
||||
(
|
||||
*program_id,
|
||||
Account {
|
||||
lamports: rent.minimum_balance(elf.len()).min(1),
|
||||
data: elf.to_vec(),
|
||||
owner: solana_program::bpf_loader::id(),
|
||||
executable: true,
|
||||
rent_epoch: 0,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
|
@ -78,16 +78,30 @@ pub fn create_genesis_config_with_vote_accounts_and_cluster_type(
|
|||
assert!(!voting_keypairs.is_empty());
|
||||
assert_eq!(voting_keypairs.len(), stakes.len());
|
||||
|
||||
let mut genesis_config_info = create_genesis_config_with_leader_ex(
|
||||
let mint_keypair = Keypair::new();
|
||||
let voting_keypair =
|
||||
Keypair::from_bytes(&voting_keypairs[0].borrow().vote_keypair.to_bytes()).unwrap();
|
||||
|
||||
let genesis_config = create_genesis_config_with_leader_ex(
|
||||
mint_lamports,
|
||||
&mint_keypair.pubkey(),
|
||||
&voting_keypairs[0].borrow().node_keypair.pubkey(),
|
||||
&voting_keypairs[0].borrow().vote_keypair,
|
||||
&voting_keypairs[0].borrow().vote_keypair.pubkey(),
|
||||
&voting_keypairs[0].borrow().stake_keypair.pubkey(),
|
||||
stakes[0],
|
||||
VALIDATOR_LAMPORTS,
|
||||
FeeRateGovernor::new(0, 0), // most tests can't handle transaction fees
|
||||
Rent::free(), // most tests don't expect rent
|
||||
cluster_type,
|
||||
vec![],
|
||||
);
|
||||
|
||||
let mut genesis_config_info = GenesisConfigInfo {
|
||||
genesis_config,
|
||||
voting_keypair,
|
||||
mint_keypair,
|
||||
};
|
||||
|
||||
for (validator_voting_keypairs, stake) in voting_keypairs[1..].iter().zip(&stakes[1..]) {
|
||||
let node_pubkey = validator_voting_keypairs.borrow().node_keypair.pubkey();
|
||||
let vote_pubkey = validator_voting_keypairs.borrow().vote_keypair.pubkey();
|
||||
|
@ -120,15 +134,28 @@ pub fn create_genesis_config_with_leader(
|
|||
validator_pubkey: &Pubkey,
|
||||
validator_stake_lamports: u64,
|
||||
) -> GenesisConfigInfo {
|
||||
create_genesis_config_with_leader_ex(
|
||||
let mint_keypair = Keypair::new();
|
||||
let voting_keypair = Keypair::new();
|
||||
|
||||
let genesis_config = create_genesis_config_with_leader_ex(
|
||||
mint_lamports,
|
||||
&mint_keypair.pubkey(),
|
||||
validator_pubkey,
|
||||
&Keypair::new(),
|
||||
&voting_keypair.pubkey(),
|
||||
&solana_sdk::pubkey::new_rand(),
|
||||
validator_stake_lamports,
|
||||
VALIDATOR_LAMPORTS,
|
||||
FeeRateGovernor::new(0, 0), // most tests can't handle transaction fees
|
||||
Rent::free(), // most tests don't expect rent
|
||||
ClusterType::Development,
|
||||
)
|
||||
vec![],
|
||||
);
|
||||
|
||||
GenesisConfigInfo {
|
||||
genesis_config,
|
||||
voting_keypair,
|
||||
mint_keypair,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn activate_all_features(genesis_config: &mut GenesisConfig) {
|
||||
|
@ -146,55 +173,48 @@ pub fn activate_all_features(genesis_config: &mut GenesisConfig) {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn create_genesis_config_with_leader_ex(
|
||||
mint_lamports: u64,
|
||||
mint_pubkey: &Pubkey,
|
||||
validator_pubkey: &Pubkey,
|
||||
validator_vote_account_keypair: &Keypair,
|
||||
validator_vote_account_pubkey: &Pubkey,
|
||||
validator_stake_account_pubkey: &Pubkey,
|
||||
validator_stake_lamports: u64,
|
||||
validator_lamports: u64,
|
||||
fee_rate_governor: FeeRateGovernor,
|
||||
rent: Rent,
|
||||
cluster_type: ClusterType,
|
||||
) -> GenesisConfigInfo {
|
||||
let mint_keypair = Keypair::new();
|
||||
mut initial_accounts: Vec<(Pubkey, Account)>,
|
||||
) -> GenesisConfig {
|
||||
let validator_vote_account = vote_state::create_account(
|
||||
&validator_vote_account_keypair.pubkey(),
|
||||
&validator_vote_account_pubkey,
|
||||
&validator_pubkey,
|
||||
0,
|
||||
validator_stake_lamports,
|
||||
);
|
||||
|
||||
let fee_rate_governor = FeeRateGovernor::new(0, 0); // most tests can't handle transaction fees
|
||||
let rent = Rent::free(); // most tests don't expect rent
|
||||
|
||||
let validator_stake_account = stake_state::create_account(
|
||||
validator_stake_account_pubkey,
|
||||
&validator_vote_account_keypair.pubkey(),
|
||||
&validator_vote_account_pubkey,
|
||||
&validator_vote_account,
|
||||
&rent,
|
||||
validator_stake_lamports,
|
||||
);
|
||||
|
||||
let accounts = [
|
||||
(
|
||||
mint_keypair.pubkey(),
|
||||
Account::new(mint_lamports, 0, &system_program::id()),
|
||||
),
|
||||
(
|
||||
*validator_pubkey,
|
||||
Account::new(validator_lamports, 0, &system_program::id()),
|
||||
),
|
||||
(
|
||||
validator_vote_account_keypair.pubkey(),
|
||||
validator_vote_account,
|
||||
),
|
||||
(*validator_stake_account_pubkey, validator_stake_account),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
initial_accounts.push((
|
||||
*mint_pubkey,
|
||||
Account::new(mint_lamports, 0, &system_program::id()),
|
||||
));
|
||||
initial_accounts.push((
|
||||
*validator_pubkey,
|
||||
Account::new(validator_lamports, 0, &system_program::id()),
|
||||
));
|
||||
initial_accounts.push((*validator_vote_account_pubkey, validator_vote_account));
|
||||
initial_accounts.push((*validator_stake_account_pubkey, validator_stake_account));
|
||||
|
||||
let mut genesis_config = GenesisConfig {
|
||||
accounts,
|
||||
accounts: initial_accounts.iter().cloned().collect(),
|
||||
fee_rate_governor,
|
||||
rent,
|
||||
cluster_type,
|
||||
|
@ -206,9 +226,5 @@ pub fn create_genesis_config_with_leader_ex(
|
|||
activate_all_features(&mut genesis_config);
|
||||
}
|
||||
|
||||
GenesisConfigInfo {
|
||||
genesis_config,
|
||||
mint_keypair,
|
||||
voting_keypair: Keypair::from_bytes(&validator_vote_account_keypair.to_bytes()).unwrap(),
|
||||
}
|
||||
genesis_config
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ else
|
|||
solana-stake-monitor
|
||||
solana-stake-o-matic
|
||||
solana-sys-tuner
|
||||
solana-test-validator
|
||||
solana-tokens
|
||||
solana-validator
|
||||
solana-watchtower
|
||||
|
|
|
@ -3,6 +3,3 @@ pub const DEFAULT_RPC_PORT: u16 = 8899;
|
|||
|
||||
/// Default port number for JSON RPC pubsub
|
||||
pub const DEFAULT_RPC_PUBSUB_PORT: u16 = 8900;
|
||||
|
||||
/// Default port number for Banks RPC API
|
||||
pub const DEFAULT_RPC_BANKS_PORT: u16 = 8901;
|
||||
|
|
|
@ -1038,7 +1038,7 @@ mod tests {
|
|||
use solana_core::test_validator::TestValidator;
|
||||
use solana_sdk::{
|
||||
clock::DEFAULT_MS_PER_SLOT,
|
||||
signature::{read_keypair_file, write_keypair_file},
|
||||
signature::{read_keypair_file, write_keypair_file, Signer},
|
||||
};
|
||||
use solana_stake_program::stake_instruction::StakeInstruction;
|
||||
|
||||
|
@ -1057,8 +1057,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_process_token_allocations() {
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let alice = test_validator.mint_keypair();
|
||||
let alice = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(alice.pubkey());
|
||||
let url = test_validator.rpc_url();
|
||||
|
||||
let client = RpcClient::new_with_commitment(url, CommitmentConfig::recent());
|
||||
|
@ -1069,8 +1069,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_process_transfer_amount_allocations() {
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let alice = test_validator.mint_keypair();
|
||||
let alice = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(alice.pubkey());
|
||||
let url = test_validator.rpc_url();
|
||||
|
||||
let client = RpcClient::new_with_commitment(url, CommitmentConfig::recent());
|
||||
|
@ -1081,8 +1081,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_process_stake_allocations() {
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let alice = test_validator.mint_keypair();
|
||||
let alice = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(alice.pubkey());
|
||||
let url = test_validator.rpc_url();
|
||||
|
||||
let client = RpcClient::new_with_commitment(url, CommitmentConfig::recent());
|
||||
|
@ -1398,8 +1398,8 @@ mod tests {
|
|||
let fees = 10_000;
|
||||
let fees_in_sol = lamports_to_sol(fees);
|
||||
|
||||
let test_validator = TestValidator::with_custom_fees(fees);
|
||||
let alice = test_validator.mint_keypair();
|
||||
let alice = Keypair::new();
|
||||
let test_validator = TestValidator::with_custom_fees(alice.pubkey(), fees);
|
||||
let url = test_validator.rpc_url();
|
||||
|
||||
let client = RpcClient::new_with_commitment(url, CommitmentConfig::recent());
|
||||
|
@ -1485,8 +1485,8 @@ mod tests {
|
|||
fn test_check_payer_balances_distribute_tokens_separate_payers() {
|
||||
let fees = 10_000;
|
||||
let fees_in_sol = lamports_to_sol(fees);
|
||||
let test_validator = TestValidator::with_custom_fees(fees);
|
||||
let alice = test_validator.mint_keypair();
|
||||
let alice = Keypair::new();
|
||||
let test_validator = TestValidator::with_custom_fees(alice.pubkey(), fees);
|
||||
let url = test_validator.rpc_url();
|
||||
|
||||
let client = RpcClient::new_with_commitment(url, CommitmentConfig::recent());
|
||||
|
@ -1598,8 +1598,8 @@ mod tests {
|
|||
fn test_check_payer_balances_distribute_stakes_single_payer() {
|
||||
let fees = 10_000;
|
||||
let fees_in_sol = lamports_to_sol(fees);
|
||||
let test_validator = TestValidator::with_custom_fees(fees);
|
||||
let alice = test_validator.mint_keypair();
|
||||
let alice = Keypair::new();
|
||||
let test_validator = TestValidator::with_custom_fees(alice.pubkey(), fees);
|
||||
let url = test_validator.rpc_url();
|
||||
let client = RpcClient::new_with_commitment(url, CommitmentConfig::recent());
|
||||
test_validator_block_0_fee_workaround(&client);
|
||||
|
@ -1707,8 +1707,8 @@ mod tests {
|
|||
fn test_check_payer_balances_distribute_stakes_separate_payers() {
|
||||
let fees = 10_000;
|
||||
let fees_in_sol = lamports_to_sol(fees);
|
||||
let test_validator = TestValidator::with_custom_fees(fees);
|
||||
let alice = test_validator.mint_keypair();
|
||||
let alice = Keypair::new();
|
||||
let test_validator = TestValidator::with_custom_fees(alice.pubkey(), fees);
|
||||
let url = test_validator.rpc_url();
|
||||
|
||||
let client = RpcClient::new_with_commitment(url, CommitmentConfig::recent());
|
||||
|
@ -2025,8 +2025,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_distribute_allocations_dump_db() {
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let sender_keypair = test_validator.mint_keypair();
|
||||
let sender_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(sender_keypair.pubkey());
|
||||
let url = test_validator.rpc_url();
|
||||
let client = RpcClient::new_with_commitment(url, CommitmentConfig::recent());
|
||||
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_core::test_validator::TestValidator;
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use solana_tokens::commands::test_process_distribute_tokens_with_client;
|
||||
|
||||
#[test]
|
||||
fn test_process_distribute_with_rpc_client() {
|
||||
solana_logger::setup();
|
||||
|
||||
let test_validator = TestValidator::with_no_fees();
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
|
||||
let client = RpcClient::new(test_validator.rpc_url());
|
||||
test_process_distribute_tokens_with_client(&client, test_validator.mint_keypair(), None);
|
||||
test_process_distribute_tokens_with_client(&client, mint_keypair, None);
|
||||
|
||||
test_validator.close();
|
||||
}
|
||||
|
|
|
@ -7,15 +7,18 @@ version = "1.5.0"
|
|||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
default-run = "solana-validator"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.1"
|
||||
chrono = { version = "0.4.11", features = ["serde"] }
|
||||
console = "0.11.3"
|
||||
indicatif = "0.15.0"
|
||||
log = "0.4.8"
|
||||
rand = "0.7.0"
|
||||
serde_json = "1.0.56"
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.5.0" }
|
||||
solana-cli-config = { path = "../cli-config", version = "1.5.0" }
|
||||
solana-client = { path = "../client", version = "1.5.0" }
|
||||
solana-core = { path = "../core", version = "1.5.0" }
|
||||
solana-download-utils = { path = "../download-utils", version = "1.5.0" }
|
||||
|
@ -29,18 +32,11 @@ solana-runtime = { path = "../runtime", version = "1.5.0" }
|
|||
solana-sdk = { path = "../sdk", version = "1.5.0" }
|
||||
solana-version = { path = "../version", version = "1.5.0" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.5.0" }
|
||||
solana-vote-signer = { path = "../vote-signer", version = "1.5.0" }
|
||||
symlink = "0.1.0"
|
||||
|
||||
[target."cfg(unix)".dependencies]
|
||||
libc = "0.2.72"
|
||||
signal-hook = "0.1.15"
|
||||
|
||||
#[[bin]]
|
||||
#name = "solana-validator"
|
||||
#path = "src/main.rs"
|
||||
#
|
||||
#[lib]
|
||||
#name = "solana_validator"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
here="$(dirname "$0")"
|
||||
set -x
|
||||
exec cargo run --manifest-path="$here"/Cargo.toml --bin solana-test-validator -- "$@"
|
|
@ -0,0 +1,262 @@
|
|||
use {
|
||||
clap::{value_t_or_exit, App, Arg},
|
||||
console::style,
|
||||
indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle},
|
||||
solana_clap_utils::{input_parsers::pubkey_of, input_validators::is_pubkey},
|
||||
solana_client::{client_error, rpc_client::RpcClient},
|
||||
solana_core::rpc::JsonRpcConfig,
|
||||
solana_sdk::{
|
||||
clock::{Slot, DEFAULT_TICKS_PER_SLOT, MS_PER_TICK},
|
||||
commitment_config::CommitmentConfig,
|
||||
fee_calculator::FeeRateGovernor,
|
||||
rent::Rent,
|
||||
rpc_port,
|
||||
signature::{read_keypair_file, Signer},
|
||||
},
|
||||
solana_validator::{start_logger, test_validator::*},
|
||||
std::{
|
||||
path::PathBuf,
|
||||
process::exit,
|
||||
thread::sleep,
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum Output {
|
||||
None,
|
||||
Log,
|
||||
Dashboard,
|
||||
}
|
||||
|
||||
/// Creates a new process bar for processing that will take an unknown amount of time
|
||||
fn new_spinner_progress_bar() -> ProgressBar {
|
||||
let progress_bar = ProgressBar::new(42);
|
||||
progress_bar.set_draw_target(ProgressDrawTarget::stdout());
|
||||
progress_bar
|
||||
.set_style(ProgressStyle::default_spinner().template("{spinner:.green} {wide_msg}"));
|
||||
progress_bar.enable_steady_tick(100);
|
||||
progress_bar
|
||||
}
|
||||
|
||||
/// Pretty print a "name value"
|
||||
fn println_name_value(name: &str, value: &str) {
|
||||
println!("{} {}", style(name).bold(), value);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let default_rpc_port = rpc_port::DEFAULT_RPC_PORT.to_string();
|
||||
|
||||
let matches = App::new("solana-test-validator").about("Test Validator")
|
||||
.version(solana_version::version!())
|
||||
.arg({
|
||||
let arg = Arg::with_name("config_file")
|
||||
.short("C")
|
||||
.long("config")
|
||||
.value_name("PATH")
|
||||
.takes_value(true)
|
||||
.help("Configuration file to use");
|
||||
if let Some(ref config_file) = *solana_cli_config::CONFIG_FILE {
|
||||
arg.default_value(&config_file)
|
||||
} else {
|
||||
arg
|
||||
}
|
||||
})
|
||||
.arg(
|
||||
Arg::with_name("mint_address")
|
||||
.long("mint")
|
||||
.value_name("PUBKEY")
|
||||
.validator(is_pubkey)
|
||||
.takes_value(true)
|
||||
.help("Address of the mint account that will receive all the initial tokens [default: client keypair]"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("ledger_path")
|
||||
.short("l")
|
||||
.long("ledger")
|
||||
.value_name("DIR")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.default_value("test-ledger")
|
||||
.help("Use DIR as ledger location"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("quiet")
|
||||
.short("q")
|
||||
.long("quiet")
|
||||
.takes_value(false)
|
||||
.conflicts_with("log")
|
||||
.help("Quiet mode: suppress normal output")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("log")
|
||||
.long("log")
|
||||
.takes_value(false)
|
||||
.conflicts_with("quiet")
|
||||
.help("Log mode: stream the validator log")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("rpc_port")
|
||||
.long("rpc-port")
|
||||
.value_name("PORT")
|
||||
.takes_value(true)
|
||||
.default_value(&default_rpc_port)
|
||||
.validator(solana_validator::port_validator)
|
||||
.help("Use this port for JSON RPC and the next port for the RPC websocket"),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let cli_config = if let Some(config_file) = matches.value_of("config_file") {
|
||||
solana_cli_config::Config::load(config_file).unwrap_or_default()
|
||||
} else {
|
||||
solana_cli_config::Config::default()
|
||||
};
|
||||
|
||||
let mint_address = pubkey_of(&matches, "mint_address").unwrap_or_else(|| {
|
||||
read_keypair_file(&cli_config.keypair_path)
|
||||
.unwrap_or_else(|err| {
|
||||
eprintln!(
|
||||
"Error: Unable to read keypair file {}: {}",
|
||||
cli_config.keypair_path, err
|
||||
);
|
||||
exit(1);
|
||||
})
|
||||
.pubkey()
|
||||
});
|
||||
|
||||
let ledger_path = value_t_or_exit!(matches, "ledger_path", PathBuf);
|
||||
let output = if matches.is_present("quiet") {
|
||||
Output::None
|
||||
} else if matches.is_present("log") {
|
||||
Output::Log
|
||||
} else {
|
||||
Output::Dashboard
|
||||
};
|
||||
|
||||
let rpc_ports = {
|
||||
let rpc_port = value_t_or_exit!(matches, "rpc_port", u16);
|
||||
(rpc_port, rpc_port + 1)
|
||||
};
|
||||
|
||||
if !ledger_path.exists() {
|
||||
let _progress_bar = if output == Output::Dashboard {
|
||||
println_name_value("Mint address:", &mint_address.to_string());
|
||||
let progress_bar = new_spinner_progress_bar();
|
||||
progress_bar.set_message("Creating ledger...");
|
||||
Some(progress_bar)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
TestValidator::initialize_ledger(
|
||||
Some(&ledger_path),
|
||||
TestValidatorGenesisConfig {
|
||||
mint_address,
|
||||
fee_rate_governor: FeeRateGovernor::default(),
|
||||
rent: Rent::default(),
|
||||
},
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
eprintln!(
|
||||
"Error: failed to initialize ledger at {}: {}",
|
||||
ledger_path.display(),
|
||||
err
|
||||
);
|
||||
exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
let validator_log_symlink = ledger_path.join("validator.log");
|
||||
let logfile = if output != Output::Log {
|
||||
let validator_log_with_timestamp = format!(
|
||||
"validator-{}.log",
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis()
|
||||
);
|
||||
|
||||
let _ = std::fs::remove_file(&validator_log_symlink);
|
||||
symlink::symlink_file(&validator_log_with_timestamp, &validator_log_symlink).unwrap();
|
||||
|
||||
Some(
|
||||
ledger_path
|
||||
.join(validator_log_with_timestamp)
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.unwrap(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let _logger_thread = start_logger(logfile);
|
||||
|
||||
let test_validator = {
|
||||
let _progress_bar = if output == Output::Dashboard {
|
||||
println_name_value("Ledger location:", &format!("{}", ledger_path.display()));
|
||||
println_name_value("Log:", &format!("{}", validator_log_symlink.display()));
|
||||
let progress_bar = new_spinner_progress_bar();
|
||||
progress_bar.set_message("Initializing...");
|
||||
Some(progress_bar)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
TestValidator::start(
|
||||
&ledger_path,
|
||||
TestValidatorStartConfig {
|
||||
rpc_config: JsonRpcConfig {
|
||||
enable_validator_exit: true,
|
||||
enable_rpc_transaction_history: true,
|
||||
..JsonRpcConfig::default()
|
||||
},
|
||||
rpc_ports: Some(rpc_ports),
|
||||
},
|
||||
)
|
||||
}
|
||||
.unwrap_or_else(|err| {
|
||||
eprintln!("Error: failed to start validator: {}", err);
|
||||
exit(1);
|
||||
});
|
||||
|
||||
if output == Output::Dashboard {
|
||||
println_name_value("JSON RPC URL:", &test_validator.rpc_url());
|
||||
println_name_value(
|
||||
"JSON RPC PubSub Websocket:",
|
||||
&test_validator.rpc_pubsub_url(),
|
||||
);
|
||||
println_name_value("Gossip Address:", &test_validator.gossip().to_string());
|
||||
println_name_value("TPU Address:", &test_validator.tpu().to_string());
|
||||
|
||||
let progress_bar = new_spinner_progress_bar();
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
|
||||
fn get_validator_stats(rpc_client: &RpcClient) -> client_error::Result<(Slot, Slot, u64)> {
|
||||
let max_slot = rpc_client.get_slot_with_commitment(CommitmentConfig::max())?;
|
||||
let recent_slot = rpc_client.get_slot_with_commitment(CommitmentConfig::recent())?;
|
||||
let transaction_count =
|
||||
rpc_client.get_transaction_count_with_commitment(CommitmentConfig::recent())?;
|
||||
Ok((recent_slot, max_slot, transaction_count))
|
||||
}
|
||||
|
||||
loop {
|
||||
match get_validator_stats(&rpc_client) {
|
||||
Ok((recent_slot, max_slot, transaction_count)) => {
|
||||
progress_bar.set_message(&format!(
|
||||
"Recent slot: {} | Max confirmed slot: {} | Transaction count: {}",
|
||||
recent_slot, max_slot, transaction_count,
|
||||
));
|
||||
}
|
||||
Err(err) => {
|
||||
progress_bar.set_message(&format!("{}", err));
|
||||
}
|
||||
}
|
||||
sleep(Duration::from_millis(
|
||||
MS_PER_TICK * DEFAULT_TICKS_PER_SLOT / 2,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
std::thread::park();
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
pub use solana_core::test_validator;
|
||||
use {
|
||||
log::*,
|
||||
std::{env, process::exit, thread::JoinHandle},
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
fn redirect_stderr(filename: &str) {
|
||||
use std::{fs::OpenOptions, os::unix::io::AsRawFd};
|
||||
match OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(filename)
|
||||
{
|
||||
Ok(file) => unsafe {
|
||||
libc::dup2(file.as_raw_fd(), libc::STDERR_FILENO);
|
||||
},
|
||||
Err(err) => eprintln!("Unable to open {}: {}", filename, err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_logger(logfile: Option<String>) -> Option<JoinHandle<()>> {
|
||||
// Default to RUST_BACKTRACE=1 for more informative validator logs
|
||||
if env::var_os("RUST_BACKTRACE").is_none() {
|
||||
env::set_var("RUST_BACKTRACE", "1")
|
||||
}
|
||||
|
||||
let logger_thread = match logfile {
|
||||
None => None,
|
||||
Some(logfile) => {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let signals = signal_hook::iterator::Signals::new(&[signal_hook::SIGUSR1])
|
||||
.unwrap_or_else(|err| {
|
||||
eprintln!("Unable to register SIGUSR1 handler: {:?}", err);
|
||||
exit(1);
|
||||
});
|
||||
|
||||
redirect_stderr(&logfile);
|
||||
Some(std::thread::spawn(move || {
|
||||
for signal in signals.forever() {
|
||||
info!(
|
||||
"received SIGUSR1 ({}), reopening log file: {:?}",
|
||||
signal, logfile
|
||||
);
|
||||
redirect_stderr(&logfile);
|
||||
}
|
||||
}))
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
println!("logging to a file is not supported on this platform");
|
||||
()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
solana_logger::setup_with_default(
|
||||
&[
|
||||
"solana=info,solana_runtime::message_processor=error", /* info logging for all solana modules */
|
||||
"rpc=trace", /* json_rpc request/response logging */
|
||||
]
|
||||
.join(","),
|
||||
);
|
||||
|
||||
logger_thread
|
||||
}
|
||||
|
||||
pub fn port_validator(port: String) -> Result<(), String> {
|
||||
port.parse::<u16>()
|
||||
.map(|_| ())
|
||||
.map_err(|e| format!("{:?}", e))
|
||||
}
|
|
@ -21,7 +21,7 @@ use solana_core::{
|
|||
gossip_service::GossipService,
|
||||
rpc::JsonRpcConfig,
|
||||
rpc_pubsub_service::PubSubConfig,
|
||||
validator::{Validator, ValidatorConfig},
|
||||
validator::{is_snapshot_config_invalid, Validator, ValidatorConfig},
|
||||
};
|
||||
use solana_download_utils::{download_genesis_if_missing, download_snapshot};
|
||||
use solana_ledger::blockstore_db::BlockstoreRecoveryMode;
|
||||
|
@ -39,6 +39,7 @@ use solana_sdk::{
|
|||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use solana_validator::start_logger;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
env,
|
||||
|
@ -51,16 +52,10 @@ use std::{
|
|||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
thread::{sleep, JoinHandle},
|
||||
thread::sleep,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
fn port_validator(port: String) -> Result<(), String> {
|
||||
port.parse::<u16>()
|
||||
.map(|_| ())
|
||||
.map_err(|e| format!("{:?}", e))
|
||||
}
|
||||
|
||||
fn port_range_validator(port_range: String) -> Result<(), String> {
|
||||
if let Some((start, end)) = solana_net_utils::parse_port_range(&port_range) {
|
||||
if end - start < MINIMUM_VALIDATOR_PORT_RANGE_WIDTH {
|
||||
|
@ -482,73 +477,6 @@ fn download_then_check_genesis_hash(
|
|||
Ok(genesis_config.hash())
|
||||
}
|
||||
|
||||
fn is_snapshot_config_invalid(
|
||||
snapshot_interval_slots: u64,
|
||||
accounts_hash_interval_slots: u64,
|
||||
) -> bool {
|
||||
snapshot_interval_slots != 0
|
||||
&& (snapshot_interval_slots < accounts_hash_interval_slots
|
||||
|| snapshot_interval_slots % accounts_hash_interval_slots != 0)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn redirect_stderr(filename: &str) {
|
||||
use std::{fs::OpenOptions, os::unix::io::AsRawFd};
|
||||
match OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(filename)
|
||||
{
|
||||
Ok(file) => unsafe {
|
||||
libc::dup2(file.as_raw_fd(), libc::STDERR_FILENO);
|
||||
},
|
||||
Err(err) => eprintln!("Unable to open {}: {}", filename, err),
|
||||
}
|
||||
}
|
||||
|
||||
fn start_logger(logfile: Option<String>) -> Option<JoinHandle<()>> {
|
||||
let logger_thread = match logfile {
|
||||
None => None,
|
||||
Some(logfile) => {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let signals = signal_hook::iterator::Signals::new(&[signal_hook::SIGUSR1])
|
||||
.unwrap_or_else(|err| {
|
||||
eprintln!("Unable to register SIGUSR1 handler: {:?}", err);
|
||||
exit(1);
|
||||
});
|
||||
|
||||
redirect_stderr(&logfile);
|
||||
Some(std::thread::spawn(move || {
|
||||
for signal in signals.forever() {
|
||||
info!(
|
||||
"received SIGUSR1 ({}), reopening log file: {:?}",
|
||||
signal, logfile
|
||||
);
|
||||
redirect_stderr(&logfile);
|
||||
}
|
||||
}))
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
println!("logging to a file is not supported on this platform");
|
||||
()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
solana_logger::setup_with_default(
|
||||
&[
|
||||
"solana=info,solana_runtime::message_processor=error", /* info logging for all solana modules */
|
||||
"rpc=trace", /* json_rpc request/response logging */
|
||||
]
|
||||
.join(","),
|
||||
);
|
||||
|
||||
logger_thread
|
||||
}
|
||||
|
||||
fn verify_reachable_ports(
|
||||
node: &Node,
|
||||
cluster_entrypoint: &ContactInfo,
|
||||
|
@ -988,8 +916,8 @@ pub fn main() {
|
|||
.long("rpc-port")
|
||||
.value_name("PORT")
|
||||
.takes_value(true)
|
||||
.validator(port_validator)
|
||||
.help("Use this port for JSON RPC, the next port for the RPC websocket, and then third port for the RPC banks API"),
|
||||
.validator(solana_validator::port_validator)
|
||||
.help("Use this port for JSON RPC and the next port for the RPC websocket"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("private_rpc")
|
||||
|
@ -1715,11 +1643,6 @@ pub fn main() {
|
|||
let use_progress_bar = logfile.is_none();
|
||||
let _logger_thread = start_logger(logfile);
|
||||
|
||||
// Default to RUST_BACKTRACE=1 for more informative validator logs
|
||||
if env::var_os("RUST_BACKTRACE").is_none() {
|
||||
env::set_var("RUST_BACKTRACE", "1")
|
||||
}
|
||||
|
||||
let gossip_host = matches
|
||||
.value_of("gossip_host")
|
||||
.map(|gossip_host| {
|
||||
|
@ -1820,17 +1743,3 @@ pub fn main() {
|
|||
validator.join().expect("validator exit");
|
||||
info!("Validator exiting..");
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_interval_check() {
|
||||
assert!(!is_snapshot_config_invalid(0, 100));
|
||||
assert!(is_snapshot_config_invalid(1, 100));
|
||||
assert!(is_snapshot_config_invalid(230, 100));
|
||||
assert!(!is_snapshot_config_invalid(500, 100));
|
||||
assert!(!is_snapshot_config_invalid(5, 5));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue