Add storage point tracking and tie in storage rewards to economics (#4824)
* Add storage point tracking and tie in storage rewards to epochs and economics * Prevent validators from updating their validations for a segment * Fix test * Retain syscall scoping for readability * Update Credits to own epoch tracking
This commit is contained in:
@ -338,11 +338,8 @@ impl Replicator {
|
|||||||
let client = crate::gossip_service::get_client(&nodes);
|
let client = crate::gossip_service::get_client(&nodes);
|
||||||
|
|
||||||
if let Ok(Some(account)) = client.get_account(&self.storage_keypair.pubkey()) {
|
if let Ok(Some(account)) = client.get_account(&self.storage_keypair.pubkey()) {
|
||||||
if let Ok(StorageContract::ReplicatorStorage {
|
if let Ok(StorageContract::ReplicatorStorage { validations, .. }) = account.state() {
|
||||||
reward_validations, ..
|
if !validations.is_empty() {
|
||||||
}) = account.state()
|
|
||||||
{
|
|
||||||
if !reward_validations.is_empty() {
|
|
||||||
let ix = storage_instruction::claim_reward(
|
let ix = storage_instruction::claim_reward(
|
||||||
&self.keypair.pubkey(),
|
&self.keypair.pubkey(),
|
||||||
&self.storage_keypair.pubkey(),
|
&self.storage_keypair.pubkey(),
|
||||||
|
@ -9,14 +9,33 @@ use solana_sdk::hash::Hash;
|
|||||||
use solana_sdk::instruction::InstructionError;
|
use solana_sdk::instruction::InstructionError;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_sdk::signature::Signature;
|
use solana_sdk::signature::Signature;
|
||||||
|
use solana_sdk::syscall;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
pub const VALIDATOR_REWARD: u64 = 200;
|
// Todo Tune this for actual use cases when PoRep is feature complete
|
||||||
pub const REPLICATOR_REWARD: u64 = 200;
|
|
||||||
// Todo Tune this for actual use cases when replicators are feature complete
|
|
||||||
pub const STORAGE_ACCOUNT_SPACE: u64 = 1024 * 8;
|
pub const STORAGE_ACCOUNT_SPACE: u64 = 1024 * 8;
|
||||||
pub const MAX_PROOFS_PER_SEGMENT: usize = 80;
|
pub const MAX_PROOFS_PER_SEGMENT: usize = 80;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
|
pub struct Credits {
|
||||||
|
// current epoch
|
||||||
|
epoch: u64,
|
||||||
|
// currently pending credits
|
||||||
|
pub current_epoch: u64,
|
||||||
|
// credits ready to be claimed
|
||||||
|
pub redeemable: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Credits {
|
||||||
|
pub fn update_epoch(&mut self, current_epoch: u64) {
|
||||||
|
if self.epoch != current_epoch {
|
||||||
|
self.epoch = current_epoch;
|
||||||
|
self.redeemable += self.current_epoch;
|
||||||
|
self.current_epoch = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, FromPrimitive)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, FromPrimitive)]
|
||||||
pub enum StorageError {
|
pub enum StorageError {
|
||||||
InvalidSegment,
|
InvalidSegment,
|
||||||
@ -64,11 +83,12 @@ pub enum StorageContract {
|
|||||||
// Most recently advertised blockhash
|
// Most recently advertised blockhash
|
||||||
hash: Hash,
|
hash: Hash,
|
||||||
// Lockouts and Rewards are per segment per replicator. It needs to remain this way until
|
// Lockouts and Rewards are per segment per replicator. It needs to remain this way until
|
||||||
// the challenge stage is added. Once challenges are in rewards can just be a number
|
// the challenge stage is added.
|
||||||
lockout_validations: BTreeMap<usize, BTreeMap<Pubkey, Vec<ProofStatus>>>,
|
lockout_validations: BTreeMap<usize, BTreeMap<Pubkey, Vec<ProofStatus>>>,
|
||||||
// lamports that are ready to be claimed
|
// Used to keep track of ongoing credits
|
||||||
pending_lamports: u64,
|
credits: Credits,
|
||||||
},
|
},
|
||||||
|
|
||||||
ReplicatorStorage {
|
ReplicatorStorage {
|
||||||
owner: Pubkey,
|
owner: Pubkey,
|
||||||
// TODO what to do about duplicate proofs across segments? - Check the blockhashes
|
// TODO what to do about duplicate proofs across segments? - Check the blockhashes
|
||||||
@ -76,7 +96,9 @@ pub enum StorageContract {
|
|||||||
proofs: BTreeMap<usize, Vec<Proof>>,
|
proofs: BTreeMap<usize, Vec<Proof>>,
|
||||||
// Map of Rewards per segment, in a BTreeMap based on the validator account that verified
|
// Map of Rewards per segment, in a BTreeMap based on the validator account that verified
|
||||||
// the proof. This can be used for challenge stage when its added
|
// the proof. This can be used for challenge stage when its added
|
||||||
reward_validations: BTreeMap<usize, BTreeMap<Pubkey, Vec<ProofStatus>>>,
|
validations: BTreeMap<usize, BTreeMap<Pubkey, Vec<ProofStatus>>>,
|
||||||
|
// Used to keep track of ongoing credits
|
||||||
|
credits: Credits,
|
||||||
},
|
},
|
||||||
|
|
||||||
RewardsPool,
|
RewardsPool,
|
||||||
@ -92,7 +114,7 @@ pub fn create_validator_storage_account(owner: Pubkey, lamports: u64) -> Account
|
|||||||
slot: 0,
|
slot: 0,
|
||||||
hash: Hash::default(),
|
hash: Hash::default(),
|
||||||
lockout_validations: BTreeMap::new(),
|
lockout_validations: BTreeMap::new(),
|
||||||
pending_lamports: 0,
|
credits: Credits::default(),
|
||||||
})
|
})
|
||||||
.expect("set_state");
|
.expect("set_state");
|
||||||
|
|
||||||
@ -115,7 +137,8 @@ impl<'a> StorageAccount<'a> {
|
|||||||
*storage_contract = StorageContract::ReplicatorStorage {
|
*storage_contract = StorageContract::ReplicatorStorage {
|
||||||
owner,
|
owner,
|
||||||
proofs: BTreeMap::new(),
|
proofs: BTreeMap::new(),
|
||||||
reward_validations: BTreeMap::new(),
|
validations: BTreeMap::new(),
|
||||||
|
credits: Credits::default(),
|
||||||
};
|
};
|
||||||
self.account.set_state(storage_contract)
|
self.account.set_state(storage_contract)
|
||||||
} else {
|
} else {
|
||||||
@ -131,7 +154,7 @@ impl<'a> StorageAccount<'a> {
|
|||||||
slot: 0,
|
slot: 0,
|
||||||
hash: Hash::default(),
|
hash: Hash::default(),
|
||||||
lockout_validations: BTreeMap::new(),
|
lockout_validations: BTreeMap::new(),
|
||||||
pending_lamports: 0,
|
credits: Credits::default(),
|
||||||
};
|
};
|
||||||
self.account.set_state(storage_contract)
|
self.account.set_state(storage_contract)
|
||||||
} else {
|
} else {
|
||||||
@ -145,16 +168,17 @@ impl<'a> StorageAccount<'a> {
|
|||||||
segment_index: usize,
|
segment_index: usize,
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
blockhash: Hash,
|
blockhash: Hash,
|
||||||
current_slot: u64,
|
current: syscall::current::Current,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let mut storage_contract = &mut self.account.state()?;
|
let mut storage_contract = &mut self.account.state()?;
|
||||||
if let StorageContract::ReplicatorStorage {
|
if let StorageContract::ReplicatorStorage {
|
||||||
proofs,
|
proofs,
|
||||||
reward_validations,
|
validations,
|
||||||
|
credits,
|
||||||
..
|
..
|
||||||
} = &mut storage_contract
|
} = &mut storage_contract
|
||||||
{
|
{
|
||||||
let current_segment = get_segment_from_slot(current_slot);
|
let current_segment = get_segment_from_slot(current.slot);
|
||||||
|
|
||||||
// clean up the account
|
// clean up the account
|
||||||
// TODO check for time correctness - storage seems to run at a delay of about 3
|
// TODO check for time correctness - storage seems to run at a delay of about 3
|
||||||
@ -163,7 +187,7 @@ impl<'a> StorageAccount<'a> {
|
|||||||
.filter(|(segment, _)| **segment >= current_segment.saturating_sub(5))
|
.filter(|(segment, _)| **segment >= current_segment.saturating_sub(5))
|
||||||
.map(|(segment, proofs)| (*segment, proofs.clone()))
|
.map(|(segment, proofs)| (*segment, proofs.clone()))
|
||||||
.collect();
|
.collect();
|
||||||
*reward_validations = reward_validations
|
*validations = validations
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(segment, _)| **segment >= current_segment.saturating_sub(10))
|
.filter(|(segment, _)| **segment >= current_segment.saturating_sub(10))
|
||||||
.map(|(segment, rewards)| (*segment, rewards.clone()))
|
.map(|(segment, rewards)| (*segment, rewards.clone()))
|
||||||
@ -207,6 +231,7 @@ impl<'a> StorageAccount<'a> {
|
|||||||
StorageError::ProofLimitReached as u32,
|
StorageError::ProofLimitReached as u32,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
credits.update_epoch(current.epoch);
|
||||||
segment_proofs.push(proof);
|
segment_proofs.push(proof);
|
||||||
self.account.set_state(storage_contract)
|
self.account.set_state(storage_contract)
|
||||||
} else {
|
} else {
|
||||||
@ -218,18 +243,18 @@ impl<'a> StorageAccount<'a> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
hash: Hash,
|
hash: Hash,
|
||||||
slot: u64,
|
slot: u64,
|
||||||
current_slot: u64,
|
current: syscall::current::Current,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let mut storage_contract = &mut self.account.state()?;
|
let mut storage_contract = &mut self.account.state()?;
|
||||||
if let StorageContract::ValidatorStorage {
|
if let StorageContract::ValidatorStorage {
|
||||||
slot: state_slot,
|
slot: state_slot,
|
||||||
hash: state_hash,
|
hash: state_hash,
|
||||||
lockout_validations,
|
lockout_validations,
|
||||||
pending_lamports,
|
credits,
|
||||||
..
|
..
|
||||||
} = &mut storage_contract
|
} = &mut storage_contract
|
||||||
{
|
{
|
||||||
let current_segment = get_segment_from_slot(current_slot);
|
let current_segment = get_segment_from_slot(current.slot);
|
||||||
let original_segment = get_segment_from_slot(*state_slot);
|
let original_segment = get_segment_from_slot(*state_slot);
|
||||||
let segment = get_segment_from_slot(slot);
|
let segment = get_segment_from_slot(slot);
|
||||||
debug!(
|
debug!(
|
||||||
@ -245,20 +270,11 @@ impl<'a> StorageAccount<'a> {
|
|||||||
*state_slot = slot;
|
*state_slot = slot;
|
||||||
*state_hash = hash;
|
*state_hash = hash;
|
||||||
|
|
||||||
// storage epoch updated, move the lockout_validations to pending_lamports
|
// storage epoch updated, move the lockout_validations to credits
|
||||||
let num_validations = count_valid_proofs(
|
let (_num_valid, total_validations) = count_valid_proofs(&lockout_validations);
|
||||||
&lockout_validations
|
|
||||||
.iter()
|
|
||||||
.flat_map(|(_segment, proofs)| {
|
|
||||||
proofs
|
|
||||||
.iter()
|
|
||||||
.flat_map(|(_, proof)| proof.clone())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
);
|
|
||||||
lockout_validations.clear();
|
lockout_validations.clear();
|
||||||
*pending_lamports += VALIDATOR_REWARD * num_validations;
|
credits.update_epoch(current.epoch);
|
||||||
|
credits.current_epoch += total_validations;
|
||||||
self.account.set_state(storage_contract)
|
self.account.set_state(storage_contract)
|
||||||
} else {
|
} else {
|
||||||
Err(InstructionError::InvalidArgument)?
|
Err(InstructionError::InvalidArgument)?
|
||||||
@ -268,6 +284,7 @@ impl<'a> StorageAccount<'a> {
|
|||||||
pub fn proof_validation(
|
pub fn proof_validation(
|
||||||
&mut self,
|
&mut self,
|
||||||
me: &Pubkey,
|
me: &Pubkey,
|
||||||
|
current: syscall::current::Current,
|
||||||
segment: u64,
|
segment: u64,
|
||||||
proofs_per_account: Vec<Vec<ProofStatus>>,
|
proofs_per_account: Vec<Vec<ProofStatus>>,
|
||||||
replicator_accounts: &mut [StorageAccount],
|
replicator_accounts: &mut [StorageAccount],
|
||||||
@ -331,7 +348,14 @@ impl<'a> StorageAccount<'a> {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.zip(accounts.into_iter())
|
.zip(accounts.into_iter())
|
||||||
.filter_map(|(checked_proofs, account)| {
|
.filter_map(|(checked_proofs, account)| {
|
||||||
if store_validation_result(me, account, segment_index, &checked_proofs).is_ok()
|
if store_validation_result(
|
||||||
|
me,
|
||||||
|
¤t,
|
||||||
|
account,
|
||||||
|
segment_index,
|
||||||
|
&checked_proofs,
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
{
|
{
|
||||||
Some((account.id, checked_proofs))
|
Some((account.id, checked_proofs))
|
||||||
} else {
|
} else {
|
||||||
@ -359,13 +383,15 @@ impl<'a> StorageAccount<'a> {
|
|||||||
pub fn claim_storage_reward(
|
pub fn claim_storage_reward(
|
||||||
&mut self,
|
&mut self,
|
||||||
rewards_pool: &mut KeyedAccount,
|
rewards_pool: &mut KeyedAccount,
|
||||||
|
current: syscall::current::Current,
|
||||||
|
rewards: syscall::rewards::Rewards,
|
||||||
owner: &mut StorageAccount,
|
owner: &mut StorageAccount,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let mut storage_contract = &mut self.account.state()?;
|
let mut storage_contract = &mut self.account.state()?;
|
||||||
|
|
||||||
if let StorageContract::ValidatorStorage {
|
if let StorageContract::ValidatorStorage {
|
||||||
owner: account_owner,
|
owner: account_owner,
|
||||||
pending_lamports,
|
credits,
|
||||||
..
|
..
|
||||||
} = &mut storage_contract
|
} = &mut storage_contract
|
||||||
{
|
{
|
||||||
@ -375,21 +401,24 @@ impl<'a> StorageAccount<'a> {
|
|||||||
))?
|
))?
|
||||||
}
|
}
|
||||||
|
|
||||||
let pending = *pending_lamports;
|
credits.update_epoch(current.epoch);
|
||||||
|
let pending = (credits.redeemable as f64 * rewards.storage_point_value) as u64;
|
||||||
if rewards_pool.account.lamports < pending {
|
if rewards_pool.account.lamports < pending {
|
||||||
println!("reward pool has {}", rewards_pool.account.lamports);
|
|
||||||
Err(InstructionError::CustomError(
|
Err(InstructionError::CustomError(
|
||||||
StorageError::RewardPoolDepleted as u32,
|
StorageError::RewardPoolDepleted as u32,
|
||||||
))?
|
))?
|
||||||
}
|
}
|
||||||
|
if pending >= 1 {
|
||||||
rewards_pool.account.lamports -= pending;
|
rewards_pool.account.lamports -= pending;
|
||||||
owner.account.lamports += pending;
|
owner.account.lamports += pending;
|
||||||
//clear pending_lamports
|
//clear credits
|
||||||
*pending_lamports = 0;
|
credits.redeemable = 0;
|
||||||
|
}
|
||||||
self.account.set_state(storage_contract)
|
self.account.set_state(storage_contract)
|
||||||
} else if let StorageContract::ReplicatorStorage {
|
} else if let StorageContract::ReplicatorStorage {
|
||||||
owner: account_owner,
|
owner: account_owner,
|
||||||
reward_validations,
|
validations,
|
||||||
|
credits,
|
||||||
..
|
..
|
||||||
} = &mut storage_contract
|
} = &mut storage_contract
|
||||||
{
|
{
|
||||||
@ -398,22 +427,21 @@ impl<'a> StorageAccount<'a> {
|
|||||||
StorageError::InvalidOwner as u32,
|
StorageError::InvalidOwner as u32,
|
||||||
))?
|
))?
|
||||||
}
|
}
|
||||||
|
credits.update_epoch(current.epoch);
|
||||||
let checked_proofs = reward_validations
|
let (num_validations, _total_proofs) = count_valid_proofs(&validations);
|
||||||
.iter()
|
credits.current_epoch += num_validations;
|
||||||
.flat_map(|(_, proofs)| {
|
validations.clear();
|
||||||
proofs
|
let reward = (credits.redeemable as f64 * rewards.storage_point_value) as u64;
|
||||||
.iter()
|
if rewards_pool.account.lamports < reward {
|
||||||
.flat_map(|(_, proofs)| proofs.clone())
|
Err(InstructionError::CustomError(
|
||||||
.collect::<Vec<_>>()
|
StorageError::RewardPoolDepleted as u32,
|
||||||
})
|
))?
|
||||||
.collect::<Vec<_>>();
|
}
|
||||||
reward_validations.clear();
|
if reward >= 1 {
|
||||||
let total_proofs = checked_proofs.len() as u64;
|
|
||||||
let num_validations = count_valid_proofs(&checked_proofs);
|
|
||||||
let reward = num_validations * REPLICATOR_REWARD * (num_validations / total_proofs);
|
|
||||||
rewards_pool.account.lamports -= reward;
|
rewards_pool.account.lamports -= reward;
|
||||||
owner.account.lamports += reward;
|
owner.account.lamports += reward;
|
||||||
|
credits.redeemable = 0;
|
||||||
|
}
|
||||||
self.account.set_state(storage_contract)
|
self.account.set_state(storage_contract)
|
||||||
} else {
|
} else {
|
||||||
Err(InstructionError::InvalidArgument)?
|
Err(InstructionError::InvalidArgument)?
|
||||||
@ -428,6 +456,7 @@ pub fn create_rewards_pool() -> Account {
|
|||||||
/// Store the result of a proof validation into the replicator account
|
/// Store the result of a proof validation into the replicator account
|
||||||
fn store_validation_result(
|
fn store_validation_result(
|
||||||
me: &Pubkey,
|
me: &Pubkey,
|
||||||
|
current: &syscall::current::Current,
|
||||||
storage_account: &mut StorageAccount,
|
storage_account: &mut StorageAccount,
|
||||||
segment: usize,
|
segment: usize,
|
||||||
proof_mask: &[ProofStatus],
|
proof_mask: &[ProofStatus],
|
||||||
@ -436,7 +465,8 @@ fn store_validation_result(
|
|||||||
match &mut storage_contract {
|
match &mut storage_contract {
|
||||||
StorageContract::ReplicatorStorage {
|
StorageContract::ReplicatorStorage {
|
||||||
proofs,
|
proofs,
|
||||||
reward_validations,
|
validations,
|
||||||
|
credits,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if !proofs.contains_key(&segment) {
|
if !proofs.contains_key(&segment) {
|
||||||
@ -447,24 +477,39 @@ fn store_validation_result(
|
|||||||
return Err(InstructionError::InvalidAccountData);
|
return Err(InstructionError::InvalidAccountData);
|
||||||
}
|
}
|
||||||
|
|
||||||
reward_validations
|
let (recorded_validations, _) = count_valid_proofs(&validations);
|
||||||
.entry(segment)
|
let entry = validations.entry(segment).or_default();
|
||||||
.or_default()
|
if !entry.contains_key(me) {
|
||||||
.insert(*me, proof_mask.to_vec());
|
entry.insert(*me, proof_mask.to_vec());
|
||||||
|
}
|
||||||
|
let (total_validations, _) = count_valid_proofs(&validations);
|
||||||
|
credits.update_epoch(current.epoch);
|
||||||
|
credits.current_epoch += total_validations - recorded_validations;
|
||||||
}
|
}
|
||||||
_ => return Err(InstructionError::InvalidAccountData),
|
_ => return Err(InstructionError::InvalidAccountData),
|
||||||
}
|
}
|
||||||
storage_account.account.set_state(&storage_contract)
|
storage_account.account.set_state(&storage_contract)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_valid_proofs(proofs: &[ProofStatus]) -> u64 {
|
fn count_valid_proofs(
|
||||||
|
validations: &BTreeMap<usize, BTreeMap<Pubkey, Vec<ProofStatus>>>,
|
||||||
|
) -> (u64, u64) {
|
||||||
|
let proofs = validations
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(_, proofs)| {
|
||||||
|
proofs
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(_, proofs)| proofs)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
let mut num = 0;
|
let mut num = 0;
|
||||||
for proof in proofs {
|
for proof in proofs.iter() {
|
||||||
if let ProofStatus::Valid = proof {
|
if let ProofStatus::Valid = proof {
|
||||||
num += 1;
|
num += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
num
|
(num, proofs.len() as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -493,7 +538,7 @@ mod tests {
|
|||||||
slot: 0,
|
slot: 0,
|
||||||
hash: Hash::default(),
|
hash: Hash::default(),
|
||||||
lockout_validations: BTreeMap::new(),
|
lockout_validations: BTreeMap::new(),
|
||||||
pending_lamports: 0,
|
credits: Credits::default(),
|
||||||
};
|
};
|
||||||
storage_account.account.set_state(&contract).unwrap();
|
storage_account.account.set_state(&contract).unwrap();
|
||||||
if let StorageContract::ReplicatorStorage { .. } = contract {
|
if let StorageContract::ReplicatorStorage { .. } = contract {
|
||||||
@ -502,7 +547,8 @@ mod tests {
|
|||||||
contract = StorageContract::ReplicatorStorage {
|
contract = StorageContract::ReplicatorStorage {
|
||||||
owner: Pubkey::default(),
|
owner: Pubkey::default(),
|
||||||
proofs: BTreeMap::new(),
|
proofs: BTreeMap::new(),
|
||||||
reward_validations: BTreeMap::new(),
|
validations: BTreeMap::new(),
|
||||||
|
credits: Credits::default(),
|
||||||
};
|
};
|
||||||
storage_account.account.set_state(&contract).unwrap();
|
storage_account.account.set_state(&contract).unwrap();
|
||||||
if let StorageContract::ValidatorStorage { .. } = contract {
|
if let StorageContract::ValidatorStorage { .. } = contract {
|
||||||
@ -530,6 +576,7 @@ mod tests {
|
|||||||
// account has no space
|
// account has no space
|
||||||
store_validation_result(
|
store_validation_result(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
|
&syscall::current::Current::default(),
|
||||||
&mut account,
|
&mut account,
|
||||||
segment_index,
|
segment_index,
|
||||||
&vec![ProofStatus::default(); 1],
|
&vec![ProofStatus::default(); 1],
|
||||||
@ -547,7 +594,8 @@ mod tests {
|
|||||||
*storage_contract = StorageContract::ReplicatorStorage {
|
*storage_contract = StorageContract::ReplicatorStorage {
|
||||||
owner: Pubkey::default(),
|
owner: Pubkey::default(),
|
||||||
proofs,
|
proofs,
|
||||||
reward_validations: BTreeMap::new(),
|
validations: BTreeMap::new(),
|
||||||
|
credits: Credits::default(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
account.account.set_state(storage_contract).unwrap();
|
account.account.set_state(storage_contract).unwrap();
|
||||||
@ -555,6 +603,7 @@ mod tests {
|
|||||||
// proof is valid
|
// proof is valid
|
||||||
store_validation_result(
|
store_validation_result(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
|
&syscall::current::Current::default(),
|
||||||
&mut account,
|
&mut account,
|
||||||
segment_index,
|
segment_index,
|
||||||
&vec![ProofStatus::Valid],
|
&vec![ProofStatus::Valid],
|
||||||
@ -564,6 +613,7 @@ mod tests {
|
|||||||
// proof failed verification but we should still be able to store it
|
// proof failed verification but we should still be able to store it
|
||||||
store_validation_result(
|
store_validation_result(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
|
&syscall::current::Current::default(),
|
||||||
&mut account,
|
&mut account,
|
||||||
segment_index,
|
segment_index,
|
||||||
&vec![ProofStatus::NotValid],
|
&vec![ProofStatus::NotValid],
|
||||||
|
@ -5,7 +5,7 @@ use solana_sdk::hash::Hash;
|
|||||||
use solana_sdk::instruction::{AccountMeta, Instruction};
|
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::syscall::current;
|
use solana_sdk::syscall::{current, rewards};
|
||||||
use solana_sdk::system_instruction;
|
use solana_sdk::system_instruction;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
@ -35,8 +35,10 @@ pub enum StorageInstruction {
|
|||||||
///
|
///
|
||||||
/// Expects 1 Account:
|
/// Expects 1 Account:
|
||||||
/// 0 - Storage account with credits to redeem
|
/// 0 - Storage account with credits to redeem
|
||||||
/// 1 - MiningPool account to redeem credits from
|
/// 1 - Current Syscall to figure out the current epoch
|
||||||
/// 2 - Replicator account to credit - this account *must* be the owner
|
/// 2 - Replicator account to credit - this account *must* be the owner
|
||||||
|
/// 3 - MiningPool account to redeem credits from
|
||||||
|
/// 4 - Rewards Syscall to figure out point values
|
||||||
ClaimStorageReward,
|
ClaimStorageReward,
|
||||||
ProofValidation {
|
ProofValidation {
|
||||||
/// The segment during which this proof was generated
|
/// The segment during which this proof was generated
|
||||||
@ -168,7 +170,10 @@ pub fn proof_validation(
|
|||||||
segment: u64,
|
segment: u64,
|
||||||
checked_proofs: Vec<(Pubkey, Vec<ProofStatus>)>,
|
checked_proofs: Vec<(Pubkey, Vec<ProofStatus>)>,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
let mut account_metas = vec![AccountMeta::new(*storage_pubkey, true)];
|
let mut account_metas = vec![
|
||||||
|
AccountMeta::new(*storage_pubkey, true),
|
||||||
|
AccountMeta::new(current::id(), false),
|
||||||
|
];
|
||||||
let mut proofs = vec![];
|
let mut proofs = vec![];
|
||||||
checked_proofs.into_iter().for_each(|(id, p)| {
|
checked_proofs.into_iter().for_each(|(id, p)| {
|
||||||
proofs.push(p);
|
proofs.push(p);
|
||||||
@ -182,6 +187,8 @@ pub fn claim_reward(owner_pubkey: &Pubkey, storage_pubkey: &Pubkey) -> Instructi
|
|||||||
let storage_instruction = StorageInstruction::ClaimStorageReward;
|
let storage_instruction = StorageInstruction::ClaimStorageReward;
|
||||||
let account_metas = vec![
|
let account_metas = vec![
|
||||||
AccountMeta::new(*storage_pubkey, false),
|
AccountMeta::new(*storage_pubkey, false),
|
||||||
|
AccountMeta::new(current::id(), false),
|
||||||
|
AccountMeta::new(rewards::id(), false),
|
||||||
AccountMeta::new(rewards_pools::random_id(), false),
|
AccountMeta::new(rewards_pools::random_id(), false),
|
||||||
AccountMeta::new(*owner_pubkey, false),
|
AccountMeta::new(*owner_pubkey, false),
|
||||||
];
|
];
|
||||||
|
@ -6,7 +6,7 @@ use crate::storage_instruction::StorageInstruction;
|
|||||||
use solana_sdk::account::KeyedAccount;
|
use solana_sdk::account::KeyedAccount;
|
||||||
use solana_sdk::instruction::InstructionError;
|
use solana_sdk::instruction::InstructionError;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_sdk::syscall::current::Current;
|
use solana_sdk::syscall;
|
||||||
|
|
||||||
pub fn process_instruction(
|
pub fn process_instruction(
|
||||||
_program_id: &Pubkey,
|
_program_id: &Pubkey,
|
||||||
@ -42,13 +42,13 @@ pub fn process_instruction(
|
|||||||
// This instruction must be signed by `me`
|
// This instruction must be signed by `me`
|
||||||
Err(InstructionError::InvalidArgument)?;
|
Err(InstructionError::InvalidArgument)?;
|
||||||
}
|
}
|
||||||
let current = Current::from(&rest[0].account).unwrap();
|
let current = syscall::current::from_keyed_account(&rest[0])?;
|
||||||
storage_account.submit_mining_proof(
|
storage_account.submit_mining_proof(
|
||||||
sha_state,
|
sha_state,
|
||||||
segment_index,
|
segment_index,
|
||||||
signature,
|
signature,
|
||||||
blockhash,
|
blockhash,
|
||||||
current.slot,
|
current,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
StorageInstruction::AdvertiseStorageRecentBlockhash { hash, slot } => {
|
StorageInstruction::AdvertiseStorageRecentBlockhash { hash, slot } => {
|
||||||
@ -56,31 +56,47 @@ pub fn process_instruction(
|
|||||||
// This instruction must be signed by `me`
|
// This instruction must be signed by `me`
|
||||||
Err(InstructionError::InvalidArgument)?;
|
Err(InstructionError::InvalidArgument)?;
|
||||||
}
|
}
|
||||||
let current = Current::from(&rest[0].account).unwrap();
|
let current = syscall::current::from_keyed_account(&rest[0])?;
|
||||||
storage_account.advertise_storage_recent_blockhash(hash, slot, current.slot)
|
storage_account.advertise_storage_recent_blockhash(hash, slot, current)
|
||||||
}
|
}
|
||||||
StorageInstruction::ClaimStorageReward => {
|
StorageInstruction::ClaimStorageReward => {
|
||||||
if rest.len() != 2 {
|
if rest.len() != 4 {
|
||||||
Err(InstructionError::InvalidArgument)?;
|
Err(InstructionError::InvalidArgument)?;
|
||||||
}
|
}
|
||||||
let (mining_pool, owner) = rest.split_at_mut(1);
|
let (current, rest) = rest.split_at_mut(1);
|
||||||
|
let (rewards, rest) = rest.split_at_mut(1);
|
||||||
|
let (rewards_pools, owner) = rest.split_at_mut(1);
|
||||||
|
|
||||||
|
let rewards = syscall::rewards::from_keyed_account(&rewards[0])?;
|
||||||
|
let current = syscall::current::from_keyed_account(¤t[0])?;
|
||||||
let mut owner = StorageAccount::new(*owner[0].unsigned_key(), &mut owner[0].account);
|
let mut owner = StorageAccount::new(*owner[0].unsigned_key(), &mut owner[0].account);
|
||||||
|
|
||||||
storage_account.claim_storage_reward(&mut mining_pool[0], &mut owner)
|
storage_account.claim_storage_reward(
|
||||||
|
&mut rewards_pools[0],
|
||||||
|
current,
|
||||||
|
rewards,
|
||||||
|
&mut owner,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
StorageInstruction::ProofValidation { segment, proofs } => {
|
StorageInstruction::ProofValidation { segment, proofs } => {
|
||||||
|
if rest.is_empty() {
|
||||||
|
Err(InstructionError::InvalidArgument)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (current, rest) = rest.split_at_mut(1);
|
||||||
if me_unsigned || rest.is_empty() {
|
if me_unsigned || rest.is_empty() {
|
||||||
// This instruction must be signed by `me` and `rest` cannot be empty
|
// This instruction must be signed by `me` and `rest` cannot be empty
|
||||||
Err(InstructionError::InvalidArgument)?;
|
Err(InstructionError::InvalidArgument)?;
|
||||||
}
|
}
|
||||||
let me_id = storage_account.id;
|
let me_id = storage_account.id;
|
||||||
|
let current = syscall::current::from_keyed_account(¤t[0])?;
|
||||||
let mut rest: Vec<_> = rest
|
let mut rest: Vec<_> = rest
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|keyed_account| {
|
.map(|keyed_account| {
|
||||||
StorageAccount::new(*keyed_account.unsigned_key(), &mut keyed_account.account)
|
StorageAccount::new(*keyed_account.unsigned_key(), &mut keyed_account.account)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
storage_account.proof_validation(&me_id, segment, proofs, &mut rest)
|
storage_account.proof_validation(&me_id, current, segment, proofs, &mut rest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,14 +12,13 @@ use solana_sdk::instruction::{Instruction, InstructionError};
|
|||||||
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 solana_sdk::syscall::current;
|
|
||||||
use solana_sdk::syscall::current::Current;
|
use solana_sdk::syscall::current::Current;
|
||||||
|
use solana_sdk::syscall::rewards::Rewards;
|
||||||
|
use solana_sdk::syscall::{current, rewards};
|
||||||
use solana_sdk::system_instruction;
|
use solana_sdk::system_instruction;
|
||||||
use solana_sdk::timing::DEFAULT_TICKS_PER_SLOT;
|
use solana_sdk::timing::DEFAULT_TICKS_PER_SLOT;
|
||||||
use solana_storage_api::storage_contract::StorageAccount;
|
use solana_storage_api::storage_contract::StorageAccount;
|
||||||
use solana_storage_api::storage_contract::{
|
use solana_storage_api::storage_contract::{ProofStatus, StorageContract, STORAGE_ACCOUNT_SPACE};
|
||||||
ProofStatus, StorageContract, REPLICATOR_REWARD, STORAGE_ACCOUNT_SPACE, VALIDATOR_REWARD,
|
|
||||||
};
|
|
||||||
use solana_storage_api::storage_instruction;
|
use solana_storage_api::storage_instruction;
|
||||||
use solana_storage_api::storage_processor::process_instruction;
|
use solana_storage_api::storage_processor::process_instruction;
|
||||||
use solana_storage_api::SLOTS_PER_SEGMENT;
|
use solana_storage_api::SLOTS_PER_SEGMENT;
|
||||||
@ -153,12 +152,12 @@ fn test_storage_tx() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_serialize_overflow() {
|
fn test_serialize_overflow() {
|
||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = Pubkey::new_rand();
|
||||||
let tick_pubkey = Pubkey::new_rand();
|
let current_id = current::id();
|
||||||
let mut keyed_accounts = Vec::new();
|
let mut keyed_accounts = Vec::new();
|
||||||
let mut user_account = Account::default();
|
let mut user_account = Account::default();
|
||||||
let mut current_account = current::create_account(1, 0, 0, 0);
|
let mut current_account = current::create_account(1, 0, 0, 0);
|
||||||
keyed_accounts.push(KeyedAccount::new(&pubkey, true, &mut user_account));
|
keyed_accounts.push(KeyedAccount::new(&pubkey, true, &mut user_account));
|
||||||
keyed_accounts.push(KeyedAccount::new(&tick_pubkey, false, &mut current_account));
|
keyed_accounts.push(KeyedAccount::new(¤t_id, false, &mut current_account));
|
||||||
|
|
||||||
let ix = storage_instruction::advertise_recent_blockhash(
|
let ix = storage_instruction::advertise_recent_blockhash(
|
||||||
&pubkey,
|
&pubkey,
|
||||||
@ -267,7 +266,7 @@ fn test_validate_mining() {
|
|||||||
mut genesis_block,
|
mut genesis_block,
|
||||||
mint_keypair,
|
mint_keypair,
|
||||||
..
|
..
|
||||||
} = create_genesis_block(100_000);
|
} = create_genesis_block(100_000_000_000);
|
||||||
genesis_block
|
genesis_block
|
||||||
.native_instruction_processors
|
.native_instruction_processors
|
||||||
.push(solana_storage_program::solana_storage_program!());
|
.push(solana_storage_program::solana_storage_program!());
|
||||||
@ -402,6 +401,17 @@ fn test_validate_mining() {
|
|||||||
|
|
||||||
assert_eq!(bank_client.get_balance(&validator_storage_id).unwrap(), 10);
|
assert_eq!(bank_client.get_balance(&validator_storage_id).unwrap(), 10);
|
||||||
|
|
||||||
|
let bank = Arc::new(Bank::new_from_parent(
|
||||||
|
&bank,
|
||||||
|
&Pubkey::default(),
|
||||||
|
bank.slot() + bank.epoch_schedule().slots_per_epoch,
|
||||||
|
));
|
||||||
|
let bank_client = BankClient::new_shared(&bank);
|
||||||
|
|
||||||
|
let rewards = bank
|
||||||
|
.get_account(&rewards::id())
|
||||||
|
.map(|account| Rewards::from(&account).unwrap())
|
||||||
|
.unwrap();
|
||||||
let message = Message::new_with_payer(
|
let message = Message::new_with_payer(
|
||||||
vec![storage_instruction::claim_reward(
|
vec![storage_instruction::claim_reward(
|
||||||
&owner_pubkey,
|
&owner_pubkey,
|
||||||
@ -412,7 +422,7 @@ fn test_validate_mining() {
|
|||||||
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(&owner_pubkey).unwrap(),
|
bank_client.get_balance(&owner_pubkey).unwrap(),
|
||||||
1 + (VALIDATOR_REWARD * 10)
|
1 + ((rewards.storage_point_value * 10_f64) as u64)
|
||||||
);
|
);
|
||||||
|
|
||||||
// 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
|
||||||
@ -432,12 +442,11 @@ fn test_validate_mining() {
|
|||||||
)],
|
)],
|
||||||
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(&owner_pubkey).unwrap(),
|
bank_client.get_balance(&owner_pubkey).unwrap(),
|
||||||
1 + (VALIDATOR_REWARD * 10) + (REPLICATOR_REWARD * 5)
|
1 + ((rewards.storage_point_value * 10_f64) as u64)
|
||||||
|
+ (rewards.storage_point_value * 5_f64) as u64
|
||||||
);
|
);
|
||||||
|
|
||||||
let message = Message::new_with_payer(
|
let message = Message::new_with_payer(
|
||||||
@ -448,10 +457,11 @@ fn test_validate_mining() {
|
|||||||
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(&owner_pubkey).unwrap(),
|
bank_client.get_balance(&owner_pubkey).unwrap(),
|
||||||
1 + (VALIDATOR_REWARD * 10) + (REPLICATOR_REWARD * 10)
|
1 + (rewards.storage_point_value * 10_f64) as u64
|
||||||
|
+ (rewards.storage_point_value * 5_f64) as u64
|
||||||
|
+ (rewards.storage_point_value * 5_f64) as u64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,8 +269,6 @@ impl Default for BlockhashQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DUMMY_REPLICATOR_POINTS: u64 = 100;
|
|
||||||
|
|
||||||
impl Bank {
|
impl Bank {
|
||||||
pub fn new(genesis_block: &GenesisBlock) -> Self {
|
pub fn new(genesis_block: &GenesisBlock) -> Self {
|
||||||
Self::new_with_paths(&genesis_block, None)
|
Self::new_with_paths(&genesis_block, None)
|
||||||
@ -463,22 +461,21 @@ impl Bank {
|
|||||||
|
|
||||||
let validator_points = self.stakes.write().unwrap().claim_points();
|
let validator_points = self.stakes.write().unwrap().claim_points();
|
||||||
|
|
||||||
let replicator_rewards =
|
let storage_rewards = self.inflation.storage(year) * self.capitalization() as f64 * period;
|
||||||
self.inflation.replicator(year) * self.capitalization() as f64 * period;
|
|
||||||
|
|
||||||
let replicator_points = DUMMY_REPLICATOR_POINTS; // TODO: real value for points earned last epoch
|
let storage_points = self.storage_accounts.write().unwrap().claim_points();
|
||||||
|
|
||||||
self.store_account(
|
self.store_account(
|
||||||
&rewards::id(),
|
&rewards::id(),
|
||||||
&rewards::create_account(
|
&rewards::create_account(
|
||||||
1,
|
1,
|
||||||
validator_rewards / validator_points as f64,
|
validator_rewards / validator_points as f64,
|
||||||
replicator_rewards / replicator_points as f64,
|
storage_rewards / storage_points as f64,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.capitalization.fetch_add(
|
self.capitalization.fetch_add(
|
||||||
(validator_rewards + replicator_rewards) as usize,
|
(validator_rewards + storage_rewards) as usize,
|
||||||
Ordering::Relaxed,
|
Ordering::Relaxed,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1471,8 +1468,13 @@ mod tests {
|
|||||||
let ((vote_id, mut vote_account), stake) =
|
let ((vote_id, mut vote_account), stake) =
|
||||||
crate::stakes::tests::create_staked_node_accounts(1_0000);
|
crate::stakes::tests::create_staked_node_accounts(1_0000);
|
||||||
|
|
||||||
// set up stakes and vote accounts
|
let ((validator_id, validator_account), (replicator_id, replicator_account)) =
|
||||||
|
crate::storage_utils::tests::create_storage_accounts_with_credits(100);
|
||||||
|
|
||||||
|
// set up stakes,vote, and storage accounts
|
||||||
bank.store_account(&stake.0, &stake.1);
|
bank.store_account(&stake.0, &stake.1);
|
||||||
|
bank.store_account(&validator_id, &validator_account);
|
||||||
|
bank.store_account(&replicator_id, &replicator_account);
|
||||||
|
|
||||||
// generate some rewards
|
// generate some rewards
|
||||||
let mut vote_state = VoteState::from(&vote_account).unwrap();
|
let mut vote_state = VoteState::from(&vote_account).unwrap();
|
||||||
@ -1484,6 +1486,7 @@ mod tests {
|
|||||||
bank.store_account(&vote_id, &vote_account);
|
bank.store_account(&vote_id, &vote_account);
|
||||||
|
|
||||||
let validator_points = bank.stakes.read().unwrap().points();
|
let validator_points = bank.stakes.read().unwrap().points();
|
||||||
|
let storage_points = bank.storage_accounts.read().unwrap().points();
|
||||||
|
|
||||||
// put a child bank in epoch 1, which calls update_rewards()...
|
// put a child bank in epoch 1, which calls update_rewards()...
|
||||||
let bank1 = Bank::new_from_parent(
|
let bank1 = Bank::new_from_parent(
|
||||||
@ -1504,8 +1507,8 @@ mod tests {
|
|||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
((rewards.validator_point_value * validator_points as f64
|
((rewards.validator_point_value * validator_points as f64
|
||||||
+ rewards.replicator_point_value * DUMMY_REPLICATOR_POINTS as f64) - // TODO: need replicator points per-epoch
|
+ rewards.storage_point_value * storage_points as f64)
|
||||||
inflation as f64)
|
- inflation as f64)
|
||||||
.abs()
|
.abs()
|
||||||
< 1.0 // rounding, truncating
|
< 1.0 // rounding, truncating
|
||||||
);
|
);
|
||||||
|
@ -7,11 +7,15 @@ use std::collections::{HashMap, HashSet};
|
|||||||
|
|
||||||
#[derive(Default, Clone, PartialEq, Debug, Deserialize, Serialize)]
|
#[derive(Default, Clone, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
pub struct StorageAccounts {
|
pub struct StorageAccounts {
|
||||||
/// validator storage accounts
|
/// validator storage accounts and their credits
|
||||||
validator_accounts: HashSet<Pubkey>,
|
validator_accounts: HashSet<Pubkey>,
|
||||||
|
|
||||||
/// replicator storage accounts
|
/// replicator storage accounts and their credits
|
||||||
replicator_accounts: HashSet<Pubkey>,
|
replicator_accounts: HashSet<Pubkey>,
|
||||||
|
|
||||||
|
/// unclaimed points.
|
||||||
|
// 1 point == 1 storage account credit
|
||||||
|
points: HashMap<Pubkey, u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_storage(account: &Account) -> bool {
|
pub fn is_storage(account: &Account) -> bool {
|
||||||
@ -21,21 +25,35 @@ pub fn is_storage(account: &Account) -> bool {
|
|||||||
impl StorageAccounts {
|
impl StorageAccounts {
|
||||||
pub fn store(&mut self, pubkey: &Pubkey, account: &Account) {
|
pub fn store(&mut self, pubkey: &Pubkey, account: &Account) {
|
||||||
if let Ok(storage_state) = account.state() {
|
if let Ok(storage_state) = account.state() {
|
||||||
if let StorageContract::ReplicatorStorage { .. } = storage_state {
|
if let StorageContract::ReplicatorStorage { credits, .. } = storage_state {
|
||||||
if account.lamports == 0 {
|
if account.lamports == 0 {
|
||||||
self.replicator_accounts.remove(pubkey);
|
self.replicator_accounts.remove(pubkey);
|
||||||
} else {
|
} else {
|
||||||
self.replicator_accounts.insert(*pubkey);
|
self.replicator_accounts.insert(*pubkey);
|
||||||
|
self.points.insert(*pubkey, credits.current_epoch);
|
||||||
}
|
}
|
||||||
} else if let StorageContract::ValidatorStorage { .. } = storage_state {
|
} else if let StorageContract::ValidatorStorage { credits, .. } = storage_state {
|
||||||
if account.lamports == 0 {
|
if account.lamports == 0 {
|
||||||
self.validator_accounts.remove(pubkey);
|
self.validator_accounts.remove(pubkey);
|
||||||
} else {
|
} else {
|
||||||
self.validator_accounts.insert(*pubkey);
|
self.validator_accounts.insert(*pubkey);
|
||||||
|
self.points.insert(*pubkey, credits.current_epoch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// currently unclaimed points
|
||||||
|
pub fn points(&self) -> u64 {
|
||||||
|
self.points.values().sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "claims" points, resets points to 0
|
||||||
|
pub fn claim_points(&mut self) -> u64 {
|
||||||
|
let points = self.points();
|
||||||
|
self.points.clear();
|
||||||
|
points
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validator_accounts(bank: &Bank) -> HashMap<Pubkey, Account> {
|
pub fn validator_accounts(bank: &Bank) -> HashMap<Pubkey, Account> {
|
||||||
@ -61,13 +79,14 @@ pub fn replicator_accounts(bank: &Bank) -> HashMap<Pubkey, Account> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
pub(crate) mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::bank_client::BankClient;
|
use crate::bank_client::BankClient;
|
||||||
use solana_sdk::client::SyncClient;
|
use solana_sdk::client::SyncClient;
|
||||||
use solana_sdk::genesis_block::create_genesis_block;
|
use solana_sdk::genesis_block::create_genesis_block;
|
||||||
use solana_sdk::message::Message;
|
use solana_sdk::message::Message;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||||
|
use solana_storage_api::storage_contract::{StorageAccount, STORAGE_ACCOUNT_SPACE};
|
||||||
use solana_storage_api::{storage_instruction, storage_processor};
|
use solana_storage_api::{storage_instruction, storage_processor};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -113,4 +132,97 @@ mod tests {
|
|||||||
assert_eq!(validator_accounts(bank.as_ref()).len(), 1);
|
assert_eq!(validator_accounts(bank.as_ref()).len(), 1);
|
||||||
assert_eq!(replicator_accounts(bank.as_ref()).len(), 1);
|
assert_eq!(replicator_accounts(bank.as_ref()).len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_points() {
|
||||||
|
// note: storage_points == storage_credits
|
||||||
|
let credits = 42;
|
||||||
|
let mut storage_accounts = StorageAccounts::default();
|
||||||
|
assert_eq!(storage_accounts.points(), 0);
|
||||||
|
assert_eq!(storage_accounts.claim_points(), 0);
|
||||||
|
|
||||||
|
// create random validator and replicator accounts with `credits`
|
||||||
|
let ((validator_pubkey, validator_account), (replicator_pubkey, replicator_account)) =
|
||||||
|
create_storage_accounts_with_credits(credits);
|
||||||
|
|
||||||
|
storage_accounts.store(&validator_pubkey, &validator_account);
|
||||||
|
storage_accounts.store(&replicator_pubkey, &replicator_account);
|
||||||
|
// check that 2x credits worth of points are available
|
||||||
|
assert_eq!(storage_accounts.points(), credits * 2);
|
||||||
|
|
||||||
|
let ((validator_pubkey, validator_account), (replicator_pubkey, mut replicator_account)) =
|
||||||
|
create_storage_accounts_with_credits(credits);
|
||||||
|
|
||||||
|
storage_accounts.store(&validator_pubkey, &validator_account);
|
||||||
|
storage_accounts.store(&replicator_pubkey, &replicator_account);
|
||||||
|
// check that 4x credits worth of points are available
|
||||||
|
assert_eq!(storage_accounts.points(), credits * 2 * 2);
|
||||||
|
|
||||||
|
storage_accounts.store(&validator_pubkey, &validator_account);
|
||||||
|
storage_accounts.store(&replicator_pubkey, &replicator_account);
|
||||||
|
// check that storing again has no effect
|
||||||
|
assert_eq!(storage_accounts.points(), credits * 2 * 2);
|
||||||
|
|
||||||
|
let storage_contract = &mut replicator_account.state().unwrap();
|
||||||
|
if let StorageContract::ReplicatorStorage {
|
||||||
|
credits: account_credits,
|
||||||
|
..
|
||||||
|
} = storage_contract
|
||||||
|
{
|
||||||
|
account_credits.current_epoch += 1;
|
||||||
|
}
|
||||||
|
replicator_account.set_state(storage_contract).unwrap();
|
||||||
|
storage_accounts.store(&replicator_pubkey, &replicator_account);
|
||||||
|
|
||||||
|
// check that incremental store increases credits
|
||||||
|
assert_eq!(storage_accounts.points(), credits * 2 * 2 + 1);
|
||||||
|
|
||||||
|
assert_eq!(storage_accounts.claim_points(), credits * 2 * 2 + 1);
|
||||||
|
// check that once redeemed, the points are gone
|
||||||
|
assert_eq!(storage_accounts.claim_points(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_storage_accounts_with_credits(
|
||||||
|
credits: u64,
|
||||||
|
) -> ((Pubkey, Account), (Pubkey, Account)) {
|
||||||
|
let validator_pubkey = Pubkey::new_rand();
|
||||||
|
let replicator_pubkey = Pubkey::new_rand();
|
||||||
|
|
||||||
|
let mut validator_account =
|
||||||
|
Account::new(1, STORAGE_ACCOUNT_SPACE as usize, &solana_storage_api::id());
|
||||||
|
let mut validator = StorageAccount::new(validator_pubkey, &mut validator_account);
|
||||||
|
validator
|
||||||
|
.initialize_validator_storage(validator_pubkey)
|
||||||
|
.unwrap();
|
||||||
|
let storage_contract = &mut validator_account.state().unwrap();
|
||||||
|
if let StorageContract::ValidatorStorage {
|
||||||
|
credits: account_credits,
|
||||||
|
..
|
||||||
|
} = storage_contract
|
||||||
|
{
|
||||||
|
account_credits.current_epoch = credits;
|
||||||
|
}
|
||||||
|
validator_account.set_state(storage_contract).unwrap();
|
||||||
|
|
||||||
|
let mut replicator_account =
|
||||||
|
Account::new(1, STORAGE_ACCOUNT_SPACE as usize, &solana_storage_api::id());
|
||||||
|
let mut replicator = StorageAccount::new(replicator_pubkey, &mut replicator_account);
|
||||||
|
replicator
|
||||||
|
.initialize_replicator_storage(replicator_pubkey)
|
||||||
|
.unwrap();
|
||||||
|
let storage_contract = &mut replicator_account.state().unwrap();
|
||||||
|
if let StorageContract::ReplicatorStorage {
|
||||||
|
credits: account_credits,
|
||||||
|
..
|
||||||
|
} = storage_contract
|
||||||
|
{
|
||||||
|
account_credits.current_epoch = credits;
|
||||||
|
}
|
||||||
|
replicator_account.set_state(storage_contract).unwrap();
|
||||||
|
|
||||||
|
(
|
||||||
|
(validator_pubkey, validator_account),
|
||||||
|
(replicator_pubkey, replicator_account),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,8 @@ pub struct Inflation {
|
|||||||
/// Duration of grant pool inflation, in years
|
/// Duration of grant pool inflation, in years
|
||||||
pub grant_term: f64,
|
pub grant_term: f64,
|
||||||
|
|
||||||
/// Percentage of total inflation allocated to replicator rewards
|
/// Percentage of total inflation allocated to storage rewards
|
||||||
pub replicator: f64,
|
pub storage: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_INITIAL: f64 = 0.15;
|
const DEFAULT_INITIAL: f64 = 0.15;
|
||||||
@ -32,7 +32,7 @@ const DEFAULT_TAPER: f64 = 0.15;
|
|||||||
const DEFAULT_FOUNDATION: f64 = 0.05;
|
const DEFAULT_FOUNDATION: f64 = 0.05;
|
||||||
const DEFAULT_GRANT: f64 = 0.05;
|
const DEFAULT_GRANT: f64 = 0.05;
|
||||||
const DEFAULT_FOUNDATION_GRANT_TERM: f64 = 7.0;
|
const DEFAULT_FOUNDATION_GRANT_TERM: f64 = 7.0;
|
||||||
const DEFAULT_REPLICATOR: f64 = 0.10;
|
const DEFAULT_STORAGE: f64 = 0.10;
|
||||||
|
|
||||||
impl Default for Inflation {
|
impl Default for Inflation {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@ -44,7 +44,7 @@ impl Default for Inflation {
|
|||||||
foundation_term: DEFAULT_FOUNDATION_GRANT_TERM,
|
foundation_term: DEFAULT_FOUNDATION_GRANT_TERM,
|
||||||
grant: DEFAULT_GRANT,
|
grant: DEFAULT_GRANT,
|
||||||
grant_term: DEFAULT_FOUNDATION_GRANT_TERM,
|
grant_term: DEFAULT_FOUNDATION_GRANT_TERM,
|
||||||
replicator: DEFAULT_REPLICATOR,
|
storage: DEFAULT_STORAGE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,12 +63,12 @@ impl Inflation {
|
|||||||
|
|
||||||
/// portion of total that goes to validators
|
/// portion of total that goes to validators
|
||||||
pub fn validator(&self, year: f64) -> f64 {
|
pub fn validator(&self, year: f64) -> f64 {
|
||||||
self.total(year) - self.replicator(year) - self.grant(year) - self.foundation(year)
|
self.total(year) - self.storage(year) - self.grant(year) - self.foundation(year)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// portion of total that goes to replicators
|
/// portion of total that goes to storage mining
|
||||||
pub fn replicator(&self, year: f64) -> f64 {
|
pub fn storage(&self, year: f64) -> f64 {
|
||||||
self.total(year) * self.replicator
|
self.total(year) * self.storage
|
||||||
}
|
}
|
||||||
|
|
||||||
/// portion of total that goes to grant pools
|
/// portion of total that goes to grant pools
|
||||||
@ -105,7 +105,7 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
total,
|
total,
|
||||||
inflation.validator(*year)
|
inflation.validator(*year)
|
||||||
+ inflation.replicator(*year)
|
+ inflation.storage(*year)
|
||||||
+ inflation.grant(*year)
|
+ inflation.grant(*year)
|
||||||
+ inflation.foundation(*year)
|
+ inflation.foundation(*year)
|
||||||
);
|
);
|
||||||
|
@ -16,7 +16,7 @@ crate::solana_name_id!(ID, "Sysca11Rewards11111111111111111111111111111");
|
|||||||
#[derive(Serialize, Deserialize, Debug, Default, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Default, PartialEq)]
|
||||||
pub struct Rewards {
|
pub struct Rewards {
|
||||||
pub validator_point_value: f64,
|
pub validator_point_value: f64,
|
||||||
pub replicator_point_value: f64,
|
pub storage_point_value: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rewards {
|
impl Rewards {
|
||||||
@ -34,13 +34,13 @@ impl Rewards {
|
|||||||
pub fn create_account(
|
pub fn create_account(
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
validator_point_value: f64,
|
validator_point_value: f64,
|
||||||
replicator_point_value: f64,
|
storage_point_value: f64,
|
||||||
) -> Account {
|
) -> Account {
|
||||||
Account::new_data(
|
Account::new_data(
|
||||||
lamports,
|
lamports,
|
||||||
&Rewards {
|
&Rewards {
|
||||||
validator_point_value,
|
validator_point_value,
|
||||||
replicator_point_value,
|
storage_point_value,
|
||||||
},
|
},
|
||||||
&syscall::id(),
|
&syscall::id(),
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user