@ -34,7 +34,6 @@ solana-metrics = { path = "../metrics", version = "1.2.0" }
|
||||
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.2.0" }
|
||||
solana-sdk = { path = "../sdk", version = "1.2.0" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.2.0" }
|
||||
solana-storage-program = { path = "../programs/storage", version = "1.2.0" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.2.0" }
|
||||
tempfile = "3.1.0"
|
||||
thiserror = "1.0"
|
||||
|
@ -429,10 +429,7 @@ impl Accounts {
|
||||
AccountAddressFilter::Exclude => !filter_by_address.contains(&pubkey),
|
||||
AccountAddressFilter::Include => filter_by_address.contains(&pubkey),
|
||||
};
|
||||
should_include_pubkey
|
||||
&& account.lamports != 0
|
||||
&& !(account.lamports == std::u64::MAX
|
||||
&& account.owner == solana_storage_program::id())
|
||||
should_include_pubkey && account.lamports != 0
|
||||
})
|
||||
.map(|(pubkey, account, _slot)| (*pubkey, account.lamports))
|
||||
{
|
||||
|
@ -19,8 +19,6 @@ use crate::{
|
||||
},
|
||||
stakes::Stakes,
|
||||
status_cache::{SlotDelta, StatusCache},
|
||||
storage_utils,
|
||||
storage_utils::StorageAccounts,
|
||||
system_instruction_processor::{self, get_system_account_kind, SystemAccountKind},
|
||||
transaction_batch::TransactionBatch,
|
||||
transaction_utils::OrderedIterator,
|
||||
@ -37,8 +35,8 @@ use solana_metrics::{
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
clock::{
|
||||
get_segment_from_slot, Epoch, Slot, SlotCount, SlotIndex, UnixTimestamp,
|
||||
DEFAULT_TICKS_PER_SECOND, MAX_PROCESSING_AGE, MAX_RECENT_BLOCKHASHES, SECONDS_PER_DAY,
|
||||
Epoch, Slot, SlotCount, SlotIndex, UnixTimestamp, DEFAULT_TICKS_PER_SECOND,
|
||||
MAX_PROCESSING_AGE, MAX_RECENT_BLOCKHASHES, SECONDS_PER_DAY,
|
||||
},
|
||||
epoch_schedule::EpochSchedule,
|
||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||
@ -237,6 +235,13 @@ impl HashAgeKind {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, PartialEq, Debug, Deserialize, Serialize)]
|
||||
struct UnusedAccounts {
|
||||
unused1: HashSet<Pubkey>,
|
||||
unused2: HashSet<Pubkey>,
|
||||
unused3: HashMap<Pubkey, u64>,
|
||||
}
|
||||
|
||||
/// Manager for the state of all accounts and programs after processing its entries.
|
||||
#[derive(Default, Deserialize, Serialize)]
|
||||
pub struct Bank {
|
||||
@ -303,8 +308,8 @@ pub struct Bank {
|
||||
/// The number of slots per year, used for inflation
|
||||
slots_per_year: f64,
|
||||
|
||||
/// The number of slots per Storage segment
|
||||
slots_per_segment: u64,
|
||||
/// Unused
|
||||
unused: u64,
|
||||
|
||||
/// Bank slot (i.e. block)
|
||||
slot: Slot,
|
||||
@ -346,8 +351,8 @@ pub struct Bank {
|
||||
/// cache of vote_account and stake_account state for this fork
|
||||
stakes: RwLock<Stakes>,
|
||||
|
||||
/// cache of validator and archiver storage accounts for this fork
|
||||
storage_accounts: RwLock<StorageAccounts>,
|
||||
/// unused
|
||||
unused_accounts: RwLock<UnusedAccounts>,
|
||||
|
||||
/// staked nodes on epoch boundaries, saved off when a bank.slot() is at
|
||||
/// a leader schedule calculation boundary
|
||||
@ -466,7 +471,7 @@ impl Bank {
|
||||
ticks_per_slot: parent.ticks_per_slot,
|
||||
ns_per_slot: parent.ns_per_slot,
|
||||
genesis_creation_time: parent.genesis_creation_time,
|
||||
slots_per_segment: parent.slots_per_segment,
|
||||
unused: parent.unused,
|
||||
slots_per_year: parent.slots_per_year,
|
||||
epoch_schedule,
|
||||
collected_rent: AtomicU64::new(0),
|
||||
@ -480,7 +485,7 @@ impl Bank {
|
||||
transaction_count: AtomicU64::new(parent.transaction_count()),
|
||||
stakes: RwLock::new(parent.stakes.read().unwrap().clone_with_epoch(epoch)),
|
||||
epoch_stakes: parent.epoch_stakes.clone(),
|
||||
storage_accounts: RwLock::new(parent.storage_accounts.read().unwrap().clone()),
|
||||
unused_accounts: RwLock::new(parent.unused_accounts.read().unwrap().clone()),
|
||||
parent_hash: parent.hash(),
|
||||
parent_slot: parent.slot(),
|
||||
collector_id: *collector_id,
|
||||
@ -595,7 +600,7 @@ impl Bank {
|
||||
pub fn clock(&self) -> sysvar::clock::Clock {
|
||||
sysvar::clock::Clock {
|
||||
slot: self.slot,
|
||||
segment: get_segment_from_slot(self.slot, self.slots_per_segment),
|
||||
unused: 0,
|
||||
epoch: self.epoch_schedule.get_epoch(self.slot),
|
||||
leader_schedule_epoch: self.epoch_schedule.get_leader_schedule_epoch(self.slot),
|
||||
unix_timestamp: self.unix_timestamp(),
|
||||
@ -716,35 +721,26 @@ impl Bank {
|
||||
// years_elapsed = slots_elapsed / slots/year
|
||||
let period = self.epoch_schedule.get_slots_in_epoch(epoch) as f64 / self.slots_per_year;
|
||||
|
||||
let (validator_rewards, storage_rewards) = {
|
||||
let validator_rewards = {
|
||||
let inflation = self.inflation.read().unwrap();
|
||||
|
||||
(
|
||||
(*inflation).validator(year) * self.capitalization() as f64 * period,
|
||||
(*inflation).storage(year) * self.capitalization() as f64 * period,
|
||||
)
|
||||
(*inflation).validator(year) * self.capitalization() as f64 * period
|
||||
};
|
||||
|
||||
let validator_points = self.stakes.write().unwrap().claim_points();
|
||||
let storage_points = self.storage_accounts.write().unwrap().claim_points();
|
||||
|
||||
let (validator_point_value, storage_point_value) = self.check_point_values(
|
||||
validator_rewards / validator_points as f64,
|
||||
storage_rewards / storage_points as f64,
|
||||
);
|
||||
let validator_point_value =
|
||||
self.check_point_value(validator_rewards / validator_points as f64);
|
||||
self.update_sysvar_account(&sysvar::rewards::id(), |account| {
|
||||
sysvar::rewards::create_account(
|
||||
self.inherit_sysvar_account_balance(account),
|
||||
validator_point_value,
|
||||
storage_point_value,
|
||||
)
|
||||
});
|
||||
|
||||
let validator_rewards = self.pay_validator_rewards(validator_point_value);
|
||||
self.capitalization.fetch_add(
|
||||
validator_rewards + storage_rewards as u64,
|
||||
Ordering::Relaxed,
|
||||
);
|
||||
|
||||
self.capitalization
|
||||
.fetch_add(validator_rewards as u64, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// iterate over all stakes, redeem vote credits for each stake we can
|
||||
@ -815,24 +811,17 @@ impl Bank {
|
||||
|
||||
// If the point values are not `normal`, bring them back into range and
|
||||
// set them to the last value or 0.
|
||||
fn check_point_values(
|
||||
&self,
|
||||
mut validator_point_value: f64,
|
||||
mut storage_point_value: f64,
|
||||
) -> (f64, f64) {
|
||||
fn check_point_value(&self, mut validator_point_value: f64) -> f64 {
|
||||
let rewards = sysvar::rewards::Rewards::from_account(
|
||||
&self
|
||||
.get_account(&sysvar::rewards::id())
|
||||
.unwrap_or_else(|| sysvar::rewards::create_account(1, 0.0, 0.0)),
|
||||
.unwrap_or_else(|| sysvar::rewards::create_account(1, 0.0)),
|
||||
)
|
||||
.unwrap_or_else(Default::default);
|
||||
if !validator_point_value.is_normal() {
|
||||
validator_point_value = rewards.validator_point_value;
|
||||
}
|
||||
if !storage_point_value.is_normal() {
|
||||
storage_point_value = rewards.storage_point_value
|
||||
}
|
||||
(validator_point_value, storage_point_value)
|
||||
validator_point_value
|
||||
}
|
||||
|
||||
fn collect_fees(&self) {
|
||||
@ -945,7 +934,7 @@ impl Bank {
|
||||
self.ns_per_slot = genesis_config.poh_config.target_tick_duration.as_nanos()
|
||||
* genesis_config.ticks_per_slot as u128;
|
||||
self.genesis_creation_time = genesis_config.creation_time;
|
||||
self.slots_per_segment = genesis_config.slots_per_segment;
|
||||
self.unused = genesis_config.unused;
|
||||
self.max_tick_height = (self.slot + 1) * self.ticks_per_slot;
|
||||
self.slots_per_year = years_as_slots(
|
||||
1.0,
|
||||
@ -2063,11 +2052,6 @@ impl Bank {
|
||||
|
||||
if Stakes::is_stake(account) {
|
||||
self.stakes.write().unwrap().store(pubkey, account);
|
||||
} else if storage_utils::is_storage(account) {
|
||||
self.storage_accounts
|
||||
.write()
|
||||
.unwrap()
|
||||
.store(pubkey, account);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2369,11 +2353,6 @@ impl Bank {
|
||||
self.slots_per_year
|
||||
}
|
||||
|
||||
/// Return the number of slots per segment
|
||||
pub fn slots_per_segment(&self) -> u64 {
|
||||
self.slots_per_segment
|
||||
}
|
||||
|
||||
/// Return the number of ticks since genesis.
|
||||
pub fn tick_height(&self) -> u64 {
|
||||
self.tick_height.load(Ordering::Relaxed)
|
||||
@ -2384,7 +2363,7 @@ impl Bank {
|
||||
*self.inflation.read().unwrap()
|
||||
}
|
||||
|
||||
/// Return the total capititalization of the Bank
|
||||
/// Return the total capitalization of the Bank
|
||||
pub fn capitalization(&self) -> u64 {
|
||||
self.capitalization.load(Ordering::Relaxed)
|
||||
}
|
||||
@ -2431,31 +2410,19 @@ impl Bank {
|
||||
let message = &tx.message();
|
||||
let acc = raccs.as_ref().unwrap();
|
||||
|
||||
for (pubkey, account) in
|
||||
message
|
||||
.account_keys
|
||||
.iter()
|
||||
.zip(acc.0.iter())
|
||||
.filter(|(_key, account)| {
|
||||
(Stakes::is_stake(account)) || storage_utils::is_storage(account)
|
||||
})
|
||||
for (pubkey, account) in message
|
||||
.account_keys
|
||||
.iter()
|
||||
.zip(acc.0.iter())
|
||||
.filter(|(_key, account)| (Stakes::is_stake(account)))
|
||||
{
|
||||
if Stakes::is_stake(account) {
|
||||
self.stakes.write().unwrap().store(pubkey, account);
|
||||
} else if storage_utils::is_storage(account) {
|
||||
self.storage_accounts
|
||||
.write()
|
||||
.unwrap()
|
||||
.store(pubkey, account);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn storage_accounts(&self) -> StorageAccounts {
|
||||
self.storage_accounts.read().unwrap().clone()
|
||||
}
|
||||
|
||||
/// current stake delegations for this bank
|
||||
/// Note: this method is exposed publicly for external usage
|
||||
pub fn stake_delegations(&self) -> HashMap<Pubkey, Delegation> {
|
||||
@ -2833,35 +2800,6 @@ mod tests {
|
||||
assert_eq!(bank1.capitalization(), 42 * 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bank_inflation() {
|
||||
let key = Pubkey::default();
|
||||
let bank = Arc::new(Bank::new(&GenesisConfig {
|
||||
accounts: (0..42)
|
||||
.into_iter()
|
||||
.map(|_| (Pubkey::new_rand(), Account::new(42, 0, &key)))
|
||||
.collect(),
|
||||
..GenesisConfig::default()
|
||||
}));
|
||||
assert_eq!(bank.capitalization(), 42 * 42);
|
||||
|
||||
// With inflation
|
||||
bank.set_entered_epoch_callback(Box::new(move |bank: &mut Bank| {
|
||||
let mut inflation = Inflation::default();
|
||||
inflation.initial = 1_000_000.0;
|
||||
bank.set_inflation(inflation)
|
||||
}));
|
||||
let bank1 = Bank::new_from_parent(&bank, &key, MINIMUM_SLOTS_PER_EPOCH + 1);
|
||||
assert_ne!(bank.capitalization(), bank1.capitalization());
|
||||
|
||||
// Without inflation
|
||||
bank.set_entered_epoch_callback(Box::new(move |bank: &mut Bank| {
|
||||
bank.set_inflation(Inflation::new_disabled())
|
||||
}));
|
||||
let bank2 = Bank::new_from_parent(&bank, &key, MINIMUM_SLOTS_PER_EPOCH * 2 + 1);
|
||||
assert_eq!(bank.capitalization(), bank2.capitalization());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_credit_debit_rent_no_side_effect_on_hash() {
|
||||
let (mut genesis_config, _mint_keypair) = create_genesis_config(10);
|
||||
@ -4158,13 +4096,8 @@ mod tests {
|
||||
let ((vote_id, mut vote_account), (stake_id, stake_account)) =
|
||||
crate::stakes::tests::create_staked_node_accounts(1_0000);
|
||||
|
||||
let ((validator_id, validator_account), (archiver_id, archiver_account)) =
|
||||
crate::storage_utils::tests::create_storage_accounts_with_credits(100);
|
||||
|
||||
// set up stakes, vote, and storage accounts
|
||||
// set up accounts
|
||||
bank.store_account(&stake_id, &stake_account);
|
||||
bank.store_account(&validator_id, &validator_account);
|
||||
bank.store_account(&archiver_id, &archiver_account);
|
||||
|
||||
// generate some rewards
|
||||
let mut vote_state = Some(VoteState::from(&vote_account).unwrap());
|
||||
@ -4185,7 +4118,6 @@ mod tests {
|
||||
bank.store_account(&vote_id, &vote_account);
|
||||
|
||||
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()...
|
||||
let bank1 = Bank::new_from_parent(
|
||||
@ -4215,15 +4147,11 @@ mod tests {
|
||||
|
||||
// verify the rewards are the right size
|
||||
assert!(
|
||||
((rewards.validator_point_value * validator_points as f64
|
||||
+ rewards.storage_point_value * storage_points as f64)
|
||||
- inflation as f64)
|
||||
.abs()
|
||||
((rewards.validator_point_value * validator_points as f64) - inflation as f64).abs()
|
||||
< 1.0 // rounding, truncating
|
||||
);
|
||||
|
||||
// verify validator rewards show up in bank1.rewards vector
|
||||
// (currently storage rewards will not show up)
|
||||
assert_eq!(
|
||||
bank1.rewards,
|
||||
Some(vec![(
|
||||
@ -5900,25 +5828,19 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_point_values() {
|
||||
fn test_check_point_value() {
|
||||
let (genesis_config, _) = create_genesis_config(500);
|
||||
let bank = Arc::new(Bank::new(&genesis_config));
|
||||
|
||||
// check that point values are 0 if no previous value was known and current values are not normal
|
||||
assert_eq!(
|
||||
bank.check_point_values(std::f64::INFINITY, std::f64::NAN),
|
||||
(0.0, 0.0)
|
||||
);
|
||||
assert_eq!(bank.check_point_value(std::f64::INFINITY), 0.0);
|
||||
|
||||
bank.store_account(
|
||||
&sysvar::rewards::id(),
|
||||
&sysvar::rewards::create_account(1, 1.0, 1.0),
|
||||
&sysvar::rewards::create_account(1, 1.0),
|
||||
);
|
||||
// check that point values are the previous value if current values are not normal
|
||||
assert_eq!(
|
||||
bank.check_point_values(std::f64::INFINITY, std::f64::NAN),
|
||||
(1.0, 1.0)
|
||||
);
|
||||
assert_eq!(bank.check_point_value(std::f64::INFINITY), 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -150,7 +150,6 @@ pub fn create_genesis_config_with_leader_ex(
|
||||
};
|
||||
|
||||
solana_stake_program::add_genesis_accounts(&mut genesis_config);
|
||||
solana_storage_program::rewards_pools::add_genesis_accounts(&mut genesis_config);
|
||||
|
||||
GenesisConfigInfo {
|
||||
genesis_config,
|
||||
|
@ -16,7 +16,6 @@ pub mod rent_collector;
|
||||
mod serde_utils;
|
||||
pub mod stakes;
|
||||
pub mod status_cache;
|
||||
pub mod storage_utils;
|
||||
mod system_instruction_processor;
|
||||
pub mod transaction_batch;
|
||||
pub mod transaction_utils;
|
||||
|
@ -1,248 +0,0 @@
|
||||
use crate::bank::Bank;
|
||||
use solana_sdk::{account::Account, account_utils::StateMut, pubkey::Pubkey};
|
||||
use solana_storage_program::storage_contract::StorageContract;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
#[derive(Default, Clone, PartialEq, Debug, Deserialize, Serialize)]
|
||||
pub struct StorageAccounts {
|
||||
/// validator storage accounts and their credits
|
||||
validator_accounts: HashSet<Pubkey>,
|
||||
|
||||
/// archiver storage accounts and their credits
|
||||
archiver_accounts: HashSet<Pubkey>,
|
||||
|
||||
/// unclaimed points.
|
||||
// 1 point == 1 storage account credit
|
||||
points: HashMap<Pubkey, u64>,
|
||||
}
|
||||
|
||||
pub fn is_storage(account: &Account) -> bool {
|
||||
solana_storage_program::check_id(&account.owner)
|
||||
}
|
||||
|
||||
impl StorageAccounts {
|
||||
pub fn store(&mut self, pubkey: &Pubkey, account: &Account) {
|
||||
if let Ok(storage_state) = account.state() {
|
||||
if let StorageContract::ArchiverStorage { credits, .. } = storage_state {
|
||||
if account.lamports == 0 {
|
||||
self.archiver_accounts.remove(pubkey);
|
||||
} else {
|
||||
self.archiver_accounts.insert(*pubkey);
|
||||
self.points.insert(*pubkey, credits.current_epoch);
|
||||
}
|
||||
} else if let StorageContract::ValidatorStorage { credits, .. } = storage_state {
|
||||
if account.lamports == 0 {
|
||||
self.validator_accounts.remove(pubkey);
|
||||
} else {
|
||||
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> {
|
||||
bank.storage_accounts()
|
||||
.validator_accounts
|
||||
.iter()
|
||||
.filter_map(|account_id| {
|
||||
bank.get_account(account_id)
|
||||
.map(|account| (*account_id, account))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn archiver_accounts(bank: &Bank) -> HashMap<Pubkey, Account> {
|
||||
bank.storage_accounts()
|
||||
.archiver_accounts
|
||||
.iter()
|
||||
.filter_map(|account_id| {
|
||||
bank.get_account(account_id)
|
||||
.map(|account| (*account_id, account))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::bank_client::BankClient;
|
||||
use solana_sdk::{
|
||||
client::SyncClient,
|
||||
genesis_config::create_genesis_config,
|
||||
message::Message,
|
||||
signature::{Keypair, Signer},
|
||||
};
|
||||
use solana_storage_program::{
|
||||
storage_contract::{StorageAccount, STORAGE_ACCOUNT_SPACE},
|
||||
storage_instruction::{self, StorageAccountType},
|
||||
storage_processor,
|
||||
};
|
||||
use std::{rc::Rc, sync::Arc};
|
||||
|
||||
#[test]
|
||||
fn test_store_and_recover() {
|
||||
let (mut genesis_config, mint_keypair) = create_genesis_config(1000);
|
||||
genesis_config.rent.lamports_per_byte_year = 0;
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let archiver_keypair = Keypair::new();
|
||||
let archiver_pubkey = archiver_keypair.pubkey();
|
||||
let validator_keypair = Keypair::new();
|
||||
let validator_pubkey = validator_keypair.pubkey();
|
||||
let mut bank = Bank::new(&genesis_config);
|
||||
bank.add_static_program(
|
||||
"storage_program",
|
||||
solana_storage_program::id(),
|
||||
storage_processor::process_instruction,
|
||||
);
|
||||
|
||||
let bank = Arc::new(bank);
|
||||
let bank_client = BankClient::new_shared(&bank);
|
||||
|
||||
let message = Message::new(&storage_instruction::create_storage_account(
|
||||
&mint_pubkey,
|
||||
&Pubkey::default(),
|
||||
&archiver_pubkey,
|
||||
11,
|
||||
StorageAccountType::Archiver,
|
||||
));
|
||||
bank_client
|
||||
.send_message(&[&mint_keypair, &archiver_keypair], message)
|
||||
.unwrap();
|
||||
|
||||
let message = Message::new(&storage_instruction::create_storage_account(
|
||||
&mint_pubkey,
|
||||
&Pubkey::default(),
|
||||
&validator_pubkey,
|
||||
11,
|
||||
StorageAccountType::Validator,
|
||||
));
|
||||
bank_client
|
||||
.send_message(&[&mint_keypair, &validator_keypair], message)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(validator_accounts(bank.as_ref()).len(), 1);
|
||||
assert_eq!(archiver_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 archiver accounts with `credits`
|
||||
let ((validator_pubkey, validator_account), (archiver_pubkey, archiver_account)) =
|
||||
create_storage_accounts_with_credits(credits);
|
||||
|
||||
storage_accounts.store(&validator_pubkey, &validator_account);
|
||||
storage_accounts.store(&archiver_pubkey, &archiver_account);
|
||||
// check that 2x credits worth of points are available
|
||||
assert_eq!(storage_accounts.points(), credits * 2);
|
||||
let ((validator_pubkey, validator_account), (archiver_pubkey, mut archiver_account)) =
|
||||
create_storage_accounts_with_credits(credits);
|
||||
|
||||
storage_accounts.store(&validator_pubkey, &validator_account);
|
||||
storage_accounts.store(&archiver_pubkey, &archiver_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(&archiver_pubkey, &archiver_account);
|
||||
// check that storing again has no effect
|
||||
assert_eq!(storage_accounts.points(), credits * 2 * 2);
|
||||
|
||||
let storage_contract = &mut archiver_account.state().unwrap();
|
||||
if let StorageContract::ArchiverStorage {
|
||||
credits: account_credits,
|
||||
..
|
||||
} = storage_contract
|
||||
{
|
||||
account_credits.current_epoch += 1;
|
||||
}
|
||||
archiver_account.set_state(storage_contract).unwrap();
|
||||
storage_accounts.store(&archiver_pubkey, &archiver_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 archiver_pubkey = Pubkey::new_rand();
|
||||
let validator_account = Account::new_ref(
|
||||
1,
|
||||
STORAGE_ACCOUNT_SPACE as usize,
|
||||
&solana_storage_program::id(),
|
||||
);
|
||||
let archiver_account = Account::new_ref(
|
||||
1,
|
||||
STORAGE_ACCOUNT_SPACE as usize,
|
||||
&solana_storage_program::id(),
|
||||
);
|
||||
{
|
||||
StorageAccount::new(validator_pubkey, &mut validator_account.borrow_mut())
|
||||
.initialize_storage(validator_pubkey, StorageAccountType::Validator)
|
||||
.unwrap();
|
||||
let storage_contract = &mut validator_account.borrow().state().unwrap();
|
||||
if let StorageContract::ValidatorStorage {
|
||||
credits: account_credits,
|
||||
..
|
||||
} = storage_contract
|
||||
{
|
||||
account_credits.current_epoch = credits;
|
||||
}
|
||||
validator_account
|
||||
.borrow_mut()
|
||||
.set_state(storage_contract)
|
||||
.unwrap();
|
||||
|
||||
StorageAccount::new(archiver_pubkey, &mut archiver_account.borrow_mut())
|
||||
.initialize_storage(archiver_pubkey, StorageAccountType::Archiver)
|
||||
.unwrap();
|
||||
let storage_contract = &mut archiver_account.borrow().state().unwrap();
|
||||
if let StorageContract::ArchiverStorage {
|
||||
credits: account_credits,
|
||||
..
|
||||
} = storage_contract
|
||||
{
|
||||
account_credits.current_epoch = credits;
|
||||
}
|
||||
archiver_account
|
||||
.borrow_mut()
|
||||
.set_state(storage_contract)
|
||||
.unwrap();
|
||||
}
|
||||
(
|
||||
(
|
||||
validator_pubkey,
|
||||
Rc::try_unwrap(validator_account).unwrap().into_inner(),
|
||||
),
|
||||
(
|
||||
archiver_pubkey,
|
||||
Rc::try_unwrap(archiver_account).unwrap().into_inner(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
@ -1,482 +0,0 @@
|
||||
use assert_matches::assert_matches;
|
||||
use bincode::deserialize;
|
||||
use log::*;
|
||||
use solana_runtime::{
|
||||
bank::Bank,
|
||||
bank_client::BankClient,
|
||||
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||
};
|
||||
use solana_sdk::{
|
||||
account_utils::StateMut,
|
||||
client::SyncClient,
|
||||
clock::{get_segment_from_slot, DEFAULT_SLOTS_PER_SEGMENT, DEFAULT_TICKS_PER_SLOT},
|
||||
hash::{hash, Hash},
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signature, Signer},
|
||||
system_instruction,
|
||||
sysvar::{
|
||||
rewards::{self, Rewards},
|
||||
Sysvar,
|
||||
},
|
||||
};
|
||||
use solana_storage_program::{
|
||||
id,
|
||||
storage_contract::{ProofStatus, StorageContract},
|
||||
storage_instruction::{self, StorageAccountType},
|
||||
storage_processor::process_instruction,
|
||||
};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
const TICKS_IN_SEGMENT: u64 = DEFAULT_SLOTS_PER_SEGMENT * DEFAULT_TICKS_PER_SLOT;
|
||||
|
||||
#[test]
|
||||
fn test_account_owner() {
|
||||
let account_owner = Pubkey::new_rand();
|
||||
let validator_storage_keypair = Keypair::new();
|
||||
let validator_storage_pubkey = validator_storage_keypair.pubkey();
|
||||
let archiver_storage_keypair = Keypair::new();
|
||||
let archiver_storage_pubkey = archiver_storage_keypair.pubkey();
|
||||
|
||||
let GenesisConfigInfo {
|
||||
genesis_config,
|
||||
mint_keypair,
|
||||
..
|
||||
} = create_genesis_config(1000);
|
||||
let mut bank = Bank::new(&genesis_config);
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
bank.add_static_program("storage_program", id(), process_instruction);
|
||||
let bank = Arc::new(bank);
|
||||
let bank_client = BankClient::new_shared(&bank);
|
||||
|
||||
let message = Message::new(&storage_instruction::create_storage_account(
|
||||
&mint_pubkey,
|
||||
&account_owner,
|
||||
&validator_storage_pubkey,
|
||||
1,
|
||||
StorageAccountType::Validator,
|
||||
));
|
||||
bank_client
|
||||
.send_message(&[&mint_keypair, &validator_storage_keypair], message)
|
||||
.expect("failed to create account");
|
||||
let account = bank
|
||||
.get_account(&validator_storage_pubkey)
|
||||
.expect("account not found");
|
||||
let storage_contract = account.state().expect("couldn't unpack account data");
|
||||
if let StorageContract::ValidatorStorage { owner, .. } = storage_contract {
|
||||
assert_eq!(owner, account_owner);
|
||||
} else {
|
||||
assert!(false, "wrong account type found")
|
||||
}
|
||||
|
||||
let message = Message::new(&storage_instruction::create_storage_account(
|
||||
&mint_pubkey,
|
||||
&account_owner,
|
||||
&archiver_storage_pubkey,
|
||||
1,
|
||||
StorageAccountType::Archiver,
|
||||
));
|
||||
bank_client
|
||||
.send_message(&[&mint_keypair, &archiver_storage_keypair], message)
|
||||
.expect("failed to create account");
|
||||
let account = bank
|
||||
.get_account(&archiver_storage_pubkey)
|
||||
.expect("account not found");
|
||||
let storage_contract = account.state().expect("couldn't unpack account data");
|
||||
if let StorageContract::ArchiverStorage { owner, .. } = storage_contract {
|
||||
assert_eq!(owner, account_owner);
|
||||
} else {
|
||||
assert!(false, "wrong account type found")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_mining() {
|
||||
solana_logger::setup();
|
||||
let GenesisConfigInfo {
|
||||
mut genesis_config,
|
||||
mint_keypair,
|
||||
..
|
||||
} = create_genesis_config(100_000_000_000);
|
||||
genesis_config
|
||||
.native_instruction_processors
|
||||
.push(solana_storage_program::solana_storage_program!());
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
// 1 owner for all archiver and validator accounts for the test
|
||||
let owner_pubkey = Pubkey::new_rand();
|
||||
|
||||
let archiver_1_storage_keypair = Keypair::new();
|
||||
let archiver_1_storage_id = archiver_1_storage_keypair.pubkey();
|
||||
|
||||
let archiver_2_storage_keypair = Keypair::new();
|
||||
let archiver_2_storage_id = archiver_2_storage_keypair.pubkey();
|
||||
|
||||
let validator_storage_keypair = Keypair::new();
|
||||
let validator_storage_id = validator_storage_keypair.pubkey();
|
||||
|
||||
let bank = Bank::new(&genesis_config);
|
||||
let bank = Arc::new(bank);
|
||||
let bank_client = BankClient::new_shared(&bank);
|
||||
|
||||
init_storage_accounts(
|
||||
&owner_pubkey,
|
||||
&bank_client,
|
||||
&mint_keypair,
|
||||
&[&validator_storage_keypair],
|
||||
&[&archiver_1_storage_keypair, &archiver_2_storage_keypair],
|
||||
10,
|
||||
);
|
||||
|
||||
// create a new bank in segment 2
|
||||
let bank = Arc::new(Bank::new_from_parent(
|
||||
&bank,
|
||||
&Pubkey::default(),
|
||||
DEFAULT_SLOTS_PER_SEGMENT * 2,
|
||||
));
|
||||
let bank_client = BankClient::new_shared(&bank);
|
||||
|
||||
// advertise for storage segment 1
|
||||
let message = Message::new_with_payer(
|
||||
&[storage_instruction::advertise_recent_blockhash(
|
||||
&validator_storage_id,
|
||||
Hash::default(),
|
||||
1,
|
||||
)],
|
||||
Some(&mint_pubkey),
|
||||
);
|
||||
assert_matches!(
|
||||
bank_client.send_message(&[&mint_keypair, &validator_storage_keypair], message),
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
// submit proofs 5 proofs for each archiver for segment 0
|
||||
let mut checked_proofs: HashMap<_, Vec<_>> = HashMap::new();
|
||||
for _ in 0..5 {
|
||||
checked_proofs
|
||||
.entry(archiver_1_storage_id)
|
||||
.or_default()
|
||||
.push(submit_proof(
|
||||
&mint_keypair,
|
||||
&archiver_1_storage_keypair,
|
||||
&bank_client,
|
||||
0,
|
||||
));
|
||||
checked_proofs
|
||||
.entry(archiver_2_storage_id)
|
||||
.or_default()
|
||||
.push(submit_proof(
|
||||
&mint_keypair,
|
||||
&archiver_2_storage_keypair,
|
||||
&bank_client,
|
||||
0,
|
||||
));
|
||||
}
|
||||
let message = Message::new_with_payer(
|
||||
&[storage_instruction::advertise_recent_blockhash(
|
||||
&validator_storage_id,
|
||||
Hash::default(),
|
||||
2,
|
||||
)],
|
||||
Some(&mint_pubkey),
|
||||
);
|
||||
|
||||
// move banks into the next segment
|
||||
let proof_segment = get_segment_from_slot(bank.slot(), bank.slots_per_segment());
|
||||
let bank = Arc::new(Bank::new_from_parent(
|
||||
&bank,
|
||||
&Pubkey::default(),
|
||||
DEFAULT_SLOTS_PER_SEGMENT + bank.slot(),
|
||||
));
|
||||
let bank_client = BankClient::new_shared(&bank);
|
||||
|
||||
assert_matches!(
|
||||
bank_client.send_message(&[&mint_keypair, &validator_storage_keypair], message),
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
let message = Message::new_with_payer(
|
||||
&[storage_instruction::proof_validation(
|
||||
&validator_storage_id,
|
||||
proof_segment as u64,
|
||||
checked_proofs.into_iter().map(|entry| entry).collect(),
|
||||
)],
|
||||
Some(&mint_pubkey),
|
||||
);
|
||||
|
||||
assert_matches!(
|
||||
bank_client.send_message(&[&mint_keypair, &validator_storage_keypair], message),
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
let message = Message::new_with_payer(
|
||||
&[storage_instruction::advertise_recent_blockhash(
|
||||
&validator_storage_id,
|
||||
Hash::default(),
|
||||
3,
|
||||
)],
|
||||
Some(&mint_pubkey),
|
||||
);
|
||||
|
||||
// move banks into the next segment
|
||||
let bank = Arc::new(Bank::new_from_parent(
|
||||
&bank,
|
||||
&Pubkey::default(),
|
||||
DEFAULT_SLOTS_PER_SEGMENT + bank.slot(),
|
||||
));
|
||||
let bank_client = BankClient::new_shared(&bank);
|
||||
|
||||
assert_matches!(
|
||||
bank_client.send_message(&[&mint_keypair, &validator_storage_keypair], message),
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
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(&account).unwrap())
|
||||
.unwrap();
|
||||
let message = Message::new_with_payer(
|
||||
&[storage_instruction::claim_reward(
|
||||
&owner_pubkey,
|
||||
&validator_storage_id,
|
||||
)],
|
||||
Some(&mint_pubkey),
|
||||
);
|
||||
assert_matches!(bank_client.send_message(&[&mint_keypair], message), Ok(_));
|
||||
assert_eq!(
|
||||
bank_client.get_balance(&owner_pubkey).unwrap(),
|
||||
1 + ((rewards.storage_point_value * 10_f64) as u64)
|
||||
);
|
||||
|
||||
// tick the bank into the next storage epoch so that rewards can be claimed
|
||||
for _ in 0..=TICKS_IN_SEGMENT {
|
||||
bank.register_tick(&bank.last_blockhash());
|
||||
}
|
||||
|
||||
assert_eq!(bank_client.get_balance(&archiver_1_storage_id).unwrap(), 10);
|
||||
|
||||
let message = Message::new_with_payer(
|
||||
&[storage_instruction::claim_reward(
|
||||
&owner_pubkey,
|
||||
&archiver_1_storage_id,
|
||||
)],
|
||||
Some(&mint_pubkey),
|
||||
);
|
||||
assert_matches!(bank_client.send_message(&[&mint_keypair], message), Ok(_));
|
||||
assert_eq!(
|
||||
bank_client.get_balance(&owner_pubkey).unwrap(),
|
||||
1 + ((rewards.storage_point_value * 10_f64) as u64)
|
||||
+ (rewards.storage_point_value * 5_f64) as u64
|
||||
);
|
||||
|
||||
let message = Message::new_with_payer(
|
||||
&[storage_instruction::claim_reward(
|
||||
&owner_pubkey,
|
||||
&archiver_2_storage_id,
|
||||
)],
|
||||
Some(&mint_pubkey),
|
||||
);
|
||||
assert_matches!(bank_client.send_message(&[&mint_keypair], message), Ok(_));
|
||||
assert_eq!(
|
||||
bank_client.get_balance(&owner_pubkey).unwrap(),
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
fn init_storage_accounts(
|
||||
owner: &Pubkey,
|
||||
client: &BankClient,
|
||||
mint: &Keypair,
|
||||
validator_accounts_to_create: &[&Keypair],
|
||||
archiver_accounts_to_create: &[&Keypair],
|
||||
lamports: u64,
|
||||
) {
|
||||
let mut signers = vec![mint];
|
||||
let mut ixs: Vec<_> = vec![system_instruction::transfer(&mint.pubkey(), owner, 1)];
|
||||
ixs.append(
|
||||
&mut validator_accounts_to_create
|
||||
.into_iter()
|
||||
.flat_map(|account| {
|
||||
signers.push(&account);
|
||||
storage_instruction::create_storage_account(
|
||||
&mint.pubkey(),
|
||||
owner,
|
||||
&account.pubkey(),
|
||||
lamports,
|
||||
StorageAccountType::Validator,
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
archiver_accounts_to_create.into_iter().for_each(|account| {
|
||||
signers.push(&account);
|
||||
ixs.append(&mut storage_instruction::create_storage_account(
|
||||
&mint.pubkey(),
|
||||
owner,
|
||||
&account.pubkey(),
|
||||
lamports,
|
||||
StorageAccountType::Archiver,
|
||||
))
|
||||
});
|
||||
let message = Message::new(&ixs);
|
||||
client.send_message(&signers, message).unwrap();
|
||||
}
|
||||
|
||||
fn get_storage_segment<C: SyncClient>(client: &C, account: &Pubkey) -> u64 {
|
||||
match client.get_account_data(&account).unwrap() {
|
||||
Some(storage_system_account_data) => {
|
||||
let contract = deserialize(&storage_system_account_data);
|
||||
if let Ok(contract) = contract {
|
||||
match contract {
|
||||
StorageContract::ValidatorStorage { segment, .. } => {
|
||||
return segment;
|
||||
}
|
||||
_ => info!("error in reading segment"),
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
info!("error in reading segment");
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
fn submit_proof(
|
||||
mint_keypair: &Keypair,
|
||||
storage_keypair: &Keypair,
|
||||
bank_client: &BankClient,
|
||||
segment_index: u64,
|
||||
) -> ProofStatus {
|
||||
let sha_state = Hash::new(Pubkey::new_rand().as_ref());
|
||||
let message = Message::new_with_payer(
|
||||
&[storage_instruction::mining_proof(
|
||||
&storage_keypair.pubkey(),
|
||||
sha_state,
|
||||
segment_index,
|
||||
Signature::default(),
|
||||
bank_client.get_recent_blockhash().unwrap().0,
|
||||
)],
|
||||
Some(&mint_keypair.pubkey()),
|
||||
);
|
||||
|
||||
assert_matches!(
|
||||
bank_client.send_message(&[mint_keypair, storage_keypair], message),
|
||||
Ok(_)
|
||||
);
|
||||
ProofStatus::Valid
|
||||
}
|
||||
|
||||
fn get_storage_blockhash<C: SyncClient>(client: &C, account: &Pubkey) -> Hash {
|
||||
if let Some(storage_system_account_data) = client.get_account_data(&account).unwrap() {
|
||||
let contract = deserialize(&storage_system_account_data);
|
||||
if let Ok(contract) = contract {
|
||||
match contract {
|
||||
StorageContract::ValidatorStorage { hash, .. } => {
|
||||
return hash;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
Hash::default()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bank_storage() {
|
||||
let GenesisConfigInfo {
|
||||
mut genesis_config,
|
||||
mint_keypair,
|
||||
..
|
||||
} = create_genesis_config(1000);
|
||||
genesis_config
|
||||
.native_instruction_processors
|
||||
.push(solana_storage_program::solana_storage_program!());
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let archiver_keypair = Keypair::new();
|
||||
let archiver_pubkey = archiver_keypair.pubkey();
|
||||
let validator_keypair = Keypair::new();
|
||||
let validator_pubkey = validator_keypair.pubkey();
|
||||
|
||||
let bank = Bank::new(&genesis_config);
|
||||
// tick the bank up until it's moved into storage segment 2
|
||||
// create a new bank in storage segment 2
|
||||
let bank = Bank::new_from_parent(
|
||||
&Arc::new(bank),
|
||||
&Pubkey::new_rand(),
|
||||
DEFAULT_SLOTS_PER_SEGMENT * 2,
|
||||
);
|
||||
let bank_client = BankClient::new(bank);
|
||||
|
||||
let x = 42;
|
||||
let x2 = x * 2;
|
||||
let storage_blockhash = hash(&[x2]);
|
||||
|
||||
let message = Message::new(&storage_instruction::create_storage_account(
|
||||
&mint_pubkey,
|
||||
&Pubkey::default(),
|
||||
&archiver_pubkey,
|
||||
11,
|
||||
StorageAccountType::Archiver,
|
||||
));
|
||||
bank_client
|
||||
.send_message(&[&mint_keypair, &archiver_keypair], message)
|
||||
.unwrap();
|
||||
|
||||
let message = Message::new(&storage_instruction::create_storage_account(
|
||||
&mint_pubkey,
|
||||
&Pubkey::default(),
|
||||
&validator_pubkey,
|
||||
1,
|
||||
StorageAccountType::Validator,
|
||||
));
|
||||
bank_client
|
||||
.send_message(&[&mint_keypair, &validator_keypair], message)
|
||||
.unwrap();
|
||||
|
||||
let message = Message::new_with_payer(
|
||||
&[storage_instruction::advertise_recent_blockhash(
|
||||
&validator_pubkey,
|
||||
storage_blockhash,
|
||||
1,
|
||||
)],
|
||||
Some(&mint_pubkey),
|
||||
);
|
||||
|
||||
assert_matches!(
|
||||
bank_client.send_message(&[&mint_keypair, &validator_keypair], message),
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
let slot = 0;
|
||||
let message = Message::new_with_payer(
|
||||
&[storage_instruction::mining_proof(
|
||||
&archiver_pubkey,
|
||||
Hash::default(),
|
||||
slot,
|
||||
Signature::default(),
|
||||
bank_client.get_recent_blockhash().unwrap().0,
|
||||
)],
|
||||
Some(&mint_pubkey),
|
||||
);
|
||||
assert_matches!(
|
||||
bank_client.send_message(&[&mint_keypair, &archiver_keypair], message),
|
||||
Ok(_)
|
||||
);
|
||||
|
||||
assert_eq!(get_storage_segment(&bank_client, &validator_pubkey), 1);
|
||||
assert_eq!(
|
||||
get_storage_blockhash(&bank_client, &validator_pubkey),
|
||||
storage_blockhash
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user