Submit all incoming proofs as valid (#4377)

This commit is contained in:
Sagar Dhawan
2019-05-24 14:49:10 -07:00
committed by GitHub
parent 9843c3a5cb
commit 4c9b7c9d2b
4 changed files with 256 additions and 127 deletions

View File

@ -18,9 +18,10 @@ use solana_sdk::message::Message;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use solana_sdk::signature::{Keypair, KeypairUtil, Signature};
use solana_sdk::transaction::Transaction; use solana_sdk::transaction::Transaction;
use solana_storage_api::storage_instruction::StorageInstruction; use solana_storage_api::storage_contract::{CheckedProof, Proof, ProofStatus};
use solana_storage_api::storage_instruction::{proof_validation, StorageInstruction};
use solana_storage_api::{get_segment_from_slot, storage_instruction}; use solana_storage_api::{get_segment_from_slot, storage_instruction};
use std::collections::HashSet; use std::collections::HashMap;
use std::io; use std::io;
use std::mem::size_of; use std::mem::size_of;
use std::net::UdpSocket; use std::net::UdpSocket;
@ -34,7 +35,7 @@ use std::time::Duration;
// Vec of [ledger blocks] x [keys] // Vec of [ledger blocks] x [keys]
type StorageResults = Vec<Hash>; type StorageResults = Vec<Hash>;
type StorageKeys = Vec<u8>; type StorageKeys = Vec<u8>;
type ReplicatorMap = Vec<HashSet<Pubkey>>; type ReplicatorMap = Vec<HashMap<Pubkey, Vec<Proof>>>;
#[derive(Default)] #[derive(Default)]
pub struct StorageStateInner { pub struct StorageStateInner {
@ -117,7 +118,7 @@ impl StorageState {
let replicator_map = &self.state.read().unwrap().replicator_map; let replicator_map = &self.state.read().unwrap().replicator_map;
if index < replicator_map.len() { if index < replicator_map.len() {
replicator_map[index] replicator_map[index]
.iter() .keys()
.cloned() .cloned()
.take(MAX_PUBKEYS_TO_RETURN) .take(MAX_PUBKEYS_TO_RETURN)
.collect::<Vec<_>>() .collect::<Vec<_>>()
@ -350,8 +351,6 @@ impl StorageStage {
} }
} }
} }
// TODO: bundle up mining submissions from replicators
// and submit them in a tx to the leader to get reward.
Ok(()) Ok(())
} }
@ -360,13 +359,13 @@ impl StorageStage {
slot: u64, slot: u64,
storage_state: &Arc<RwLock<StorageStateInner>>, storage_state: &Arc<RwLock<StorageStateInner>>,
current_key_idx: &mut usize, current_key_idx: &mut usize,
transaction_key0: Pubkey, storage_account_key: Pubkey,
) { ) {
match deserialize(data) { match deserialize(data) {
Ok(StorageInstruction::SubmitMiningProof { Ok(StorageInstruction::SubmitMiningProof {
slot: proof_slot, slot: proof_slot,
signature, signature,
.. sha_state,
}) => { }) => {
if proof_slot < slot { if proof_slot < slot {
{ {
@ -383,14 +382,21 @@ impl StorageStage {
let mut statew = storage_state.write().unwrap(); 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) as usize;
if statew.replicator_map.len() <= max_segment_index { if statew.replicator_map.len() < max_segment_index {
statew statew
.replicator_map .replicator_map
.resize(max_segment_index, HashSet::new()); .resize(max_segment_index, HashMap::new());
} }
let proof_segment_index = get_segment_from_slot(proof_slot) as usize; let proof_segment_index = get_segment_from_slot(proof_slot) as usize;
if proof_segment_index < statew.replicator_map.len() { if proof_segment_index < statew.replicator_map.len() {
statew.replicator_map[proof_segment_index].insert(transaction_key0); // Copy the submitted proof
statew.replicator_map[proof_segment_index]
.entry(storage_account_key)
.or_default()
.push(Proof {
signature,
sha_state,
});
} }
} }
debug!("storage proof: slot: {}", slot); debug!("storage proof: slot: {}", slot);
@ -426,14 +432,18 @@ impl StorageStage {
// Go through the transactions, find proofs, and use them to update // Go through the transactions, find proofs, and use them to update
// the storage_keys with their signatures // the storage_keys with their signatures
for tx in &entry.transactions { for tx in &entry.transactions {
for (i, program_id) in tx.message.program_ids().iter().enumerate() { 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) { 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( Self::process_storage_transaction(
&tx.message().instructions[i].data, &instruction.data,
slot, slot,
storage_state, storage_state,
current_key_idx, current_key_idx,
tx.message.account_keys[0], storage_account_key,
); );
} }
} }
@ -454,6 +464,47 @@ impl StorageStage {
slot, slot,
instruction_sender, instruction_sender,
)?; )?;
// bundle up mining submissions from replicators
// and submit them in a tx to the leader to get rewarded.
let mut w_state = storage_state.write().unwrap();
let instructions: Vec<_> = w_state
.replicator_map
.iter_mut()
.enumerate()
.flat_map(|(segment, proof_map)| {
let checked_proofs = proof_map
.iter_mut()
.map(|(id, proofs)| {
(
*id,
proofs
.drain(..)
.map(|proof| CheckedProof {
proof,
status: ProofStatus::Valid,
})
.collect::<Vec<_>>(),
)
})
.collect::<HashMap<_, _>>();
if !checked_proofs.is_empty() {
let ix = proof_validation(
&storage_keypair.pubkey(),
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| instruction_sender.send(ix))
.collect();
res?
} }
} }
} }

View File

@ -13,7 +13,7 @@ use std::collections::HashMap;
pub const TOTAL_VALIDATOR_REWARDS: u64 = 1; pub const TOTAL_VALIDATOR_REWARDS: u64 = 1;
pub const TOTAL_REPLICATOR_REWARDS: u64 = 1; pub const TOTAL_REPLICATOR_REWARDS: u64 = 1;
// Todo Tune this for actual use cases when replicators are feature complete // Todo Tune this for actual use cases when replicators are feature complete
pub const STORAGE_ACCOUNT_SPACE: u64 = 1024 * 4; pub const STORAGE_ACCOUNT_SPACE: u64 = 1024 * 8;
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub enum ProofStatus { pub enum ProofStatus {
@ -30,7 +30,6 @@ impl Default for ProofStatus {
#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)] #[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct Proof { pub struct Proof {
pub id: Pubkey,
pub signature: Signature, pub signature: Signature,
pub sha_state: Hash, pub sha_state: Hash,
} }
@ -46,17 +45,19 @@ pub enum StorageContract {
Uninitialized, // Must be first (aka, 0) Uninitialized, // Must be first (aka, 0)
ValidatorStorage { ValidatorStorage {
// Most recently advertised slot
slot: u64, slot: u64,
// Most recently advertised blockhash
hash: Hash, hash: Hash,
lockout_validations: HashMap<usize, HashMap<Hash, CheckedProof>>, lockout_validations: HashMap<usize, HashMap<Hash, ProofStatus>>,
reward_validations: HashMap<usize, HashMap<Hash, CheckedProof>>, reward_validations: HashMap<usize, HashMap<Hash, ProofStatus>>,
}, },
ReplicatorStorage { ReplicatorStorage {
/// Map of Proofs per segment, in a HashMap based on the sha_state /// Map of Proofs per segment, in a HashMap based on the sha_state
proofs: HashMap<usize, HashMap<Hash, Proof>>, proofs: HashMap<usize, HashMap<Hash, Proof>>,
/// Map of Rewards per segment, in a HashMap based on the sha_state /// Map of Rewards per segment, in a HashMap based on the sha_state
/// Multiple validators can validate the same set of proofs so it needs a Vec /// Multiple validators can validate the same set of proofs so it needs a Vec
reward_validations: HashMap<usize, HashMap<Hash, Vec<CheckedProof>>>, reward_validations: HashMap<usize, HashMap<Hash, Vec<ProofStatus>>>,
}, },
MiningPool, MiningPool,
@ -127,7 +128,6 @@ impl<'a> StorageAccount<'a> {
pub fn submit_mining_proof( pub fn submit_mining_proof(
&mut self, &mut self,
id: Pubkey,
sha_state: Hash, sha_state: Hash,
slot: u64, slot: u64,
signature: Signature, signature: Signature,
@ -148,14 +148,19 @@ impl<'a> StorageAccount<'a> {
sha_state, slot sha_state, slot
); );
proofs.entry(segment_index).or_default().insert( let segment_proofs = proofs.entry(segment_index).or_default();
if segment_proofs.contains_key(&sha_state) {
// do not accept duplicate proofs
return Err(InstructionError::InvalidArgument);
}
segment_proofs.insert(
sha_state, sha_state,
Proof { Proof {
id,
sha_state, sha_state,
signature, signature,
}, },
); );
self.account.set_state(storage_contract) self.account.set_state(storage_contract)
} else { } else {
Err(InstructionError::InvalidArgument)? Err(InstructionError::InvalidArgument)?
@ -200,8 +205,8 @@ impl<'a> StorageAccount<'a> {
pub fn proof_validation( pub fn proof_validation(
&mut self, &mut self,
slot: u64, segment: u64,
proofs: Vec<CheckedProof>, proofs: Vec<(Pubkey, Vec<CheckedProof>)>,
replicator_accounts: &mut [StorageAccount], replicator_accounts: &mut [StorageAccount],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let mut storage_contract = &mut self.account.state()?; let mut storage_contract = &mut self.account.state()?;
@ -211,14 +216,14 @@ impl<'a> StorageAccount<'a> {
.. ..
} = &mut storage_contract } = &mut storage_contract
{ {
let segment_index = get_segment_from_slot(slot); let segment_index = segment as usize;
let state_segment = get_segment_from_slot(*state_slot); let state_segment = get_segment_from_slot(*state_slot);
if segment_index > state_segment { if segment_index > state_segment {
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
let mut previous_proofs = replicator_accounts let accounts_and_proofs = replicator_accounts
.iter_mut() .iter_mut()
.filter_map(|account| { .filter_map(|account| {
account account
@ -226,32 +231,33 @@ impl<'a> StorageAccount<'a> {
.state() .state()
.ok() .ok()
.map(move |contract| match contract { .map(move |contract| match contract {
StorageContract::ReplicatorStorage { proofs, .. } => Some(( StorageContract::ReplicatorStorage { proofs, .. } => {
account, if let Some(proofs) = proofs.get(&segment_index).cloned() {
proofs.get(&segment_index).cloned().unwrap_or_default(), Some((account, proofs))
)), } else {
None
}
}
_ => None, _ => None,
}) })
}) })
.flatten() .flatten()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if previous_proofs.len() != proofs.len() { if accounts_and_proofs.len() != proofs.len() {
// don't have all the accounts to validate the proofs against // don't have all the accounts to validate the proofs against
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
let valid_proofs: Vec<_> = proofs let valid_proofs: Vec<_> = proofs
.into_iter() .into_iter()
.enumerate() .zip(accounts_and_proofs.into_iter())
.filter_map(|(i, entry)| { .flat_map(|((_id, checked_proofs), (account, proofs))| {
let (account, proofs) = &mut previous_proofs[i]; checked_proofs.into_iter().filter_map(move |checked_proof| {
proofs.get(&entry.proof.sha_state).map(|proof| { proofs.get(&checked_proof.proof.sha_state).map(|proof| {
if process_validation(account, segment_index, &proof, &entry).is_ok() { process_validation(account, segment_index, &proof, &checked_proof)
Some(entry) .map(|_| checked_proof)
} else { })
None
}
}) })
}) })
.flatten() .flatten()
@ -262,8 +268,9 @@ impl<'a> StorageAccount<'a> {
lockout_validations lockout_validations
.entry(segment_index) .entry(segment_index)
.or_default() .or_default()
.insert(proof.proof.sha_state, proof); .insert(proof.proof.sha_state, proof.status);
}); });
self.account.set_state(storage_contract) self.account.set_state(storage_contract)
} else { } else {
Err(InstructionError::InvalidArgument)? Err(InstructionError::InvalidArgument)?
@ -333,11 +340,11 @@ impl<'a> StorageAccount<'a> {
.map(|mut proofs| { .map(|mut proofs| {
proofs proofs
.drain() .drain()
.map(|(_, proof)| { .map(|(sha_state, proof)| {
proof proof
.into_iter() .into_iter()
.map(|proof| { .map(|proof| {
segment_proofs.remove(&proof.proof.sha_state); segment_proofs.remove(&sha_state);
proof proof
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
@ -346,16 +353,12 @@ impl<'a> StorageAccount<'a> {
.collect::<Vec<_>>() .collect::<Vec<_>>()
}) })
.unwrap_or_default(); .unwrap_or_default();
let _num_validations = count_valid_proofs(&checked_proofs); let total_proofs = checked_proofs.len() as u64;
let num_validations = count_valid_proofs(&checked_proofs);
// TODO enable when rewards are working let reward =
/* num_validations * TOTAL_REPLICATOR_REWARDS * (num_validations / total_proofs);
let reward = num_validations
* TOTAL_REPLICATOR_REWARDS
* (num_validations / reward_validations[&claim_segment].len() as u64);
mining_pool.account.lamports -= reward; mining_pool.account.lamports -= reward;
self.account.lamports += reward; self.account.lamports += reward;
*/
self.account.set_state(storage_contract) self.account.set_state(storage_contract)
} else { } else {
Err(InstructionError::InvalidArgument)? Err(InstructionError::InvalidArgument)?
@ -390,7 +393,7 @@ fn store_validation_result(
.or_default() .or_default()
.entry(checked_proof.proof.sha_state) .entry(checked_proof.proof.sha_state)
.or_default() .or_default()
.push(checked_proof); .push(checked_proof.status);
} else { } else {
return Err(InstructionError::InvalidAccountData); return Err(InstructionError::InvalidAccountData);
} }
@ -400,10 +403,10 @@ fn store_validation_result(
storage_account.account.set_state(&storage_contract) storage_account.account.set_state(&storage_contract)
} }
fn count_valid_proofs(proofs: &[CheckedProof]) -> u64 { fn count_valid_proofs(proofs: &[ProofStatus]) -> u64 {
let mut num = 0; let mut num = 0;
for proof in proofs { for proof in proofs {
if let ProofStatus::Valid = proof.status { if let ProofStatus::Valid = proof {
num += 1; num += 1;
} }
} }
@ -477,7 +480,6 @@ mod tests {
}; };
let segment_index = 0_usize; let segment_index = 0_usize;
let proof = Proof { let proof = Proof {
id: Pubkey::default(),
signature: Signature::default(), signature: Signature::default(),
sha_state: Hash::default(), sha_state: Hash::default(),
}; };

View File

@ -6,6 +6,7 @@ use solana_sdk::instruction::{AccountMeta, Instruction};
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Signature; use solana_sdk::signature::Signature;
use solana_sdk::system_instruction; use solana_sdk::system_instruction;
use std::collections::HashMap;
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub enum StorageInstruction { pub enum StorageInstruction {
@ -35,8 +36,8 @@ pub enum StorageInstruction {
slot: u64, slot: u64,
}, },
ProofValidation { ProofValidation {
slot: u64, segment: u64,
proofs: Vec<CheckedProof>, proofs: Vec<(Pubkey, Vec<CheckedProof>)>,
}, },
} }
@ -131,16 +132,18 @@ pub fn advertise_recent_blockhash(
Instruction::new(id(), &storage_instruction, account_metas) Instruction::new(id(), &storage_instruction, account_metas)
} }
pub fn proof_validation( pub fn proof_validation<S: std::hash::BuildHasher>(
storage_pubkey: &Pubkey, storage_pubkey: &Pubkey,
slot: u64, segment: u64,
proofs: Vec<CheckedProof>, checked_proofs: HashMap<Pubkey, Vec<CheckedProof>, S>,
) -> Instruction { ) -> Instruction {
let mut account_metas = vec![AccountMeta::new(*storage_pubkey, true)]; let mut account_metas = vec![AccountMeta::new(*storage_pubkey, true)];
proofs.iter().for_each(|checked_proof| { let mut proofs = vec![];
account_metas.push(AccountMeta::new(checked_proof.proof.id, false)) checked_proofs.into_iter().for_each(|(id, p)| {
proofs.push((id, p));
account_metas.push(AccountMeta::new(id, false))
}); });
let storage_instruction = StorageInstruction::ProofValidation { slot, proofs }; let storage_instruction = StorageInstruction::ProofValidation { segment, proofs };
Instruction::new(id(), &storage_instruction, account_metas) Instruction::new(id(), &storage_instruction, account_metas)
} }

View File

@ -18,7 +18,6 @@ pub fn process_instruction(
let (me, rest) = keyed_accounts.split_at_mut(1); let (me, rest) = keyed_accounts.split_at_mut(1);
let me_unsigned = me[0].signer_key().is_none(); let me_unsigned = me[0].signer_key().is_none();
let storage_account_pubkey = *me[0].unsigned_key();
let mut storage_account = StorageAccount::new(&mut me[0].account); let mut storage_account = StorageAccount::new(&mut me[0].account);
match bincode::deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? { match bincode::deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
@ -50,7 +49,6 @@ pub fn process_instruction(
Err(InstructionError::InvalidArgument)?; Err(InstructionError::InvalidArgument)?;
} }
storage_account.submit_mining_proof( storage_account.submit_mining_proof(
storage_account_pubkey,
sha_state, sha_state,
slot, slot,
signature, signature,
@ -78,16 +76,16 @@ pub fn process_instruction(
tick_height / DEFAULT_TICKS_PER_SLOT, tick_height / DEFAULT_TICKS_PER_SLOT,
) )
} }
StorageInstruction::ProofValidation { slot, proofs } => { StorageInstruction::ProofValidation { segment, proofs } => {
if me_unsigned || rest.is_empty() { if me_unsigned || rest.is_empty() {
// This instruction must be signed by `me` // This instruction must be signed by `me` and `rest` cannot be empty
Err(InstructionError::InvalidArgument)?; Err(InstructionError::InvalidArgument)?;
} }
let mut rest: Vec<_> = rest let mut rest: Vec<_> = rest
.iter_mut() .iter_mut()
.map(|keyed_account| StorageAccount::new(&mut keyed_account.account)) .map(|keyed_account| StorageAccount::new(&mut keyed_account.account))
.collect(); .collect();
storage_account.proof_validation(slot, proofs, &mut rest) storage_account.proof_validation(segment, proofs, &mut rest)
} }
} }
} }
@ -95,13 +93,13 @@ pub fn process_instruction(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::id;
use crate::storage_contract::{ use crate::storage_contract::{
CheckedProof, Proof, ProofStatus, StorageContract, STORAGE_ACCOUNT_SPACE, CheckedProof, Proof, ProofStatus, StorageContract, STORAGE_ACCOUNT_SPACE,
TOTAL_VALIDATOR_REWARDS, TOTAL_REPLICATOR_REWARDS, TOTAL_VALIDATOR_REWARDS,
}; };
use crate::storage_instruction; use crate::storage_instruction;
use crate::SLOTS_PER_SEGMENT; use crate::SLOTS_PER_SEGMENT;
use crate::{get_segment_from_slot, id};
use assert_matches::assert_matches; use assert_matches::assert_matches;
use bincode::deserialize; use bincode::deserialize;
use log::*; use log::*;
@ -115,6 +113,7 @@ mod tests {
use solana_sdk::message::Message; use solana_sdk::message::Message;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use solana_sdk::signature::{Keypair, KeypairUtil, Signature};
use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
const TICKS_IN_SEGMENT: u64 = SLOTS_PER_SEGMENT * DEFAULT_TICKS_PER_SLOT; const TICKS_IN_SEGMENT: u64 = SLOTS_PER_SEGMENT * DEFAULT_TICKS_PER_SLOT;
@ -251,10 +250,16 @@ mod tests {
solana_logger::setup(); solana_logger::setup();
let (genesis_block, mint_keypair) = create_genesis_block(1000); let (genesis_block, mint_keypair) = create_genesis_block(1000);
let mint_pubkey = mint_keypair.pubkey(); let mint_pubkey = mint_keypair.pubkey();
let replicator_keypair = Keypair::new();
let replicator_pubkey = replicator_keypair.pubkey(); let replicator_1_storage_keypair = Keypair::new();
let validator_keypair = Keypair::new(); let replicator_1_storage_id = replicator_1_storage_keypair.pubkey();
let validator_pubkey = validator_keypair.pubkey();
let replicator_2_storage_keypair = Keypair::new();
let replicator_2_storage_id = replicator_2_storage_keypair.pubkey();
let validator_storage_keypair = Keypair::new();
let validator_storage_id = validator_storage_keypair.pubkey();
let mining_pool_keypair = Keypair::new(); let mining_pool_keypair = Keypair::new();
let mining_pool_pubkey = mining_pool_keypair.pubkey(); let mining_pool_pubkey = mining_pool_keypair.pubkey();
@ -264,20 +269,13 @@ mod tests {
let slot = 0; let slot = 0;
let bank_client = BankClient::new_shared(&bank); let bank_client = BankClient::new_shared(&bank);
let message = Message::new(storage_instruction::create_validator_storage_account( init_storage_accounts(
&mint_pubkey, &bank_client,
&validator_pubkey, &mint_keypair,
&[&validator_storage_id],
&[&replicator_1_storage_id, &replicator_2_storage_id],
10, 10,
)); );
bank_client.send_message(&[&mint_keypair], message).unwrap();
let message = Message::new(storage_instruction::create_replicator_storage_account(
&mint_pubkey,
&replicator_pubkey,
10,
));
bank_client.send_message(&[&mint_keypair], message).unwrap();
let message = Message::new(storage_instruction::create_mining_pool_account( let message = Message::new(storage_instruction::create_mining_pool_account(
&mint_pubkey, &mint_pubkey,
&mining_pool_pubkey, &mining_pool_pubkey,
@ -294,35 +292,42 @@ mod tests {
// advertise for storage segment 1 // advertise for storage segment 1
let message = Message::new_with_payer( let message = Message::new_with_payer(
vec![storage_instruction::advertise_recent_blockhash( vec![storage_instruction::advertise_recent_blockhash(
&validator_pubkey, &validator_storage_id,
Hash::default(), Hash::default(),
SLOTS_PER_SEGMENT, SLOTS_PER_SEGMENT,
)], )],
Some(&mint_pubkey), Some(&mint_pubkey),
); );
assert_matches!( assert_matches!(
bank_client.send_message(&[&mint_keypair, &validator_keypair], message), bank_client.send_message(&[&mint_keypair, &validator_storage_keypair], message),
Ok(_)
);
let message = Message::new_with_payer(
vec![storage_instruction::mining_proof(
&replicator_pubkey,
Hash::default(),
slot,
Signature::default(),
)],
Some(&mint_pubkey),
);
assert_matches!(
bank_client.send_message(&[&mint_keypair, &replicator_keypair], message),
Ok(_) Ok(_)
); );
// submit proofs 5 proofs for each replicator for segment 0
let mut checked_proofs: HashMap<_, Vec<_>> = HashMap::new();
for slot in 0..5 {
checked_proofs
.entry(replicator_1_storage_id)
.or_default()
.push(submit_proof(
&mint_keypair,
&replicator_1_storage_keypair,
slot,
&bank_client,
));
checked_proofs
.entry(replicator_2_storage_id)
.or_default()
.push(submit_proof(
&mint_keypair,
&replicator_2_storage_keypair,
slot,
&bank_client,
));
}
let message = Message::new_with_payer( let message = Message::new_with_payer(
vec![storage_instruction::advertise_recent_blockhash( vec![storage_instruction::advertise_recent_blockhash(
&validator_pubkey, &validator_storage_id,
Hash::default(), Hash::default(),
SLOTS_PER_SEGMENT * 2, SLOTS_PER_SEGMENT * 2,
)], )],
@ -335,34 +340,27 @@ mod tests {
} }
assert_matches!( assert_matches!(
bank_client.send_message(&[&mint_keypair, &validator_keypair], message), bank_client.send_message(&[&mint_keypair, &validator_storage_keypair], message),
Ok(_) Ok(_)
); );
let message = Message::new_with_payer( let message = Message::new_with_payer(
vec![storage_instruction::proof_validation( vec![storage_instruction::proof_validation(
&validator_pubkey, &validator_storage_id,
slot, get_segment_from_slot(slot) as u64,
vec![CheckedProof { checked_proofs,
proof: Proof {
id: replicator_pubkey,
signature: Signature::default(),
sha_state: Hash::default(),
},
status: ProofStatus::Valid,
}],
)], )],
Some(&mint_pubkey), Some(&mint_pubkey),
); );
assert_matches!( assert_matches!(
bank_client.send_message(&[&mint_keypair, &validator_keypair], message), bank_client.send_message(&[&mint_keypair, &validator_storage_keypair], message),
Ok(_) Ok(_)
); );
let message = Message::new_with_payer( let message = Message::new_with_payer(
vec![storage_instruction::advertise_recent_blockhash( vec![storage_instruction::advertise_recent_blockhash(
&validator_pubkey, &validator_storage_id,
Hash::default(), Hash::default(),
SLOTS_PER_SEGMENT * 3, SLOTS_PER_SEGMENT * 3,
)], )],
@ -375,25 +373,24 @@ mod tests {
} }
assert_matches!( assert_matches!(
bank_client.send_message(&[&mint_keypair, &validator_keypair], message), bank_client.send_message(&[&mint_keypair, &validator_storage_keypair], message),
Ok(_) Ok(_)
); );
assert_eq!(bank_client.get_balance(&validator_pubkey).unwrap(), 10,); assert_eq!(bank_client.get_balance(&validator_storage_id).unwrap(), 10);
let message = Message::new_with_payer( let message = Message::new_with_payer(
vec![storage_instruction::claim_reward( vec![storage_instruction::claim_reward(
&validator_pubkey, &validator_storage_id,
&mining_pool_pubkey, &mining_pool_pubkey,
slot, slot,
)], )],
Some(&mint_pubkey), Some(&mint_pubkey),
); );
assert_matches!(bank_client.send_message(&[&mint_keypair], message), Ok(_)); assert_matches!(bank_client.send_message(&[&mint_keypair], message), Ok(_));
assert_eq!( assert_eq!(
bank_client.get_balance(&validator_pubkey).unwrap(), bank_client.get_balance(&validator_storage_id).unwrap(),
10 + TOTAL_VALIDATOR_REWARDS 10 + (TOTAL_VALIDATOR_REWARDS * 10)
); );
// tick the bank into the next storage epoch so that rewards can be claimed // tick the bank into the next storage epoch so that rewards can be claimed
@ -401,11 +398,24 @@ mod tests {
bank.register_tick(&bank.last_blockhash()); bank.register_tick(&bank.last_blockhash());
} }
assert_eq!(bank_client.get_balance(&replicator_pubkey).unwrap(), 10); assert_eq!(
bank_client.get_balance(&replicator_1_storage_id).unwrap(),
10
);
let message = Message::new_with_payer( let message = Message::new_with_payer(
vec![storage_instruction::claim_reward( vec![storage_instruction::claim_reward(
&replicator_pubkey, &replicator_1_storage_id,
&mining_pool_pubkey,
slot,
)],
Some(&mint_pubkey),
);
assert_matches!(bank_client.send_message(&[&mint_keypair], message), Ok(_));
let message = Message::new_with_payer(
vec![storage_instruction::claim_reward(
&replicator_2_storage_id,
&mining_pool_pubkey, &mining_pool_pubkey,
slot, slot,
)], )],
@ -414,7 +424,40 @@ mod tests {
assert_matches!(bank_client.send_message(&[&mint_keypair], message), Ok(_)); assert_matches!(bank_client.send_message(&[&mint_keypair], message), Ok(_));
// TODO enable when rewards are working // TODO enable when rewards are working
// assert_eq!(bank_client.get_balance(&replicator_pubkey).unwrap(), 10 + TOTAL_REPLICATOR_REWARDS); assert_eq!(
bank_client.get_balance(&replicator_1_storage_id).unwrap(),
10 + (TOTAL_REPLICATOR_REWARDS * 5)
);
}
fn init_storage_accounts(
client: &BankClient,
mint: &Keypair,
validator_accounts_to_create: &[&Pubkey],
replicator_accounts_to_create: &[&Pubkey],
lamports: u64,
) {
let mut ixs: Vec<_> = validator_accounts_to_create
.into_iter()
.flat_map(|account| {
storage_instruction::create_validator_storage_account(
&mint.pubkey(),
account,
lamports,
)
})
.collect();
replicator_accounts_to_create
.into_iter()
.for_each(|account| {
ixs.append(&mut storage_instruction::create_replicator_storage_account(
&mint.pubkey(),
account,
lamports,
))
});
let message = Message::new(ixs);
client.send_message(&[mint], message).unwrap();
} }
fn get_storage_slot<C: SyncClient>(client: &C, account: &Pubkey) -> u64 { fn get_storage_slot<C: SyncClient>(client: &C, account: &Pubkey) -> u64 {
@ -437,6 +480,36 @@ mod tests {
0 0
} }
fn submit_proof(
mint_keypair: &Keypair,
storage_keypair: &Keypair,
slot: u64,
bank_client: &BankClient,
) -> 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,
Signature::default(),
)],
Some(&mint_keypair.pubkey()),
);
assert_matches!(
bank_client.send_message(&[&mint_keypair, &storage_keypair], message),
Ok(_)
);
CheckedProof {
proof: Proof {
signature: Signature::default(),
sha_state,
},
status: ProofStatus::Valid,
}
}
fn get_storage_blockhash<C: SyncClient>(client: &C, account: &Pubkey) -> Hash { fn get_storage_blockhash<C: SyncClient>(client: &C, account: &Pubkey) -> Hash {
if let Some(storage_system_account_data) = client.get_account_data(&account).unwrap() { if let Some(storage_system_account_data) = client.get_account_data(&account).unwrap() {
let contract = deserialize(&storage_system_account_data); let contract = deserialize(&storage_system_account_data);