@ -12,6 +12,7 @@ edition = "2018"
|
||||
assert_matches = "1.3.0"
|
||||
bincode = "1.1.4"
|
||||
log = "0.4.2"
|
||||
rand = "0.6.5"
|
||||
num-derive = "0.2"
|
||||
num-traits = "0.2"
|
||||
serde = "1.0.92"
|
||||
|
@ -1,3 +1,4 @@
|
||||
pub mod rewards_pools;
|
||||
pub mod storage_contract;
|
||||
pub mod storage_instruction;
|
||||
pub mod storage_processor;
|
||||
|
63
programs/storage_api/src/rewards_pools.rs
Normal file
63
programs/storage_api/src/rewards_pools.rs
Normal file
@ -0,0 +1,63 @@
|
||||
//! rewards_pools
|
||||
//! * initialize genesis with rewards pools
|
||||
//! * keep track of rewards
|
||||
//! * own mining pools
|
||||
|
||||
use crate::storage_contract::create_rewards_pool;
|
||||
use rand::{thread_rng, Rng};
|
||||
use solana_sdk::genesis_block::Builder;
|
||||
use solana_sdk::hash::{hash, Hash};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
// base rewards pool ID
|
||||
const ID: [u8; 32] = [
|
||||
6, 162, 25, 123, 127, 71, 141, 232, 129, 171, 58, 183, 79, 88, 181, 17, 163, 11, 51, 111, 22,
|
||||
123, 67, 115, 5, 131, 109, 161, 16, 0, 0, 0,
|
||||
];
|
||||
|
||||
solana_sdk::solana_name_id!(ID, "StorageMiningPoo111111111111111111111111111");
|
||||
|
||||
// to cut down on collisions for redemptions, we make multiple accounts
|
||||
pub const NUM_REWARDS_POOLS: usize = 32;
|
||||
|
||||
pub fn genesis(mut builder: Builder) -> Builder {
|
||||
let mut pubkey = id();
|
||||
|
||||
for _i in 0..NUM_REWARDS_POOLS {
|
||||
builder = builder.rewards_pool(pubkey, create_rewards_pool());
|
||||
pubkey = Pubkey::new(hash(pubkey.as_ref()).as_ref());
|
||||
}
|
||||
builder
|
||||
}
|
||||
|
||||
pub fn random_id() -> Pubkey {
|
||||
let mut id = Hash::new(&ID);
|
||||
|
||||
for _i in 0..thread_rng().gen_range(0, NUM_REWARDS_POOLS) {
|
||||
id = hash(id.as_ref());
|
||||
}
|
||||
|
||||
Pubkey::new(id.as_ref())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use solana_sdk::genesis_block::Builder;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let builder = Builder::new();
|
||||
|
||||
let genesis_block = genesis(builder).build();
|
||||
|
||||
for _i in 0..NUM_REWARDS_POOLS {
|
||||
let id = random_id();
|
||||
assert!(genesis_block
|
||||
.rewards_pools
|
||||
.iter()
|
||||
.position(|x| x.0 == id)
|
||||
.is_some());
|
||||
}
|
||||
}
|
||||
}
|
@ -79,7 +79,7 @@ pub enum StorageContract {
|
||||
reward_validations: BTreeMap<usize, BTreeMap<Pubkey, Vec<ProofStatus>>>,
|
||||
},
|
||||
|
||||
MiningPool,
|
||||
RewardsPool,
|
||||
}
|
||||
|
||||
// utility function, used by Bank, tests, genesis
|
||||
@ -99,17 +99,6 @@ pub fn create_validator_storage_account(owner: Pubkey, lamports: u64) -> Account
|
||||
storage_account
|
||||
}
|
||||
|
||||
// utility function, used by genesis
|
||||
pub fn create_mining_pool_account(lamports: u64) -> Account {
|
||||
let mut storage_account = Account::new(lamports, STORAGE_ACCOUNT_SPACE as usize, &crate::id());
|
||||
|
||||
storage_account
|
||||
.set_state(&StorageContract::MiningPool)
|
||||
.expect("set_state");
|
||||
|
||||
storage_account
|
||||
}
|
||||
|
||||
pub struct StorageAccount<'a> {
|
||||
pub(crate) id: Pubkey,
|
||||
account: &'a mut Account,
|
||||
@ -120,16 +109,6 @@ impl<'a> StorageAccount<'a> {
|
||||
Self { id, account }
|
||||
}
|
||||
|
||||
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, owner: Pubkey) -> Result<(), InstructionError> {
|
||||
let storage_contract = &mut self.account.state()?;
|
||||
if let StorageContract::Uninitialized = storage_contract {
|
||||
@ -379,7 +358,7 @@ impl<'a> StorageAccount<'a> {
|
||||
|
||||
pub fn claim_storage_reward(
|
||||
&mut self,
|
||||
mining_pool: &mut KeyedAccount,
|
||||
rewards_pool: &mut KeyedAccount,
|
||||
owner: &mut StorageAccount,
|
||||
) -> Result<(), InstructionError> {
|
||||
let mut storage_contract = &mut self.account.state()?;
|
||||
@ -397,12 +376,13 @@ impl<'a> StorageAccount<'a> {
|
||||
}
|
||||
|
||||
let pending = *pending_lamports;
|
||||
if mining_pool.account.lamports < pending {
|
||||
if rewards_pool.account.lamports < pending {
|
||||
println!("reward pool has {}", rewards_pool.account.lamports);
|
||||
Err(InstructionError::CustomError(
|
||||
StorageError::RewardPoolDepleted as u32,
|
||||
))?
|
||||
}
|
||||
mining_pool.account.lamports -= pending;
|
||||
rewards_pool.account.lamports -= pending;
|
||||
owner.account.lamports += pending;
|
||||
//clear pending_lamports
|
||||
*pending_lamports = 0;
|
||||
@ -432,7 +412,7 @@ impl<'a> StorageAccount<'a> {
|
||||
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);
|
||||
mining_pool.account.lamports -= reward;
|
||||
rewards_pool.account.lamports -= reward;
|
||||
owner.account.lamports += reward;
|
||||
self.account.set_state(storage_contract)
|
||||
} else {
|
||||
@ -441,6 +421,10 @@ impl<'a> StorageAccount<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_rewards_pool() -> Account {
|
||||
Account::new_data(std::u64::MAX, &StorageContract::RewardsPool, &crate::id()).unwrap()
|
||||
}
|
||||
|
||||
/// Store the result of a proof validation into the replicator account
|
||||
fn store_validation_result(
|
||||
me: &Pubkey,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::id;
|
||||
use crate::storage_contract::{ProofStatus, STORAGE_ACCOUNT_SPACE};
|
||||
use crate::{id, rewards_pools};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::instruction::{AccountMeta, Instruction};
|
||||
@ -10,11 +10,10 @@ use solana_sdk::system_instruction;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub enum StorageInstruction {
|
||||
/// Initialize the account as a mining pool, validator or replicator
|
||||
/// Initialize the account as a validator or replicator
|
||||
///
|
||||
/// Expects 1 Account:
|
||||
/// 0 - Account to be initialized
|
||||
InitializeMiningPool,
|
||||
InitializeValidatorStorage {
|
||||
owner: Pubkey,
|
||||
},
|
||||
@ -128,27 +127,6 @@ pub fn create_replicator_storage_account(
|
||||
]
|
||||
}
|
||||
|
||||
pub fn create_mining_pool_account(
|
||||
from_pubkey: &Pubkey,
|
||||
storage_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
) -> Vec<Instruction> {
|
||||
vec![
|
||||
system_instruction::create_account(
|
||||
from_pubkey,
|
||||
storage_pubkey,
|
||||
lamports,
|
||||
STORAGE_ACCOUNT_SPACE,
|
||||
&id(),
|
||||
),
|
||||
Instruction::new(
|
||||
id(),
|
||||
&StorageInstruction::InitializeMiningPool,
|
||||
vec![AccountMeta::new(*storage_pubkey, false)],
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn mining_proof(
|
||||
storage_pubkey: &Pubkey,
|
||||
sha_state: Hash,
|
||||
@ -200,15 +178,11 @@ pub fn proof_validation(
|
||||
Instruction::new(id(), &storage_instruction, account_metas)
|
||||
}
|
||||
|
||||
pub fn claim_reward(
|
||||
owner_pubkey: &Pubkey,
|
||||
storage_pubkey: &Pubkey,
|
||||
mining_pool_pubkey: &Pubkey,
|
||||
) -> Instruction {
|
||||
pub fn claim_reward(owner_pubkey: &Pubkey, storage_pubkey: &Pubkey) -> Instruction {
|
||||
let storage_instruction = StorageInstruction::ClaimStorageReward;
|
||||
let account_metas = vec![
|
||||
AccountMeta::new(*storage_pubkey, false),
|
||||
AccountMeta::new(*mining_pool_pubkey, false),
|
||||
AccountMeta::new(rewards_pools::random_id(), false),
|
||||
AccountMeta::new(*owner_pubkey, false),
|
||||
];
|
||||
Instruction::new(id(), &storage_instruction, account_metas)
|
||||
|
@ -20,12 +20,6 @@ pub fn process_instruction(
|
||||
let mut storage_account = StorageAccount::new(*me[0].unsigned_key(), &mut me[0].account);
|
||||
|
||||
match bincode::deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
|
||||
StorageInstruction::InitializeMiningPool => {
|
||||
if !rest.is_empty() {
|
||||
Err(InstructionError::InvalidArgument)?;
|
||||
}
|
||||
storage_account.initialize_mining_pool()
|
||||
}
|
||||
StorageInstruction::InitializeReplicatorStorage { owner } => {
|
||||
if !rest.is_empty() {
|
||||
Err(InstructionError::InvalidArgument)?;
|
||||
|
@ -3,10 +3,10 @@ use bincode::deserialize;
|
||||
use log::*;
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_runtime::bank_client::BankClient;
|
||||
use solana_runtime::genesis_utils::{create_genesis_block, GenesisBlockInfo};
|
||||
use solana_sdk::account::{create_keyed_accounts, Account, KeyedAccount};
|
||||
use solana_sdk::account_utils::State;
|
||||
use solana_sdk::client::SyncClient;
|
||||
use solana_sdk::genesis_block::create_genesis_block;
|
||||
use solana_sdk::hash::{hash, Hash};
|
||||
use solana_sdk::instruction::{Instruction, InstructionError};
|
||||
use solana_sdk::message::Message;
|
||||
@ -53,7 +53,11 @@ fn test_account_owner() {
|
||||
let validator_storage_pubkey = Pubkey::new_rand();
|
||||
let replicator_storage_pubkey = Pubkey::new_rand();
|
||||
|
||||
let (genesis_block, mint_keypair) = create_genesis_block(1000);
|
||||
let GenesisBlockInfo {
|
||||
genesis_block,
|
||||
mint_keypair,
|
||||
..
|
||||
} = create_genesis_block(1000);
|
||||
let mut bank = Bank::new(&genesis_block);
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
bank.add_instruction_processor(id(), process_instruction);
|
||||
@ -259,7 +263,11 @@ fn test_submit_mining_ok() {
|
||||
#[test]
|
||||
fn test_validate_mining() {
|
||||
solana_logger::setup();
|
||||
let (mut genesis_block, mint_keypair) = create_genesis_block(100_000);
|
||||
let GenesisBlockInfo {
|
||||
mut genesis_block,
|
||||
mint_keypair,
|
||||
..
|
||||
} = create_genesis_block(100_000);
|
||||
genesis_block
|
||||
.native_instruction_processors
|
||||
.push(solana_storage_program::solana_storage_program!());
|
||||
@ -276,9 +284,6 @@ fn test_validate_mining() {
|
||||
let validator_storage_keypair = Keypair::new();
|
||||
let validator_storage_id = validator_storage_keypair.pubkey();
|
||||
|
||||
let mining_pool_keypair = Keypair::new();
|
||||
let mining_pool_pubkey = mining_pool_keypair.pubkey();
|
||||
|
||||
let bank = Bank::new(&genesis_block);
|
||||
let bank = Arc::new(bank);
|
||||
let bank_client = BankClient::new_shared(&bank);
|
||||
@ -291,12 +296,6 @@ fn test_validate_mining() {
|
||||
&[&replicator_1_storage_id, &replicator_2_storage_id],
|
||||
10,
|
||||
);
|
||||
let message = Message::new(storage_instruction::create_mining_pool_account(
|
||||
&mint_pubkey,
|
||||
&mining_pool_pubkey,
|
||||
10_000,
|
||||
));
|
||||
bank_client.send_message(&[&mint_keypair], message).unwrap();
|
||||
|
||||
// create a new bank in segment 2
|
||||
let bank = Arc::new(Bank::new_from_parent(
|
||||
@ -407,7 +406,6 @@ fn test_validate_mining() {
|
||||
vec![storage_instruction::claim_reward(
|
||||
&owner_pubkey,
|
||||
&validator_storage_id,
|
||||
&mining_pool_pubkey,
|
||||
)],
|
||||
Some(&mint_pubkey),
|
||||
);
|
||||
@ -431,7 +429,6 @@ fn test_validate_mining() {
|
||||
vec![storage_instruction::claim_reward(
|
||||
&owner_pubkey,
|
||||
&replicator_1_storage_id,
|
||||
&mining_pool_pubkey,
|
||||
)],
|
||||
Some(&mint_pubkey),
|
||||
);
|
||||
@ -447,7 +444,6 @@ fn test_validate_mining() {
|
||||
vec![storage_instruction::claim_reward(
|
||||
&owner_pubkey,
|
||||
&replicator_2_storage_id,
|
||||
&mining_pool_pubkey,
|
||||
)],
|
||||
Some(&mint_pubkey),
|
||||
);
|
||||
@ -561,7 +557,11 @@ fn get_storage_blockhash<C: SyncClient>(client: &C, account: &Pubkey) -> Hash {
|
||||
|
||||
#[test]
|
||||
fn test_bank_storage() {
|
||||
let (mut genesis_block, mint_keypair) = create_genesis_block(1000);
|
||||
let GenesisBlockInfo {
|
||||
mut genesis_block,
|
||||
mint_keypair,
|
||||
..
|
||||
} = create_genesis_block(1000);
|
||||
genesis_block
|
||||
.native_instruction_processors
|
||||
.push(solana_storage_program::solana_storage_program!());
|
||||
|
Reference in New Issue
Block a user