From 8019bff3917475ced24bee4dcfa8f4eb8e953153 Mon Sep 17 00:00:00 2001 From: Sagar Dhawan Date: Tue, 11 Jun 2019 18:27:47 -0700 Subject: [PATCH] Fixes for storage program and rework storage stage (#4654) automerge --- core/src/replay_stage.rs | 27 +- core/src/replicator.rs | 16 +- core/src/rpc.rs | 4 +- core/src/storage_stage.rs | 376 +++++++++++------- core/src/tvu.rs | 4 +- programs/storage_api/src/lib.rs | 2 +- programs/storage_api/src/storage_contract.rs | 69 ++-- .../storage_api/src/storage_instruction.rs | 25 +- programs/storage_api/src/storage_processor.rs | 33 +- .../tests/storage_processor.rs | 141 ++++--- wallet/src/wallet.rs | 8 +- 11 files changed, 394 insertions(+), 311 deletions(-) diff --git a/core/src/replay_stage.rs b/core/src/replay_stage.rs index 6eda01e116..f71bbbd656 100644 --- a/core/src/replay_stage.rs +++ b/core/src/replay_stage.rs @@ -70,7 +70,11 @@ impl ForkProgress { } impl ReplayStage { - #[allow(clippy::new_ret_no_self, clippy::too_many_arguments)] + #[allow( + clippy::new_ret_no_self, + clippy::too_many_arguments, + clippy::type_complexity + )] pub fn new( my_pubkey: &Pubkey, vote_account: &Pubkey, @@ -83,11 +87,11 @@ impl ReplayStage { subscriptions: &Arc, poh_recorder: &Arc>, leader_schedule_cache: &Arc, - ) -> (Self, Receiver<(u64, Pubkey)>, Receiver>) + ) -> (Self, Receiver<(u64, Pubkey)>, Receiver>>) where T: 'static + KeypairUtil + Send + Sync, { - let (root_slot_sender, root_slot_receiver) = channel(); + let (root_bank_sender, root_bank_receiver) = channel(); let (slot_full_sender, slot_full_receiver) = channel(); trace!("replay stage"); let exit_ = exit.clone(); @@ -152,7 +156,7 @@ impl ReplayStage { &cluster_info, &blocktree, &leader_schedule_cache, - &root_slot_sender, + &root_bank_sender, )?; Self::reset_poh_recorder( @@ -208,7 +212,7 @@ impl ReplayStage { Ok(()) }) .unwrap(); - (Self { t_replay }, slot_full_receiver, root_slot_receiver) + (Self { t_replay }, slot_full_receiver, root_bank_receiver) } pub fn start_leader( my_pubkey: &Pubkey, @@ -297,7 +301,7 @@ impl ReplayStage { cluster_info: &Arc>, blocktree: &Arc, leader_schedule_cache: &Arc, - root_slot_sender: &Sender>, + root_bank_sender: &Sender>>, ) -> Result<()> where T: 'static + KeypairUtil + Send + Sync, @@ -310,12 +314,9 @@ impl ReplayStage { .get(new_root) .expect("Root bank doesn't exist") .clone(); - let mut rooted_slots = root_bank - .parents() - .into_iter() - .map(|bank| bank.slot()) - .collect::>(); - rooted_slots.push(root_bank.slot()); + let mut rooted_banks = root_bank.parents(); + rooted_banks.push(root_bank); + let rooted_slots: Vec<_> = rooted_banks.iter().map(|bank| bank.slot()).collect(); blocktree .set_roots(&rooted_slots) .expect("Ledger set roots failed"); @@ -325,7 +326,7 @@ impl ReplayStage { leader_schedule_cache.set_root(new_root); bank_forks.write().unwrap().set_root(new_root); Self::handle_new_root(&bank_forks, progress); - root_slot_sender.send(rooted_slots)?; + root_bank_sender.send(rooted_banks)?; } locktower.update_epoch(&bank); if let Some(ref voting_keypair) = voting_keypair { diff --git a/core/src/replicator.rs b/core/src/replicator.rs index 1005becad8..f24c94c4dc 100644 --- a/core/src/replicator.rs +++ b/core/src/replicator.rs @@ -317,7 +317,7 @@ impl Replicator { Ok(blockhash_and_slot) => blockhash_and_slot, Err(e) => { warn!( - "Error could get a newer blockhash than {:?}. {:?}", + "Error couldn't get a newer blockhash than {:?}. {:?}", self.blockhash, e ); break; @@ -471,7 +471,7 @@ impl Replicator { let instruction = storage_instruction::mining_proof( &self.storage_keypair.pubkey(), self.sha_state, - self.slot, + get_segment_from_slot(self.slot), Signature::new(&self.signature.to_bytes()), ); let message = Message::new_with_payer(vec![instruction], Some(&self.keypair.pubkey())); @@ -508,7 +508,7 @@ impl Replicator { fn poll_for_blockhash_and_slot( cluster_info: &Arc>, previous_blockhash: &str, - ) -> Result<(String, u64)> { + ) -> result::Result<(String, u64), Error> { for _ in 0..10 { let rpc_client = { let cluster_info = cluster_info.read().unwrap(); @@ -519,12 +519,18 @@ impl Replicator { }; let storage_blockhash = rpc_client .retry_make_rpc_request(&RpcRequest::GetStorageBlockhash, None, 0) - .expect("rpc request") + .map_err(|err| { + warn!("Error while making rpc request {:?}", err); + Error::IO(io::Error::new(ErrorKind::Other, "rpc error")) + })? .to_string(); if storage_blockhash != *previous_blockhash { let storage_slot = rpc_client .retry_make_rpc_request(&RpcRequest::GetStorageSlot, None, 0) - .expect("rpc request") + .map_err(|err| { + warn!("Error while making rpc request {:?}", err); + Error::IO(io::Error::new(ErrorKind::Other, "rpc error")) + })? .as_u64() .unwrap(); info!("storage slot: {}", storage_slot); diff --git a/core/src/rpc.rs b/core/src/rpc.rs index 6abbc05f3f..abe28128b8 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -119,7 +119,9 @@ impl JsonRpcRequestProcessor { } fn get_storage_pubkeys_for_slot(&self, slot: u64) -> Result> { - Ok(self.storage_state.get_pubkeys_for_slot(slot)) + Ok(self + .storage_state + .get_pubkeys_for_slot(slot, &self.bank_forks)) } pub fn fullnode_exit(&self) -> Result { diff --git a/core/src/storage_stage.rs b/core/src/storage_stage.rs index 45c1128927..6c1b37ae1c 100644 --- a/core/src/storage_stage.rs +++ b/core/src/storage_stage.rs @@ -9,17 +9,20 @@ use crate::chacha_cuda::chacha_cbc_encrypt_file_many_keys; use crate::cluster_info::ClusterInfo; use crate::result::{Error, Result}; use crate::service::Service; -use bincode::deserialize; use rand::{Rng, SeedableRng}; use rand_chacha::ChaChaRng; +use solana_runtime::bank::Bank; +use solana_runtime::storage_utils::replicator_accounts; +use solana_sdk::account::Account; +use solana_sdk::account_utils::State; use solana_sdk::hash::Hash; use solana_sdk::instruction::Instruction; use solana_sdk::message::Message; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use solana_sdk::transaction::Transaction; -use solana_storage_api::storage_contract::{CheckedProof, Proof, ProofStatus}; -use solana_storage_api::storage_instruction::{proof_validation, StorageInstruction}; +use solana_storage_api::storage_contract::{CheckedProof, Proof, ProofStatus, StorageContract}; +use solana_storage_api::storage_instruction::proof_validation; use solana_storage_api::{get_segment_from_slot, storage_instruction}; use std::collections::HashMap; use std::io; @@ -29,7 +32,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, Receiver, RecvTimeoutError, Sender}; use std::sync::{Arc, RwLock}; use std::thread::{self, sleep, Builder, JoinHandle}; -use std::time::Duration; +use std::time::{Duration, Instant}; // Block of hash answers to validate against // Vec of [ledger blocks] x [keys] @@ -51,7 +54,7 @@ pub struct StorageStateInner { struct StorageSlots { last_root: u64, slot_count: u64, - pending_roots: Vec, + pending_root_banks: Vec>, } #[derive(Clone, Default)] @@ -119,17 +122,35 @@ impl StorageState { self.state.read().unwrap().slot } - pub fn get_pubkeys_for_slot(&self, slot: u64) -> Vec { + pub fn get_pubkeys_for_slot( + &self, + slot: u64, + bank_forks: &Arc>, + ) -> Vec { // TODO: keep track of age? const MAX_PUBKEYS_TO_RETURN: usize = 5; let index = get_segment_from_slot(slot) as usize; let replicator_map = &self.state.read().unwrap().replicator_map; + let working_bank = bank_forks.read().unwrap().working_bank(); + let accounts = replicator_accounts(&working_bank); if index < replicator_map.len() { - replicator_map[index] + //perform an account owner lookup + let mut slot_replicators = replicator_map[index] .keys() - .cloned() - .take(MAX_PUBKEYS_TO_RETURN) - .collect::>() + .filter_map(|account_id| { + accounts.get(account_id).and_then(|account| { + if let Ok(StorageContract::ReplicatorStorage { owner, .. }) = + account.state() + { + Some(owner) + } else { + None + } + }) + }) + .collect::>(); + slot_replicators.truncate(MAX_PUBKEYS_TO_RETURN); + slot_replicators } else { vec![] } @@ -140,7 +161,7 @@ impl StorageStage { #[allow(clippy::too_many_arguments)] pub fn new( storage_state: &StorageState, - slot_receiver: Receiver>, + bank_receiver: Receiver>>, blocktree: Option>, keypair: &Arc, storage_keypair: &Arc, @@ -165,7 +186,7 @@ impl StorageStage { if let Err(e) = Self::process_entries( &storage_keypair, &storage_state_inner, - &slot_receiver, + &bank_receiver, &some_blocktree, &mut storage_slots, &mut current_key, @@ -278,11 +299,54 @@ impl StorageStage { let signer_keys = vec![keypair.as_ref(), storage_keypair.as_ref()]; let message = Message::new_with_payer(vec![instruction], Some(&signer_keys[0].pubkey())); let transaction = Transaction::new(&signer_keys, message, blockhash); + // try sending the transaction upto 5 times + for _ in 0..5 { + transactions_socket.send_to( + &bincode::serialize(&transaction).unwrap(), + cluster_info.read().unwrap().my_data().tpu, + )?; + sleep(Duration::from_millis(100)); + if Self::poll_for_signature_confirmation(bank_forks, &transaction.signatures[0], 0) + .is_ok() + { + break; + }; + } + Ok(()) + } - transactions_socket.send_to( - &bincode::serialize(&transaction).unwrap(), - cluster_info.read().unwrap().my_data().tpu, - )?; + fn poll_for_signature_confirmation( + bank_forks: &Arc>, + signature: &Signature, + min_confirmed_blocks: usize, + ) -> Result<()> { + let mut now = Instant::now(); + let mut confirmed_blocks = 0; + loop { + let response = bank_forks + .read() + .unwrap() + .working_bank() + .get_signature_confirmation_status(signature); + if let Some((confirmations, res)) = response { + if res.is_ok() { + if confirmed_blocks != confirmations { + now = Instant::now(); + confirmed_blocks = confirmations; + } + if confirmations >= min_confirmed_blocks { + break; + } + } + }; + if now.elapsed().as_secs() > 5 { + return Err(Error::from(io::Error::new( + io::ErrorKind::Other, + "signature not found", + ))); + } + sleep(Duration::from_millis(250)); + } Ok(()) } @@ -364,20 +428,18 @@ impl StorageStage { Ok(()) } - fn process_storage_transaction( - data: &[u8], + fn process_replicator_storage( slot: u64, + account_id: Pubkey, + account: Account, storage_state: &Arc>, current_key_idx: &mut usize, - storage_account_key: Pubkey, ) { - match deserialize(data) { - Ok(StorageInstruction::SubmitMiningProof { - slot: proof_slot, - signature, - sha_state, - }) => { - if proof_slot < slot { + if let Ok(StorageContract::ReplicatorStorage { proofs, .. }) = account.state() { + //convert slot to segment + let segment = get_segment_from_slot(slot); + if let Some(proofs) = proofs.get(&segment) { + for (_, proof) in proofs.iter() { { debug!( "generating storage_keys from storage txs current_key_idx: {}", @@ -385,43 +447,37 @@ impl StorageStage { ); let storage_keys = &mut storage_state.write().unwrap().storage_keys; storage_keys[*current_key_idx..*current_key_idx + size_of::()] - .copy_from_slice(signature.as_ref()); + .copy_from_slice(proof.signature.as_ref()); *current_key_idx += size_of::(); *current_key_idx %= storage_keys.len(); } let mut statew = storage_state.write().unwrap(); - let max_segment_index = get_segment_from_slot(slot) as usize; + let max_segment_index = get_segment_from_slot(slot); if statew.replicator_map.len() < max_segment_index { statew .replicator_map .resize(max_segment_index, HashMap::new()); } - let proof_segment_index = get_segment_from_slot(proof_slot) as usize; + let proof_segment_index = proof.segment_index; if proof_segment_index < statew.replicator_map.len() { + // TODO randomly select and verify the proof first // Copy the submitted proof statew.replicator_map[proof_segment_index] - .entry(storage_account_key) + .entry(account_id) .or_default() - .push(Proof { - signature, - sha_state, - }); + .push(proof.clone()); } } debug!("storage proof: slot: {}", slot); } - Ok(_) => {} - Err(e) => { - info!("error: {:?}", e); - } } } fn process_entries( storage_keypair: &Arc, storage_state: &Arc>, - slot_receiver: &Receiver>, + bank_receiver: &Receiver>>, blocktree: &Arc, storage_slots: &mut StorageSlots, current_key_idx: &mut usize, @@ -430,65 +486,48 @@ impl StorageStage { ) -> Result<()> { let timeout = Duration::new(1, 0); storage_slots - .pending_roots - .append(&mut slot_receiver.recv_timeout(timeout)?); + .pending_root_banks + .append(&mut bank_receiver.recv_timeout(timeout)?); storage_slots - .pending_roots - .sort_unstable_by(|a, b| b.cmp(a)); + .pending_root_banks + .sort_unstable_by(|a, b| b.slot().cmp(&a.slot())); // check if any rooted slots were missed leading up to this one and bump slot count and process proofs for each missed root - while let Some(slot) = storage_slots.pending_roots.pop() { - if slot > storage_slots.last_root { - if !blocktree.is_full(slot) { - // stick this slot back into pending_roots. Evaluate it next time around. - storage_slots.pending_roots.push(slot); - break; - } + while let Some(bank) = storage_slots.pending_root_banks.pop() { + if bank.slot() > storage_slots.last_root { storage_slots.slot_count += 1; - storage_slots.last_root = slot; + storage_slots.last_root = bank.slot(); - if let Ok(entries) = blocktree.get_slot_entries(slot, 0, None) { - for entry in &entries { - // Go through the transactions, find proofs, and use them to update - // the storage_keys with their signatures - for tx in &entry.transactions { - for instruction in tx.message.instructions.iter() { - let program_id = - tx.message.account_keys[instruction.program_ids_index as usize]; - if solana_storage_api::check_id(&program_id) { - let storage_account_key = - tx.message.account_keys[instruction.accounts[0] as usize]; - Self::process_storage_transaction( - &instruction.data, - slot, - storage_state, - current_key_idx, - storage_account_key, - ); - } - } - } - } - if storage_slots.slot_count % storage_rotate_count == 0 { - // assume the last entry in the slot is the blockhash for that slot - let entry_hash = entries.last().unwrap().hash; - debug!( - "crosses sending at root slot: {}! with last entry's hash {}", - storage_slots.slot_count, entry_hash + if storage_slots.slot_count % storage_rotate_count == 0 { + // load all the replicator accounts in the bank. collect all their proofs at the current slot + let replicator_accounts = replicator_accounts(bank.as_ref()); + // find proofs, and use them to update + // the storage_keys with their signatures + for (account_id, account) in replicator_accounts.into_iter() { + Self::process_replicator_storage( + bank.slot(), + account_id, + account, + storage_state, + current_key_idx, ); - Self::process_entry_crossing( - &storage_keypair, - &storage_state, - &blocktree, - entry_hash, - slot, - instruction_sender, - )?; - Self::submit_verifications( - &storage_state, - &storage_keypair, - instruction_sender, - )? } + + // TODO un-ignore this result and be sure to drain all pending proofs + //process a "crossing" + let _ignored = Self::process_entry_crossing( + &storage_keypair, + &storage_state, + &blocktree, + bank.last_blockhash(), + bank.slot(), + instruction_sender, + ); + Self::submit_verifications( + get_segment_from_slot(bank.slot()), + &storage_state, + &storage_keypair, + instruction_sender, + )? } } } @@ -496,6 +535,7 @@ impl StorageStage { } fn submit_verifications( + current_segment: usize, storage_state: &Arc>, storage_keypair: &Arc, ix_sender: &Sender, @@ -507,35 +547,44 @@ impl StorageStage { .replicator_map .iter_mut() .enumerate() - .flat_map(|(segment, proof_map)| { + .flat_map(|(_, proof_map)| { let checked_proofs = proof_map .iter_mut() - .map(|(id, proofs)| { - ( - *id, - proofs - .drain(..) - .map(|proof| CheckedProof { - proof, - status: ProofStatus::Valid, - }) - .collect::>(), - ) + .filter_map(|(id, proofs)| { + if !proofs.is_empty() { + Some(( + *id, + proofs + .drain(..) + .map(|proof| CheckedProof { + proof, + status: ProofStatus::Valid, + }) + .collect::>(), + )) + } else { + None + } }) .collect::>(); if !checked_proofs.is_empty() { - let ix = - proof_validation(&storage_keypair.pubkey(), segment as u64, checked_proofs); + let ix = proof_validation( + &storage_keypair.pubkey(), + current_segment as u64, + checked_proofs, + ); Some(ix) } else { None } }) .collect(); - // TODO Avoid AccountInUse errors in this loop let res: std::result::Result<_, _> = instructions .into_iter() - .map(|ix| ix_sender.send(ix)) + .map(|ix| { + sleep(Duration::from_millis(100)); + ix_sender.send(ix) + }) .collect(); res?; Ok(()) @@ -554,18 +603,18 @@ impl Service for StorageStage { #[cfg(test)] mod tests { use super::*; - use crate::blocktree::tests::{entries_to_blobs, make_slot_entries}; use crate::blocktree::{create_new_tmp_ledger, Blocktree}; use crate::cluster_info::ClusterInfo; use crate::contact_info::ContactInfo; - use crate::entry::Entry; use crate::genesis_utils::{create_genesis_block, GenesisBlockInfo}; use crate::service::Service; + use crate::{blocktree_processor, entry}; use rayon::prelude::*; use solana_runtime::bank::Bank; use solana_sdk::hash::{Hash, Hasher}; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{Keypair, KeypairUtil}; + use solana_sdk::timing::DEFAULT_TICKS_PER_SLOT; use solana_storage_api::SLOTS_PER_SEGMENT; use std::cmp::{max, min}; use std::fs::remove_dir_all; @@ -609,7 +658,7 @@ mod tests { } #[test] - fn test_storage_stage_process_entries() { + fn test_storage_stage_process_banks() { solana_logger::setup(); let keypair = Arc::new(Keypair::new()); let storage_keypair = Arc::new(Keypair::new()); @@ -618,19 +667,17 @@ mod tests { let GenesisBlockInfo { genesis_block, .. } = create_genesis_block(1000); let (ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_block); - let (blobs, _entries) = make_slot_entries(1, 0, 64); let blocktree = Arc::new(Blocktree::open(&ledger_path).unwrap()); let slot = 1; let bank = Arc::new(Bank::new(&genesis_block)); - let bank_forks = Arc::new(RwLock::new(BankForks::new_from_banks(&[bank], 0))); - blocktree.insert_data_blobs(blobs).unwrap(); + let bank_forks = Arc::new(RwLock::new(BankForks::new_from_banks(&[bank.clone()], 0))); let cluster_info = test_cluster_info(&keypair.pubkey()); - let (slot_sender, slot_receiver) = channel(); + let (bank_sender, bank_receiver) = channel(); let storage_state = StorageState::new(); let storage_stage = StorageStage::new( &storage_state, - slot_receiver, + bank_receiver, Some(blocktree.clone()), &keypair, &storage_keypair, @@ -639,22 +686,34 @@ mod tests { STORAGE_ROTATE_TEST_COUNT, &cluster_info, ); - slot_sender.send(vec![slot]).unwrap(); + bank_sender.send(vec![bank.clone()]).unwrap(); let keypair = Keypair::new(); let hash = Hash::default(); let signature = keypair.sign_message(&hash.as_ref()); + #[cfg(feature = "cuda")] let mut result = storage_state.get_mining_result(&signature); + #[cfg(not(feature = "cuda"))] + let result = storage_state.get_mining_result(&signature); + assert_eq!(result, Hash::default()); - let rooted_slots = (slot..slot + SLOTS_PER_SEGMENT + 1) + let mut last_bank = bank; + let rooted_banks = (slot..slot + SLOTS_PER_SEGMENT + 1) .map(|i| { - let (blobs, _entries) = make_slot_entries(i, i - 1, 64); - blocktree.insert_data_blobs(blobs).unwrap(); - i + let bank = Bank::new_from_parent(&last_bank, &keypair.pubkey(), i); + blocktree_processor::process_entries( + &bank, + &entry::create_ticks(64, bank.last_blockhash()), + ) + .expect("failed process entries"); + last_bank = Arc::new(bank); + last_bank.clone() }) .collect::>(); - slot_sender.send(rooted_slots).unwrap(); + bank_sender.send(rooted_banks).unwrap(); + + #[cfg(feature = "cuda")] for _ in 0..5 { result = storage_state.get_mining_result(&signature); if result != Hash::default() { @@ -679,28 +738,35 @@ mod tests { } #[test] - fn test_storage_stage_process_proof_entries() { + fn test_storage_stage_process_account_proofs() { solana_logger::setup(); let keypair = Arc::new(Keypair::new()); let storage_keypair = Arc::new(Keypair::new()); + let replicator_keypair = Arc::new(Keypair::new()); let exit = Arc::new(AtomicBool::new(false)); - let GenesisBlockInfo { genesis_block, .. } = create_genesis_block(1000); + let GenesisBlockInfo { + mut genesis_block, + mint_keypair, + .. + } = create_genesis_block(1000); + genesis_block + .native_instruction_processors + .push(solana_storage_program::solana_storage_program!()); let (ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_block); let blocktree = Arc::new(Blocktree::open(&ledger_path).unwrap()); - let (blobs, entries) = make_slot_entries(1, 0, 128); - blocktree.insert_data_blobs(blobs).unwrap(); - let bank = Arc::new(Bank::new(&genesis_block)); - let bank_forks = Arc::new(RwLock::new(BankForks::new_from_banks(&[bank], 0))); + let bank = Bank::new(&genesis_block); + let bank = Arc::new(bank); + let bank_forks = Arc::new(RwLock::new(BankForks::new_from_banks(&[bank.clone()], 0))); let cluster_info = test_cluster_info(&keypair.pubkey()); - let (slot_sender, slot_receiver) = channel(); + let (bank_sender, bank_receiver) = channel(); let storage_state = StorageState::new(); let storage_stage = StorageStage::new( &storage_state, - slot_receiver, + bank_receiver, Some(blocktree.clone()), &keypair, &storage_keypair, @@ -709,7 +775,24 @@ mod tests { STORAGE_ROTATE_TEST_COUNT, &cluster_info, ); - slot_sender.send(vec![1]).unwrap(); + bank_sender.send(vec![bank.clone()]).unwrap(); + + // create accounts + let bank = Arc::new(Bank::new_from_parent(&bank, &keypair.pubkey(), 1)); + let account_ix = storage_instruction::create_replicator_storage_account( + &mint_keypair.pubkey(), + &Pubkey::new_rand(), + &replicator_keypair.pubkey(), + 1, + ); + let account_tx = Transaction::new_signed_instructions( + &[&mint_keypair], + account_ix, + bank.last_blockhash(), + ); + bank.process_transaction(&account_tx).expect("create"); + + bank_sender.send(vec![bank.clone()]).unwrap(); let mut reference_keys; { @@ -719,21 +802,34 @@ mod tests { } let keypair = Keypair::new(); + let mining_proof_ix = storage_instruction::mining_proof( - &keypair.pubkey(), + &replicator_keypair.pubkey(), Hash::default(), 0, keypair.sign_message(b"test"), ); - let mining_proof_tx = Transaction::new_unsigned_instructions(vec![mining_proof_ix]); - let mining_txs = vec![mining_proof_tx]; - let next_hash = solana_sdk::hash::hash(entries.last().unwrap().hash.as_ref()); - let proof_entry = Entry::new(&next_hash, 1, mining_txs); - blocktree - .insert_data_blobs(entries_to_blobs(&vec![proof_entry], 2, 1, true)) - .unwrap(); - slot_sender.send(vec![2]).unwrap(); + let next_bank = Arc::new(Bank::new_from_parent(&bank, &keypair.pubkey(), 2)); + //register ticks so the program reports a different segment + blocktree_processor::process_entries( + &next_bank, + &entry::create_ticks( + DEFAULT_TICKS_PER_SLOT * SLOTS_PER_SEGMENT + 1, + bank.last_blockhash(), + ), + ) + .unwrap(); + let message = Message::new_with_payer(vec![mining_proof_ix], Some(&mint_keypair.pubkey())); + let mining_proof_tx = Transaction::new( + &[&mint_keypair, replicator_keypair.as_ref()], + message, + next_bank.last_blockhash(), + ); + next_bank + .process_transaction(&mining_proof_tx) + .expect("process txs"); + bank_sender.send(vec![next_bank]).unwrap(); for _ in 0..5 { { diff --git a/core/src/tvu.rs b/core/src/tvu.rs index a246c37600..1d7977d78b 100644 --- a/core/src/tvu.rs +++ b/core/src/tvu.rs @@ -111,7 +111,7 @@ impl Tvu { *bank_forks.read().unwrap().working_bank().epoch_schedule(), ); - let (replay_stage, slot_full_receiver, root_slot_receiver) = ReplayStage::new( + let (replay_stage, slot_full_receiver, root_bank_receiver) = ReplayStage::new( &keypair.pubkey(), vote_account, voting_keypair, @@ -139,7 +139,7 @@ impl Tvu { let storage_stage = StorageStage::new( storage_state, - root_slot_receiver, + root_bank_receiver, Some(blocktree), &keypair, storage_keypair, diff --git a/programs/storage_api/src/lib.rs b/programs/storage_api/src/lib.rs index 2e081d31e0..89a12534cc 100644 --- a/programs/storage_api/src/lib.rs +++ b/programs/storage_api/src/lib.rs @@ -5,7 +5,7 @@ pub mod storage_processor; pub const SLOTS_PER_SEGMENT: u64 = 16; pub fn get_segment_from_slot(slot: u64) -> usize { - (slot / SLOTS_PER_SEGMENT) as usize + ((slot + (SLOTS_PER_SEGMENT - 1)) / SLOTS_PER_SEGMENT) as usize } const STORAGE_PROGRAM_ID: [u8; 32] = [ diff --git a/programs/storage_api/src/storage_contract.rs b/programs/storage_api/src/storage_contract.rs index 6bba0f769e..522282fd18 100644 --- a/programs/storage_api/src/storage_contract.rs +++ b/programs/storage_api/src/storage_contract.rs @@ -32,6 +32,8 @@ impl Default for ProofStatus { pub struct Proof { pub signature: Signature, pub sha_state: Hash, + /// The start index of the segment proof is for + pub segment_index: usize, } #[derive(Default, Debug, Serialize, Deserialize, Clone)] @@ -134,13 +136,12 @@ impl<'a> StorageAccount<'a> { pub fn submit_mining_proof( &mut self, sha_state: Hash, - slot: u64, + segment_index: usize, signature: Signature, current_slot: u64, ) -> Result<(), InstructionError> { let mut storage_contract = &mut self.account.state()?; if let StorageContract::ReplicatorStorage { proofs, .. } = &mut storage_contract { - let segment_index = get_segment_from_slot(slot); let current_segment = get_segment_from_slot(current_slot); if segment_index >= current_segment { @@ -149,11 +150,12 @@ impl<'a> StorageAccount<'a> { } debug!( - "Mining proof submitted with contract {:?} slot: {}", - sha_state, slot + "Mining proof submitted with contract {:?} segment_index: {}", + sha_state, segment_index ); - let segment_proofs = proofs.entry(segment_index).or_default(); + // store the proofs in the "current" segment's entry in the hash map. + let segment_proofs = proofs.entry(current_segment).or_default(); if segment_proofs.contains_key(&sha_state) { // do not accept duplicate proofs return Err(InstructionError::InvalidArgument); @@ -163,8 +165,11 @@ impl<'a> StorageAccount<'a> { Proof { sha_state, signature, + segment_index, }, ); + // TODO check for time correctness + proofs.retain(|segment, _| *segment >= current_segment.saturating_sub(5)); self.account.set_state(storage_contract) } else { @@ -286,33 +291,20 @@ impl<'a> StorageAccount<'a> { pub fn claim_storage_reward( &mut self, mining_pool: &mut KeyedAccount, - slot: u64, - current_slot: u64, ) -> Result<(), InstructionError> { let mut storage_contract = &mut self.account.state()?; if let StorageContract::ValidatorStorage { - reward_validations, - slot: state_slot, - .. + reward_validations, .. } = &mut storage_contract { - let state_segment = get_segment_from_slot(*state_slot); - let claim_segment = get_segment_from_slot(slot); - if state_segment <= claim_segment || !reward_validations.contains_key(&claim_segment) { - debug!( - "current {:?}, claim {:?}, have rewards for {:?} segments", - state_segment, - claim_segment, - reward_validations.len() - ); - return Err(InstructionError::InvalidArgument); - } let num_validations = count_valid_proofs( &reward_validations - .remove(&claim_segment) - .map(|mut proofs| proofs.drain().map(|(_, proof)| proof).collect::>()) - .unwrap_or_default(), + .drain() + .flat_map(|(_segment, mut proofs)| { + proofs.drain().map(|(_, proof)| proof).collect::>() + }) + .collect::>(), ); let reward = TOTAL_VALIDATOR_REWARDS * num_validations; mining_pool.account.lamports -= reward; @@ -324,34 +316,20 @@ impl<'a> StorageAccount<'a> { .. } = &mut storage_contract { - // if current tick height is a full segment away, allow reward collection - let claim_index = get_segment_from_slot(current_slot); - let claim_segment = get_segment_from_slot(slot); - // Todo this might might always be true - if claim_index <= claim_segment - || !reward_validations.contains_key(&claim_segment) - || !proofs.contains_key(&claim_segment) - { - info!( - "current {:?}, claim {:?}, have rewards for {:?} segments", - claim_index, - claim_segment, - reward_validations.len() - ); - return Err(InstructionError::InvalidArgument); - } // remove proofs for which rewards have already been collected - let segment_proofs = proofs.get_mut(&claim_segment).unwrap(); + let segment_proofs = proofs; let checked_proofs = reward_validations - .remove(&claim_segment) - .map(|mut proofs| { + .drain() + .flat_map(|(segment, mut proofs)| { proofs .drain() .map(|(sha_state, proof)| { proof .into_iter() .map(|proof| { - segment_proofs.remove(&sha_state); + segment_proofs.get_mut(&segment).and_then(|segment_proofs| { + segment_proofs.remove(&sha_state) + }); proof }) .collect::>() @@ -359,7 +337,7 @@ impl<'a> StorageAccount<'a> { .flatten() .collect::>() }) - .unwrap_or_default(); + .collect::>(); let total_proofs = checked_proofs.len() as u64; let num_validations = count_valid_proofs(&checked_proofs); let reward = @@ -491,6 +469,7 @@ mod tests { let proof = Proof { signature: Signature::default(), sha_state: Hash::default(), + segment_index, }; let mut checked_proof = CheckedProof { proof: proof.clone(), diff --git a/programs/storage_api/src/storage_instruction.rs b/programs/storage_api/src/storage_instruction.rs index 53ca6634f8..bf650c4682 100644 --- a/programs/storage_api/src/storage_instruction.rs +++ b/programs/storage_api/src/storage_instruction.rs @@ -5,7 +5,7 @@ use solana_sdk::hash::Hash; use solana_sdk::instruction::{AccountMeta, Instruction}; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::Signature; -use solana_sdk::syscall::tick_height; +use solana_sdk::syscall::current; use solana_sdk::system_instruction; use std::collections::HashMap; @@ -25,7 +25,7 @@ pub enum StorageInstruction { SubmitMiningProof { sha_state: Hash, - slot: u64, + segment_index: usize, signature: Signature, }, AdvertiseStorageRecentBlockhash { @@ -37,9 +37,7 @@ pub enum StorageInstruction { /// Expects 1 Account: /// 0 - Storage account with credits to redeem /// 1 - MiningPool account to redeem credits from - ClaimStorageReward { - slot: u64, - }, + ClaimStorageReward, ProofValidation { segment: u64, proofs: Vec<(Pubkey, Vec)>, @@ -118,17 +116,17 @@ pub fn create_mining_pool_account( pub fn mining_proof( storage_pubkey: &Pubkey, sha_state: Hash, - slot: u64, + segment_index: usize, signature: Signature, ) -> Instruction { let storage_instruction = StorageInstruction::SubmitMiningProof { sha_state, - slot, + segment_index, signature, }; let account_metas = vec![ AccountMeta::new(*storage_pubkey, true), - AccountMeta::new(tick_height::id(), false), + AccountMeta::new(current::id(), false), ]; Instruction::new(id(), &storage_instruction, account_metas) } @@ -144,7 +142,7 @@ pub fn advertise_recent_blockhash( }; let account_metas = vec![ AccountMeta::new(*storage_pubkey, true), - AccountMeta::new(tick_height::id(), false), + AccountMeta::new(current::id(), false), ]; Instruction::new(id(), &storage_instruction, account_metas) } @@ -164,16 +162,11 @@ pub fn proof_validation( Instruction::new(id(), &storage_instruction, account_metas) } -pub fn claim_reward( - storage_pubkey: &Pubkey, - mining_pool_pubkey: &Pubkey, - slot: u64, -) -> Instruction { - let storage_instruction = StorageInstruction::ClaimStorageReward { slot }; +pub fn claim_reward(storage_pubkey: &Pubkey, mining_pool_pubkey: &Pubkey) -> Instruction { + let storage_instruction = StorageInstruction::ClaimStorageReward; let account_metas = vec![ AccountMeta::new(*storage_pubkey, false), AccountMeta::new(*mining_pool_pubkey, false), - AccountMeta::new(tick_height::id(), false), ]; Instruction::new(id(), &storage_instruction, account_metas) } diff --git a/programs/storage_api/src/storage_processor.rs b/programs/storage_api/src/storage_processor.rs index c7856cd8b1..c684847272 100644 --- a/programs/storage_api/src/storage_processor.rs +++ b/programs/storage_api/src/storage_processor.rs @@ -6,8 +6,7 @@ use crate::storage_instruction::StorageInstruction; use solana_sdk::account::KeyedAccount; use solana_sdk::instruction::InstructionError; use solana_sdk::pubkey::Pubkey; -use solana_sdk::syscall::tick_height::TickHeight; -use solana_sdk::timing::DEFAULT_TICKS_PER_SLOT; +use solana_sdk::syscall::current::Current; pub fn process_instruction( _program_id: &Pubkey, @@ -41,43 +40,29 @@ pub fn process_instruction( } StorageInstruction::SubmitMiningProof { sha_state, - slot, + segment_index, signature, } => { if me_unsigned || rest.len() != 1 { // This instruction must be signed by `me` Err(InstructionError::InvalidArgument)?; } - let tick_height = TickHeight::from(&rest[0].account).unwrap(); - storage_account.submit_mining_proof( - sha_state, - slot, - signature, - tick_height / DEFAULT_TICKS_PER_SLOT, - ) + let current = Current::from(&rest[0].account).unwrap(); + storage_account.submit_mining_proof(sha_state, segment_index, signature, current.slot) } StorageInstruction::AdvertiseStorageRecentBlockhash { hash, slot } => { if me_unsigned || rest.len() != 1 { // This instruction must be signed by `me` Err(InstructionError::InvalidArgument)?; } - let tick_height = TickHeight::from(&rest[0].account).unwrap(); - storage_account.advertise_storage_recent_blockhash( - hash, - slot, - tick_height / DEFAULT_TICKS_PER_SLOT, - ) + let current = Current::from(&rest[0].account).unwrap(); + storage_account.advertise_storage_recent_blockhash(hash, slot, current.slot) } - StorageInstruction::ClaimStorageReward { slot } => { - if rest.len() != 2 { + StorageInstruction::ClaimStorageReward => { + if rest.len() != 1 { Err(InstructionError::InvalidArgument)?; } - let tick_height = TickHeight::from(&rest[1].account).unwrap(); - storage_account.claim_storage_reward( - &mut rest[0], - slot, - tick_height / DEFAULT_TICKS_PER_SLOT, - ) + storage_account.claim_storage_reward(&mut rest[0]) } StorageInstruction::ProofValidation { segment, proofs } => { if me_unsigned || rest.is_empty() { diff --git a/programs/storage_program/tests/storage_processor.rs b/programs/storage_program/tests/storage_processor.rs index c6f31356dc..0ff6c18197 100644 --- a/programs/storage_program/tests/storage_processor.rs +++ b/programs/storage_program/tests/storage_processor.rs @@ -12,8 +12,8 @@ use solana_sdk::instruction::{Instruction, InstructionError}; use solana_sdk::message::Message; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; -use solana_sdk::syscall::tick_height; -use solana_sdk::syscall::tick_height::TickHeight; +use solana_sdk::syscall::current; +use solana_sdk::syscall::current::Current; use solana_sdk::timing::DEFAULT_TICKS_PER_SLOT; use solana_storage_api::storage_contract::StorageAccount; use solana_storage_api::storage_contract::{ @@ -114,18 +114,22 @@ fn test_proof_bounds() { .unwrap(); } - let ix = storage_instruction::mining_proof( - &pubkey, - Hash::default(), - SLOTS_PER_SEGMENT, - Signature::default(), + let ix = storage_instruction::mining_proof(&pubkey, Hash::default(), 0, Signature::default()); + // the proof is for segment 0, need to move the slot into segment 2 + let mut current_account = current::create_account(1); + Current::to( + &Current { + slot: SLOTS_PER_SEGMENT * 2, + epoch: 0, + stakers_epoch: 0, + }, + &mut current_account, ); - // 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 mut tick_account = tick_height::create_account(1); - TickHeight::to(ticks_till_next_segment, &mut tick_account); - assert_eq!(test_instruction(&ix, &mut [account, tick_account]), Ok(())); + assert_eq!( + test_instruction(&ix, &mut [account, current_account]), + Ok(()) + ); } #[test] @@ -142,9 +146,9 @@ fn test_serialize_overflow() { let tick_pubkey = Pubkey::new_rand(); let mut keyed_accounts = Vec::new(); let mut user_account = Account::default(); - let mut tick_account = tick_height::create_account(1); + let mut current_account = current::create_account(1); keyed_accounts.push(KeyedAccount::new(&pubkey, true, &mut user_account)); - keyed_accounts.push(KeyedAccount::new(&tick_pubkey, false, &mut tick_account)); + keyed_accounts.push(KeyedAccount::new(&tick_pubkey, false, &mut current_account)); let ix = storage_instruction::advertise_recent_blockhash( &pubkey, @@ -165,13 +169,19 @@ fn test_invalid_accounts_len() { let ix = storage_instruction::mining_proof(&pubkey, Hash::default(), 0, Signature::default()); // move tick height into segment 1 - let ticks_till_next_segment = TICKS_IN_SEGMENT + 1; - let mut tick_account = tick_height::create_account(1); - TickHeight::to(ticks_till_next_segment, &mut tick_account); + let mut current_account = current::create_account(1); + Current::to( + &Current { + slot: 16, + epoch: 0, + stakers_epoch: 0, + }, + &mut current_account, + ); assert!(test_instruction(&ix, &mut accounts).is_err()); - let mut accounts = [Account::default(), tick_account, Account::default()]; + let mut accounts = [Account::default(), current_account, Account::default()]; assert!(test_instruction(&ix, &mut accounts).is_err()); } @@ -205,18 +215,30 @@ fn test_submit_mining_ok() { } let ix = storage_instruction::mining_proof(&pubkey, Hash::default(), 0, Signature::default()); - // move tick height into segment 1 - let ticks_till_next_segment = TICKS_IN_SEGMENT + 1; - let mut tick_account = tick_height::create_account(1); - TickHeight::to(ticks_till_next_segment, &mut tick_account); + // move slot into segment 1 + let mut current_account = current::create_account(1); + Current::to( + &Current { + slot: SLOTS_PER_SEGMENT, + epoch: 0, + stakers_epoch: 0, + }, + &mut current_account, + ); - assert_matches!(test_instruction(&ix, &mut [account, tick_account]), Ok(_)); + assert_matches!( + test_instruction(&ix, &mut [account, current_account]), + Ok(_) + ); } #[test] fn test_validate_mining() { solana_logger::setup(); - let (genesis_block, mint_keypair) = create_genesis_block(1000); + let (mut genesis_block, mint_keypair) = create_genesis_block(1000); + genesis_block + .native_instruction_processors + .push(solana_storage_program::solana_storage_program!()); let mint_pubkey = mint_keypair.pubkey(); let replicator_1_storage_keypair = Keypair::new(); @@ -231,10 +253,8 @@ fn test_validate_mining() { let mining_pool_keypair = Keypair::new(); let mining_pool_pubkey = mining_pool_keypair.pubkey(); - let mut bank = Bank::new(&genesis_block); - bank.add_instruction_processor(id(), process_instruction); + let bank = Bank::new(&genesis_block); let bank = Arc::new(bank); - let slot = 0; let bank_client = BankClient::new_shared(&bank); init_storage_accounts( @@ -251,11 +271,13 @@ fn test_validate_mining() { )); bank_client.send_message(&[&mint_keypair], message).unwrap(); - // tick the bank up until it's moved into storage segment 2 because the next advertise is for segment 1 - let next_storage_segment_tick_height = TICKS_IN_SEGMENT * 2; - for _ in 0..next_storage_segment_tick_height { - bank.register_tick(&bank.last_blockhash()); - } + // create a new bank in segment 2 + let bank = Arc::new(Bank::new_from_parent( + &bank, + &Pubkey::default(), + SLOTS_PER_SEGMENT * 2, + )); + let bank_client = BankClient::new_shared(&bank); // advertise for storage segment 1 let message = Message::new_with_payer( @@ -273,15 +295,15 @@ fn test_validate_mining() { // submit proofs 5 proofs for each replicator for segment 0 let mut checked_proofs: HashMap<_, Vec<_>> = HashMap::new(); - for slot in 0..5 { + for _ in 0..5 { checked_proofs .entry(replicator_1_storage_id) .or_default() .push(submit_proof( &mint_keypair, &replicator_1_storage_keypair, - slot, &bank_client, + 0, )); checked_proofs .entry(replicator_2_storage_id) @@ -289,8 +311,8 @@ fn test_validate_mining() { .push(submit_proof( &mint_keypair, &replicator_2_storage_keypair, - slot, &bank_client, + 0, )); } let message = Message::new_with_payer( @@ -302,10 +324,14 @@ fn test_validate_mining() { Some(&mint_pubkey), ); - let next_storage_segment_tick_height = TICKS_IN_SEGMENT; - for _ in 0..next_storage_segment_tick_height { - bank.register_tick(&bank.last_blockhash()); - } + // move banks into the next segment + let proof_segment = get_segment_from_slot(bank.slot()); + let bank = Arc::new(Bank::new_from_parent( + &bank, + &Pubkey::default(), + SLOTS_PER_SEGMENT + bank.slot(), + )); + let bank_client = BankClient::new_shared(&bank); assert_matches!( bank_client.send_message(&[&mint_keypair, &validator_storage_keypair], message), @@ -315,7 +341,7 @@ fn test_validate_mining() { let message = Message::new_with_payer( vec![storage_instruction::proof_validation( &validator_storage_id, - get_segment_from_slot(slot) as u64, + proof_segment as u64, checked_proofs, )], Some(&mint_pubkey), @@ -335,10 +361,13 @@ fn test_validate_mining() { Some(&mint_pubkey), ); - let next_storage_segment_tick_height = TICKS_IN_SEGMENT; - for _ in 0..next_storage_segment_tick_height { - bank.register_tick(&bank.last_blockhash()); - } + // move banks into the next segment + let bank = Arc::new(Bank::new_from_parent( + &bank, + &Pubkey::default(), + SLOTS_PER_SEGMENT + bank.slot(), + )); + let bank_client = BankClient::new_shared(&bank); assert_matches!( bank_client.send_message(&[&mint_keypair, &validator_storage_keypair], message), @@ -351,7 +380,6 @@ fn test_validate_mining() { vec![storage_instruction::claim_reward( &validator_storage_id, &mining_pool_pubkey, - slot, )], Some(&mint_pubkey), ); @@ -375,7 +403,6 @@ fn test_validate_mining() { vec![storage_instruction::claim_reward( &replicator_1_storage_id, &mining_pool_pubkey, - slot, )], Some(&mint_pubkey), ); @@ -385,7 +412,6 @@ fn test_validate_mining() { vec![storage_instruction::claim_reward( &replicator_2_storage_id, &mining_pool_pubkey, - slot, )], Some(&mint_pubkey), ); @@ -453,15 +479,15 @@ fn get_storage_slot(client: &C, account: &Pubkey) -> u64 { fn submit_proof( mint_keypair: &Keypair, storage_keypair: &Keypair, - slot: u64, bank_client: &BankClient, + segment_index: u64, ) -> CheckedProof { let sha_state = Hash::new(Pubkey::new_rand().as_ref()); let message = Message::new_with_payer( vec![storage_instruction::mining_proof( &storage_keypair.pubkey(), sha_state, - slot, + segment_index as usize, Signature::default(), )], Some(&mint_keypair.pubkey()), @@ -475,6 +501,7 @@ fn submit_proof( proof: Proof { signature: Signature::default(), sha_state, + segment_index: segment_index as usize, }, status: ProofStatus::Valid, } @@ -497,20 +524,20 @@ fn get_storage_blockhash(client: &C, account: &Pubkey) -> Hash { #[test] fn test_bank_storage() { - let (genesis_block, mint_keypair) = create_genesis_block(1000); + let (mut genesis_block, mint_keypair) = create_genesis_block(1000); + genesis_block + .native_instruction_processors + .push(solana_storage_program::solana_storage_program!()); let mint_pubkey = mint_keypair.pubkey(); let replicator_keypair = Keypair::new(); let replicator_pubkey = replicator_keypair.pubkey(); let validator_keypair = Keypair::new(); let validator_pubkey = validator_keypair.pubkey(); - let mut bank = Bank::new(&genesis_block); - bank.add_instruction_processor(id(), process_instruction); + let bank = Bank::new(&genesis_block); // tick the bank up until it's moved into storage segment 2 - let next_storage_segment_tick_height = TICKS_IN_SEGMENT * 2; - for _ in 0..next_storage_segment_tick_height { - bank.register_tick(&bank.last_blockhash()); - } + // create a new bank in storage segment 2 + let bank = Bank::new_from_parent(&Arc::new(bank), &Pubkey::new_rand(), SLOTS_PER_SEGMENT * 2); let bank_client = BankClient::new(bank); let x = 42; @@ -541,7 +568,7 @@ fn test_bank_storage() { vec![storage_instruction::advertise_recent_blockhash( &validator_pubkey, storage_blockhash, - SLOTS_PER_SEGMENT, + SLOTS_PER_SEGMENT as u64, )], Some(&mint_pubkey), ); diff --git a/wallet/src/wallet.rs b/wallet/src/wallet.rs index 873484f7c3..b10b322814 100644 --- a/wallet/src/wallet.rs +++ b/wallet/src/wallet.rs @@ -58,7 +58,7 @@ pub enum WalletCommand { CreateStorageMiningPoolAccount(Pubkey, u64), CreateReplicatorStorageAccount(Pubkey, Pubkey), CreateValidatorStorageAccount(Pubkey, Pubkey), - ClaimStorageReward(Pubkey, Pubkey, u64), + ClaimStorageReward(Pubkey, Pubkey), ShowStorageAccount(Pubkey), Deploy(String), GetTransactionCount, @@ -302,11 +302,9 @@ pub fn parse_command( let storage_mining_pool_account_pubkey = value_of(matches, "storage_mining_pool_account_pubkey").unwrap(); let storage_account_pubkey = value_of(matches, "storage_account_pubkey").unwrap(); - let slot = matches.value_of("slot").unwrap().parse()?; Ok(WalletCommand::ClaimStorageReward( storage_mining_pool_account_pubkey, storage_account_pubkey, - slot, )) } ("show-storage-account", Some(matches)) => { @@ -738,14 +736,12 @@ fn process_claim_storage_reward( config: &WalletConfig, storage_mining_pool_account_pubkey: &Pubkey, storage_account_pubkey: &Pubkey, - slot: u64, ) -> ProcessResult { let (recent_blockhash, _fee_calculator) = rpc_client.get_recent_blockhash()?; let instruction = storage_instruction::claim_reward( storage_account_pubkey, storage_mining_pool_account_pubkey, - slot, ); let signers = [&config.keypair]; let message = Message::new_with_payer(vec![instruction], Some(&signers[0].pubkey())); @@ -1132,13 +1128,11 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult { WalletCommand::ClaimStorageReward( storage_mining_pool_account_pubkey, storage_account_pubkey, - slot, ) => process_claim_storage_reward( &rpc_client, config, &storage_mining_pool_account_pubkey, &storage_account_pubkey, - *slot, ), WalletCommand::ShowStorageAccount(storage_account_pubkey) => {