diff --git a/Cargo.lock b/Cargo.lock index 320c138c68..ed58578713 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2313,30 +2313,6 @@ dependencies = [ "solana-sdk 0.13.0", ] -[[package]] -name = "solana-rewards-api" -version = "0.13.0" -dependencies = [ - "bincode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", - "solana-sdk 0.13.0", - "solana-vote-api 0.13.0", -] - -[[package]] -name = "solana-rewards-program" -version = "0.13.0" -dependencies = [ - "bincode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "solana-logger 0.13.0", - "solana-rewards-api 0.13.0", - "solana-runtime 0.13.0", - "solana-sdk 0.13.0", - "solana-vote-api 0.13.0", -] - [[package]] name = "solana-runtime" version = "0.13.0" diff --git a/Cargo.toml b/Cargo.toml index 6cff888966..9f913037f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,8 +22,6 @@ members = [ "programs/token_api", "programs/failure", "programs/noop", - "programs/rewards", - "programs/rewards_api", "programs/storage", "programs/storage_api", "programs/vote", diff --git a/ci/publish-crate.sh b/ci/publish-crate.sh index 54ef135520..b47980ba54 100755 --- a/ci/publish-crate.sh +++ b/ci/publish-crate.sh @@ -19,7 +19,7 @@ CRATES=( metrics client drone - programs/{budget_api,config_api,rewards_api,storage_api,token_api,vote_api} + programs/{budget_api,config_api,storage_api,token_api,vote_api} runtime programs/{budget,bpf_loader,config,vote,rewards,storage,token,vote} vote-signer diff --git a/programs/rewards/Cargo.toml b/programs/rewards/Cargo.toml deleted file mode 100644 index 31889d8739..0000000000 --- a/programs/rewards/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "solana-rewards-program" -version = "0.13.0" -description = "Solana rewards program" -authors = ["Solana Maintainers "] -repository = "https://github.com/solana-labs/solana" -license = "Apache-2.0" -homepage = "https://solana.com/" -edition = "2018" - -[dependencies] -bincode = "1.1.2" -log = "0.4.2" -solana-logger = { path = "../../logger", version = "0.13.0" } -solana-sdk = { path = "../../sdk", version = "0.13.0" } -solana-rewards-api = { path = "../rewards_api", version = "0.13.0" } -solana-vote-api = { path = "../vote_api", version = "0.13.0" } - -[dev-dependencies] -solana-runtime = { path = "../../runtime", version = "0.13.0" } - -[lib] -name = "solana_rewards_program" -crate-type = ["cdylib"] diff --git a/programs/rewards/src/lib.rs b/programs/rewards/src/lib.rs deleted file mode 100644 index d6dbe90c43..0000000000 --- a/programs/rewards/src/lib.rs +++ /dev/null @@ -1,144 +0,0 @@ -//! Rewards program -//! Exchanges validation and storage proofs for lamports - -use bincode::deserialize; -use log::*; -use solana_rewards_api::rewards_instruction::RewardsInstruction; -use solana_sdk::account::KeyedAccount; -use solana_sdk::pubkey::Pubkey; -use solana_sdk::solana_entrypoint; -use solana_sdk::transaction::InstructionError; -use solana_vote_api::vote_state::VoteState; - -const INTEREST_PER_CREDIT_DIVISOR: u64 = 100; // Staker earns 1/INTEREST_PER_CREDIT_DIVISOR of interest per credit -const MINIMUM_CREDITS_PER_REDEMPTION: u64 = 1; // Raise this to either minimize congestion or lengthen the interest period - -// Below is a temporary solution for calculating validator rewards. It targets an ROI for -// stakeholders and assumes the validator votes on every slot. It's convenient in that -// the calculation does not require knowing the size of the staking pool. -// -// TODO: Migrate to reward mechanism described by the book: -// https://github.com/solana-labs/solana/blob/master/book/src/ed_vce_state_validation_protocol_based_rewards.md -// https://github.com/solana-labs/solana/blob/master/book/src/staking-rewards.md -fn calc_vote_reward(credits: u64, stake: u64) -> Result { - if credits < MINIMUM_CREDITS_PER_REDEMPTION { - error!("Credit redemption too early"); - Err(InstructionError::GenericError)?; - } - Ok(credits * (stake / INTEREST_PER_CREDIT_DIVISOR)) -} - -fn redeem_vote_credits(keyed_accounts: &mut [KeyedAccount]) -> Result<(), InstructionError> { - // The owner of the vote account needs to authorize having its credits cleared. - if keyed_accounts[0].signer_key().is_none() { - error!("account[0] is unsigned"); - Err(InstructionError::InvalidArgument)?; - } - - if !solana_vote_api::check_id(&keyed_accounts[0].account.owner) { - error!("account[0] is not assigned to the VOTE_PROGRAM"); - Err(InstructionError::InvalidArgument)?; - } - - // TODO: Verify the next instruction in the transaction being processed is - // VoteInstruction::ClearCredits and that it points to the same vote account - // as keyed_accounts[0]. - - let vote_state = VoteState::deserialize(&keyed_accounts[0].account.data)?; - - // TODO: This assumes the stake is static. If not, it should use the account value - // at the time of voting, not at credit redemption. - let stake = keyed_accounts[0].account.lamports; - if stake == 0 { - error!("staking account has no stake"); - Err(InstructionError::InvalidArgument)?; - } - - let lamports = calc_vote_reward(vote_state.credits(), stake)?; - - // Transfer rewards from the rewards pool to the staking account. - keyed_accounts[1].account.lamports -= lamports; - keyed_accounts[0].account.lamports += lamports; - - Ok(()) -} - -solana_entrypoint!(entrypoint); -fn entrypoint( - _program_id: &Pubkey, - keyed_accounts: &mut [KeyedAccount], - data: &[u8], - _tick_height: u64, -) -> Result<(), InstructionError> { - solana_logger::setup(); - - trace!("process_instruction: {:?}", data); - trace!("keyed_accounts: {:?}", keyed_accounts); - - match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? { - RewardsInstruction::RedeemVoteCredits => redeem_vote_credits(keyed_accounts), - } -} - -#[cfg(test)] -mod tests { - use super::*; - use solana_rewards_api; - use solana_rewards_api::rewards_state::RewardsState; - use solana_sdk::account::Account; - use solana_sdk::signature::{Keypair, KeypairUtil}; - use solana_vote_api::vote_instruction::Vote; - use solana_vote_api::vote_state; - - fn create_rewards_account(lamports: u64) -> Account { - let space = RewardsState::max_size(); - Account::new(lamports, space, &solana_rewards_api::id()) - } - - fn redeem_vote_credits_( - rewards_id: &Pubkey, - rewards_account: &mut Account, - vote_id: &Pubkey, - vote_account: &mut Account, - ) -> Result<(), InstructionError> { - let mut keyed_accounts = [ - KeyedAccount::new(vote_id, true, vote_account), - KeyedAccount::new(rewards_id, false, rewards_account), - ]; - redeem_vote_credits(&mut keyed_accounts) - } - - #[test] - fn test_redeem_vote_credits_via_program() { - let vote_id = Keypair::new().pubkey(); - let mut vote_account = vote_state::create_vote_account(100); - vote_state::initialize_and_deserialize(&vote_id, &mut vote_account).unwrap(); - - for i in 0..vote_state::MAX_LOCKOUT_HISTORY { - let vote = Vote::new(i as u64); - let vote_state = - vote_state::vote_and_deserialize(&vote_id, &mut vote_account, vote.clone()) - .unwrap(); - assert_eq!(vote_state.credits(), 0); - } - - let vote = Vote::new(vote_state::MAX_LOCKOUT_HISTORY as u64 + 1); - let vote_state = - vote_state::vote_and_deserialize(&vote_id, &mut vote_account, vote.clone()).unwrap(); - assert_eq!(vote_state.credits(), 1); - - let rewards_id = Keypair::new().pubkey(); - let mut rewards_account = create_rewards_account(100); - - let lamports_before = vote_account.lamports; - - redeem_vote_credits_( - &rewards_id, - &mut rewards_account, - &vote_id, - &mut vote_account, - ) - .unwrap(); - assert!(vote_account.lamports > lamports_before); - } -} diff --git a/programs/rewards/tests/rewards.rs b/programs/rewards/tests/rewards.rs deleted file mode 100644 index b0b4808b3d..0000000000 --- a/programs/rewards/tests/rewards.rs +++ /dev/null @@ -1,115 +0,0 @@ -use solana_rewards_api::rewards_transaction::RewardsTransaction; -use solana_runtime::bank::{Bank, Result}; -use solana_sdk::genesis_block::GenesisBlock; -use solana_sdk::hash::hash; -use solana_sdk::pubkey::Pubkey; -use solana_sdk::signature::{Keypair, KeypairUtil}; -use solana_vote_api::vote_state::{self, VoteState}; -use solana_vote_api::vote_transaction::VoteTransaction; - -struct RewardsBank<'a> { - bank: &'a Bank, -} - -impl<'a> RewardsBank<'a> { - fn new(bank: &'a Bank) -> Self { - bank.add_native_program("solana_rewards_program", &solana_rewards_api::id()); - Self { bank } - } - - fn create_rewards_account( - &self, - from_keypair: &Keypair, - rewards_id: &Pubkey, - lamports: u64, - ) -> Result<()> { - let blockhash = self.bank.last_blockhash(); - let tx = RewardsTransaction::new_account(from_keypair, rewards_id, blockhash, lamports, 0); - self.bank.process_transaction(&tx) - } - - fn create_vote_account( - &self, - from_keypair: &Keypair, - vote_id: &Pubkey, - lamports: u64, - ) -> Result<()> { - let blockhash = self.bank.last_blockhash(); - let tx = VoteTransaction::new_account(from_keypair, vote_id, blockhash, lamports, 0); - self.bank.process_transaction(&tx) - } - - fn submit_vote( - &self, - staking_account: &Pubkey, - vote_keypair: &Keypair, - tick_height: u64, - ) -> Result { - let blockhash = self.bank.last_blockhash(); - let tx = - VoteTransaction::new_vote(staking_account, vote_keypair, tick_height, blockhash, 0); - self.bank.process_transaction(&tx)?; - self.bank.register_tick(&hash(blockhash.as_ref())); - - let vote_account = self.bank.get_account(&vote_keypair.pubkey()).unwrap(); - Ok(VoteState::deserialize(&vote_account.data).unwrap()) - } - - fn redeem_credits(&self, rewards_id: &Pubkey, vote_keypair: &Keypair) -> Result { - let blockhash = self.bank.last_blockhash(); - let tx = RewardsTransaction::new_redeem_credits(&vote_keypair, rewards_id, blockhash, 0); - self.bank.process_transaction(&tx)?; - let vote_account = self.bank.get_account(&vote_keypair.pubkey()).unwrap(); - Ok(VoteState::deserialize(&vote_account.data).unwrap()) - } -} - -#[test] -fn test_redeem_vote_credits_via_bank() { - let (genesis_block, from_keypair) = GenesisBlock::new(10_000); - let bank = Bank::new(&genesis_block); - let rewards_bank = RewardsBank::new(&bank); - - // Create a rewards account to hold all rewards pool lamports. - let rewards_keypair = Keypair::new(); - let rewards_id = rewards_keypair.pubkey(); - rewards_bank - .create_rewards_account(&from_keypair, &rewards_id, 100) - .unwrap(); - - // A staker create a vote account account and delegates a validator to vote on its behalf. - let vote_keypair = Keypair::new(); - let vote_id = vote_keypair.pubkey(); - rewards_bank - .create_vote_account(&from_keypair, &vote_id, 100) - .unwrap(); - - // The validator submits votes to accumulate credits. - for i in 0..vote_state::MAX_LOCKOUT_HISTORY { - let vote_state = rewards_bank - .submit_vote(&vote_id, &vote_keypair, i as u64) - .unwrap(); - assert_eq!(vote_state.credits(), 0); - } - let vote_state = rewards_bank - .submit_vote( - &vote_id, - &vote_keypair, - vote_state::MAX_LOCKOUT_HISTORY as u64 + 1, - ) - .unwrap(); - assert_eq!(vote_state.credits(), 1); - - // TODO: Add VoteInstruction::RegisterStakerId so that we don't need to point the "to" - // account to the "from" account. - let to_id = vote_id; - let to_lamports = bank.get_balance(&vote_id); - - // Periodically, the staker sumbits its vote account to the rewards pool - // to exchange its credits for lamports. - let vote_state = rewards_bank - .redeem_credits(&rewards_id, &vote_keypair) - .unwrap(); - assert!(bank.get_balance(&to_id) > to_lamports); - assert_eq!(vote_state.credits(), 0); -} diff --git a/programs/rewards_api/Cargo.toml b/programs/rewards_api/Cargo.toml deleted file mode 100644 index 2e2799229b..0000000000 --- a/programs/rewards_api/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "solana-rewards-api" -version = "0.13.0" -description = "Solana rewards API" -authors = ["Solana Maintainers "] -repository = "https://github.com/solana-labs/solana" -license = "Apache-2.0" -homepage = "https://solana.com/" -edition = "2018" - -[dependencies] -bincode = "1.1.2" -serde = "1.0.89" -serde_derive = "1.0.89" -solana-sdk = { path = "../../sdk", version = "0.13.0" } -solana-vote-api = { path = "../vote_api", version = "0.13.0" } - -[lib] -name = "solana_rewards_api" -crate-type = ["lib"] diff --git a/programs/rewards_api/src/lib.rs b/programs/rewards_api/src/lib.rs deleted file mode 100644 index 01fa8a6d5c..0000000000 --- a/programs/rewards_api/src/lib.rs +++ /dev/null @@ -1,18 +0,0 @@ -pub mod rewards_instruction; -pub mod rewards_state; -pub mod rewards_transaction; - -use solana_sdk::pubkey::Pubkey; - -const REWARDS_PROGRAM_ID: [u8; 32] = [ - 133, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, -]; - -pub fn check_id(program_id: &Pubkey) -> bool { - program_id.as_ref() == REWARDS_PROGRAM_ID -} - -pub fn id() -> Pubkey { - Pubkey::new(&REWARDS_PROGRAM_ID) -} diff --git a/programs/rewards_api/src/rewards_instruction.rs b/programs/rewards_api/src/rewards_instruction.rs deleted file mode 100644 index 2ab10f9fb8..0000000000 --- a/programs/rewards_api/src/rewards_instruction.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::id; -use serde_derive::{Deserialize, Serialize}; -use solana_sdk::pubkey::Pubkey; -use solana_sdk::transaction::{AccountMeta, Instruction}; - -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] -pub enum RewardsInstruction { - RedeemVoteCredits, -} - -impl RewardsInstruction { - pub fn new_redeem_vote_credits(vote_id: &Pubkey, rewards_id: &Pubkey) -> Instruction { - let account_metas = vec![ - AccountMeta::new(*vote_id, true), - AccountMeta::new(*rewards_id, false), - ]; - Instruction::new(id(), &RewardsInstruction::RedeemVoteCredits, account_metas) - } -} diff --git a/programs/rewards_api/src/rewards_state.rs b/programs/rewards_api/src/rewards_state.rs deleted file mode 100644 index b5961547ad..0000000000 --- a/programs/rewards_api/src/rewards_state.rs +++ /dev/null @@ -1,13 +0,0 @@ -use bincode::serialized_size; -use serde_derive::{Deserialize, Serialize}; - -#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)] -pub struct RewardsState {} - -impl RewardsState { - /// Upper limit on the serialized size of RewardsState. - pub fn max_size() -> usize { - let rewards_state = RewardsState::default(); - serialized_size(&rewards_state).unwrap() as usize - } -} diff --git a/programs/rewards_api/src/rewards_transaction.rs b/programs/rewards_api/src/rewards_transaction.rs deleted file mode 100644 index 9d7cb8f037..0000000000 --- a/programs/rewards_api/src/rewards_transaction.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! The `rewards_transaction` module provides functionality for creating a global -//! rewards account and enabling stakers to redeem credits from their vote accounts. - -use crate::id; -use crate::rewards_instruction::RewardsInstruction; -use crate::rewards_state::RewardsState; -use solana_sdk::hash::Hash; -use solana_sdk::pubkey::Pubkey; -use solana_sdk::signature::{Keypair, KeypairUtil}; -use solana_sdk::system_transaction::SystemTransaction; -use solana_sdk::transaction::Transaction; -use solana_vote_api::vote_instruction::VoteInstruction; - -pub struct RewardsTransaction {} - -impl RewardsTransaction { - pub fn new_account( - from_keypair: &Keypair, - rewards_id: &Pubkey, - blockhash: Hash, - lamports: u64, - fee: u64, - ) -> Transaction { - SystemTransaction::new_program_account( - from_keypair, - rewards_id, - blockhash, - lamports, - RewardsState::max_size() as u64, - &id(), - fee, - ) - } - - pub fn new_redeem_credits( - vote_keypair: &Keypair, - rewards_id: &Pubkey, - blockhash: Hash, - fee: u64, - ) -> Transaction { - let vote_id = vote_keypair.pubkey(); - let redeem_ix = RewardsInstruction::new_redeem_vote_credits(&vote_id, rewards_id); - let clear_ix = VoteInstruction::new_clear_credits(&vote_id); - let mut tx = Transaction::new(vec![redeem_ix, clear_ix]); - tx.fee = fee; - tx.sign(&[vote_keypair], blockhash); - tx - } -}