Replicator rework
* Move more of the replicator logic into the replicator class * Add support for the RPC interface to query the storage last_id value that the replicator would sign and use to pick a block. * Fix replicator connecting to gossip and change test to exercise that scenario.
This commit is contained in:
committed by
sakridge
parent
fa288ab197
commit
3441d3399b
@ -42,6 +42,7 @@ use std::result;
|
|||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
use storage_stage::StorageState;
|
||||||
use tokio::prelude::Future;
|
use tokio::prelude::Future;
|
||||||
|
|
||||||
/// The number of most recent `last_id` values that the bank will track the signatures
|
/// The number of most recent `last_id` values that the bank will track the signatures
|
||||||
@ -301,6 +302,8 @@ pub struct Bank {
|
|||||||
/// Tracks and updates the leader schedule based on the votes and account stakes
|
/// Tracks and updates the leader schedule based on the votes and account stakes
|
||||||
/// processed by the bank
|
/// processed by the bank
|
||||||
pub leader_scheduler: Arc<RwLock<LeaderScheduler>>,
|
pub leader_scheduler: Arc<RwLock<LeaderScheduler>>,
|
||||||
|
|
||||||
|
pub storage_state: StorageState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Bank {
|
impl Default for Bank {
|
||||||
@ -313,6 +316,7 @@ impl Default for Bank {
|
|||||||
account_subscriptions: RwLock::new(HashMap::new()),
|
account_subscriptions: RwLock::new(HashMap::new()),
|
||||||
signature_subscriptions: RwLock::new(HashMap::new()),
|
signature_subscriptions: RwLock::new(HashMap::new()),
|
||||||
leader_scheduler: Arc::new(RwLock::new(LeaderScheduler::default())),
|
leader_scheduler: Arc::new(RwLock::new(LeaderScheduler::default())),
|
||||||
|
storage_state: StorageState::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,25 +8,14 @@ extern crate solana_drone;
|
|||||||
extern crate solana_sdk;
|
extern crate solana_sdk;
|
||||||
|
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use solana::chacha::{chacha_cbc_encrypt_file, CHACHA_BLOCK_SIZE};
|
use solana::cluster_info::{Node, NodeInfo};
|
||||||
use solana::client::mk_client;
|
|
||||||
use solana::cluster_info::Node;
|
|
||||||
use solana::fullnode::Config;
|
use solana::fullnode::Config;
|
||||||
use solana::ledger::LEDGER_DATA_FILE;
|
|
||||||
use solana::logger;
|
use solana::logger;
|
||||||
use solana::replicator::{sample_file, Replicator};
|
use solana::replicator::Replicator;
|
||||||
use solana_drone::drone::{request_airdrop_transaction, DRONE_PORT};
|
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||||
use solana_sdk::storage_program::StorageTransaction;
|
|
||||||
use solana_sdk::transaction::Transaction;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::net::{Ipv4Addr, SocketAddr};
|
use std::net::{Ipv4Addr, SocketAddr};
|
||||||
use std::path::Path;
|
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::thread::sleep;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
logger::setup();
|
logger::setup();
|
||||||
@ -85,70 +74,14 @@ fn main() {
|
|||||||
gossip
|
gossip
|
||||||
);
|
);
|
||||||
|
|
||||||
let exit = Arc::new(AtomicBool::new(false));
|
|
||||||
let done = Arc::new(AtomicBool::new(false));
|
|
||||||
|
|
||||||
let network_addr = matches
|
let network_addr = matches
|
||||||
.value_of("network")
|
.value_of("network")
|
||||||
.map(|network| network.parse().expect("failed to parse network address"));
|
.map(|network| network.parse().expect("failed to parse network address"))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// TODO: ask network what slice we should store
|
let leader_info = NodeInfo::new_entry_point(&network_addr);
|
||||||
let entry_height = 0;
|
|
||||||
|
|
||||||
let (replicator, leader_info) = Replicator::new(
|
let replicator = Replicator::new(ledger_path, node, &leader_info, &keypair).unwrap();
|
||||||
entry_height,
|
|
||||||
5,
|
|
||||||
&exit,
|
|
||||||
ledger_path,
|
|
||||||
node,
|
|
||||||
network_addr,
|
|
||||||
done.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
while !done.load(Ordering::Relaxed) {
|
|
||||||
sleep(Duration::from_millis(100));
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Done downloading ledger");
|
|
||||||
|
|
||||||
let ledger_path = Path::new(ledger_path.unwrap());
|
|
||||||
let ledger_data_file = ledger_path.join(LEDGER_DATA_FILE);
|
|
||||||
let ledger_data_file_encrypted = ledger_path.join(format!("{}.enc", LEDGER_DATA_FILE));
|
|
||||||
let mut ivec = [0u8; CHACHA_BLOCK_SIZE];
|
|
||||||
ivec[0..4].copy_from_slice(&[2, 3, 4, 5]);
|
|
||||||
|
|
||||||
if let Err(e) =
|
|
||||||
chacha_cbc_encrypt_file(&ledger_data_file, &ledger_data_file_encrypted, &mut ivec)
|
|
||||||
{
|
|
||||||
println!("Error while encrypting ledger: {:?}", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Done encrypting the ledger");
|
|
||||||
|
|
||||||
let sampling_offsets = [0, 1, 2, 3];
|
|
||||||
|
|
||||||
let mut client = mk_client(&leader_info);
|
|
||||||
|
|
||||||
let mut drone_addr = leader_info.tpu;
|
|
||||||
drone_addr.set_port(DRONE_PORT);
|
|
||||||
let airdrop_amount = 5;
|
|
||||||
let last_id = client.get_last_id();
|
|
||||||
let transaction =
|
|
||||||
request_airdrop_transaction(&drone_addr, &keypair.pubkey(), airdrop_amount, last_id)
|
|
||||||
.unwrap();
|
|
||||||
let signature = client.transfer_signed(&transaction).unwrap();
|
|
||||||
client.poll_for_signature(&signature).unwrap();
|
|
||||||
|
|
||||||
match sample_file(&ledger_data_file_encrypted, &sampling_offsets) {
|
|
||||||
Ok(hash) => {
|
|
||||||
let last_id = client.get_last_id();
|
|
||||||
println!("sampled hash: {}", hash);
|
|
||||||
let tx = Transaction::storage_new_mining_proof(&keypair, hash, last_id);
|
|
||||||
client.transfer_signed(&tx).expect("transfer didn't work!");
|
|
||||||
}
|
|
||||||
Err(e) => println!("Error occurred while sampling: {:?}", e),
|
|
||||||
}
|
|
||||||
|
|
||||||
replicator.join();
|
replicator.join();
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,22 @@
|
|||||||
use blob_fetch_stage::BlobFetchStage;
|
use blob_fetch_stage::BlobFetchStage;
|
||||||
|
#[cfg(feature = "chacha")]
|
||||||
|
use chacha::{chacha_cbc_encrypt_file, CHACHA_BLOCK_SIZE};
|
||||||
|
use client::mk_client;
|
||||||
use cluster_info::{ClusterInfo, Node, NodeInfo};
|
use cluster_info::{ClusterInfo, Node, NodeInfo};
|
||||||
use db_ledger::DbLedger;
|
use db_ledger::DbLedger;
|
||||||
use gossip_service::GossipService;
|
use gossip_service::GossipService;
|
||||||
use leader_scheduler::LeaderScheduler;
|
use leader_scheduler::LeaderScheduler;
|
||||||
|
use ledger::LEDGER_DATA_FILE;
|
||||||
|
use rand::thread_rng;
|
||||||
|
use rand::Rng;
|
||||||
|
use result::Result;
|
||||||
|
use rpc_request::{RpcClient, RpcRequest};
|
||||||
use service::Service;
|
use service::Service;
|
||||||
|
use solana_drone::drone::{request_airdrop_transaction, DRONE_PORT};
|
||||||
use solana_sdk::hash::{Hash, Hasher};
|
use solana_sdk::hash::{Hash, Hasher};
|
||||||
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||||
|
use solana_sdk::storage_program::StorageTransaction;
|
||||||
|
use solana_sdk::transaction::Transaction;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
@ -13,17 +25,16 @@ use std::io::Seek;
|
|||||||
use std::io::SeekFrom;
|
use std::io::SeekFrom;
|
||||||
use std::io::{Error, ErrorKind};
|
use std::io::{Error, ErrorKind};
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::net::SocketAddr;
|
|
||||||
use std::net::UdpSocket;
|
use std::net::UdpSocket;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
use std::thread::sleep;
|
||||||
use std::thread::JoinHandle;
|
use std::thread::JoinHandle;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use store_ledger_stage::StoreLedgerStage;
|
use store_ledger_stage::StoreLedgerStage;
|
||||||
use streamer::BlobReceiver;
|
use streamer::BlobReceiver;
|
||||||
use thin_client::poll_gossip_for_leader;
|
|
||||||
use window;
|
use window;
|
||||||
use window_service::window_service;
|
use window_service::window_service;
|
||||||
|
|
||||||
@ -33,6 +44,7 @@ pub struct Replicator {
|
|||||||
store_ledger_stage: StoreLedgerStage,
|
store_ledger_stage: StoreLedgerStage,
|
||||||
t_window: JoinHandle<()>,
|
t_window: JoinHandle<()>,
|
||||||
pub retransmit_receiver: BlobReceiver,
|
pub retransmit_receiver: BlobReceiver,
|
||||||
|
exit: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample_file(in_path: &Path, sample_offsets: &[u64]) -> io::Result<Hash> {
|
pub fn sample_file(in_path: &Path, sample_offsets: &[u64]) -> io::Result<Hash> {
|
||||||
@ -72,39 +84,34 @@ pub fn sample_file(in_path: &Path, sample_offsets: &[u64]) -> io::Result<Hash> {
|
|||||||
|
|
||||||
impl Replicator {
|
impl Replicator {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
entry_height: u64,
|
|
||||||
max_entry_height: u64,
|
|
||||||
exit: &Arc<AtomicBool>,
|
|
||||||
ledger_path: Option<&str>,
|
ledger_path: Option<&str>,
|
||||||
node: Node,
|
node: Node,
|
||||||
network_addr: Option<SocketAddr>,
|
leader_info: &NodeInfo,
|
||||||
done: Arc<AtomicBool>,
|
keypair: &Keypair,
|
||||||
) -> (Replicator, NodeInfo) {
|
) -> Result<Self> {
|
||||||
|
let exit = Arc::new(AtomicBool::new(false));
|
||||||
|
let done = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
|
let entry_height = 0;
|
||||||
|
let max_entry_height = 1;
|
||||||
|
|
||||||
const REPLICATOR_WINDOW_SIZE: usize = 32 * 1024;
|
const REPLICATOR_WINDOW_SIZE: usize = 32 * 1024;
|
||||||
let window = window::new_window(REPLICATOR_WINDOW_SIZE);
|
let window = window::new_window(REPLICATOR_WINDOW_SIZE);
|
||||||
let shared_window = Arc::new(RwLock::new(window));
|
let shared_window = Arc::new(RwLock::new(window));
|
||||||
|
|
||||||
|
info!("Replicator: id: {}", keypair.pubkey());
|
||||||
|
info!("Creating cluster info....");
|
||||||
let cluster_info = Arc::new(RwLock::new(ClusterInfo::new(node.info)));
|
let cluster_info = Arc::new(RwLock::new(ClusterInfo::new(node.info)));
|
||||||
|
|
||||||
let leader_info = network_addr.map(|i| NodeInfo::new_entry_point(&i));
|
let leader_pubkey = leader_info.id;
|
||||||
let leader_pubkey;
|
{
|
||||||
if let Some(leader_info) = leader_info {
|
let mut cluster_info_w = cluster_info.write().unwrap();
|
||||||
leader_pubkey = leader_info.id;
|
cluster_info_w.insert_info(leader_info.clone());
|
||||||
cluster_info.write().unwrap().insert_info(leader_info);
|
cluster_info_w.set_leader(leader_info.id);
|
||||||
} else {
|
|
||||||
panic!("No leader info!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let repair_socket = Arc::new(node.sockets.repair);
|
|
||||||
let mut blob_sockets: Vec<Arc<UdpSocket>> =
|
|
||||||
node.sockets.replicate.into_iter().map(Arc::new).collect();
|
|
||||||
blob_sockets.push(repair_socket.clone());
|
|
||||||
let (fetch_stage, blob_fetch_receiver) =
|
|
||||||
BlobFetchStage::new_multi_socket(blob_sockets, exit.clone());
|
|
||||||
|
|
||||||
let (entry_window_sender, entry_window_receiver) = channel();
|
let (entry_window_sender, entry_window_receiver) = channel();
|
||||||
// todo: pull blobs off the retransmit_receiver and recycle them?
|
let store_ledger_stage = StoreLedgerStage::new(entry_window_receiver, ledger_path);
|
||||||
let (retransmit_sender, retransmit_receiver) = channel();
|
|
||||||
|
|
||||||
// Create the RocksDb ledger, eventually will simply repurpose the input
|
// Create the RocksDb ledger, eventually will simply repurpose the input
|
||||||
// ledger path as the RocksDb ledger path once we replace the ledger with
|
// ledger path as the RocksDb ledger path once we replace the ledger with
|
||||||
@ -116,6 +123,53 @@ impl Replicator {
|
|||||||
.expect("Expected to be able to open database ledger"),
|
.expect("Expected to be able to open database ledger"),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let gossip_service = GossipService::new(
|
||||||
|
&cluster_info,
|
||||||
|
shared_window.clone(),
|
||||||
|
ledger_path,
|
||||||
|
node.sockets.gossip,
|
||||||
|
exit.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
info!("polling for leader");
|
||||||
|
let leader;
|
||||||
|
loop {
|
||||||
|
if let Some(l) = cluster_info.read().unwrap().get_gossip_top_leader() {
|
||||||
|
leader = l.clone();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(Duration::from_millis(900));
|
||||||
|
info!("{}", cluster_info.read().unwrap().node_info_trace());
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Got leader: {:?}", leader);
|
||||||
|
|
||||||
|
let rpc_client = {
|
||||||
|
let cluster_info = cluster_info.read().unwrap();
|
||||||
|
let rpc_peers = cluster_info.rpc_peers();
|
||||||
|
info!("rpc peers: {:?}", rpc_peers);
|
||||||
|
let node_idx = thread_rng().gen_range(0, rpc_peers.len());
|
||||||
|
RpcClient::new_from_socket(rpc_peers[node_idx].rpc)
|
||||||
|
};
|
||||||
|
|
||||||
|
let storage_last_id = RpcRequest::GetStorageMiningLastId
|
||||||
|
.make_rpc_request(&rpc_client, 2, None)
|
||||||
|
.expect("rpc request")
|
||||||
|
.to_string();
|
||||||
|
let _signature = keypair.sign(storage_last_id.as_ref());
|
||||||
|
// TODO: use this signature to pick the key and block
|
||||||
|
|
||||||
|
let repair_socket = Arc::new(node.sockets.repair);
|
||||||
|
let mut blob_sockets: Vec<Arc<UdpSocket>> =
|
||||||
|
node.sockets.replicate.into_iter().map(Arc::new).collect();
|
||||||
|
blob_sockets.push(repair_socket.clone());
|
||||||
|
let (fetch_stage, blob_fetch_receiver) =
|
||||||
|
BlobFetchStage::new_multi_socket(blob_sockets, exit.clone());
|
||||||
|
|
||||||
|
// todo: pull blobs off the retransmit_receiver and recycle them?
|
||||||
|
let (retransmit_sender, retransmit_receiver) = channel();
|
||||||
|
|
||||||
let t_window = window_service(
|
let t_window = window_service(
|
||||||
db_ledger,
|
db_ledger,
|
||||||
cluster_info.clone(),
|
cluster_info.clone(),
|
||||||
@ -129,32 +183,82 @@ impl Replicator {
|
|||||||
Arc::new(RwLock::new(LeaderScheduler::from_bootstrap_leader(
|
Arc::new(RwLock::new(LeaderScheduler::from_bootstrap_leader(
|
||||||
leader_pubkey,
|
leader_pubkey,
|
||||||
))),
|
))),
|
||||||
done,
|
done.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let store_ledger_stage = StoreLedgerStage::new(entry_window_receiver, ledger_path);
|
info!("window created, waiting for ledger download done");
|
||||||
|
while !done.load(Ordering::Relaxed) {
|
||||||
|
sleep(Duration::from_millis(100));
|
||||||
|
}
|
||||||
|
|
||||||
let gossip_service = GossipService::new(
|
let mut client = mk_client(&leader);
|
||||||
&cluster_info,
|
|
||||||
shared_window.clone(),
|
|
||||||
ledger_path,
|
|
||||||
node.sockets.gossip,
|
|
||||||
exit.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let leader =
|
if client.get_balance(&keypair.pubkey()).is_err() {
|
||||||
poll_gossip_for_leader(network_addr.unwrap(), Some(10)).expect("couldn't reach leader");
|
let mut drone_addr = leader_info.tpu;
|
||||||
|
drone_addr.set_port(DRONE_PORT);
|
||||||
|
|
||||||
(
|
let airdrop_amount = 1;
|
||||||
Replicator {
|
|
||||||
gossip_service,
|
let last_id = client.get_last_id();
|
||||||
fetch_stage,
|
match request_airdrop_transaction(
|
||||||
store_ledger_stage,
|
&drone_addr,
|
||||||
t_window,
|
&keypair.pubkey(),
|
||||||
retransmit_receiver,
|
airdrop_amount,
|
||||||
},
|
last_id,
|
||||||
leader,
|
) {
|
||||||
)
|
Ok(transaction) => {
|
||||||
|
let signature = client.transfer_signed(&transaction).unwrap();
|
||||||
|
client.poll_for_signature(&signature).unwrap();
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
panic!(
|
||||||
|
"Error requesting airdrop: {:?} to addr: {:?} amount: {}",
|
||||||
|
err, drone_addr, airdrop_amount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Done downloading ledger at {}", ledger_path.unwrap());
|
||||||
|
|
||||||
|
let ledger_path = Path::new(ledger_path.unwrap());
|
||||||
|
let ledger_data_file_encrypted = ledger_path.join(format!("{}.enc", LEDGER_DATA_FILE));
|
||||||
|
#[cfg(feature = "chacha")]
|
||||||
|
{
|
||||||
|
let ledger_data_file = ledger_path.join(LEDGER_DATA_FILE);
|
||||||
|
let mut ivec = [0u8; CHACHA_BLOCK_SIZE];
|
||||||
|
ivec[0..4].copy_from_slice(&[2, 3, 4, 5]);
|
||||||
|
|
||||||
|
chacha_cbc_encrypt_file(&ledger_data_file, &ledger_data_file_encrypted, &mut ivec)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Done encrypting the ledger");
|
||||||
|
|
||||||
|
let sampling_offsets = [0, 1, 2, 3];
|
||||||
|
|
||||||
|
match sample_file(&ledger_data_file_encrypted, &sampling_offsets) {
|
||||||
|
Ok(hash) => {
|
||||||
|
let last_id = client.get_last_id();
|
||||||
|
info!("sampled hash: {}", hash);
|
||||||
|
let tx = Transaction::storage_new_mining_proof(&keypair, hash, last_id);
|
||||||
|
client.transfer_signed(&tx).expect("transfer didn't work!");
|
||||||
|
}
|
||||||
|
Err(e) => info!("Error occurred while sampling: {:?}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Replicator {
|
||||||
|
gossip_service,
|
||||||
|
fetch_stage,
|
||||||
|
store_ledger_stage,
|
||||||
|
t_window,
|
||||||
|
retransmit_receiver,
|
||||||
|
exit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(self) {
|
||||||
|
self.exit.store(true, Ordering::Relaxed);
|
||||||
|
self.join()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn join(self) {
|
pub fn join(self) {
|
||||||
|
11
src/rpc.rs
11
src/rpc.rs
@ -153,6 +153,9 @@ build_rpc_trait! {
|
|||||||
|
|
||||||
#[rpc(meta, name = "sendTransaction")]
|
#[rpc(meta, name = "sendTransaction")]
|
||||||
fn send_transaction(&self, Self::Metadata, Vec<u8>) -> Result<String>;
|
fn send_transaction(&self, Self::Metadata, Vec<u8>) -> Result<String>;
|
||||||
|
|
||||||
|
#[rpc(meta, name = "getStorageMiningLastId")]
|
||||||
|
fn get_storage_mining_last_id(&self, Self::Metadata) -> Result<String>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,6 +282,9 @@ impl RpcSol for RpcSolImpl {
|
|||||||
);
|
);
|
||||||
Ok(signature)
|
Ok(signature)
|
||||||
}
|
}
|
||||||
|
fn get_storage_mining_last_id(&self, meta: Self::Metadata) -> Result<String> {
|
||||||
|
meta.request_processor.get_storage_mining_last_id()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct JsonRpcRequestProcessor {
|
pub struct JsonRpcRequestProcessor {
|
||||||
@ -313,6 +319,10 @@ impl JsonRpcRequestProcessor {
|
|||||||
fn get_transaction_count(&self) -> Result<u64> {
|
fn get_transaction_count(&self) -> Result<u64> {
|
||||||
Ok(self.bank.transaction_count() as u64)
|
Ok(self.bank.transaction_count() as u64)
|
||||||
}
|
}
|
||||||
|
fn get_storage_mining_last_id(&self) -> Result<String> {
|
||||||
|
let id = self.bank.storage_state.get_last_id();
|
||||||
|
Ok(bs58::encode(id).into_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_leader_addr(cluster_info: &Arc<RwLock<ClusterInfo>>) -> Result<SocketAddr> {
|
fn get_leader_addr(cluster_info: &Arc<RwLock<ClusterInfo>>) -> Result<SocketAddr> {
|
||||||
@ -391,6 +401,7 @@ mod tests {
|
|||||||
let request_processor = JsonRpcRequestProcessor::new(Arc::new(bank));
|
let request_processor = JsonRpcRequestProcessor::new(Arc::new(bank));
|
||||||
let cluster_info = Arc::new(RwLock::new(ClusterInfo::new(NodeInfo::default())));
|
let cluster_info = Arc::new(RwLock::new(ClusterInfo::new(NodeInfo::default())));
|
||||||
let leader = NodeInfo::new_with_socketaddr(&socketaddr!("127.0.0.1:1234"));
|
let leader = NodeInfo::new_with_socketaddr(&socketaddr!("127.0.0.1:1234"));
|
||||||
|
|
||||||
cluster_info.write().unwrap().insert_info(leader.clone());
|
cluster_info.write().unwrap().insert_info(leader.clone());
|
||||||
cluster_info.write().unwrap().set_leader(leader.id);
|
cluster_info.write().unwrap().set_leader(leader.id);
|
||||||
let rpc_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0);
|
let rpc_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0);
|
||||||
|
@ -53,6 +53,7 @@ pub enum RpcRequest {
|
|||||||
RegisterNode,
|
RegisterNode,
|
||||||
SignVote,
|
SignVote,
|
||||||
DeregisterNode,
|
DeregisterNode,
|
||||||
|
GetStorageMiningLastId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RpcRequest {
|
impl RpcRequest {
|
||||||
@ -95,6 +96,7 @@ impl RpcRequest {
|
|||||||
RpcRequest::RegisterNode => "registerNode",
|
RpcRequest::RegisterNode => "registerNode",
|
||||||
RpcRequest::SignVote => "signVote",
|
RpcRequest::SignVote => "signVote",
|
||||||
RpcRequest::DeregisterNode => "deregisterNode",
|
RpcRequest::DeregisterNode => "deregisterNode",
|
||||||
|
RpcRequest::GetStorageMiningLastId => "getStorageMiningLastId",
|
||||||
};
|
};
|
||||||
let mut request = json!({
|
let mut request = json!({
|
||||||
"jsonrpc": jsonrpc,
|
"jsonrpc": jsonrpc,
|
||||||
|
@ -10,7 +10,6 @@ use rand_chacha::ChaChaRng;
|
|||||||
use result::{Error, Result};
|
use result::{Error, Result};
|
||||||
use service::Service;
|
use service::Service;
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
|
||||||
use solana_sdk::signature::Keypair;
|
use solana_sdk::signature::Keypair;
|
||||||
use solana_sdk::signature::Signature;
|
use solana_sdk::signature::Signature;
|
||||||
use solana_sdk::vote_program;
|
use solana_sdk::vote_program;
|
||||||
@ -30,6 +29,7 @@ type StorageKeys = Vec<u8>;
|
|||||||
pub struct StorageState {
|
pub struct StorageState {
|
||||||
storage_results: Arc<RwLock<StorageResults>>,
|
storage_results: Arc<RwLock<StorageResults>>,
|
||||||
storage_keys: Arc<RwLock<StorageKeys>>,
|
storage_keys: Arc<RwLock<StorageKeys>>,
|
||||||
|
last_id: Hash,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StorageStage {
|
pub struct StorageStage {
|
||||||
@ -49,7 +49,7 @@ const NUM_SAMPLES: usize = 4;
|
|||||||
pub const ENTRIES_PER_SLICE: u64 = 16;
|
pub const ENTRIES_PER_SLICE: u64 = 16;
|
||||||
const KEY_SIZE: usize = 64;
|
const KEY_SIZE: usize = 64;
|
||||||
|
|
||||||
fn get_identity_index_from_pubkey(key: &Pubkey) -> usize {
|
fn get_identity_index_from_signature(key: &Signature) -> usize {
|
||||||
let rkey = key.as_ref();
|
let rkey = key.as_ref();
|
||||||
let mut res: usize = (rkey[0] as usize)
|
let mut res: usize = (rkey[0] as usize)
|
||||||
| ((rkey[1] as usize) << 8)
|
| ((rkey[1] as usize) << 8)
|
||||||
@ -67,18 +67,23 @@ impl StorageState {
|
|||||||
StorageState {
|
StorageState {
|
||||||
storage_keys,
|
storage_keys,
|
||||||
storage_results,
|
storage_results,
|
||||||
|
last_id: Hash::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mining_key(&self, key: &Pubkey) -> Vec<u8> {
|
pub fn get_mining_key(&self, key: &Signature) -> Vec<u8> {
|
||||||
let idx = get_identity_index_from_pubkey(key);
|
let idx = get_identity_index_from_signature(key);
|
||||||
self.storage_keys.read().unwrap()[idx..idx + KEY_SIZE].to_vec()
|
self.storage_keys.read().unwrap()[idx..idx + KEY_SIZE].to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mining_result(&self, key: &Pubkey) -> Hash {
|
pub fn get_mining_result(&self, key: &Signature) -> Hash {
|
||||||
let idx = get_identity_index_from_pubkey(key);
|
let idx = get_identity_index_from_signature(key);
|
||||||
self.storage_results.read().unwrap()[idx]
|
self.storage_results.read().unwrap()[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_last_id(&self) -> Hash {
|
||||||
|
self.last_id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StorageStage {
|
impl StorageStage {
|
||||||
@ -267,7 +272,8 @@ mod tests {
|
|||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use service::Service;
|
use service::Service;
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::hash::Hasher;
|
||||||
|
use solana_sdk::signature::{Keypair, KeypairUtil, Signature};
|
||||||
use solana_sdk::transaction::Transaction;
|
use solana_sdk::transaction::Transaction;
|
||||||
use solana_sdk::vote_program::Vote;
|
use solana_sdk::vote_program::Vote;
|
||||||
use solana_sdk::vote_transaction::VoteTransaction;
|
use solana_sdk::vote_transaction::VoteTransaction;
|
||||||
@ -280,7 +286,7 @@ mod tests {
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use storage_stage::StorageState;
|
use storage_stage::StorageState;
|
||||||
use storage_stage::NUM_IDENTITIES;
|
use storage_stage::NUM_IDENTITIES;
|
||||||
use storage_stage::{get_identity_index_from_pubkey, StorageStage};
|
use storage_stage::{get_identity_index_from_signature, StorageStage};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_storage_stage_none_ledger() {
|
fn test_storage_stage_none_ledger() {
|
||||||
@ -335,14 +341,16 @@ mod tests {
|
|||||||
storage_entry_sender.send(entries.clone()).unwrap();
|
storage_entry_sender.send(entries.clone()).unwrap();
|
||||||
|
|
||||||
let keypair = Keypair::new();
|
let keypair = Keypair::new();
|
||||||
let mut result = storage_state.get_mining_result(&keypair.pubkey());
|
let hash = Hash::default();
|
||||||
|
let signature = Signature::new(keypair.sign(&hash.as_ref()).as_ref());
|
||||||
|
let mut result = storage_state.get_mining_result(&signature);
|
||||||
assert_eq!(result, Hash::default());
|
assert_eq!(result, Hash::default());
|
||||||
|
|
||||||
for _ in 0..9 {
|
for _ in 0..9 {
|
||||||
storage_entry_sender.send(entries.clone()).unwrap();
|
storage_entry_sender.send(entries.clone()).unwrap();
|
||||||
}
|
}
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
result = storage_state.get_mining_result(&keypair.pubkey());
|
result = storage_state.get_mining_result(&signature);
|
||||||
if result != Hash::default() {
|
if result != Hash::default() {
|
||||||
info!("found result = {:?} sleeping..", result);
|
info!("found result = {:?} sleeping..", result);
|
||||||
break;
|
break;
|
||||||
@ -437,19 +445,22 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pubkey_distribution() {
|
fn test_signature_distribution() {
|
||||||
// See that pub keys have an even-ish distribution..
|
// See that signatures have an even-ish distribution..
|
||||||
let mut hist = Arc::new(vec![]);
|
let mut hist = Arc::new(vec![]);
|
||||||
for _ in 0..NUM_IDENTITIES {
|
for _ in 0..NUM_IDENTITIES {
|
||||||
Arc::get_mut(&mut hist).unwrap().push(AtomicUsize::new(0));
|
Arc::get_mut(&mut hist).unwrap().push(AtomicUsize::new(0));
|
||||||
}
|
}
|
||||||
|
let hasher = Hasher::default();
|
||||||
{
|
{
|
||||||
let hist = hist.clone();
|
let hist = hist.clone();
|
||||||
(0..(32 * NUM_IDENTITIES))
|
(0..(32 * NUM_IDENTITIES))
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.for_each(move |_| {
|
.for_each(move |_| {
|
||||||
let keypair = Keypair::new();
|
let keypair = Keypair::new();
|
||||||
let ix = get_identity_index_from_pubkey(&keypair.pubkey());
|
let hash = hasher.clone().result();
|
||||||
|
let signature = Signature::new(keypair.sign(&hash.as_ref()).as_ref());
|
||||||
|
let ix = get_identity_index_from_signature(&signature);
|
||||||
hist[ix].fetch_add(1, Ordering::Relaxed);
|
hist[ix].fetch_add(1, Ordering::Relaxed);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ use std::net::UdpSocket;
|
|||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use storage_stage::{StorageStage, StorageState};
|
use storage_stage::StorageStage;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum TvuReturnType {
|
pub enum TvuReturnType {
|
||||||
@ -105,9 +105,8 @@ impl Tvu {
|
|||||||
let (ledger_write_stage, storage_entry_receiver) =
|
let (ledger_write_stage, storage_entry_receiver) =
|
||||||
LedgerWriteStage::new(ledger_path, ledger_entry_receiver);
|
LedgerWriteStage::new(ledger_path, ledger_entry_receiver);
|
||||||
|
|
||||||
let storage_state = StorageState::new();
|
|
||||||
let storage_stage = StorageStage::new(
|
let storage_stage = StorageStage::new(
|
||||||
&storage_state,
|
&bank.storage_state,
|
||||||
storage_entry_receiver,
|
storage_entry_receiver,
|
||||||
ledger_path,
|
ledger_path,
|
||||||
keypair,
|
keypair,
|
||||||
|
@ -4,7 +4,7 @@ extern crate solana;
|
|||||||
extern crate solana_sdk;
|
extern crate solana_sdk;
|
||||||
|
|
||||||
use solana::client::mk_client;
|
use solana::client::mk_client;
|
||||||
use solana::cluster_info::Node;
|
use solana::cluster_info::{Node, NodeInfo};
|
||||||
use solana::db_ledger::DbLedger;
|
use solana::db_ledger::DbLedger;
|
||||||
use solana::fullnode::Fullnode;
|
use solana::fullnode::Fullnode;
|
||||||
use solana::leader_scheduler::LeaderScheduler;
|
use solana::leader_scheduler::LeaderScheduler;
|
||||||
@ -13,7 +13,6 @@ use solana::logger;
|
|||||||
use solana::replicator::Replicator;
|
use solana::replicator::Replicator;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||||
use std::fs::remove_dir_all;
|
use std::fs::remove_dir_all;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@ -22,16 +21,11 @@ use std::time::Duration;
|
|||||||
fn test_replicator_startup() {
|
fn test_replicator_startup() {
|
||||||
logger::setup();
|
logger::setup();
|
||||||
info!("starting replicator test");
|
info!("starting replicator test");
|
||||||
let entry_height = 0;
|
|
||||||
let replicator_ledger_path = &get_tmp_ledger_path("replicator_test_replicator_ledger");
|
let replicator_ledger_path = &get_tmp_ledger_path("replicator_test_replicator_ledger");
|
||||||
|
|
||||||
let exit = Arc::new(AtomicBool::new(false));
|
|
||||||
let done = Arc::new(AtomicBool::new(false));
|
|
||||||
|
|
||||||
info!("starting leader node");
|
info!("starting leader node");
|
||||||
let leader_keypair = Arc::new(Keypair::new());
|
let leader_keypair = Arc::new(Keypair::new());
|
||||||
let leader_node = Node::new_localhost_with_pubkey(leader_keypair.pubkey());
|
let leader_node = Node::new_localhost_with_pubkey(leader_keypair.pubkey());
|
||||||
let network_addr = leader_node.sockets.gossip.local_addr().unwrap();
|
|
||||||
let leader_info = leader_node.info.clone();
|
let leader_info = leader_node.info.clone();
|
||||||
let vote_account_keypair = Arc::new(Keypair::new());
|
let vote_account_keypair = Arc::new(Keypair::new());
|
||||||
|
|
||||||
@ -61,17 +55,21 @@ fn test_replicator_startup() {
|
|||||||
|
|
||||||
let replicator_keypair = Keypair::new();
|
let replicator_keypair = Keypair::new();
|
||||||
|
|
||||||
|
leader_client
|
||||||
|
.transfer(1, &mint.keypair(), replicator_keypair.pubkey(), &last_id)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
info!("starting replicator node");
|
info!("starting replicator node");
|
||||||
let replicator_node = Node::new_localhost_with_pubkey(replicator_keypair.pubkey());
|
let replicator_node = Node::new_localhost_with_pubkey(replicator_keypair.pubkey());
|
||||||
let (replicator, _leader_info) = Replicator::new(
|
|
||||||
entry_height,
|
let leader_info = NodeInfo::new_entry_point(&leader_info.gossip);
|
||||||
1,
|
|
||||||
&exit,
|
let replicator = Replicator::new(
|
||||||
Some(replicator_ledger_path),
|
Some(replicator_ledger_path),
|
||||||
replicator_node,
|
replicator_node,
|
||||||
Some(network_addr),
|
&leader_info,
|
||||||
done.clone(),
|
&replicator_keypair,
|
||||||
);
|
).unwrap();
|
||||||
|
|
||||||
let mut num_entries = 0;
|
let mut num_entries = 0;
|
||||||
for _ in 0..60 {
|
for _ in 0..60 {
|
||||||
@ -95,10 +93,8 @@ fn test_replicator_startup() {
|
|||||||
.transfer(1, &mint.keypair(), bob.pubkey(), &last_id)
|
.transfer(1, &mint.keypair(), bob.pubkey(), &last_id)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
assert_eq!(done.load(Ordering::Relaxed), true);
|
|
||||||
assert!(num_entries > 0);
|
assert!(num_entries > 0);
|
||||||
exit.store(true, Ordering::Relaxed);
|
replicator.close();
|
||||||
replicator.join();
|
|
||||||
leader.exit();
|
leader.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user