2019-05-03 16:27:53 -07:00
|
|
|
use crate::get_segment_from_slot;
|
2019-04-04 12:01:09 -07:00
|
|
|
use log::*;
|
2019-03-22 14:30:18 -06:00
|
|
|
use serde_derive::{Deserialize, Serialize};
|
2019-04-04 12:01:09 -07:00
|
|
|
use solana_sdk::account::Account;
|
2019-05-23 14:50:23 -07:00
|
|
|
use solana_sdk::account::KeyedAccount;
|
2019-05-21 15:19:41 -07:00
|
|
|
use solana_sdk::account_utils::State;
|
2019-03-22 14:30:18 -06:00
|
|
|
use solana_sdk::hash::Hash;
|
2019-04-04 12:01:09 -07:00
|
|
|
use solana_sdk::instruction::InstructionError;
|
2019-03-22 14:30:18 -06:00
|
|
|
use solana_sdk::pubkey::Pubkey;
|
|
|
|
use solana_sdk::signature::Signature;
|
2019-05-17 14:52:54 -07:00
|
|
|
use std::collections::HashMap;
|
2019-04-04 12:01:09 -07:00
|
|
|
|
|
|
|
pub const TOTAL_VALIDATOR_REWARDS: u64 = 1;
|
|
|
|
pub const TOTAL_REPLICATOR_REWARDS: u64 = 1;
|
2019-05-21 11:07:13 -07:00
|
|
|
// Todo Tune this for actual use cases when replicators are feature complete
|
2019-05-24 14:49:10 -07:00
|
|
|
pub const STORAGE_ACCOUNT_SPACE: u64 = 1024 * 8;
|
2019-03-22 14:30:18 -06:00
|
|
|
|
2019-04-02 13:08:55 -07:00
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
2019-03-22 14:30:18 -06:00
|
|
|
pub enum ProofStatus {
|
2019-04-02 13:08:55 -07:00
|
|
|
Skipped,
|
2019-03-22 14:30:18 -06:00
|
|
|
Valid,
|
|
|
|
NotValid,
|
2019-04-02 13:08:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for ProofStatus {
|
|
|
|
fn default() -> Self {
|
|
|
|
ProofStatus::Skipped
|
|
|
|
}
|
2019-03-22 14:30:18 -06:00
|
|
|
}
|
|
|
|
|
2019-05-17 14:52:54 -07:00
|
|
|
#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
|
2019-04-03 15:51:05 -07:00
|
|
|
pub struct Proof {
|
2019-03-22 14:30:18 -06:00
|
|
|
pub signature: Signature,
|
|
|
|
pub sha_state: Hash,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default, Debug, Serialize, Deserialize, Clone)]
|
2019-04-03 15:51:05 -07:00
|
|
|
pub struct CheckedProof {
|
|
|
|
pub proof: Proof,
|
|
|
|
pub status: ProofStatus,
|
2019-03-22 14:30:18 -06:00
|
|
|
}
|
|
|
|
|
2019-04-02 13:08:55 -07:00
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
pub enum StorageContract {
|
2019-05-23 14:50:23 -07:00
|
|
|
Uninitialized, // Must be first (aka, 0)
|
2019-03-22 14:30:18 -06:00
|
|
|
|
2019-04-02 13:08:55 -07:00
|
|
|
ValidatorStorage {
|
2019-05-24 14:49:10 -07:00
|
|
|
// Most recently advertised slot
|
2019-05-03 16:27:53 -07:00
|
|
|
slot: u64,
|
2019-05-24 14:49:10 -07:00
|
|
|
// Most recently advertised blockhash
|
2019-04-02 13:08:55 -07:00
|
|
|
hash: Hash,
|
2019-05-24 14:49:10 -07:00
|
|
|
lockout_validations: HashMap<usize, HashMap<Hash, ProofStatus>>,
|
|
|
|
reward_validations: HashMap<usize, HashMap<Hash, ProofStatus>>,
|
2019-04-02 13:08:55 -07:00
|
|
|
},
|
|
|
|
ReplicatorStorage {
|
2019-05-17 14:52:54 -07:00
|
|
|
/// Map of Proofs per segment, in a HashMap based on the sha_state
|
|
|
|
proofs: HashMap<usize, HashMap<Hash, Proof>>,
|
|
|
|
/// 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
|
2019-05-24 14:49:10 -07:00
|
|
|
reward_validations: HashMap<usize, HashMap<Hash, Vec<ProofStatus>>>,
|
2019-04-02 13:08:55 -07:00
|
|
|
},
|
2019-05-23 14:50:23 -07:00
|
|
|
|
|
|
|
MiningPool,
|
|
|
|
}
|
|
|
|
|
|
|
|
// utility function, used by Bank, tests, genesis
|
|
|
|
pub fn create_validator_storage_account(lamports: u64) -> Account {
|
|
|
|
let mut storage_account = Account::new(lamports, STORAGE_ACCOUNT_SPACE as usize, &crate::id());
|
|
|
|
|
|
|
|
storage_account
|
|
|
|
.set_state(&StorageContract::ValidatorStorage {
|
|
|
|
slot: 0,
|
|
|
|
hash: Hash::default(),
|
|
|
|
lockout_validations: HashMap::new(),
|
|
|
|
reward_validations: HashMap::new(),
|
|
|
|
})
|
|
|
|
.expect("set_state");
|
|
|
|
|
|
|
|
storage_account
|
2019-03-22 14:30:18 -06:00
|
|
|
}
|
2019-04-04 12:01:09 -07:00
|
|
|
|
|
|
|
pub struct StorageAccount<'a> {
|
|
|
|
account: &'a mut Account,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> StorageAccount<'a> {
|
|
|
|
pub fn new(account: &'a mut Account) -> Self {
|
|
|
|
Self { account }
|
|
|
|
}
|
|
|
|
|
2019-05-23 14:50:23 -07:00
|
|
|
pub fn initialize_mining_pool(&mut self) -> Result<(), InstructionError> {
|
|
|
|
let storage_contract = &mut self.account.state()?;
|
|
|
|
if let StorageContract::Uninitialized = storage_contract {
|
|
|
|
*storage_contract = StorageContract::MiningPool;
|
|
|
|
self.account.set_state(storage_contract)
|
|
|
|
} else {
|
|
|
|
Err(InstructionError::AccountAlreadyInitialized)?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn initialize_replicator_storage(&mut self) -> Result<(), InstructionError> {
|
|
|
|
let storage_contract = &mut self.account.state()?;
|
|
|
|
if let StorageContract::Uninitialized = storage_contract {
|
|
|
|
*storage_contract = StorageContract::ReplicatorStorage {
|
|
|
|
proofs: HashMap::new(),
|
|
|
|
reward_validations: HashMap::new(),
|
|
|
|
};
|
|
|
|
self.account.set_state(storage_contract)
|
|
|
|
} else {
|
|
|
|
Err(InstructionError::AccountAlreadyInitialized)?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn initialize_validator_storage(&mut self) -> Result<(), InstructionError> {
|
|
|
|
let storage_contract = &mut self.account.state()?;
|
|
|
|
if let StorageContract::Uninitialized = storage_contract {
|
|
|
|
*storage_contract = StorageContract::ValidatorStorage {
|
|
|
|
slot: 0,
|
|
|
|
hash: Hash::default(),
|
|
|
|
lockout_validations: HashMap::new(),
|
|
|
|
reward_validations: HashMap::new(),
|
|
|
|
};
|
|
|
|
self.account.set_state(storage_contract)
|
|
|
|
} else {
|
|
|
|
Err(InstructionError::AccountAlreadyInitialized)?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-04 12:01:09 -07:00
|
|
|
pub fn submit_mining_proof(
|
|
|
|
&mut self,
|
|
|
|
sha_state: Hash,
|
2019-05-03 16:27:53 -07:00
|
|
|
slot: u64,
|
2019-04-04 12:01:09 -07:00
|
|
|
signature: Signature,
|
2019-05-17 14:52:54 -07:00
|
|
|
current_slot: u64,
|
2019-04-04 12:01:09 -07:00
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
let mut storage_contract = &mut self.account.state()?;
|
|
|
|
if let StorageContract::ReplicatorStorage { proofs, .. } = &mut storage_contract {
|
2019-05-03 16:27:53 -07:00
|
|
|
let segment_index = get_segment_from_slot(slot);
|
2019-05-17 14:52:54 -07:00
|
|
|
let current_segment = get_segment_from_slot(current_slot);
|
2019-04-04 12:01:09 -07:00
|
|
|
|
2019-05-17 14:52:54 -07:00
|
|
|
if segment_index >= current_segment {
|
|
|
|
// attempt to submit proof for unconfirmed segment
|
2019-04-04 12:01:09 -07:00
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
|
|
|
|
debug!(
|
2019-05-03 16:27:53 -07:00
|
|
|
"Mining proof submitted with contract {:?} slot: {}",
|
|
|
|
sha_state, slot
|
2019-04-04 12:01:09 -07:00
|
|
|
);
|
|
|
|
|
2019-05-24 14:49:10 -07:00
|
|
|
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(
|
2019-04-04 12:01:09 -07:00
|
|
|
sha_state,
|
2019-05-17 14:52:54 -07:00
|
|
|
Proof {
|
|
|
|
sha_state,
|
|
|
|
signature,
|
|
|
|
},
|
|
|
|
);
|
2019-05-24 14:49:10 -07:00
|
|
|
|
2019-04-04 12:01:09 -07:00
|
|
|
self.account.set_state(storage_contract)
|
|
|
|
} else {
|
|
|
|
Err(InstructionError::InvalidArgument)?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn advertise_storage_recent_blockhash(
|
|
|
|
&mut self,
|
|
|
|
hash: Hash,
|
2019-05-03 16:27:53 -07:00
|
|
|
slot: u64,
|
2019-05-17 14:52:54 -07:00
|
|
|
current_slot: u64,
|
2019-04-04 12:01:09 -07:00
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
let mut storage_contract = &mut self.account.state()?;
|
|
|
|
if let StorageContract::ValidatorStorage {
|
2019-05-03 16:27:53 -07:00
|
|
|
slot: state_slot,
|
2019-04-04 12:01:09 -07:00
|
|
|
hash: state_hash,
|
|
|
|
reward_validations,
|
|
|
|
lockout_validations,
|
|
|
|
} = &mut storage_contract
|
|
|
|
{
|
2019-05-17 14:52:54 -07:00
|
|
|
let current_segment = get_segment_from_slot(current_slot);
|
|
|
|
let original_segment = get_segment_from_slot(*state_slot);
|
|
|
|
let segment = get_segment_from_slot(slot);
|
2019-04-04 12:01:09 -07:00
|
|
|
debug!(
|
2019-05-17 14:52:54 -07:00
|
|
|
"advertise new segment: {} orig: {}",
|
|
|
|
segment, current_segment
|
2019-04-04 12:01:09 -07:00
|
|
|
);
|
2019-05-17 14:52:54 -07:00
|
|
|
if segment < original_segment || segment >= current_segment {
|
2019-04-04 12:01:09 -07:00
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
|
2019-05-03 16:27:53 -07:00
|
|
|
*state_slot = slot;
|
2019-04-04 12:01:09 -07:00
|
|
|
*state_hash = hash;
|
|
|
|
|
2019-05-17 14:52:54 -07:00
|
|
|
// move storage epoch updated, move the lockout_validations to reward_validations
|
|
|
|
reward_validations.extend(lockout_validations.drain());
|
2019-04-04 12:01:09 -07:00
|
|
|
self.account.set_state(storage_contract)
|
|
|
|
} else {
|
|
|
|
Err(InstructionError::InvalidArgument)?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn proof_validation(
|
|
|
|
&mut self,
|
2019-05-24 14:49:10 -07:00
|
|
|
segment: u64,
|
|
|
|
proofs: Vec<(Pubkey, Vec<CheckedProof>)>,
|
2019-04-04 12:01:09 -07:00
|
|
|
replicator_accounts: &mut [StorageAccount],
|
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
let mut storage_contract = &mut self.account.state()?;
|
|
|
|
if let StorageContract::ValidatorStorage {
|
2019-05-17 14:52:54 -07:00
|
|
|
slot: state_slot,
|
2019-04-04 12:01:09 -07:00
|
|
|
lockout_validations,
|
|
|
|
..
|
|
|
|
} = &mut storage_contract
|
|
|
|
{
|
2019-05-24 14:49:10 -07:00
|
|
|
let segment_index = segment as usize;
|
2019-05-17 14:52:54 -07:00
|
|
|
let state_segment = get_segment_from_slot(*state_slot);
|
|
|
|
|
|
|
|
if segment_index > state_segment {
|
2019-04-04 12:01:09 -07:00
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
|
2019-05-24 14:49:10 -07:00
|
|
|
let accounts_and_proofs = replicator_accounts
|
2019-04-04 12:01:09 -07:00
|
|
|
.iter_mut()
|
|
|
|
.filter_map(|account| {
|
|
|
|
account
|
|
|
|
.account
|
|
|
|
.state()
|
|
|
|
.ok()
|
|
|
|
.map(move |contract| match contract {
|
2019-05-24 14:49:10 -07:00
|
|
|
StorageContract::ReplicatorStorage { proofs, .. } => {
|
|
|
|
if let Some(proofs) = proofs.get(&segment_index).cloned() {
|
|
|
|
Some((account, proofs))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2019-04-04 12:01:09 -07:00
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.flatten()
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
2019-05-24 14:49:10 -07:00
|
|
|
if accounts_and_proofs.len() != proofs.len() {
|
2019-04-04 12:01:09 -07:00
|
|
|
// don't have all the accounts to validate the proofs against
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
|
|
|
|
2019-05-17 14:52:54 -07:00
|
|
|
let valid_proofs: Vec<_> = proofs
|
2019-04-04 12:01:09 -07:00
|
|
|
.into_iter()
|
2019-05-24 14:49:10 -07:00
|
|
|
.zip(accounts_and_proofs.into_iter())
|
|
|
|
.flat_map(|((_id, checked_proofs), (account, proofs))| {
|
|
|
|
checked_proofs.into_iter().filter_map(move |checked_proof| {
|
|
|
|
proofs.get(&checked_proof.proof.sha_state).map(|proof| {
|
|
|
|
process_validation(account, segment_index, &proof, &checked_proof)
|
|
|
|
.map(|_| checked_proof)
|
|
|
|
})
|
2019-05-17 14:52:54 -07:00
|
|
|
})
|
2019-04-04 12:01:09 -07:00
|
|
|
})
|
2019-05-17 14:52:54 -07:00
|
|
|
.flatten()
|
2019-04-04 12:01:09 -07:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
// allow validators to store successful validations
|
2019-05-17 14:52:54 -07:00
|
|
|
valid_proofs.into_iter().for_each(|proof| {
|
|
|
|
lockout_validations
|
|
|
|
.entry(segment_index)
|
|
|
|
.or_default()
|
2019-05-24 14:49:10 -07:00
|
|
|
.insert(proof.proof.sha_state, proof.status);
|
2019-05-17 14:52:54 -07:00
|
|
|
});
|
2019-05-24 14:49:10 -07:00
|
|
|
|
2019-04-04 12:01:09 -07:00
|
|
|
self.account.set_state(storage_contract)
|
|
|
|
} else {
|
|
|
|
Err(InstructionError::InvalidArgument)?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn claim_storage_reward(
|
|
|
|
&mut self,
|
2019-05-23 14:50:23 -07:00
|
|
|
mining_pool: &mut KeyedAccount,
|
2019-05-03 16:27:53 -07:00
|
|
|
slot: u64,
|
2019-05-17 14:52:54 -07:00
|
|
|
current_slot: u64,
|
2019-04-04 12:01:09 -07:00
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
let mut storage_contract = &mut self.account.state()?;
|
|
|
|
|
|
|
|
if let StorageContract::ValidatorStorage {
|
2019-05-17 14:52:54 -07:00
|
|
|
reward_validations,
|
|
|
|
slot: state_slot,
|
|
|
|
..
|
2019-04-04 12:01:09 -07:00
|
|
|
} = &mut storage_contract
|
|
|
|
{
|
2019-05-17 14:52:54 -07:00
|
|
|
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);
|
|
|
|
}
|
2019-05-23 14:50:23 -07:00
|
|
|
let num_validations = count_valid_proofs(
|
2019-05-17 14:52:54 -07:00
|
|
|
&reward_validations
|
|
|
|
.remove(&claim_segment)
|
|
|
|
.map(|mut proofs| proofs.drain().map(|(_, proof)| proof).collect::<Vec<_>>())
|
|
|
|
.unwrap_or_default(),
|
|
|
|
);
|
2019-05-23 14:50:23 -07:00
|
|
|
let reward = TOTAL_VALIDATOR_REWARDS * num_validations;
|
|
|
|
mining_pool.account.lamports -= reward;
|
|
|
|
self.account.lamports += reward;
|
2019-04-04 12:01:09 -07:00
|
|
|
self.account.set_state(storage_contract)
|
|
|
|
} else if let StorageContract::ReplicatorStorage {
|
2019-05-17 14:52:54 -07:00
|
|
|
proofs,
|
|
|
|
reward_validations,
|
2019-04-04 12:01:09 -07:00
|
|
|
} = &mut storage_contract
|
|
|
|
{
|
2019-05-17 14:52:54 -07:00
|
|
|
// 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)
|
|
|
|
{
|
2019-05-23 14:50:23 -07:00
|
|
|
info!(
|
2019-05-17 14:52:54 -07:00
|
|
|
"current {:?}, claim {:?}, have rewards for {:?} segments",
|
|
|
|
claim_index,
|
|
|
|
claim_segment,
|
2019-04-04 12:01:09 -07:00
|
|
|
reward_validations.len()
|
|
|
|
);
|
|
|
|
return Err(InstructionError::InvalidArgument);
|
|
|
|
}
|
2019-05-17 14:52:54 -07:00
|
|
|
// remove proofs for which rewards have already been collected
|
|
|
|
let segment_proofs = proofs.get_mut(&claim_segment).unwrap();
|
|
|
|
let checked_proofs = reward_validations
|
|
|
|
.remove(&claim_segment)
|
|
|
|
.map(|mut proofs| {
|
|
|
|
proofs
|
|
|
|
.drain()
|
2019-05-24 14:49:10 -07:00
|
|
|
.map(|(sha_state, proof)| {
|
2019-05-17 14:52:54 -07:00
|
|
|
proof
|
|
|
|
.into_iter()
|
|
|
|
.map(|proof| {
|
2019-05-24 14:49:10 -07:00
|
|
|
segment_proofs.remove(&sha_state);
|
2019-05-17 14:52:54 -07:00
|
|
|
proof
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
})
|
|
|
|
.flatten()
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
})
|
|
|
|
.unwrap_or_default();
|
2019-05-24 14:49:10 -07:00
|
|
|
let total_proofs = checked_proofs.len() as u64;
|
|
|
|
let num_validations = count_valid_proofs(&checked_proofs);
|
|
|
|
let reward =
|
|
|
|
num_validations * TOTAL_REPLICATOR_REWARDS * (num_validations / total_proofs);
|
2019-05-23 14:50:23 -07:00
|
|
|
mining_pool.account.lamports -= reward;
|
|
|
|
self.account.lamports += reward;
|
2019-04-04 12:01:09 -07:00
|
|
|
self.account.set_state(storage_contract)
|
|
|
|
} else {
|
|
|
|
Err(InstructionError::InvalidArgument)?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Store the result of a proof validation into the replicator account
|
|
|
|
fn store_validation_result(
|
|
|
|
storage_account: &mut StorageAccount,
|
2019-05-17 14:52:54 -07:00
|
|
|
segment: usize,
|
|
|
|
checked_proof: CheckedProof,
|
2019-04-04 12:01:09 -07:00
|
|
|
) -> Result<(), InstructionError> {
|
|
|
|
let mut storage_contract = storage_account.account.state()?;
|
|
|
|
match &mut storage_contract {
|
|
|
|
StorageContract::ReplicatorStorage {
|
|
|
|
proofs,
|
|
|
|
reward_validations,
|
|
|
|
..
|
|
|
|
} => {
|
2019-05-17 14:52:54 -07:00
|
|
|
if !proofs.contains_key(&segment) {
|
2019-04-04 12:01:09 -07:00
|
|
|
return Err(InstructionError::InvalidAccountData);
|
|
|
|
}
|
2019-05-17 14:52:54 -07:00
|
|
|
|
|
|
|
if proofs
|
|
|
|
.get(&segment)
|
|
|
|
.unwrap()
|
|
|
|
.contains_key(&checked_proof.proof.sha_state)
|
|
|
|
{
|
|
|
|
reward_validations
|
|
|
|
.entry(segment)
|
|
|
|
.or_default()
|
|
|
|
.entry(checked_proof.proof.sha_state)
|
|
|
|
.or_default()
|
2019-05-24 14:49:10 -07:00
|
|
|
.push(checked_proof.status);
|
2019-05-17 14:52:54 -07:00
|
|
|
} else {
|
|
|
|
return Err(InstructionError::InvalidAccountData);
|
2019-04-04 12:01:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => return Err(InstructionError::InvalidAccountData),
|
|
|
|
}
|
|
|
|
storage_account.account.set_state(&storage_contract)
|
|
|
|
}
|
|
|
|
|
2019-05-24 14:49:10 -07:00
|
|
|
fn count_valid_proofs(proofs: &[ProofStatus]) -> u64 {
|
2019-04-04 12:01:09 -07:00
|
|
|
let mut num = 0;
|
|
|
|
for proof in proofs {
|
2019-05-24 14:49:10 -07:00
|
|
|
if let ProofStatus::Valid = proof {
|
2019-04-04 12:01:09 -07:00
|
|
|
num += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
num
|
|
|
|
}
|
|
|
|
|
|
|
|
fn process_validation(
|
|
|
|
account: &mut StorageAccount,
|
|
|
|
segment_index: usize,
|
|
|
|
proof: &Proof,
|
|
|
|
checked_proof: &CheckedProof,
|
|
|
|
) -> Result<(), InstructionError> {
|
2019-05-17 14:52:54 -07:00
|
|
|
store_validation_result(account, segment_index, checked_proof.clone())?;
|
2019-04-04 12:01:09 -07:00
|
|
|
if proof.signature != checked_proof.proof.signature
|
|
|
|
|| checked_proof.status != ProofStatus::Valid
|
|
|
|
{
|
|
|
|
return Err(InstructionError::GenericError);
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use crate::id;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_account_data() {
|
|
|
|
solana_logger::setup();
|
|
|
|
let mut account = Account::default();
|
2019-05-23 14:50:23 -07:00
|
|
|
account.data.resize(STORAGE_ACCOUNT_SPACE as usize, 0);
|
2019-04-04 12:01:09 -07:00
|
|
|
let storage_account = StorageAccount::new(&mut account);
|
|
|
|
// pretend it's a validator op code
|
|
|
|
let mut contract = storage_account.account.state().unwrap();
|
|
|
|
if let StorageContract::ValidatorStorage { .. } = contract {
|
|
|
|
assert!(true)
|
|
|
|
}
|
|
|
|
if let StorageContract::ReplicatorStorage { .. } = &mut contract {
|
|
|
|
panic!("Contract should not decode into two types");
|
|
|
|
}
|
|
|
|
|
|
|
|
contract = StorageContract::ValidatorStorage {
|
2019-05-03 16:27:53 -07:00
|
|
|
slot: 0,
|
2019-04-04 12:01:09 -07:00
|
|
|
hash: Hash::default(),
|
2019-05-17 14:52:54 -07:00
|
|
|
lockout_validations: HashMap::new(),
|
|
|
|
reward_validations: HashMap::new(),
|
2019-04-04 12:01:09 -07:00
|
|
|
};
|
|
|
|
storage_account.account.set_state(&contract).unwrap();
|
|
|
|
if let StorageContract::ReplicatorStorage { .. } = contract {
|
|
|
|
panic!("Wrong contract type");
|
|
|
|
}
|
|
|
|
contract = StorageContract::ReplicatorStorage {
|
2019-05-17 14:52:54 -07:00
|
|
|
proofs: HashMap::new(),
|
|
|
|
reward_validations: HashMap::new(),
|
2019-04-04 12:01:09 -07:00
|
|
|
};
|
|
|
|
storage_account.account.set_state(&contract).unwrap();
|
|
|
|
if let StorageContract::ValidatorStorage { .. } = contract {
|
|
|
|
panic!("Wrong contract type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_process_validation() {
|
|
|
|
let mut account = StorageAccount {
|
|
|
|
account: &mut Account {
|
|
|
|
lamports: 0,
|
|
|
|
data: vec![],
|
|
|
|
owner: id(),
|
|
|
|
executable: false,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
let segment_index = 0_usize;
|
|
|
|
let proof = Proof {
|
|
|
|
signature: Signature::default(),
|
|
|
|
sha_state: Hash::default(),
|
|
|
|
};
|
|
|
|
let mut checked_proof = CheckedProof {
|
|
|
|
proof: proof.clone(),
|
|
|
|
status: ProofStatus::Valid,
|
|
|
|
};
|
|
|
|
|
|
|
|
// account has no space
|
|
|
|
process_validation(&mut account, segment_index, &proof, &checked_proof).unwrap_err();
|
|
|
|
|
2019-05-23 14:50:23 -07:00
|
|
|
account
|
|
|
|
.account
|
|
|
|
.data
|
|
|
|
.resize(STORAGE_ACCOUNT_SPACE as usize, 0);
|
2019-04-04 12:01:09 -07:00
|
|
|
let storage_contract = &mut account.account.state().unwrap();
|
2019-05-23 14:50:23 -07:00
|
|
|
if let StorageContract::Uninitialized = storage_contract {
|
2019-05-17 14:52:54 -07:00
|
|
|
let mut proof_map = HashMap::new();
|
|
|
|
proof_map.insert(proof.sha_state, proof.clone());
|
|
|
|
let mut proofs = HashMap::new();
|
|
|
|
proofs.insert(0, proof_map);
|
2019-04-04 12:01:09 -07:00
|
|
|
*storage_contract = StorageContract::ReplicatorStorage {
|
2019-05-17 14:52:54 -07:00
|
|
|
proofs,
|
|
|
|
reward_validations: HashMap::new(),
|
2019-04-04 12:01:09 -07:00
|
|
|
};
|
|
|
|
};
|
|
|
|
account.account.set_state(storage_contract).unwrap();
|
|
|
|
|
|
|
|
// proof is valid
|
|
|
|
process_validation(&mut account, segment_index, &proof, &checked_proof).unwrap();
|
|
|
|
|
|
|
|
checked_proof.status = ProofStatus::NotValid;
|
|
|
|
|
|
|
|
// proof failed verification
|
|
|
|
process_validation(&mut account, segment_index, &proof, &checked_proof).unwrap_err();
|
|
|
|
}
|
|
|
|
}
|