2018-11-29 09:29:17 -08:00
|
|
|
#[macro_use]
|
|
|
|
extern crate log;
|
2018-12-08 21:44:20 -08:00
|
|
|
|
2019-02-26 17:11:26 -08:00
|
|
|
#[macro_use]
|
|
|
|
extern crate solana;
|
|
|
|
|
2019-03-14 12:57:02 -07:00
|
|
|
use bincode::{deserialize, serialize};
|
2019-03-27 17:21:49 -07:00
|
|
|
use solana::blocktree::{create_new_tmp_ledger, Blocktree};
|
2019-03-27 15:54:09 -07:00
|
|
|
use solana::cluster_info::{ClusterInfo, Node, FULLNODE_PORT_RANGE};
|
2019-03-08 17:23:07 -08:00
|
|
|
use solana::contact_info::ContactInfo;
|
2019-03-27 17:21:49 -07:00
|
|
|
use solana::fullnode::FullnodeConfig;
|
2019-04-01 16:11:42 -07:00
|
|
|
use solana::gossip_service::discover_nodes;
|
2019-04-15 15:27:45 -07:00
|
|
|
use solana::local_cluster::{ClusterConfig, LocalCluster};
|
2018-11-29 09:29:17 -08:00
|
|
|
use solana::replicator::Replicator;
|
2019-03-14 12:57:02 -07:00
|
|
|
use solana::replicator::ReplicatorRequest;
|
2019-01-02 11:02:15 -08:00
|
|
|
use solana::storage_stage::STORAGE_ROTATE_TEST_COUNT;
|
2019-03-14 12:57:02 -07:00
|
|
|
use solana::streamer::blob_receiver;
|
2019-03-27 15:54:09 -07:00
|
|
|
use solana_client::thin_client::create_client;
|
2019-05-07 11:16:22 -07:00
|
|
|
use solana_sdk::genesis_block::create_genesis_block;
|
2019-03-14 12:57:02 -07:00
|
|
|
use solana_sdk::hash::Hash;
|
2019-01-10 09:21:38 -08:00
|
|
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
2018-11-29 09:29:17 -08:00
|
|
|
use std::fs::remove_dir_all;
|
2019-03-14 12:57:02 -07:00
|
|
|
use std::net::SocketAddr;
|
|
|
|
use std::net::UdpSocket;
|
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
use std::sync::mpsc::channel;
|
2019-02-05 08:03:52 -08:00
|
|
|
use std::sync::Arc;
|
2019-03-14 12:57:02 -07:00
|
|
|
use std::thread::sleep;
|
2018-11-29 09:29:17 -08:00
|
|
|
use std::time::Duration;
|
|
|
|
|
2019-03-14 12:57:02 -07:00
|
|
|
fn get_slot_height(to: SocketAddr) -> u64 {
|
|
|
|
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
|
|
|
socket
|
|
|
|
.set_read_timeout(Some(Duration::from_secs(5)))
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let req = ReplicatorRequest::GetSlotHeight(socket.local_addr().unwrap());
|
|
|
|
let serialized_req = serialize(&req).unwrap();
|
|
|
|
for _ in 0..10 {
|
|
|
|
socket.send_to(&serialized_req, to).unwrap();
|
|
|
|
let mut buf = [0; 1024];
|
|
|
|
if let Ok((size, _addr)) = socket.recv_from(&mut buf) {
|
|
|
|
return deserialize(&buf[..size]).unwrap();
|
|
|
|
}
|
|
|
|
sleep(Duration::from_millis(500));
|
|
|
|
}
|
|
|
|
panic!("Couldn't get slot height!");
|
|
|
|
}
|
|
|
|
|
|
|
|
fn download_from_replicator(replicator_info: &ContactInfo) {
|
|
|
|
// Create a client which downloads from the replicator and see that it
|
|
|
|
// can respond with blobs.
|
|
|
|
let tn = Node::new_localhost();
|
|
|
|
let cluster_info = ClusterInfo::new_with_invalid_keypair(tn.info.clone());
|
|
|
|
let mut repair_index = get_slot_height(replicator_info.storage_addr);
|
|
|
|
info!("repair index: {}", repair_index);
|
|
|
|
|
|
|
|
repair_index = 0;
|
|
|
|
let req = cluster_info
|
|
|
|
.window_index_request_bytes(0, repair_index)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let exit = Arc::new(AtomicBool::new(false));
|
|
|
|
let (s_reader, r_reader) = channel();
|
|
|
|
let repair_socket = Arc::new(tn.sockets.repair);
|
|
|
|
let t_receiver = blob_receiver(repair_socket.clone(), &exit, s_reader);
|
|
|
|
|
|
|
|
info!(
|
|
|
|
"Sending repair requests from: {} to: {}",
|
|
|
|
tn.info.id, replicator_info.gossip
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut received_blob = false;
|
|
|
|
for _ in 0..5 {
|
|
|
|
repair_socket.send_to(&req, replicator_info.gossip).unwrap();
|
|
|
|
|
|
|
|
let x = r_reader.recv_timeout(Duration::new(1, 0));
|
|
|
|
|
|
|
|
if let Ok(blobs) = x {
|
|
|
|
for b in blobs {
|
|
|
|
let br = b.read().unwrap();
|
|
|
|
assert!(br.index() == repair_index);
|
2019-03-18 16:16:06 -07:00
|
|
|
info!("br: {:?}", br);
|
|
|
|
let entries = Blocktree::deserialize_blob_data(&br.data()).unwrap();
|
|
|
|
for entry in &entries {
|
|
|
|
info!("entry: {:?}", entry);
|
|
|
|
assert_ne!(entry.hash, Hash::default());
|
|
|
|
received_blob = true;
|
|
|
|
}
|
2019-03-14 12:57:02 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exit.store(true, Ordering::Relaxed);
|
|
|
|
t_receiver.join().unwrap();
|
|
|
|
|
|
|
|
assert!(received_blob);
|
|
|
|
}
|
|
|
|
|
2019-03-27 13:10:33 -07:00
|
|
|
/// Start the cluster with the given configuration and wait till the replicators are discovered
|
|
|
|
/// Then download blobs from one of them.
|
|
|
|
fn run_replicator_startup_basic(num_nodes: usize, num_replicators: usize) {
|
2019-01-09 11:19:04 -08:00
|
|
|
solana_logger::setup();
|
2018-11-29 09:29:17 -08:00
|
|
|
info!("starting replicator test");
|
|
|
|
|
2019-03-13 14:23:32 -07:00
|
|
|
let mut fullnode_config = FullnodeConfig::default();
|
|
|
|
fullnode_config.storage_rotate_count = STORAGE_ROTATE_TEST_COUNT;
|
2019-04-15 15:27:45 -07:00
|
|
|
let config = ClusterConfig {
|
|
|
|
fullnode_config,
|
2019-05-07 08:05:12 -07:00
|
|
|
num_replicators,
|
2019-04-15 15:27:45 -07:00
|
|
|
node_stakes: vec![100; num_nodes],
|
|
|
|
cluster_lamports: 10_000,
|
|
|
|
..ClusterConfig::default()
|
|
|
|
};
|
|
|
|
let cluster = LocalCluster::new(&config);
|
2019-03-18 11:02:32 -07:00
|
|
|
|
2019-04-01 16:11:42 -07:00
|
|
|
let cluster_nodes = discover_nodes(
|
2019-03-14 12:57:02 -07:00
|
|
|
&cluster.entry_point_info.gossip,
|
2019-03-27 13:10:33 -07:00
|
|
|
num_nodes + num_replicators,
|
2019-03-14 12:57:02 -07:00
|
|
|
)
|
|
|
|
.unwrap();
|
2019-03-27 13:10:33 -07:00
|
|
|
assert_eq!(cluster_nodes.len(), num_nodes + num_replicators);
|
2019-03-18 11:02:32 -07:00
|
|
|
let mut replicator_count = 0;
|
2019-03-14 12:57:02 -07:00
|
|
|
let mut replicator_info = ContactInfo::default();
|
2019-03-18 11:02:32 -07:00
|
|
|
for node in &cluster_nodes {
|
|
|
|
info!("storage: {:?} rpc: {:?}", node.storage_addr, node.rpc);
|
|
|
|
if ContactInfo::is_valid_address(&node.storage_addr) {
|
|
|
|
replicator_count += 1;
|
2019-03-14 12:57:02 -07:00
|
|
|
replicator_info = node.clone();
|
2019-03-18 11:02:32 -07:00
|
|
|
}
|
|
|
|
}
|
2019-03-14 12:57:02 -07:00
|
|
|
assert_eq!(replicator_count, num_replicators);
|
|
|
|
|
|
|
|
download_from_replicator(&replicator_info);
|
2018-11-29 09:29:17 -08:00
|
|
|
}
|
2019-01-21 13:37:41 -08:00
|
|
|
|
2019-03-27 13:10:33 -07:00
|
|
|
#[test]
|
|
|
|
fn test_replicator_startup_1_node() {
|
|
|
|
run_replicator_startup_basic(1, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_replicator_startup_2_nodes() {
|
|
|
|
run_replicator_startup_basic(2, 1);
|
|
|
|
}
|
|
|
|
|
2019-01-21 13:37:41 -08:00
|
|
|
#[test]
|
|
|
|
fn test_replicator_startup_leader_hang() {
|
|
|
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
solana_logger::setup();
|
|
|
|
info!("starting replicator test");
|
|
|
|
|
|
|
|
let leader_ledger_path = "replicator_test_leader_ledger";
|
2019-05-07 11:16:22 -07:00
|
|
|
let (genesis_block, _mint_keypair) = create_genesis_block(10_000);
|
2019-03-02 10:25:16 -08:00
|
|
|
let (replicator_ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_block);
|
2019-01-21 13:37:41 -08:00
|
|
|
|
|
|
|
{
|
2019-03-06 19:09:37 -08:00
|
|
|
let replicator_keypair = Arc::new(Keypair::new());
|
2019-03-27 15:54:09 -07:00
|
|
|
let storage_keypair = Arc::new(Keypair::new());
|
2019-01-21 13:37:41 -08:00
|
|
|
|
|
|
|
info!("starting replicator node");
|
2019-03-09 19:28:43 -08:00
|
|
|
let replicator_node = Node::new_localhost_with_pubkey(&replicator_keypair.pubkey());
|
2019-01-21 13:37:41 -08:00
|
|
|
|
|
|
|
let fake_gossip = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0);
|
2019-03-08 17:23:07 -08:00
|
|
|
let leader_info = ContactInfo::new_gossip_entry_point(&fake_gossip);
|
2019-01-21 13:37:41 -08:00
|
|
|
|
|
|
|
let replicator_res = Replicator::new(
|
2019-02-26 21:16:18 -08:00
|
|
|
&replicator_ledger_path,
|
2019-01-21 13:37:41 -08:00
|
|
|
replicator_node,
|
2019-03-12 11:37:48 -07:00
|
|
|
leader_info,
|
|
|
|
replicator_keypair,
|
2019-03-27 15:54:09 -07:00
|
|
|
storage_keypair,
|
2019-01-21 13:37:41 -08:00
|
|
|
Some(Duration::from_secs(3)),
|
|
|
|
);
|
|
|
|
|
|
|
|
assert!(replicator_res.is_err());
|
|
|
|
}
|
|
|
|
|
2019-02-07 20:52:39 -08:00
|
|
|
let _ignored = Blocktree::destroy(&leader_ledger_path);
|
|
|
|
let _ignored = Blocktree::destroy(&replicator_ledger_path);
|
2019-01-21 13:37:41 -08:00
|
|
|
let _ignored = remove_dir_all(&leader_ledger_path);
|
|
|
|
let _ignored = remove_dir_all(&replicator_ledger_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_replicator_startup_ledger_hang() {
|
|
|
|
solana_logger::setup();
|
|
|
|
info!("starting replicator test");
|
2019-03-27 17:21:49 -07:00
|
|
|
let mut fullnode_config = FullnodeConfig::default();
|
|
|
|
fullnode_config.storage_rotate_count = STORAGE_ROTATE_TEST_COUNT;
|
2019-04-15 15:27:45 -07:00
|
|
|
let cluster = LocalCluster::new_with_equal_stakes(2, 10_000, 100);;
|
2019-03-27 17:21:49 -07:00
|
|
|
|
|
|
|
info!("starting replicator node");
|
|
|
|
let bad_keys = Arc::new(Keypair::new());
|
|
|
|
let storage_keypair = Arc::new(Keypair::new());
|
|
|
|
let mut replicator_node = Node::new_localhost_with_pubkey(&bad_keys.pubkey());
|
|
|
|
|
|
|
|
// Pass bad TVU sockets to prevent successful ledger download
|
|
|
|
replicator_node.sockets.tvu = vec![std::net::UdpSocket::bind("0.0.0.0:0").unwrap()];
|
|
|
|
let (replicator_ledger_path, _blockhash) = create_new_tmp_ledger!(&cluster.genesis_block);
|
|
|
|
|
|
|
|
let replicator_res = Replicator::new(
|
|
|
|
&replicator_ledger_path,
|
|
|
|
replicator_node,
|
|
|
|
cluster.entry_point_info.clone(),
|
|
|
|
bad_keys,
|
|
|
|
storage_keypair,
|
|
|
|
Some(Duration::from_secs(3)),
|
|
|
|
);
|
2019-01-21 13:37:41 -08:00
|
|
|
|
2019-03-27 17:21:49 -07:00
|
|
|
assert!(replicator_res.is_err());
|
2019-01-21 13:37:41 -08:00
|
|
|
}
|
2019-03-27 15:54:09 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_account_setup() {
|
|
|
|
let num_nodes = 1;
|
|
|
|
let num_replicators = 1;
|
|
|
|
let mut fullnode_config = FullnodeConfig::default();
|
|
|
|
fullnode_config.storage_rotate_count = STORAGE_ROTATE_TEST_COUNT;
|
2019-04-15 15:27:45 -07:00
|
|
|
let config = ClusterConfig {
|
|
|
|
fullnode_config,
|
2019-03-27 15:54:09 -07:00
|
|
|
num_replicators,
|
2019-04-15 15:27:45 -07:00
|
|
|
node_stakes: vec![100; num_nodes],
|
|
|
|
cluster_lamports: 10_000,
|
|
|
|
..ClusterConfig::default()
|
|
|
|
};
|
|
|
|
let cluster = LocalCluster::new(&config);
|
2019-03-27 15:54:09 -07:00
|
|
|
|
2019-04-01 16:11:42 -07:00
|
|
|
let _ = discover_nodes(
|
2019-03-27 15:54:09 -07:00
|
|
|
&cluster.entry_point_info.gossip,
|
2019-04-15 15:27:45 -07:00
|
|
|
num_nodes + num_replicators as usize,
|
2019-03-27 15:54:09 -07:00
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
// now check that the cluster actually has accounts for the replicator.
|
|
|
|
let client = create_client(
|
|
|
|
cluster.entry_point_info.client_facing_addr(),
|
|
|
|
FULLNODE_PORT_RANGE,
|
|
|
|
);
|
|
|
|
cluster.replicator_infos.iter().for_each(|(_, value)| {
|
|
|
|
assert_eq!(
|
|
|
|
client
|
|
|
|
.poll_get_balance(&value.replicator_storage_id)
|
|
|
|
.unwrap(),
|
|
|
|
1
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|