Update replicator sampling and proof generation (#4522)

* Update replicator sampling and proof generation

* Clippy
This commit is contained in:
Sagar Dhawan
2019-06-03 17:27:28 -07:00
committed by GitHub
parent dea663d509
commit 167e15a5ae
4 changed files with 58 additions and 54 deletions

View File

@ -14,10 +14,6 @@ use crate::window_service::WindowService;
use bincode::deserialize; use bincode::deserialize;
use rand::thread_rng; use rand::thread_rng;
use rand::Rng; use rand::Rng;
#[cfg(feature = "chacha")]
use rand::SeedableRng;
#[cfg(feature = "chacha")]
use rand_chacha::ChaChaRng;
use solana_client::rpc_client::RpcClient; use solana_client::rpc_client::RpcClient;
use solana_client::rpc_request::RpcRequest; use solana_client::rpc_request::RpcRequest;
use solana_client::thin_client::ThinClient; use solana_client::thin_client::ThinClient;
@ -57,17 +53,16 @@ pub struct Replicator {
ledger_path: String, ledger_path: String,
keypair: Arc<Keypair>, keypair: Arc<Keypair>,
storage_keypair: Arc<Keypair>, storage_keypair: Arc<Keypair>,
blockhash: String,
signature: ed25519_dalek::Signature, signature: ed25519_dalek::Signature,
cluster_info: Arc<RwLock<ClusterInfo>>, cluster_info: Arc<RwLock<ClusterInfo>>,
ledger_data_file_encrypted: PathBuf, ledger_data_file_encrypted: PathBuf,
sampling_offsets: Vec<u64>, sampling_offsets: Vec<u64>,
hash: Hash, sha_state: Hash,
#[cfg(feature = "chacha")] #[cfg(feature = "chacha")]
num_chacha_blocks: usize, num_chacha_blocks: usize,
#[cfg(feature = "chacha")] #[cfg(feature = "chacha")]
blocktree: Arc<Blocktree>, blocktree: Arc<Blocktree>,
#[cfg(feature = "chacha")]
rng: ChaChaRng,
} }
pub(crate) fn sample_file(in_path: &Path, sample_offsets: &[u64]) -> io::Result<Hash> { pub(crate) fn sample_file(in_path: &Path, sample_offsets: &[u64]) -> io::Result<Hash> {
@ -212,7 +207,7 @@ impl Replicator {
let client = crate::gossip_service::get_client(&nodes); let client = crate::gossip_service::get_client(&nodes);
let (storage_blockhash, storage_slot) = let (storage_blockhash, storage_slot) =
match Self::poll_for_blockhash_and_slot(&cluster_info) { match Self::poll_for_blockhash_and_slot(&cluster_info, &Hash::default().to_string()) {
Ok(blockhash_and_slot) => blockhash_and_slot, Ok(blockhash_and_slot) => blockhash_and_slot,
Err(e) => { Err(e) => {
//shutdown services before exiting //shutdown services before exiting
@ -285,11 +280,6 @@ impl Replicator {
//always push this last //always push this last
thread_handles.push(t_replicate); thread_handles.push(t_replicate);
let mut rng_seed = [0u8; 32];
rng_seed.copy_from_slice(&signature.to_bytes()[0..32]);
#[cfg(feature = "chacha")]
let rng = ChaChaRng::from_seed(rng_seed);
Ok(Self { Ok(Self {
gossip_service, gossip_service,
fetch_stage, fetch_stage,
@ -300,36 +290,46 @@ impl Replicator {
ledger_path: ledger_path.to_string(), ledger_path: ledger_path.to_string(),
keypair, keypair,
storage_keypair, storage_keypair,
blockhash: storage_blockhash,
signature, signature,
cluster_info, cluster_info,
ledger_data_file_encrypted: PathBuf::default(), ledger_data_file_encrypted: PathBuf::default(),
sampling_offsets: vec![], sampling_offsets: vec![],
hash: Hash::default(), sha_state: Hash::default(),
#[cfg(feature = "chacha")] #[cfg(feature = "chacha")]
num_chacha_blocks: 0, num_chacha_blocks: 0,
#[cfg(feature = "chacha")] #[cfg(feature = "chacha")]
blocktree, blocktree,
#[cfg(feature = "chacha")]
rng,
}) })
} }
pub fn run(&mut self) { pub fn run(&mut self) {
info!("waiting for ledger download"); info!("waiting for ledger download");
self.thread_handles.pop().unwrap().join().unwrap(); self.thread_handles.pop().unwrap().join().unwrap();
loop {
self.encrypt_ledger() self.encrypt_ledger()
.expect("ledger encrypt not successful"); .expect("ledger encrypt not successful");
let mut proof_index = 0;
loop {
self.create_sampling_offsets(); self.create_sampling_offsets();
proof_index += 1;
if let Err(err) = self.sample_file_to_create_mining_hash() { if let Err(err) = self.sample_file_to_create_mining_hash() {
warn!("Error sampling file, exiting: {:?}", err); warn!("Error sampling file, exiting: {:?}", err);
break; break;
} }
self.submit_mining_proof(proof_index); self.submit_mining_proof();
// TODO: Replicators should be submitting proofs as fast as possible
sleep(Duration::from_secs(2)); // prep the next proof
let (storage_blockhash, _) =
match Self::poll_for_blockhash_and_slot(&self.cluster_info, &self.blockhash) {
Ok(blockhash_and_slot) => blockhash_and_slot,
Err(e) => {
warn!(
"Error could get a newer blockhash than {:?}. {:?}",
self.blockhash, e
);
break;
}
};
self.signature = self.storage_keypair.sign(storage_blockhash.as_ref());
self.blockhash = storage_blockhash;
} }
} }
@ -406,16 +406,22 @@ impl Replicator {
#[cfg(feature = "chacha")] #[cfg(feature = "chacha")]
{ {
use crate::storage_stage::NUM_STORAGE_SAMPLES; use crate::storage_stage::NUM_STORAGE_SAMPLES;
use rand::{Rng, SeedableRng};
use rand_chacha::ChaChaRng;
let mut rng_seed = [0u8; 32];
rng_seed.copy_from_slice(&self.signature.to_bytes()[0..32]);
let mut rng = ChaChaRng::from_seed(rng_seed);
for _ in 0..NUM_STORAGE_SAMPLES { for _ in 0..NUM_STORAGE_SAMPLES {
self.sampling_offsets self.sampling_offsets
.push(self.rng.gen_range(0, self.num_chacha_blocks) as u64); .push(rng.gen_range(0, self.num_chacha_blocks) as u64);
} }
} }
} }
fn sample_file_to_create_mining_hash(&mut self) -> Result<()> { fn sample_file_to_create_mining_hash(&mut self) -> Result<()> {
self.hash = sample_file(&self.ledger_data_file_encrypted, &self.sampling_offsets)?; self.sha_state = sample_file(&self.ledger_data_file_encrypted, &self.sampling_offsets)?;
info!("sampled hash: {}", self.hash); info!("sampled sha_state: {}", self.sha_state);
Ok(()) Ok(())
} }
@ -457,7 +463,7 @@ impl Replicator {
Ok(()) Ok(())
} }
fn submit_mining_proof(&self, proof_index: u64) { fn submit_mining_proof(&self) {
// No point if we've got no storage account... // No point if we've got no storage account...
let nodes = self.cluster_info.read().unwrap().tvu_peers(); let nodes = self.cluster_info.read().unwrap().tvu_peers();
let client = crate::gossip_service::get_client(&nodes); let client = crate::gossip_service::get_client(&nodes);
@ -473,10 +479,9 @@ impl Replicator {
let (blockhash, _) = client.get_recent_blockhash().expect("No recent blockhash"); let (blockhash, _) = client.get_recent_blockhash().expect("No recent blockhash");
let instruction = storage_instruction::mining_proof( let instruction = storage_instruction::mining_proof(
&self.storage_keypair.pubkey(), &self.storage_keypair.pubkey(),
self.hash, self.sha_state,
self.slot, self.slot,
Signature::new(&self.signature.to_bytes()), Signature::new(&self.signature.to_bytes()),
proof_index,
); );
let message = Message::new_with_payer(vec![instruction], Some(&self.keypair.pubkey())); let message = Message::new_with_payer(vec![instruction], Some(&self.keypair.pubkey()));
let mut transaction = Transaction::new( let mut transaction = Transaction::new(
@ -508,8 +513,10 @@ impl Replicator {
} }
} }
/// Poll for a different blockhash and associated max_slot than `previous_blockhash`
fn poll_for_blockhash_and_slot( fn poll_for_blockhash_and_slot(
cluster_info: &Arc<RwLock<ClusterInfo>>, cluster_info: &Arc<RwLock<ClusterInfo>>,
previous_blockhash: &str,
) -> Result<(String, u64)> { ) -> Result<(String, u64)> {
for _ in 0..10 { for _ in 0..10 {
let rpc_client = { let rpc_client = {
@ -523,6 +530,7 @@ impl Replicator {
.retry_make_rpc_request(&RpcRequest::GetStorageBlockhash, None, 0) .retry_make_rpc_request(&RpcRequest::GetStorageBlockhash, None, 0)
.expect("rpc request") .expect("rpc request")
.to_string(); .to_string();
if storage_blockhash != *previous_blockhash {
let storage_slot = rpc_client let storage_slot = rpc_client
.retry_make_rpc_request(&RpcRequest::GetStorageSlot, None, 0) .retry_make_rpc_request(&RpcRequest::GetStorageSlot, None, 0)
.expect("rpc request") .expect("rpc request")
@ -532,6 +540,7 @@ impl Replicator {
if get_segment_from_slot(storage_slot) != 0 { if get_segment_from_slot(storage_slot) != 0 {
return Ok((storage_blockhash, storage_slot)); return Ok((storage_blockhash, storage_slot));
} }
}
info!("waiting for segment..."); info!("waiting for segment...");
sleep(Duration::from_secs(5)); sleep(Duration::from_secs(5));
} }

View File

@ -290,16 +290,16 @@ impl StorageStage {
storage_keypair: &Arc<Keypair>, storage_keypair: &Arc<Keypair>,
state: &Arc<RwLock<StorageStateInner>>, state: &Arc<RwLock<StorageStateInner>>,
_blocktree: &Arc<Blocktree>, _blocktree: &Arc<Blocktree>,
entry_id: Hash, blockhash: Hash,
slot: u64, slot: u64,
instruction_sender: &InstructionSender, instruction_sender: &InstructionSender,
) -> Result<()> { ) -> Result<()> {
let mut seed = [0u8; 32]; let mut seed = [0u8; 32];
let signature = storage_keypair.sign(&entry_id.as_ref()); let signature = storage_keypair.sign(&blockhash.as_ref());
let ix = storage_instruction::advertise_recent_blockhash( let ix = storage_instruction::advertise_recent_blockhash(
&storage_keypair.pubkey(), &storage_keypair.pubkey(),
entry_id, blockhash,
slot, slot,
); );
instruction_sender.send(ix)?; instruction_sender.send(ix)?;
@ -308,7 +308,11 @@ impl StorageStage {
let mut rng = ChaChaRng::from_seed(seed); let mut rng = ChaChaRng::from_seed(seed);
state.write().unwrap().slot = slot; {
let mut w_state = state.write().unwrap();
w_state.slot = slot;
w_state.storage_blockhash = blockhash;
}
// Regenerate the answers // Regenerate the answers
let num_segments = get_segment_from_slot(slot) as usize; let num_segments = get_segment_from_slot(slot) as usize;
@ -372,7 +376,6 @@ impl StorageStage {
slot: proof_slot, slot: proof_slot,
signature, signature,
sha_state, sha_state,
..
}) => { }) => {
if proof_slot < slot { if proof_slot < slot {
{ {
@ -476,7 +479,7 @@ impl StorageStage {
&storage_keypair, &storage_keypair,
&storage_state, &storage_state,
&blocktree, &blocktree,
entries.last().unwrap().hash, entry_hash,
slot, slot,
instruction_sender, instruction_sender,
)?; )?;
@ -711,7 +714,6 @@ mod tests {
Hash::default(), Hash::default(),
0, 0,
keypair.sign_message(b"test"), keypair.sign_message(b"test"),
0,
); );
let mining_proof_tx = Transaction::new_unsigned_instructions(vec![mining_proof_ix]); let mining_proof_tx = Transaction::new_unsigned_instructions(vec![mining_proof_ix]);
let mining_txs = vec![mining_proof_tx]; let mining_txs = vec![mining_proof_tx];

View File

@ -23,7 +23,6 @@ pub enum StorageInstruction {
sha_state: Hash, sha_state: Hash,
slot: u64, slot: u64,
signature: Signature, signature: Signature,
proof_index: u64,
}, },
AdvertiseStorageRecentBlockhash { AdvertiseStorageRecentBlockhash {
hash: Hash, hash: Hash,
@ -111,13 +110,11 @@ pub fn mining_proof(
sha_state: Hash, sha_state: Hash,
slot: u64, slot: u64,
signature: Signature, signature: Signature,
proof_index: u64,
) -> Instruction { ) -> Instruction {
let storage_instruction = StorageInstruction::SubmitMiningProof { let storage_instruction = StorageInstruction::SubmitMiningProof {
sha_state, sha_state,
slot, slot,
signature, signature,
proof_index,
}; };
let account_metas = vec![ let account_metas = vec![
AccountMeta::new(*storage_pubkey, true), AccountMeta::new(*storage_pubkey, true),

View File

@ -43,7 +43,6 @@ pub fn process_instruction(
sha_state, sha_state,
slot, slot,
signature, signature,
..
} => { } => {
if me_unsigned || rest.len() != 1 { if me_unsigned || rest.len() != 1 {
// This instruction must be signed by `me` // This instruction must be signed by `me`
@ -158,7 +157,6 @@ mod tests {
Hash::default(), Hash::default(),
SLOTS_PER_SEGMENT, SLOTS_PER_SEGMENT,
Signature::default(), Signature::default(),
0,
); );
// the proof is for slot 16, which is in segment 0, need to move the tick height into segment 2 // the proof is for slot 16, which is in segment 0, need to move the tick height into segment 2
let ticks_till_next_segment = TICKS_IN_SEGMENT * 2; let ticks_till_next_segment = TICKS_IN_SEGMENT * 2;
@ -204,7 +202,7 @@ mod tests {
let mut accounts = [Account::default()]; let mut accounts = [Account::default()];
let ix = let ix =
storage_instruction::mining_proof(&pubkey, Hash::default(), 0, Signature::default(), 0); storage_instruction::mining_proof(&pubkey, Hash::default(), 0, Signature::default());
// move tick height into segment 1 // move tick height into segment 1
let ticks_till_next_segment = TICKS_IN_SEGMENT + 1; let ticks_till_next_segment = TICKS_IN_SEGMENT + 1;
let mut tick_account = tick_height::create_account(1); let mut tick_account = tick_height::create_account(1);
@ -226,7 +224,7 @@ mod tests {
accounts[1].data.resize(STORAGE_ACCOUNT_SPACE as usize, 0); accounts[1].data.resize(STORAGE_ACCOUNT_SPACE as usize, 0);
let ix = let ix =
storage_instruction::mining_proof(&pubkey, Hash::default(), 0, Signature::default(), 0); storage_instruction::mining_proof(&pubkey, Hash::default(), 0, Signature::default());
// submitting a proof for a slot in the past, so this should fail // submitting a proof for a slot in the past, so this should fail
assert!(test_instruction(&ix, &mut accounts).is_err()); assert!(test_instruction(&ix, &mut accounts).is_err());
@ -244,7 +242,7 @@ mod tests {
} }
let ix = let ix =
storage_instruction::mining_proof(&pubkey, Hash::default(), 0, Signature::default(), 0); storage_instruction::mining_proof(&pubkey, Hash::default(), 0, Signature::default());
// move tick height into segment 1 // move tick height into segment 1
let ticks_till_next_segment = TICKS_IN_SEGMENT + 1; let ticks_till_next_segment = TICKS_IN_SEGMENT + 1;
let mut tick_account = tick_height::create_account(1); let mut tick_account = tick_height::create_account(1);
@ -501,7 +499,6 @@ mod tests {
sha_state, sha_state,
slot, slot,
Signature::default(), Signature::default(),
0,
)], )],
Some(&mint_keypair.pubkey()), Some(&mint_keypair.pubkey()),
); );
@ -595,7 +592,6 @@ mod tests {
Hash::default(), Hash::default(),
slot, slot,
Signature::default(), Signature::default(),
0,
)], )],
Some(&mint_pubkey), Some(&mint_pubkey),
); );