Cherry-pick vote and stake authority changes (#6127)
* add authorized parameters to vote api (#6072) * add authorized parameters to vote api * code review * add authorities to stake init (#6104) * add authorities to stake init * fixups * code review
This commit is contained in:
141
cli/src/vote.rs
141
cli/src/vote.rs
@@ -15,13 +15,16 @@ use solana_sdk::{
|
|||||||
};
|
};
|
||||||
use solana_vote_api::{
|
use solana_vote_api::{
|
||||||
vote_instruction::{self, VoteError},
|
vote_instruction::{self, VoteError},
|
||||||
vote_state::VoteState,
|
vote_state::{VoteAuthorize, VoteInit, VoteState},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parse_vote_create_account(matches: &ArgMatches<'_>) -> Result<WalletCommand, WalletError> {
|
pub fn parse_vote_create_account(matches: &ArgMatches<'_>) -> Result<WalletCommand, WalletError> {
|
||||||
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
|
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
|
||||||
let node_pubkey = pubkey_of(matches, "node_pubkey").unwrap();
|
let node_pubkey = pubkey_of(matches, "node_pubkey").unwrap();
|
||||||
let commission = value_of(&matches, "commission").unwrap_or(0);
|
let commission = value_of(&matches, "commission").unwrap_or(0);
|
||||||
|
let authorized_voter = pubkey_of(matches, "authorized_voter").unwrap_or(vote_account_pubkey);
|
||||||
|
let authorized_withdrawer =
|
||||||
|
pubkey_of(matches, "authorized_withdrawer").unwrap_or(vote_account_pubkey);
|
||||||
let lamports = crate::wallet::parse_amount_lamports(
|
let lamports = crate::wallet::parse_amount_lamports(
|
||||||
matches.value_of("amount").unwrap(),
|
matches.value_of("amount").unwrap(),
|
||||||
matches.value_of("unit"),
|
matches.value_of("unit"),
|
||||||
@@ -30,21 +33,29 @@ pub fn parse_vote_create_account(matches: &ArgMatches<'_>) -> Result<WalletComma
|
|||||||
|
|
||||||
Ok(WalletCommand::CreateVoteAccount(
|
Ok(WalletCommand::CreateVoteAccount(
|
||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
|
VoteInit {
|
||||||
node_pubkey,
|
node_pubkey,
|
||||||
|
authorized_voter,
|
||||||
|
authorized_withdrawer,
|
||||||
commission,
|
commission,
|
||||||
|
},
|
||||||
lamports,
|
lamports,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_vote_authorize_voter(matches: &ArgMatches<'_>) -> Result<WalletCommand, WalletError> {
|
pub fn parse_vote_authorize(
|
||||||
|
matches: &ArgMatches<'_>,
|
||||||
|
vote_authorize: VoteAuthorize,
|
||||||
|
) -> Result<WalletCommand, WalletError> {
|
||||||
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
|
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
|
||||||
let authorized_voter_keypair = keypair_of(matches, "authorized_voter_keypair_file").unwrap();
|
let authorized_keypair = keypair_of(matches, "authorized_keypair_file").unwrap();
|
||||||
let new_authorized_voter_pubkey = pubkey_of(matches, "new_authorized_voter_pubkey").unwrap();
|
let new_authorized_pubkey = pubkey_of(matches, "new_authorized_pubkey").unwrap();
|
||||||
|
|
||||||
Ok(WalletCommand::AuthorizeVoter(
|
Ok(WalletCommand::VoteAuthorize(
|
||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
authorized_voter_keypair,
|
authorized_keypair,
|
||||||
new_authorized_voter_pubkey,
|
new_authorized_pubkey,
|
||||||
|
vote_authorize,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,13 +74,12 @@ pub fn process_create_vote_account(
|
|||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
config: &WalletConfig,
|
config: &WalletConfig,
|
||||||
vote_account_pubkey: &Pubkey,
|
vote_account_pubkey: &Pubkey,
|
||||||
node_pubkey: &Pubkey,
|
vote_init: &VoteInit,
|
||||||
commission: u8,
|
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
check_unique_pubkeys(
|
check_unique_pubkeys(
|
||||||
(vote_account_pubkey, "vote_account_pubkey".to_string()),
|
(vote_account_pubkey, "vote_account_pubkey".to_string()),
|
||||||
(node_pubkey, "node_pubkey".to_string()),
|
(&vote_init.node_pubkey, "node_pubkey".to_string()),
|
||||||
)?;
|
)?;
|
||||||
check_unique_pubkeys(
|
check_unique_pubkeys(
|
||||||
(&config.keypair.pubkey(), "wallet keypair".to_string()),
|
(&config.keypair.pubkey(), "wallet keypair".to_string()),
|
||||||
@@ -78,8 +88,7 @@ pub fn process_create_vote_account(
|
|||||||
let ixs = vote_instruction::create_account(
|
let ixs = vote_instruction::create_account(
|
||||||
&config.keypair.pubkey(),
|
&config.keypair.pubkey(),
|
||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
node_pubkey,
|
vote_init,
|
||||||
commission,
|
|
||||||
lamports,
|
lamports,
|
||||||
);
|
);
|
||||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
@@ -89,36 +98,35 @@ pub fn process_create_vote_account(
|
|||||||
log_instruction_custom_error::<SystemError>(result)
|
log_instruction_custom_error::<SystemError>(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_authorize_voter(
|
pub fn process_vote_authorize(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
config: &WalletConfig,
|
config: &WalletConfig,
|
||||||
vote_account_pubkey: &Pubkey,
|
vote_account_pubkey: &Pubkey,
|
||||||
authorized_voter_keypair: &Keypair,
|
authorized_keypair: &Keypair,
|
||||||
new_authorized_voter_pubkey: &Pubkey,
|
new_authorized_pubkey: &Pubkey,
|
||||||
|
vote_authorize: VoteAuthorize,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
check_unique_pubkeys(
|
check_unique_pubkeys(
|
||||||
(vote_account_pubkey, "vote_account_pubkey".to_string()),
|
(vote_account_pubkey, "vote_account_pubkey".to_string()),
|
||||||
(
|
(new_authorized_pubkey, "new_authorized_pubkey".to_string()),
|
||||||
new_authorized_voter_pubkey,
|
|
||||||
"new_authorized_voter_pubkey".to_string(),
|
|
||||||
),
|
|
||||||
)?;
|
)?;
|
||||||
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
let ixs = vec![vote_instruction::authorize_voter(
|
let ixs = vec![vote_instruction::authorize(
|
||||||
vote_account_pubkey, // vote account to update
|
vote_account_pubkey, // vote account to update
|
||||||
&authorized_voter_keypair.pubkey(), // current authorized voter (often the vote account itself)
|
&authorized_keypair.pubkey(), // current authorized voter (often the vote account itself)
|
||||||
new_authorized_voter_pubkey, // new vote signer
|
new_authorized_pubkey, // new vote signer
|
||||||
|
vote_authorize, // vote or withdraw
|
||||||
)];
|
)];
|
||||||
|
|
||||||
let mut tx = Transaction::new_signed_with_payer(
|
let mut tx = Transaction::new_signed_with_payer(
|
||||||
ixs,
|
ixs,
|
||||||
Some(&config.keypair.pubkey()),
|
Some(&config.keypair.pubkey()),
|
||||||
&[&config.keypair, &authorized_voter_keypair],
|
&[&config.keypair, &authorized_keypair],
|
||||||
recent_blockhash,
|
recent_blockhash,
|
||||||
);
|
);
|
||||||
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
|
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
|
||||||
let result = rpc_client
|
let result =
|
||||||
.send_and_confirm_transaction(&mut tx, &[&config.keypair, &authorized_voter_keypair]);
|
rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair, &authorized_keypair]);
|
||||||
log_instruction_custom_error::<VoteError>(result)
|
log_instruction_custom_error::<VoteError>(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,9 +170,10 @@ pub fn process_show_vote_account(
|
|||||||
crate::wallet::build_balance_message(vote_account.lamports, use_lamports_unit)
|
crate::wallet::build_balance_message(vote_account.lamports, use_lamports_unit)
|
||||||
);
|
);
|
||||||
println!("node id: {}", vote_state.node_pubkey);
|
println!("node id: {}", vote_state.node_pubkey);
|
||||||
|
println!("authorized voter: {}", vote_state.authorized_voter);
|
||||||
println!(
|
println!(
|
||||||
"authorized voter pubkey: {}",
|
"authorized withdrawer: {}",
|
||||||
vote_state.authorized_voter_pubkey
|
vote_state.authorized_withdrawer
|
||||||
);
|
);
|
||||||
println!("credits: {}", vote_state.credits());
|
println!("credits: {}", vote_state.credits());
|
||||||
println!(
|
println!(
|
||||||
@@ -230,10 +239,7 @@ pub fn process_uptime(
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
println!("Node id: {}", vote_state.node_pubkey);
|
println!("Node id: {}", vote_state.node_pubkey);
|
||||||
println!(
|
println!("Authorized voter: {}", vote_state.authorized_voter);
|
||||||
"Authorized voter pubkey: {}",
|
|
||||||
vote_state.authorized_voter_pubkey
|
|
||||||
);
|
|
||||||
if !vote_state.votes.is_empty() {
|
if !vote_state.votes.is_empty() {
|
||||||
println!("Uptime:");
|
println!("Uptime:");
|
||||||
|
|
||||||
@@ -300,14 +306,14 @@ mod tests {
|
|||||||
|
|
||||||
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
|
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
|
||||||
"test",
|
"test",
|
||||||
"authorize-voter",
|
"vote-authorize-voter",
|
||||||
&pubkey_string,
|
&pubkey_string,
|
||||||
&keypair_file,
|
&keypair_file,
|
||||||
&pubkey_string,
|
&pubkey_string,
|
||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&pubkey, &test_authorize_voter).unwrap(),
|
parse_command(&pubkey, &test_authorize_voter).unwrap(),
|
||||||
WalletCommand::AuthorizeVoter(pubkey, keypair, pubkey)
|
WalletCommand::VoteAuthorize(pubkey, keypair, pubkey, VoteAuthorize::Voter)
|
||||||
);
|
);
|
||||||
fs::remove_file(&keypair_file).unwrap();
|
fs::remove_file(&keypair_file).unwrap();
|
||||||
|
|
||||||
@@ -326,7 +332,16 @@ mod tests {
|
|||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&pubkey, &test_create_vote_account).unwrap(),
|
parse_command(&pubkey, &test_create_vote_account).unwrap(),
|
||||||
WalletCommand::CreateVoteAccount(pubkey, node_pubkey, 10, 50)
|
WalletCommand::CreateVoteAccount(
|
||||||
|
pubkey,
|
||||||
|
VoteInit {
|
||||||
|
node_pubkey,
|
||||||
|
authorized_voter: pubkey,
|
||||||
|
authorized_withdrawer: pubkey,
|
||||||
|
commission: 10
|
||||||
|
},
|
||||||
|
50
|
||||||
|
)
|
||||||
);
|
);
|
||||||
let test_create_vote_account2 = test_commands.clone().get_matches_from(vec![
|
let test_create_vote_account2 = test_commands.clone().get_matches_from(vec![
|
||||||
"test",
|
"test",
|
||||||
@@ -337,7 +352,63 @@ mod tests {
|
|||||||
]);
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&pubkey, &test_create_vote_account2).unwrap(),
|
parse_command(&pubkey, &test_create_vote_account2).unwrap(),
|
||||||
WalletCommand::CreateVoteAccount(pubkey, node_pubkey, 0, 858993459200)
|
WalletCommand::CreateVoteAccount(
|
||||||
|
pubkey,
|
||||||
|
VoteInit {
|
||||||
|
node_pubkey,
|
||||||
|
authorized_voter: pubkey,
|
||||||
|
authorized_withdrawer: pubkey,
|
||||||
|
commission: 0
|
||||||
|
},
|
||||||
|
858993459200
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// test init with an authed voter
|
||||||
|
let authed = Pubkey::new_rand();
|
||||||
|
let test_create_vote_account3 = test_commands.clone().get_matches_from(vec![
|
||||||
|
"test",
|
||||||
|
"create-vote-account",
|
||||||
|
&pubkey_string,
|
||||||
|
&node_pubkey_string,
|
||||||
|
"50",
|
||||||
|
"--authorized-voter",
|
||||||
|
&authed.to_string(),
|
||||||
|
]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_command(&pubkey, &test_create_vote_account3).unwrap(),
|
||||||
|
WalletCommand::CreateVoteAccount(
|
||||||
|
pubkey,
|
||||||
|
VoteInit {
|
||||||
|
node_pubkey,
|
||||||
|
authorized_voter: authed,
|
||||||
|
authorized_withdrawer: pubkey,
|
||||||
|
commission: 0
|
||||||
|
},
|
||||||
|
858993459200
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// test init with an authed withdrawer
|
||||||
|
let test_create_vote_account4 = test_commands.clone().get_matches_from(vec![
|
||||||
|
"test",
|
||||||
|
"create-vote-account",
|
||||||
|
&pubkey_string,
|
||||||
|
&node_pubkey_string,
|
||||||
|
"50",
|
||||||
|
"--authorized-withdrawer",
|
||||||
|
&authed.to_string(),
|
||||||
|
]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_command(&pubkey, &test_create_vote_account4).unwrap(),
|
||||||
|
WalletCommand::CreateVoteAccount(
|
||||||
|
pubkey,
|
||||||
|
VoteInit {
|
||||||
|
node_pubkey,
|
||||||
|
authorized_voter: pubkey,
|
||||||
|
authorized_withdrawer: authed,
|
||||||
|
commission: 0
|
||||||
|
},
|
||||||
|
858993459200
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Test Uptime Subcommand
|
// Test Uptime Subcommand
|
||||||
|
@@ -7,11 +7,9 @@ use clap::{value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
|
|||||||
use console::{style, Emoji};
|
use console::{style, Emoji};
|
||||||
use log::*;
|
use log::*;
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
use serde_json;
|
use serde_json::{self, json, Value};
|
||||||
use serde_json::{json, Value};
|
|
||||||
use solana_budget_api::budget_instruction::{self, BudgetError};
|
use solana_budget_api::budget_instruction::{self, BudgetError};
|
||||||
use solana_client::client_error::ClientError;
|
use solana_client::{client_error::ClientError, rpc_client::RpcClient};
|
||||||
use solana_client::rpc_client::RpcClient;
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use solana_drone::drone::request_airdrop_transaction;
|
use solana_drone::drone::request_airdrop_transaction;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -31,16 +29,21 @@ use solana_sdk::{
|
|||||||
system_transaction,
|
system_transaction,
|
||||||
transaction::{Transaction, TransactionError},
|
transaction::{Transaction, TransactionError},
|
||||||
};
|
};
|
||||||
use solana_stake_api::stake_instruction::{self, StakeError};
|
use solana_stake_api::{
|
||||||
|
stake_instruction::{self, StakeError},
|
||||||
|
stake_state::{Authorized, Lockup},
|
||||||
|
};
|
||||||
use solana_storage_api::storage_instruction;
|
use solana_storage_api::storage_instruction;
|
||||||
use solana_vote_api::vote_state::VoteState;
|
use solana_vote_api::vote_state::{VoteAuthorize, VoteInit, VoteState};
|
||||||
use std::collections::VecDeque;
|
use std::{
|
||||||
use std::fs::File;
|
collections::VecDeque,
|
||||||
use std::io::{Read, Write};
|
fs::File,
|
||||||
use std::net::{IpAddr, SocketAddr};
|
io::{Read, Write},
|
||||||
use std::thread::sleep;
|
net::{IpAddr, SocketAddr},
|
||||||
use std::time::{Duration, Instant};
|
thread::sleep,
|
||||||
use std::{error, fmt};
|
time::{Duration, Instant},
|
||||||
|
{error, fmt},
|
||||||
|
};
|
||||||
|
|
||||||
const USERDATA_CHUNK_SIZE: usize = 229; // Keep program chunks under PACKET_DATA_SIZE
|
const USERDATA_CHUNK_SIZE: usize = 229; // Keep program chunks under PACKET_DATA_SIZE
|
||||||
|
|
||||||
@@ -64,8 +67,8 @@ pub enum WalletCommand {
|
|||||||
},
|
},
|
||||||
Cancel(Pubkey),
|
Cancel(Pubkey),
|
||||||
Confirm(Signature),
|
Confirm(Signature),
|
||||||
AuthorizeVoter(Pubkey, Keypair, Pubkey),
|
VoteAuthorize(Pubkey, Keypair, Pubkey, VoteAuthorize),
|
||||||
CreateVoteAccount(Pubkey, Pubkey, u8, u64),
|
CreateVoteAccount(Pubkey, VoteInit, u64),
|
||||||
ShowAccount {
|
ShowAccount {
|
||||||
pubkey: Pubkey,
|
pubkey: Pubkey,
|
||||||
output_file: Option<String>,
|
output_file: Option<String>,
|
||||||
@@ -80,7 +83,7 @@ pub enum WalletCommand {
|
|||||||
aggregate: bool,
|
aggregate: bool,
|
||||||
span: Option<u64>,
|
span: Option<u64>,
|
||||||
},
|
},
|
||||||
DelegateStake(Keypair, Pubkey, u64, bool),
|
DelegateStake(Keypair, Pubkey, u64, Authorized, bool),
|
||||||
WithdrawStake(Keypair, Pubkey, u64),
|
WithdrawStake(Keypair, Pubkey, u64),
|
||||||
DeactivateStake(Keypair, Pubkey),
|
DeactivateStake(Keypair, Pubkey),
|
||||||
RedeemVoteCredits(Pubkey, Pubkey),
|
RedeemVoteCredits(Pubkey, Pubkey),
|
||||||
@@ -242,7 +245,12 @@ pub fn parse_command(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
("create-vote-account", Some(matches)) => parse_vote_create_account(matches),
|
("create-vote-account", Some(matches)) => parse_vote_create_account(matches),
|
||||||
("authorize-voter", Some(matches)) => parse_vote_authorize_voter(matches),
|
("vote-authorize-voter", Some(matches)) => {
|
||||||
|
parse_vote_authorize(matches, VoteAuthorize::Voter)
|
||||||
|
}
|
||||||
|
("vote-authorize-withdrawer", Some(matches)) => {
|
||||||
|
parse_vote_authorize(matches, VoteAuthorize::Withdrawer)
|
||||||
|
}
|
||||||
("show-vote-account", Some(matches)) => parse_vote_get_account_command(matches),
|
("show-vote-account", Some(matches)) => parse_vote_get_account_command(matches),
|
||||||
("uptime", Some(matches)) => parse_vote_uptime_command(matches),
|
("uptime", Some(matches)) => parse_vote_uptime_command(matches),
|
||||||
("delegate-stake", Some(matches)) => {
|
("delegate-stake", Some(matches)) => {
|
||||||
@@ -252,11 +260,13 @@ pub fn parse_command(
|
|||||||
matches.value_of("amount").unwrap(),
|
matches.value_of("amount").unwrap(),
|
||||||
matches.value_of("unit"),
|
matches.value_of("unit"),
|
||||||
)?;
|
)?;
|
||||||
|
let authorized = Authorized::auto(&stake_account_keypair.pubkey());
|
||||||
let force = matches.is_present("force");
|
let force = matches.is_present("force");
|
||||||
Ok(WalletCommand::DelegateStake(
|
Ok(WalletCommand::DelegateStake(
|
||||||
stake_account_keypair,
|
stake_account_keypair,
|
||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
lamports,
|
lamports,
|
||||||
|
authorized,
|
||||||
force,
|
force,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -602,6 +612,7 @@ fn process_delegate_stake(
|
|||||||
stake_account_keypair: &Keypair,
|
stake_account_keypair: &Keypair,
|
||||||
vote_account_pubkey: &Pubkey,
|
vote_account_pubkey: &Pubkey,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
|
authorized: &Authorized,
|
||||||
force: bool,
|
force: bool,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
check_unique_pubkeys(
|
check_unique_pubkeys(
|
||||||
@@ -618,6 +629,7 @@ fn process_delegate_stake(
|
|||||||
&stake_account_keypair.pubkey(),
|
&stake_account_keypair.pubkey(),
|
||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
lamports,
|
lamports,
|
||||||
|
authorized,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Sanity check the vote account to ensure it is attached to a validator that has recently
|
// Sanity check the vote account to ensure it is attached to a validator that has recently
|
||||||
@@ -735,8 +747,16 @@ fn process_show_stake_account(
|
|||||||
format!("{:?} is not a stake account", stake_account_pubkey).to_string(),
|
format!("{:?} is not a stake account", stake_account_pubkey).to_string(),
|
||||||
))?;
|
))?;
|
||||||
}
|
}
|
||||||
|
fn show_authorized(authorized: &Authorized) {
|
||||||
|
println!("authorized staker: {}", authorized.staker);
|
||||||
|
println!("authorized withdrawer: {}", authorized.staker);
|
||||||
|
}
|
||||||
|
fn show_lockup(lockup: &Lockup) {
|
||||||
|
println!("lockup slot: {}", lockup.slot);
|
||||||
|
println!("lockup custodian: {}", lockup.custodian);
|
||||||
|
}
|
||||||
match stake_account.state() {
|
match stake_account.state() {
|
||||||
Ok(StakeState::Stake(stake)) => {
|
Ok(StakeState::Stake(authorized, lockup, stake)) => {
|
||||||
println!(
|
println!(
|
||||||
"total stake: {}",
|
"total stake: {}",
|
||||||
build_balance_message(stake_account.lamports, use_lamports_unit)
|
build_balance_message(stake_account.lamports, use_lamports_unit)
|
||||||
@@ -759,11 +779,17 @@ fn process_show_stake_account(
|
|||||||
stake.deactivation_epoch
|
stake.deactivation_epoch
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
show_authorized(&authorized);
|
||||||
|
show_lockup(&lockup);
|
||||||
Ok("".to_string())
|
Ok("".to_string())
|
||||||
}
|
}
|
||||||
Ok(StakeState::RewardsPool) => Ok("Stake account is a rewards pool".to_string()),
|
Ok(StakeState::RewardsPool) => Ok("Stake account is a rewards pool".to_string()),
|
||||||
Ok(StakeState::Uninitialized) | Ok(StakeState::Lockup(_)) => {
|
Ok(StakeState::Uninitialized) => Ok("Stake account is uninitialized".to_string()),
|
||||||
Ok("Stake account is uninitialized".to_string())
|
Ok(StakeState::Initialized(authorized, lockup)) => {
|
||||||
|
println!("Stake account is undelegated");
|
||||||
|
show_authorized(&authorized);
|
||||||
|
show_lockup(&lockup);
|
||||||
|
Ok("".to_string())
|
||||||
}
|
}
|
||||||
Err(err) => Err(WalletError::RpcRequestError(format!(
|
Err(err) => Err(WalletError::RpcRequestError(format!(
|
||||||
"Account data could not be deserialized to stake state: {:?}",
|
"Account data could not be deserialized to stake state: {:?}",
|
||||||
@@ -1286,30 +1312,28 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult {
|
|||||||
WalletCommand::Confirm(signature) => process_confirm(&rpc_client, signature),
|
WalletCommand::Confirm(signature) => process_confirm(&rpc_client, signature),
|
||||||
|
|
||||||
// Create vote account
|
// Create vote account
|
||||||
WalletCommand::CreateVoteAccount(
|
WalletCommand::CreateVoteAccount(vote_account_pubkey, vote_init, lamports) => {
|
||||||
vote_account_pubkey,
|
process_create_vote_account(
|
||||||
node_pubkey,
|
|
||||||
commission,
|
|
||||||
lamports,
|
|
||||||
) => process_create_vote_account(
|
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
config,
|
config,
|
||||||
&vote_account_pubkey,
|
&vote_account_pubkey,
|
||||||
&node_pubkey,
|
&vote_init,
|
||||||
*commission,
|
|
||||||
*lamports,
|
*lamports,
|
||||||
),
|
)
|
||||||
|
}
|
||||||
|
|
||||||
WalletCommand::AuthorizeVoter(
|
WalletCommand::VoteAuthorize(
|
||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
authorized_voter_keypair,
|
authorized_keypair,
|
||||||
new_authorized_voter_pubkey,
|
new_authorized_pubkey,
|
||||||
) => process_authorize_voter(
|
vote_authorize,
|
||||||
|
) => process_vote_authorize(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
config,
|
config,
|
||||||
&vote_account_pubkey,
|
&vote_account_pubkey,
|
||||||
&authorized_voter_keypair,
|
&authorized_keypair,
|
||||||
&new_authorized_voter_pubkey,
|
&new_authorized_pubkey,
|
||||||
|
*vote_authorize,
|
||||||
),
|
),
|
||||||
|
|
||||||
WalletCommand::ShowAccount {
|
WalletCommand::ShowAccount {
|
||||||
@@ -1344,6 +1368,7 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult {
|
|||||||
stake_account_keypair,
|
stake_account_keypair,
|
||||||
vote_account_pubkey,
|
vote_account_pubkey,
|
||||||
lamports,
|
lamports,
|
||||||
|
authorized,
|
||||||
force,
|
force,
|
||||||
) => process_delegate_stake(
|
) => process_delegate_stake(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
@@ -1351,6 +1376,7 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult {
|
|||||||
&stake_account_keypair,
|
&stake_account_keypair,
|
||||||
&vote_account_pubkey,
|
&vote_account_pubkey,
|
||||||
*lamports,
|
*lamports,
|
||||||
|
&authorized,
|
||||||
*force,
|
*force,
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -1675,7 +1701,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("authorize-voter")
|
SubCommand::with_name("vote-authorize-voter")
|
||||||
.about("Authorize a new vote signing keypair for the given vote account")
|
.about("Authorize a new vote signing keypair for the given vote account")
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("vote_account_pubkey")
|
Arg::with_name("vote_account_pubkey")
|
||||||
@@ -1687,7 +1713,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
|
|||||||
.help("Vote account in which to set the authorized voter"),
|
.help("Vote account in which to set the authorized voter"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("authorized_voter_keypair_file")
|
Arg::with_name("authorized_keypair_file")
|
||||||
.index(2)
|
.index(2)
|
||||||
.value_name("CURRENT VOTER KEYPAIR FILE")
|
.value_name("CURRENT VOTER KEYPAIR FILE")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
@@ -1696,7 +1722,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
|
|||||||
.help("Keypair file for the currently authorized vote signer"),
|
.help("Keypair file for the currently authorized vote signer"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("new_authorized_voter_pubkey")
|
Arg::with_name("new_authorized_pubkey")
|
||||||
.index(3)
|
.index(3)
|
||||||
.value_name("NEW VOTER PUBKEY")
|
.value_name("NEW VOTER PUBKEY")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
@@ -1705,6 +1731,37 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
|
|||||||
.help("New vote signer to authorize"),
|
.help("New vote signer to authorize"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("vote-authorize-withdrawer")
|
||||||
|
.about("Authorize a new withdraw signing keypair for the given vote account")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("vote_account_pubkey")
|
||||||
|
.index(1)
|
||||||
|
.value_name("VOTE ACCOUNT PUBKEY")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
|
.help("Vote account in which to set the authorized withdrawer"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("authorized_keypair_file")
|
||||||
|
.index(2)
|
||||||
|
.value_name("CURRENT WITHDRAWER KEYPAIR FILE")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.validator(is_keypair)
|
||||||
|
.help("Keypair file for the currently authorized withdrawer"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("new_authorized_pubkey")
|
||||||
|
.index(3)
|
||||||
|
.value_name("NEW WITHDRAWER PUBKEY")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
|
.help("New withdrawer to authorize"),
|
||||||
|
),
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("create-vote-account")
|
SubCommand::with_name("create-vote-account")
|
||||||
.about("Create a vote account")
|
.about("Create a vote account")
|
||||||
@@ -1747,7 +1804,25 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
|
|||||||
.value_name("NUM")
|
.value_name("NUM")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.help("The commission taken on reward redemption (0-255), default: 0"),
|
.help("The commission taken on reward redemption (0-255), default: 0"),
|
||||||
),
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("authorized_voter")
|
||||||
|
.long("authorized-voter")
|
||||||
|
.value_name("PUBKEY")
|
||||||
|
.takes_value(true)
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
|
.help("Public key of the authorized voter (defaults to vote account pubkey)"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("authorized_withdrawer")
|
||||||
|
.long("authorized-withdrawer")
|
||||||
|
.value_name("PUBKEY")
|
||||||
|
.takes_value(true)
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
|
.help("Public key of the authorized withdrawer (defaults to vote account pubkey)"),
|
||||||
|
)
|
||||||
|
|
||||||
|
,
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("show-account")
|
SubCommand::with_name("show-account")
|
||||||
@@ -2425,9 +2500,16 @@ mod tests {
|
|||||||
"42",
|
"42",
|
||||||
"lamports",
|
"lamports",
|
||||||
]);
|
]);
|
||||||
|
let stake_pubkey = keypair.pubkey();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&pubkey, &test_delegate_stake).unwrap(),
|
parse_command(&pubkey, &test_delegate_stake).unwrap(),
|
||||||
WalletCommand::DelegateStake(keypair, pubkey, 42, false)
|
WalletCommand::DelegateStake(
|
||||||
|
keypair,
|
||||||
|
pubkey,
|
||||||
|
42,
|
||||||
|
Authorized::auto(&stake_pubkey),
|
||||||
|
false,
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
let keypair = read_keypair(&keypair_file).unwrap();
|
let keypair = read_keypair(&keypair_file).unwrap();
|
||||||
@@ -2440,9 +2522,16 @@ mod tests {
|
|||||||
"42",
|
"42",
|
||||||
"lamports",
|
"lamports",
|
||||||
]);
|
]);
|
||||||
|
let stake_pubkey = keypair.pubkey();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&pubkey, &test_delegate_stake).unwrap(),
|
parse_command(&pubkey, &test_delegate_stake).unwrap(),
|
||||||
WalletCommand::DelegateStake(keypair, pubkey, 42, true)
|
WalletCommand::DelegateStake(
|
||||||
|
keypair,
|
||||||
|
pubkey,
|
||||||
|
42,
|
||||||
|
Authorized::auto(&stake_pubkey),
|
||||||
|
true
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Test WithdrawStake Subcommand
|
// Test WithdrawStake Subcommand
|
||||||
@@ -2669,14 +2758,27 @@ mod tests {
|
|||||||
|
|
||||||
let bob_pubkey = Pubkey::new_rand();
|
let bob_pubkey = Pubkey::new_rand();
|
||||||
let node_pubkey = Pubkey::new_rand();
|
let node_pubkey = Pubkey::new_rand();
|
||||||
config.command = WalletCommand::CreateVoteAccount(bob_pubkey, node_pubkey, 0, 10);
|
config.command = WalletCommand::CreateVoteAccount(
|
||||||
|
bob_pubkey,
|
||||||
|
VoteInit {
|
||||||
|
node_pubkey,
|
||||||
|
authorized_voter: bob_pubkey,
|
||||||
|
authorized_withdrawer: bob_pubkey,
|
||||||
|
commission: 0,
|
||||||
|
},
|
||||||
|
10,
|
||||||
|
);
|
||||||
let signature = process_command(&config);
|
let signature = process_command(&config);
|
||||||
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
|
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
|
||||||
|
|
||||||
let bob_keypair = Keypair::new();
|
let bob_keypair = Keypair::new();
|
||||||
let new_authorized_voter_pubkey = Pubkey::new_rand();
|
let new_authorized_pubkey = Pubkey::new_rand();
|
||||||
config.command =
|
config.command = WalletCommand::VoteAuthorize(
|
||||||
WalletCommand::AuthorizeVoter(bob_pubkey, bob_keypair, new_authorized_voter_pubkey);
|
bob_pubkey,
|
||||||
|
bob_keypair,
|
||||||
|
new_authorized_pubkey,
|
||||||
|
VoteAuthorize::Voter,
|
||||||
|
);
|
||||||
let signature = process_command(&config);
|
let signature = process_command(&config);
|
||||||
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
|
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
|
||||||
|
|
||||||
@@ -2826,10 +2928,24 @@ mod tests {
|
|||||||
};
|
};
|
||||||
assert!(process_command(&config).is_err());
|
assert!(process_command(&config).is_err());
|
||||||
|
|
||||||
config.command = WalletCommand::CreateVoteAccount(bob_pubkey, node_pubkey, 0, 10);
|
config.command = WalletCommand::CreateVoteAccount(
|
||||||
|
bob_pubkey,
|
||||||
|
VoteInit {
|
||||||
|
node_pubkey,
|
||||||
|
authorized_voter: bob_pubkey,
|
||||||
|
authorized_withdrawer: bob_pubkey,
|
||||||
|
commission: 0,
|
||||||
|
},
|
||||||
|
10,
|
||||||
|
);
|
||||||
assert!(process_command(&config).is_err());
|
assert!(process_command(&config).is_err());
|
||||||
|
|
||||||
config.command = WalletCommand::AuthorizeVoter(bob_pubkey, Keypair::new(), bob_pubkey);
|
config.command = WalletCommand::VoteAuthorize(
|
||||||
|
bob_pubkey,
|
||||||
|
Keypair::new(),
|
||||||
|
bob_pubkey,
|
||||||
|
VoteAuthorize::Voter,
|
||||||
|
);
|
||||||
assert!(process_command(&config).is_err());
|
assert!(process_command(&config).is_err());
|
||||||
|
|
||||||
config.command = WalletCommand::GetSlot;
|
config.command = WalletCommand::GetSlot;
|
||||||
|
@@ -228,7 +228,7 @@ mod tests {
|
|||||||
let ancestors = vec![3, 4, 5, 7, 9, 11];
|
let ancestors = vec![3, 4, 5, 7, 9, 11];
|
||||||
let mut confidence = HashMap::new();
|
let mut confidence = HashMap::new();
|
||||||
let lamports = 5;
|
let lamports = 5;
|
||||||
let mut vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
|
let mut vote_state = VoteState::default();
|
||||||
|
|
||||||
let root = ancestors.last().unwrap();
|
let root = ancestors.last().unwrap();
|
||||||
vote_state.root_slot = Some(*root);
|
vote_state.root_slot = Some(*root);
|
||||||
@@ -251,7 +251,7 @@ mod tests {
|
|||||||
let ancestors = vec![3, 4, 5, 7, 9, 11];
|
let ancestors = vec![3, 4, 5, 7, 9, 11];
|
||||||
let mut confidence = HashMap::new();
|
let mut confidence = HashMap::new();
|
||||||
let lamports = 5;
|
let lamports = 5;
|
||||||
let mut vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
|
let mut vote_state = VoteState::default();
|
||||||
|
|
||||||
let root = ancestors[2];
|
let root = ancestors[2];
|
||||||
vote_state.root_slot = Some(root);
|
vote_state.root_slot = Some(root);
|
||||||
@@ -281,7 +281,7 @@ mod tests {
|
|||||||
let ancestors = vec![3, 4, 5, 7, 9, 10, 11];
|
let ancestors = vec![3, 4, 5, 7, 9, 10, 11];
|
||||||
let mut confidence = HashMap::new();
|
let mut confidence = HashMap::new();
|
||||||
let lamports = 5;
|
let lamports = 5;
|
||||||
let mut vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
|
let mut vote_state = VoteState::default();
|
||||||
|
|
||||||
let root = ancestors[2];
|
let root = ancestors[2];
|
||||||
vote_state.root_slot = Some(root);
|
vote_state.root_slot = Some(root);
|
||||||
@@ -319,18 +319,20 @@ mod tests {
|
|||||||
mut genesis_block, ..
|
mut genesis_block, ..
|
||||||
} = create_genesis_block(10_000);
|
} = create_genesis_block(10_000);
|
||||||
|
|
||||||
|
let sk1 = Pubkey::new_rand();
|
||||||
let pk1 = Pubkey::new_rand();
|
let pk1 = Pubkey::new_rand();
|
||||||
let mut vote_account1 = vote_state::create_account(&pk1, &Pubkey::new_rand(), 0, 100);
|
let mut vote_account1 = vote_state::create_account(&pk1, &Pubkey::new_rand(), 0, 100);
|
||||||
let stake_account1 = stake_state::create_account(&pk1, &vote_account1, 100);
|
let stake_account1 = stake_state::create_account(&sk1, &pk1, &vote_account1, 100);
|
||||||
|
let sk2 = Pubkey::new_rand();
|
||||||
let pk2 = Pubkey::new_rand();
|
let pk2 = Pubkey::new_rand();
|
||||||
let mut vote_account2 = vote_state::create_account(&pk2, &Pubkey::new_rand(), 0, 50);
|
let mut vote_account2 = vote_state::create_account(&pk2, &Pubkey::new_rand(), 0, 50);
|
||||||
let stake_account2 = stake_state::create_account(&pk2, &vote_account2, 50);
|
let stake_account2 = stake_state::create_account(&sk2, &pk2, &vote_account2, 50);
|
||||||
|
|
||||||
genesis_block.accounts.extend(vec![
|
genesis_block.accounts.extend(vec![
|
||||||
(pk1, vote_account1.clone()),
|
(pk1, vote_account1.clone()),
|
||||||
(Pubkey::new_rand(), stake_account1),
|
(sk1, stake_account1),
|
||||||
(pk2, vote_account2.clone()),
|
(pk2, vote_account2.clone()),
|
||||||
(Pubkey::new_rand(), stake_account2),
|
(sk2, stake_account2),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Create bank
|
// Create bank
|
||||||
|
@@ -1,9 +1,7 @@
|
|||||||
use solana_runtime::bank::Bank;
|
use solana_runtime::bank::Bank;
|
||||||
use solana_sdk::account::Account;
|
use solana_sdk::{account::Account, pubkey::Pubkey};
|
||||||
use solana_sdk::pubkey::Pubkey;
|
|
||||||
use solana_vote_api::vote_state::VoteState;
|
use solana_vote_api::vote_state::VoteState;
|
||||||
use std::borrow::Borrow;
|
use std::{borrow::Borrow, collections::HashMap};
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
/// Looks through vote accounts, and finds the latest slot that has achieved
|
/// Looks through vote accounts, and finds the latest slot that has achieved
|
||||||
/// supermajority lockout
|
/// supermajority lockout
|
||||||
@@ -99,14 +97,18 @@ where
|
|||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::genesis_utils::{create_genesis_block, GenesisBlockInfo, BOOTSTRAP_LEADER_LAMPORTS};
|
use crate::genesis_utils::{create_genesis_block, GenesisBlockInfo, BOOTSTRAP_LEADER_LAMPORTS};
|
||||||
use solana_sdk::instruction::Instruction;
|
use solana_sdk::{
|
||||||
use solana_sdk::pubkey::Pubkey;
|
instruction::Instruction,
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
pubkey::Pubkey,
|
||||||
use solana_sdk::sysvar::stake_history::{self, StakeHistory};
|
signature::{Keypair, KeypairUtil},
|
||||||
use solana_sdk::transaction::Transaction;
|
sysvar::stake_history::{self, StakeHistory},
|
||||||
use solana_stake_api::stake_instruction;
|
transaction::Transaction,
|
||||||
use solana_stake_api::stake_state::Stake;
|
};
|
||||||
use solana_vote_api::vote_instruction;
|
use solana_stake_api::{
|
||||||
|
stake_instruction,
|
||||||
|
stake_state::{Authorized, Stake},
|
||||||
|
};
|
||||||
|
use solana_vote_api::{vote_instruction, vote_state::VoteInit};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
fn new_from_parent(parent: &Arc<Bank>, slot: u64) -> Bank {
|
fn new_from_parent(parent: &Arc<Bank>, slot: u64) -> Bank {
|
||||||
@@ -140,8 +142,12 @@ pub(crate) mod tests {
|
|||||||
vote_instruction::create_account(
|
vote_instruction::create_account(
|
||||||
&from_account.pubkey(),
|
&from_account.pubkey(),
|
||||||
vote_pubkey,
|
vote_pubkey,
|
||||||
node_pubkey,
|
&VoteInit {
|
||||||
0,
|
node_pubkey: *node_pubkey,
|
||||||
|
authorized_voter: *vote_pubkey,
|
||||||
|
authorized_withdrawer: *vote_pubkey,
|
||||||
|
commission: 0,
|
||||||
|
},
|
||||||
amount,
|
amount,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -157,6 +163,7 @@ pub(crate) mod tests {
|
|||||||
&stake_account_pubkey,
|
&stake_account_pubkey,
|
||||||
vote_pubkey,
|
vote_pubkey,
|
||||||
amount,
|
amount,
|
||||||
|
&Authorized::auto(&stake_account_pubkey),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -288,15 +295,28 @@ pub(crate) mod tests {
|
|||||||
fn test_to_staked_nodes() {
|
fn test_to_staked_nodes() {
|
||||||
let mut stakes = Vec::new();
|
let mut stakes = Vec::new();
|
||||||
let node1 = Pubkey::new_rand();
|
let node1 = Pubkey::new_rand();
|
||||||
let node2 = Pubkey::new_rand();
|
|
||||||
|
|
||||||
// Node 1 has stake of 3
|
// Node 1 has stake of 3
|
||||||
for i in 0..3 {
|
for i in 0..3 {
|
||||||
stakes.push((i, VoteState::new(&Pubkey::new_rand(), &node1, 0)));
|
stakes.push((
|
||||||
|
i,
|
||||||
|
VoteState::new(&VoteInit {
|
||||||
|
node_pubkey: node1,
|
||||||
|
..VoteInit::default()
|
||||||
|
}),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Node 1 has stake of 5
|
// Node 1 has stake of 5
|
||||||
stakes.push((5, VoteState::new(&Pubkey::new_rand(), &node2, 0)));
|
let node2 = Pubkey::new_rand();
|
||||||
|
|
||||||
|
stakes.push((
|
||||||
|
5,
|
||||||
|
VoteState::new(&VoteInit {
|
||||||
|
node_pubkey: node2,
|
||||||
|
..VoteInit::default()
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
|
||||||
let result = to_staked_nodes(stakes.into_iter());
|
let result = to_staked_nodes(stakes.into_iter());
|
||||||
assert_eq!(result.len(), 2);
|
assert_eq!(result.len(), 2);
|
||||||
|
@@ -315,6 +315,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
let stake_account = stake_state::create_account(
|
let stake_account = stake_state::create_account(
|
||||||
|
&bootstrap_stake_keypair.pubkey(),
|
||||||
&bootstrap_vote_keypair.pubkey(),
|
&bootstrap_vote_keypair.pubkey(),
|
||||||
&vote_account,
|
&vote_account,
|
||||||
bootstrap_leader_stake_lamports,
|
bootstrap_leader_stake_lamports,
|
||||||
|
@@ -12,8 +12,7 @@ use solana_core::{
|
|||||||
};
|
};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
client::SyncClient,
|
client::SyncClient,
|
||||||
clock::DEFAULT_TICKS_PER_SLOT,
|
clock::{DEFAULT_SLOTS_PER_EPOCH, DEFAULT_SLOTS_PER_SEGMENT, DEFAULT_TICKS_PER_SLOT},
|
||||||
clock::{DEFAULT_SLOTS_PER_EPOCH, DEFAULT_SLOTS_PER_SEGMENT},
|
|
||||||
genesis_block::GenesisBlock,
|
genesis_block::GenesisBlock,
|
||||||
message::Message,
|
message::Message,
|
||||||
poh_config::PohConfig,
|
poh_config::PohConfig,
|
||||||
@@ -22,9 +21,15 @@ use solana_sdk::{
|
|||||||
system_transaction,
|
system_transaction,
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use solana_stake_api::{config as stake_config, stake_instruction, stake_state::StakeState};
|
use solana_stake_api::{
|
||||||
|
config as stake_config, stake_instruction,
|
||||||
|
stake_state::{Authorized as StakeAuthorized, StakeState},
|
||||||
|
};
|
||||||
use solana_storage_api::{storage_contract, storage_instruction};
|
use solana_storage_api::{storage_contract, storage_instruction};
|
||||||
use solana_vote_api::{vote_instruction, vote_state::VoteState};
|
use solana_vote_api::{
|
||||||
|
vote_instruction,
|
||||||
|
vote_state::{VoteInit, VoteState},
|
||||||
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fs::remove_dir_all,
|
fs::remove_dir_all,
|
||||||
@@ -436,8 +441,12 @@ impl LocalCluster {
|
|||||||
vote_instruction::create_account(
|
vote_instruction::create_account(
|
||||||
&from_account.pubkey(),
|
&from_account.pubkey(),
|
||||||
&vote_account_pubkey,
|
&vote_account_pubkey,
|
||||||
&node_pubkey,
|
&VoteInit {
|
||||||
0,
|
node_pubkey,
|
||||||
|
authorized_voter: vote_account_pubkey,
|
||||||
|
authorized_withdrawer: vote_account_pubkey,
|
||||||
|
commission: 0,
|
||||||
|
},
|
||||||
amount,
|
amount,
|
||||||
),
|
),
|
||||||
client.get_recent_blockhash().unwrap().0,
|
client.get_recent_blockhash().unwrap().0,
|
||||||
@@ -456,6 +465,7 @@ impl LocalCluster {
|
|||||||
&stake_account_pubkey,
|
&stake_account_pubkey,
|
||||||
&vote_account_pubkey,
|
&vote_account_pubkey,
|
||||||
amount,
|
amount,
|
||||||
|
&StakeAuthorized::auto(&stake_account_pubkey),
|
||||||
),
|
),
|
||||||
client.get_recent_blockhash().unwrap().0,
|
client.get_recent_blockhash().unwrap().0,
|
||||||
);
|
);
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
config, id,
|
config, id,
|
||||||
stake_state::{StakeAccount, StakeState},
|
stake_state::{Authorized, Lockup, StakeAccount, StakeAuthorize, StakeState},
|
||||||
};
|
};
|
||||||
use bincode::deserialize;
|
use bincode::deserialize;
|
||||||
use log::*;
|
use log::*;
|
||||||
@@ -8,7 +8,6 @@ use num_derive::{FromPrimitive, ToPrimitive};
|
|||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::KeyedAccount,
|
account::KeyedAccount,
|
||||||
clock::Slot,
|
|
||||||
instruction::{AccountMeta, Instruction, InstructionError},
|
instruction::{AccountMeta, Instruction, InstructionError},
|
||||||
instruction_processor_utils::DecodeError,
|
instruction_processor_utils::DecodeError,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
@@ -36,30 +35,33 @@ impl std::fmt::Display for StakeError {
|
|||||||
}
|
}
|
||||||
impl std::error::Error for StakeError {}
|
impl std::error::Error for StakeError {}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
pub enum StakeInstruction {
|
pub enum StakeInstruction {
|
||||||
/// `Lockup` a stake until the specified slot
|
/// `Initialize` a stake with Lockup and Authorized information
|
||||||
///
|
///
|
||||||
/// Expects 1 Account:
|
/// Expects 1 Account:
|
||||||
/// 0 - Uninitialized StakeAccount to be lockup'd
|
/// 0 - Uninitialized StakeAccount
|
||||||
///
|
///
|
||||||
/// The Slot parameter denotes slot height at which this stake
|
/// Authorized carries pubkeys that must sign staker transactions
|
||||||
/// will allow withdrawal from the stake account.
|
/// and withdrawer transactions.
|
||||||
/// The Pubkey parameter denotes a "custodian" account, the only
|
/// Lockup carries information about withdrawal restrictions
|
||||||
/// account to which this stake will honor a withdrawal *before*
|
|
||||||
// lockup expires.
|
|
||||||
///
|
///
|
||||||
Lockup((Slot, Pubkey)),
|
Initialize(Authorized, Lockup),
|
||||||
|
|
||||||
/// Authorize a system account to manage stake
|
/// Authorize a key to manage stake or withdrawal
|
||||||
|
/// requires Authorized::staker or Authorized::withdrawer
|
||||||
|
/// signature, depending on which key's being updated
|
||||||
///
|
///
|
||||||
/// Expects 1 Account:
|
/// Expects 1 Account:
|
||||||
/// 0 - Locked-up or delegated StakeAccount to be updated with authorized staker
|
/// 0 - StakeAccount to be updated with the Pubkey for
|
||||||
Authorize(Pubkey),
|
/// authorization
|
||||||
|
Authorize(Pubkey, StakeAuthorize),
|
||||||
|
|
||||||
/// `Delegate` a stake to a particular vote account
|
/// `Delegate` a stake to a particular vote account
|
||||||
|
/// requires Authorized::staker signature
|
||||||
///
|
///
|
||||||
/// Expects 4 Accounts:
|
/// Expects 4 Accounts:
|
||||||
/// 0 - Lockup'd StakeAccount to be delegated <= transaction must have this signature
|
/// 0 - Initialized StakeAccount to be delegated
|
||||||
/// 1 - VoteAccount to which this Stake will be delegated
|
/// 1 - VoteAccount to which this Stake will be delegated
|
||||||
/// 2 - Clock sysvar Account that carries clock bank epoch
|
/// 2 - Clock sysvar Account that carries clock bank epoch
|
||||||
/// 3 - Config Account that carries stake config
|
/// 3 - Config Account that carries stake config
|
||||||
@@ -71,9 +73,10 @@ pub enum StakeInstruction {
|
|||||||
DelegateStake,
|
DelegateStake,
|
||||||
|
|
||||||
/// Redeem credits in the stake account
|
/// Redeem credits in the stake account
|
||||||
|
/// requires Authorized::staker signature
|
||||||
///
|
///
|
||||||
/// Expects 5 Accounts:
|
/// Expects 5 Accounts:
|
||||||
/// 0 - Delegate StakeAccount to be updated with rewards
|
/// 0 - StakeAccount to be updated with rewards
|
||||||
/// 1 - VoteAccount to which the Stake is delegated,
|
/// 1 - VoteAccount to which the Stake is delegated,
|
||||||
/// 2 - RewardsPool Stake Account from which to redeem credits
|
/// 2 - RewardsPool Stake Account from which to redeem credits
|
||||||
/// 3 - Rewards sysvar Account that carries points values
|
/// 3 - Rewards sysvar Account that carries points values
|
||||||
@@ -81,21 +84,23 @@ pub enum StakeInstruction {
|
|||||||
RedeemVoteCredits,
|
RedeemVoteCredits,
|
||||||
|
|
||||||
/// Withdraw unstaked lamports from the stake account
|
/// Withdraw unstaked lamports from the stake account
|
||||||
|
/// requires Authorized::withdrawer signature
|
||||||
///
|
///
|
||||||
/// Expects 4 Accounts:
|
/// Expects 4 Accounts:
|
||||||
/// 0 - Delegate StakeAccount <= transaction must have this signature
|
/// 0 - StakeAccount from which to withdraw
|
||||||
/// 1 - System account to which the lamports will be transferred,
|
/// 1 - System account to which the lamports will be transferred,
|
||||||
/// 2 - Syscall Account that carries epoch
|
/// 2 - Syscall Account that carries epoch
|
||||||
/// 3 - StakeHistory sysvar that carries stake warmup/cooldown history
|
/// 3 - StakeHistory sysvar that carries stake warmup/cooldown history
|
||||||
///
|
///
|
||||||
/// The u64 is the portion of the Stake account balance to be withdrawn,
|
/// The u64 is the portion of the Stake account balance to be withdrawn,
|
||||||
/// must be <= StakeAccount.lamports - staked lamports
|
/// must be <= StakeAccount.lamports - staked lamports.
|
||||||
Withdraw(u64),
|
Withdraw(u64),
|
||||||
|
|
||||||
/// Deactivates the stake in the account
|
/// Deactivates the stake in the account
|
||||||
|
/// requires Authorized::staker signature
|
||||||
///
|
///
|
||||||
/// Expects 3 Accounts:
|
/// Expects 3 Accounts:
|
||||||
/// 0 - Delegate StakeAccount <= transaction must have this signature
|
/// 0 - Delegate StakeAccount
|
||||||
/// 1 - VoteAccount to which the Stake is delegated
|
/// 1 - VoteAccount to which the Stake is delegated
|
||||||
/// 2 - Syscall Account that carries epoch
|
/// 2 - Syscall Account that carries epoch
|
||||||
///
|
///
|
||||||
@@ -106,8 +111,8 @@ pub fn create_stake_account_with_lockup(
|
|||||||
from_pubkey: &Pubkey,
|
from_pubkey: &Pubkey,
|
||||||
stake_pubkey: &Pubkey,
|
stake_pubkey: &Pubkey,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
lockup: Slot,
|
authorized: &Authorized,
|
||||||
custodian: &Pubkey,
|
lockup: &Lockup,
|
||||||
) -> Vec<Instruction> {
|
) -> Vec<Instruction> {
|
||||||
vec![
|
vec![
|
||||||
system_instruction::create_account(
|
system_instruction::create_account(
|
||||||
@@ -119,7 +124,7 @@ pub fn create_stake_account_with_lockup(
|
|||||||
),
|
),
|
||||||
Instruction::new(
|
Instruction::new(
|
||||||
id(),
|
id(),
|
||||||
&StakeInstruction::Lockup((lockup, *custodian)),
|
&StakeInstruction::Initialize(*authorized, *lockup),
|
||||||
vec![AccountMeta::new(*stake_pubkey, false)],
|
vec![AccountMeta::new(*stake_pubkey, false)],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@@ -129,8 +134,15 @@ pub fn create_stake_account(
|
|||||||
from_pubkey: &Pubkey,
|
from_pubkey: &Pubkey,
|
||||||
stake_pubkey: &Pubkey,
|
stake_pubkey: &Pubkey,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
|
authorized: &Authorized,
|
||||||
) -> Vec<Instruction> {
|
) -> Vec<Instruction> {
|
||||||
create_stake_account_with_lockup(from_pubkey, stake_pubkey, lamports, 0, &Pubkey::default())
|
create_stake_account_with_lockup(
|
||||||
|
from_pubkey,
|
||||||
|
stake_pubkey,
|
||||||
|
lamports,
|
||||||
|
authorized,
|
||||||
|
&Lockup::default(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_stake_account_and_delegate_stake(
|
pub fn create_stake_account_and_delegate_stake(
|
||||||
@@ -138,21 +150,23 @@ pub fn create_stake_account_and_delegate_stake(
|
|||||||
stake_pubkey: &Pubkey,
|
stake_pubkey: &Pubkey,
|
||||||
vote_pubkey: &Pubkey,
|
vote_pubkey: &Pubkey,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
|
authorized: &Authorized,
|
||||||
) -> Vec<Instruction> {
|
) -> Vec<Instruction> {
|
||||||
let mut instructions = create_stake_account(from_pubkey, stake_pubkey, lamports);
|
let mut instructions = create_stake_account(from_pubkey, stake_pubkey, lamports, authorized);
|
||||||
instructions.push(delegate_stake(stake_pubkey, vote_pubkey));
|
instructions.push(delegate_stake(stake_pubkey, vote_pubkey));
|
||||||
instructions
|
instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
fn metas_for_authorized_staker(
|
// for instructions that whose authorized signer may differ from the account's pubkey
|
||||||
stake_pubkey: &Pubkey,
|
fn metas_for_authorized_signer(
|
||||||
authorized_pubkey: &Pubkey, // currently authorized
|
account_pubkey: &Pubkey,
|
||||||
|
authorized_signer: &Pubkey, // currently authorized
|
||||||
other_params: &[AccountMeta],
|
other_params: &[AccountMeta],
|
||||||
) -> Vec<AccountMeta> {
|
) -> Vec<AccountMeta> {
|
||||||
let is_own_signer = authorized_pubkey == stake_pubkey;
|
let is_own_signer = authorized_signer == account_pubkey;
|
||||||
|
|
||||||
// stake account
|
// vote account
|
||||||
let mut account_metas = vec![AccountMeta::new(*stake_pubkey, is_own_signer)];
|
let mut account_metas = vec![AccountMeta::new(*account_pubkey, is_own_signer)];
|
||||||
|
|
||||||
for meta in other_params {
|
for meta in other_params {
|
||||||
account_metas.push(meta.clone());
|
account_metas.push(meta.clone());
|
||||||
@@ -160,7 +174,7 @@ fn metas_for_authorized_staker(
|
|||||||
|
|
||||||
// append signer at the end
|
// append signer at the end
|
||||||
if !is_own_signer {
|
if !is_own_signer {
|
||||||
account_metas.push(AccountMeta::new_credit_only(*authorized_pubkey, true)) // signer
|
account_metas.push(AccountMeta::new_credit_only(*authorized_signer, true)) // signer
|
||||||
}
|
}
|
||||||
|
|
||||||
account_metas
|
account_metas
|
||||||
@@ -170,12 +184,13 @@ pub fn authorize(
|
|||||||
stake_pubkey: &Pubkey,
|
stake_pubkey: &Pubkey,
|
||||||
authorized_pubkey: &Pubkey,
|
authorized_pubkey: &Pubkey,
|
||||||
new_authorized_pubkey: &Pubkey,
|
new_authorized_pubkey: &Pubkey,
|
||||||
|
stake_authorize: StakeAuthorize,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
let account_metas = metas_for_authorized_staker(stake_pubkey, authorized_pubkey, &[]);
|
let account_metas = metas_for_authorized_signer(stake_pubkey, authorized_pubkey, &[]);
|
||||||
|
|
||||||
Instruction::new(
|
Instruction::new(
|
||||||
id(),
|
id(),
|
||||||
&StakeInstruction::Authorize(*new_authorized_pubkey),
|
&StakeInstruction::Authorize(*new_authorized_pubkey, stake_authorize),
|
||||||
account_metas,
|
account_metas,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -239,8 +254,10 @@ pub fn process_instruction(
|
|||||||
|
|
||||||
// TODO: data-driven unpack and dispatch of KeyedAccounts
|
// TODO: data-driven unpack and dispatch of KeyedAccounts
|
||||||
match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
|
match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
|
||||||
StakeInstruction::Lockup((lockup, custodian)) => me.lockup(lockup, &custodian),
|
StakeInstruction::Initialize(authorized, lockup) => me.initialize(&authorized, &lockup),
|
||||||
StakeInstruction::Authorize(authorized_pubkey) => me.authorize(&authorized_pubkey, &rest),
|
StakeInstruction::Authorize(authorized_pubkey, stake_authorize) => {
|
||||||
|
me.authorize(&authorized_pubkey, stake_authorize, &rest)
|
||||||
|
}
|
||||||
StakeInstruction::DelegateStake => {
|
StakeInstruction::DelegateStake => {
|
||||||
if rest.len() < 3 {
|
if rest.len() < 3 {
|
||||||
Err(InstructionError::InvalidInstructionData)?;
|
Err(InstructionError::InvalidInstructionData)?;
|
||||||
@@ -366,7 +383,11 @@ mod tests {
|
|||||||
super::process_instruction(
|
super::process_instruction(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
&mut [],
|
&mut [],
|
||||||
&serialize(&StakeInstruction::Lockup((0, Pubkey::default()))).unwrap(),
|
&serialize(&StakeInstruction::Initialize(
|
||||||
|
Authorized::default(),
|
||||||
|
Lockup::default()
|
||||||
|
))
|
||||||
|
.unwrap(),
|
||||||
),
|
),
|
||||||
Err(InstructionError::InvalidInstructionData),
|
Err(InstructionError::InvalidInstructionData),
|
||||||
);
|
);
|
||||||
|
@@ -18,12 +18,12 @@ use solana_sdk::{
|
|||||||
};
|
};
|
||||||
use solana_vote_api::vote_state::VoteState;
|
use solana_vote_api::vote_state::VoteState;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum StakeState {
|
pub enum StakeState {
|
||||||
Uninitialized,
|
Uninitialized,
|
||||||
Lockup(Lockup),
|
Initialized(Authorized, Lockup),
|
||||||
Stake(Stake),
|
Stake(Authorized, Lockup, Stake),
|
||||||
RewardsPool,
|
RewardsPool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,26 +43,48 @@ impl StakeState {
|
|||||||
Self::from(account).and_then(|state: Self| state.stake())
|
Self::from(account).and_then(|state: Self| state.stake())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn authorized_from(account: &Account) -> Option<Authorized> {
|
||||||
|
Self::from(account).and_then(|state: Self| state.authorized())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stake(&self) -> Option<Stake> {
|
pub fn stake(&self) -> Option<Stake> {
|
||||||
match self {
|
match self {
|
||||||
StakeState::Stake(stake) => Some(stake.clone()),
|
StakeState::Stake(_authorized, _lockup, stake) => Some(*stake),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn authorized(&self) -> Option<Authorized> {
|
||||||
|
match self {
|
||||||
|
StakeState::Stake(authorized, _lockup, _stake) => Some(*authorized),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||||
pub struct Lockup {
|
pub enum StakeAuthorize {
|
||||||
/// slot height at which this stake will allow withdrawal, unless to the custodian
|
Staker,
|
||||||
pub slot: Slot,
|
Withdrawer,
|
||||||
/// custodian account, the only account to which this stake will honor a
|
|
||||||
/// withdrawal *before* lockup expires
|
|
||||||
pub custodian: Pubkey,
|
|
||||||
/// alternate signer that is enabled to act on the Stake account
|
|
||||||
pub authority: Pubkey,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||||
|
pub struct Lockup {
|
||||||
|
/// slot height at which this stake will allow withdrawal, unless
|
||||||
|
/// to the custodian
|
||||||
|
pub slot: Slot,
|
||||||
|
/// custodian account, the only account to which this stake will honor a
|
||||||
|
/// withdrawal before lockup expires. After lockup expires, custodian
|
||||||
|
/// is irrelevant
|
||||||
|
pub custodian: Pubkey,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||||
|
pub struct Authorized {
|
||||||
|
pub staker: Pubkey,
|
||||||
|
pub withdrawer: Pubkey,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||||
pub struct Stake {
|
pub struct Stake {
|
||||||
/// most recently delegated vote account pubkey
|
/// most recently delegated vote account pubkey
|
||||||
pub voter_pubkey: Pubkey,
|
pub voter_pubkey: Pubkey,
|
||||||
@@ -78,8 +100,6 @@ pub struct Stake {
|
|||||||
pub deactivation_epoch: Epoch,
|
pub deactivation_epoch: Epoch,
|
||||||
/// stake config (warmup, etc.)
|
/// stake config (warmup, etc.)
|
||||||
pub config: Config,
|
pub config: Config,
|
||||||
/// the Lockup information, see above
|
|
||||||
pub lockup: Lockup,
|
|
||||||
/// history of prior delegates and the epoch ranges for which
|
/// history of prior delegates and the epoch ranges for which
|
||||||
/// they were set, circular buffer
|
/// they were set, circular buffer
|
||||||
pub prior_delegates: [(Pubkey, Epoch, Epoch); MAX_PRIOR_DELEGATES],
|
pub prior_delegates: [(Pubkey, Epoch, Epoch); MAX_PRIOR_DELEGATES],
|
||||||
@@ -92,7 +112,6 @@ const MAX_PRIOR_DELEGATES: usize = 32; // this is how many epochs a stake is exp
|
|||||||
impl Default for Stake {
|
impl Default for Stake {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
lockup: Lockup::default(),
|
|
||||||
voter_pubkey: Pubkey::default(),
|
voter_pubkey: Pubkey::default(),
|
||||||
voter_pubkey_epoch: 0,
|
voter_pubkey_epoch: 0,
|
||||||
credits_observed: 0,
|
credits_observed: 0,
|
||||||
@@ -106,20 +125,54 @@ impl Default for Stake {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Authorized {
|
||||||
|
pub fn auto(authorized: &Pubkey) -> Self {
|
||||||
|
Self {
|
||||||
|
staker: *authorized,
|
||||||
|
withdrawer: *authorized,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn check(
|
||||||
|
&self,
|
||||||
|
stake_signer: Option<&Pubkey>,
|
||||||
|
other_signers: &[KeyedAccount],
|
||||||
|
stake_authorize: StakeAuthorize,
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
|
let authorized = match stake_authorize {
|
||||||
|
StakeAuthorize::Staker => Some(&self.staker),
|
||||||
|
StakeAuthorize::Withdrawer => Some(&self.withdrawer),
|
||||||
|
};
|
||||||
|
if stake_signer != authorized
|
||||||
|
&& other_signers
|
||||||
|
.iter()
|
||||||
|
.all(|account| account.signer_key() != authorized)
|
||||||
|
{
|
||||||
|
Err(InstructionError::MissingRequiredSignature)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn authorize(
|
||||||
|
&mut self,
|
||||||
|
stake_signer: Option<&Pubkey>,
|
||||||
|
other_signers: &[KeyedAccount],
|
||||||
|
new_authorized: &Pubkey,
|
||||||
|
stake_authorize: StakeAuthorize,
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
|
self.check(stake_signer, other_signers, stake_authorize)?;
|
||||||
|
match stake_authorize {
|
||||||
|
StakeAuthorize::Staker => self.staker = *new_authorized,
|
||||||
|
StakeAuthorize::Withdrawer => self.withdrawer = *new_authorized,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Stake {
|
impl Stake {
|
||||||
fn is_bootstrap(&self) -> bool {
|
fn is_bootstrap(&self) -> bool {
|
||||||
self.activation_epoch == std::u64::MAX
|
self.activation_epoch == std::u64::MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_authorized(
|
|
||||||
&self,
|
|
||||||
stake_pubkey_signer: Option<&Pubkey>,
|
|
||||||
other_signers: &[KeyedAccount],
|
|
||||||
) -> Result<(), InstructionError> {
|
|
||||||
self.lockup
|
|
||||||
.check_authorized(stake_pubkey_signer, other_signers)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stake(&self, epoch: Epoch, history: Option<&StakeHistory>) -> u64 {
|
pub fn stake(&self, epoch: Epoch, history: Option<&StakeHistory>) -> u64 {
|
||||||
self.stake_activating_and_deactivating(epoch, history).0
|
self.stake_activating_and_deactivating(epoch, history).0
|
||||||
}
|
}
|
||||||
@@ -310,7 +363,6 @@ impl Stake {
|
|||||||
vote_state,
|
vote_state,
|
||||||
std::u64::MAX,
|
std::u64::MAX,
|
||||||
&Config::default(),
|
&Config::default(),
|
||||||
&Lockup::default(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,7 +392,6 @@ impl Stake {
|
|||||||
vote_state: &VoteState,
|
vote_state: &VoteState,
|
||||||
activation_epoch: Epoch,
|
activation_epoch: Epoch,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
lockup: &Lockup,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
stake,
|
stake,
|
||||||
@@ -349,7 +400,6 @@ impl Stake {
|
|||||||
voter_pubkey_epoch: activation_epoch,
|
voter_pubkey_epoch: activation_epoch,
|
||||||
credits_observed: vote_state.credits(),
|
credits_observed: vote_state.credits(),
|
||||||
config: *config,
|
config: *config,
|
||||||
lockup: *lockup,
|
|
||||||
..Stake::default()
|
..Stake::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -359,29 +409,16 @@ impl Stake {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Lockup {
|
|
||||||
fn check_authorized(
|
|
||||||
&self,
|
|
||||||
stake_pubkey_signer: Option<&Pubkey>,
|
|
||||||
other_signers: &[KeyedAccount],
|
|
||||||
) -> Result<(), InstructionError> {
|
|
||||||
let authorized = Some(&self.authority);
|
|
||||||
if stake_pubkey_signer != authorized
|
|
||||||
&& other_signers
|
|
||||||
.iter()
|
|
||||||
.all(|account| account.signer_key() != authorized)
|
|
||||||
{
|
|
||||||
return Err(InstructionError::MissingRequiredSignature);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait StakeAccount {
|
pub trait StakeAccount {
|
||||||
fn lockup(&mut self, slot: Slot, custodian: &Pubkey) -> Result<(), InstructionError>;
|
fn initialize(
|
||||||
|
&mut self,
|
||||||
|
authorized: &Authorized,
|
||||||
|
lockup: &Lockup,
|
||||||
|
) -> Result<(), InstructionError>;
|
||||||
fn authorize(
|
fn authorize(
|
||||||
&mut self,
|
&mut self,
|
||||||
authorized_pubkey: &Pubkey,
|
authority: &Pubkey,
|
||||||
|
stake_authorize: StakeAuthorize,
|
||||||
other_signers: &[KeyedAccount],
|
other_signers: &[KeyedAccount],
|
||||||
) -> Result<(), InstructionError>;
|
) -> Result<(), InstructionError>;
|
||||||
fn delegate_stake(
|
fn delegate_stake(
|
||||||
@@ -415,13 +452,13 @@ pub trait StakeAccount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> StakeAccount for KeyedAccount<'a> {
|
impl<'a> StakeAccount for KeyedAccount<'a> {
|
||||||
fn lockup(&mut self, slot: Slot, custodian: &Pubkey) -> Result<(), InstructionError> {
|
fn initialize(
|
||||||
|
&mut self,
|
||||||
|
authorized: &Authorized,
|
||||||
|
lockup: &Lockup,
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
if let StakeState::Uninitialized = self.state()? {
|
if let StakeState::Uninitialized = self.state()? {
|
||||||
self.set_state(&StakeState::Lockup(Lockup {
|
self.set_state(&StakeState::Initialized(*authorized, *lockup))
|
||||||
slot,
|
|
||||||
custodian: *custodian,
|
|
||||||
authority: *self.unsigned_key(),
|
|
||||||
}))
|
|
||||||
} else {
|
} else {
|
||||||
Err(InstructionError::InvalidAccountData)
|
Err(InstructionError::InvalidAccountData)
|
||||||
}
|
}
|
||||||
@@ -432,17 +469,17 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||||||
fn authorize(
|
fn authorize(
|
||||||
&mut self,
|
&mut self,
|
||||||
authority: &Pubkey,
|
authority: &Pubkey,
|
||||||
|
stake_authorize: StakeAuthorize,
|
||||||
other_signers: &[KeyedAccount],
|
other_signers: &[KeyedAccount],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let stake_state = self.state()?;
|
let stake_state = self.state()?;
|
||||||
if let StakeState::Stake(mut stake) = stake_state {
|
|
||||||
stake.check_authorized(self.signer_key(), other_signers)?;
|
if let StakeState::Stake(mut authorized, lockup, stake) = stake_state {
|
||||||
stake.lockup.authority = *authority;
|
authorized.authorize(self.signer_key(), other_signers, authority, stake_authorize)?;
|
||||||
self.set_state(&StakeState::Stake(stake))
|
self.set_state(&StakeState::Stake(authorized, lockup, stake))
|
||||||
} else if let StakeState::Lockup(mut lockup) = stake_state {
|
} else if let StakeState::Initialized(mut authorized, lockup) = stake_state {
|
||||||
lockup.check_authorized(self.signer_key(), other_signers)?;
|
authorized.authorize(self.signer_key(), other_signers, authority, stake_authorize)?;
|
||||||
lockup.authority = *authority;
|
self.set_state(&StakeState::Initialized(authorized, lockup))
|
||||||
self.set_state(&StakeState::Lockup(lockup))
|
|
||||||
} else {
|
} else {
|
||||||
Err(InstructionError::InvalidAccountData)
|
Err(InstructionError::InvalidAccountData)
|
||||||
}
|
}
|
||||||
@@ -454,26 +491,25 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||||||
config: &Config,
|
config: &Config,
|
||||||
other_signers: &[KeyedAccount],
|
other_signers: &[KeyedAccount],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
if let StakeState::Lockup(lockup) = self.state()? {
|
if let StakeState::Initialized(authorized, lockup) = self.state()? {
|
||||||
lockup.check_authorized(self.signer_key(), other_signers)?;
|
authorized.check(self.signer_key(), other_signers, StakeAuthorize::Staker)?;
|
||||||
let stake = Stake::new(
|
let stake = Stake::new(
|
||||||
self.account.lamports,
|
self.account.lamports,
|
||||||
vote_account.unsigned_key(),
|
vote_account.unsigned_key(),
|
||||||
&vote_account.state()?,
|
&vote_account.state()?,
|
||||||
clock.epoch,
|
clock.epoch,
|
||||||
config,
|
config,
|
||||||
&lockup,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
self.set_state(&StakeState::Stake(stake))
|
self.set_state(&StakeState::Stake(authorized, lockup, stake))
|
||||||
} else if let StakeState::Stake(mut stake) = self.state()? {
|
} else if let StakeState::Stake(authorized, lockup, mut stake) = self.state()? {
|
||||||
stake.check_authorized(self.signer_key(), other_signers)?;
|
authorized.check(self.signer_key(), other_signers, StakeAuthorize::Staker)?;
|
||||||
stake.redelegate(
|
stake.redelegate(
|
||||||
vote_account.unsigned_key(),
|
vote_account.unsigned_key(),
|
||||||
&vote_account.state()?,
|
&vote_account.state()?,
|
||||||
clock.epoch,
|
clock.epoch,
|
||||||
)?;
|
)?;
|
||||||
self.set_state(&StakeState::Stake(stake))
|
self.set_state(&StakeState::Stake(authorized, lockup, stake))
|
||||||
} else {
|
} else {
|
||||||
Err(InstructionError::InvalidAccountData)
|
Err(InstructionError::InvalidAccountData)
|
||||||
}
|
}
|
||||||
@@ -484,11 +520,11 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||||||
clock: &sysvar::clock::Clock,
|
clock: &sysvar::clock::Clock,
|
||||||
other_signers: &[KeyedAccount],
|
other_signers: &[KeyedAccount],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
if let StakeState::Stake(mut stake) = self.state()? {
|
if let StakeState::Stake(authorized, lockup, mut stake) = self.state()? {
|
||||||
stake.check_authorized(self.signer_key(), other_signers)?;
|
authorized.check(self.signer_key(), other_signers, StakeAuthorize::Staker)?;
|
||||||
stake.deactivate(clock.epoch);
|
stake.deactivate(clock.epoch);
|
||||||
|
|
||||||
self.set_state(&StakeState::Stake(stake))
|
self.set_state(&StakeState::Stake(authorized, lockup, stake))
|
||||||
} else {
|
} else {
|
||||||
Err(InstructionError::InvalidAccountData)
|
Err(InstructionError::InvalidAccountData)
|
||||||
}
|
}
|
||||||
@@ -500,7 +536,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||||||
rewards: &sysvar::rewards::Rewards,
|
rewards: &sysvar::rewards::Rewards,
|
||||||
stake_history: &sysvar::stake_history::StakeHistory,
|
stake_history: &sysvar::stake_history::StakeHistory,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
if let (StakeState::Stake(mut stake), StakeState::RewardsPool) =
|
if let (StakeState::Stake(authorized, lockup, mut stake), StakeState::RewardsPool) =
|
||||||
(self.state()?, rewards_account.state()?)
|
(self.state()?, rewards_account.state()?)
|
||||||
{
|
{
|
||||||
let vote_state: VoteState = vote_account.state()?;
|
let vote_state: VoteState = vote_account.state()?;
|
||||||
@@ -528,7 +564,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||||||
|
|
||||||
stake.credits_observed = credits_observed;
|
stake.credits_observed = credits_observed;
|
||||||
|
|
||||||
self.set_state(&StakeState::Stake(stake))
|
self.set_state(&StakeState::Stake(authorized, lockup, stake))
|
||||||
} else {
|
} else {
|
||||||
// not worth collecting
|
// not worth collecting
|
||||||
Err(StakeError::NoCreditsToRedeem.into())
|
Err(StakeError::NoCreditsToRedeem.into())
|
||||||
@@ -546,8 +582,8 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||||||
other_signers: &[KeyedAccount],
|
other_signers: &[KeyedAccount],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let lockup = match self.state()? {
|
let lockup = match self.state()? {
|
||||||
StakeState::Stake(stake) => {
|
StakeState::Stake(authorized, lockup, stake) => {
|
||||||
stake.check_authorized(self.signer_key(), other_signers)?;
|
authorized.check(self.signer_key(), other_signers, StakeAuthorize::Withdrawer)?;
|
||||||
// if we have a deactivation epoch and we're in cooldown
|
// if we have a deactivation epoch and we're in cooldown
|
||||||
let staked = if clock.epoch >= stake.deactivation_epoch {
|
let staked = if clock.epoch >= stake.deactivation_epoch {
|
||||||
stake.stake(clock.epoch, Some(stake_history))
|
stake.stake(clock.epoch, Some(stake_history))
|
||||||
@@ -561,10 +597,10 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||||||
if lamports > self.account.lamports.saturating_sub(staked) {
|
if lamports > self.account.lamports.saturating_sub(staked) {
|
||||||
return Err(InstructionError::InsufficientFunds);
|
return Err(InstructionError::InsufficientFunds);
|
||||||
}
|
}
|
||||||
stake.lockup
|
lockup
|
||||||
}
|
}
|
||||||
StakeState::Lockup(lockup) => {
|
StakeState::Initialized(authorized, lockup) => {
|
||||||
lockup.check_authorized(self.signer_key(), other_signers)?;
|
authorized.check(self.signer_key(), other_signers, StakeAuthorize::Withdrawer)?;
|
||||||
lockup
|
lockup
|
||||||
}
|
}
|
||||||
StakeState::Uninitialized => {
|
StakeState::Uninitialized => {
|
||||||
@@ -615,17 +651,25 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// utility function, used by Bank, tests, genesis
|
// utility function, used by Bank, tests, genesis
|
||||||
pub fn create_account(voter_pubkey: &Pubkey, vote_account: &Account, lamports: u64) -> Account {
|
pub fn create_account(
|
||||||
|
authorized: &Pubkey,
|
||||||
|
voter_pubkey: &Pubkey,
|
||||||
|
vote_account: &Account,
|
||||||
|
lamports: u64,
|
||||||
|
) -> Account {
|
||||||
let mut stake_account = Account::new(lamports, std::mem::size_of::<StakeState>(), &id());
|
let mut stake_account = Account::new(lamports, std::mem::size_of::<StakeState>(), &id());
|
||||||
|
|
||||||
let vote_state = VoteState::from(vote_account).expect("vote_state");
|
let vote_state = VoteState::from(vote_account).expect("vote_state");
|
||||||
|
|
||||||
stake_account
|
stake_account
|
||||||
.set_state(&StakeState::Stake(Stake::new_bootstrap(
|
.set_state(&StakeState::Stake(
|
||||||
lamports,
|
Authorized {
|
||||||
voter_pubkey,
|
staker: *authorized,
|
||||||
&vote_state,
|
withdrawer: *authorized,
|
||||||
)))
|
},
|
||||||
|
Lockup::default(),
|
||||||
|
Stake::new_bootstrap(lamports, voter_pubkey, &vote_state),
|
||||||
|
))
|
||||||
.expect("set_state");
|
.expect("set_state");
|
||||||
|
|
||||||
stake_account
|
stake_account
|
||||||
@@ -691,10 +735,13 @@ mod tests {
|
|||||||
let stake_lamports = 42;
|
let stake_lamports = 42;
|
||||||
let mut stake_account = Account::new_data_with_space(
|
let mut stake_account = Account::new_data_with_space(
|
||||||
stake_lamports,
|
stake_lamports,
|
||||||
&StakeState::Lockup(Lockup {
|
&StakeState::Initialized(
|
||||||
authority: stake_pubkey,
|
Authorized {
|
||||||
..Lockup::default()
|
staker: stake_pubkey,
|
||||||
}),
|
withdrawer: stake_pubkey,
|
||||||
|
},
|
||||||
|
Lockup::default(),
|
||||||
|
),
|
||||||
std::mem::size_of::<StakeState>(),
|
std::mem::size_of::<StakeState>(),
|
||||||
&id(),
|
&id(),
|
||||||
)
|
)
|
||||||
@@ -707,10 +754,13 @@ mod tests {
|
|||||||
let stake_state: StakeState = stake_keyed_account.state().unwrap();
|
let stake_state: StakeState = stake_keyed_account.state().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake_state,
|
stake_state,
|
||||||
StakeState::Lockup(Lockup {
|
StakeState::Initialized(
|
||||||
authority: stake_pubkey,
|
Authorized {
|
||||||
..Lockup::default()
|
staker: stake_pubkey,
|
||||||
})
|
withdrawer: stake_pubkey,
|
||||||
|
},
|
||||||
|
Lockup::default(),
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -741,10 +791,6 @@ mod tests {
|
|||||||
stake: stake_lamports,
|
stake: stake_lamports,
|
||||||
activation_epoch: clock.epoch,
|
activation_epoch: clock.epoch,
|
||||||
deactivation_epoch: std::u64::MAX,
|
deactivation_epoch: std::u64::MAX,
|
||||||
lockup: Lockup {
|
|
||||||
authority: stake_pubkey,
|
|
||||||
..Lockup::default()
|
|
||||||
},
|
|
||||||
..Stake::default()
|
..Stake::default()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -1062,21 +1108,32 @@ mod tests {
|
|||||||
// unsigned keyed account
|
// unsigned keyed account
|
||||||
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account);
|
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account);
|
||||||
let custodian = Pubkey::new_rand();
|
let custodian = Pubkey::new_rand();
|
||||||
assert_eq!(stake_keyed_account.lockup(1, &custodian), Ok(()));
|
assert_eq!(
|
||||||
|
stake_keyed_account.initialize(
|
||||||
|
&Authorized {
|
||||||
|
staker: stake_pubkey,
|
||||||
|
withdrawer: stake_pubkey
|
||||||
|
},
|
||||||
|
&Lockup { slot: 1, custodian }
|
||||||
|
),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
|
||||||
// first time works, as is uninit
|
// first time works, as is uninit
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
StakeState::from(&stake_keyed_account.account).unwrap(),
|
StakeState::from(&stake_keyed_account.account).unwrap(),
|
||||||
StakeState::Lockup(Lockup {
|
StakeState::Initialized(
|
||||||
slot: 1,
|
Authorized {
|
||||||
authority: stake_pubkey,
|
staker: stake_pubkey,
|
||||||
custodian
|
withdrawer: stake_pubkey
|
||||||
})
|
},
|
||||||
|
Lockup { slot: 1, custodian }
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// 2nd time fails, can't move it from anything other than uninit->lockup
|
// 2nd time fails, can't move it from anything other than uninit->lockup
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake_keyed_account.lockup(1, &Pubkey::default()),
|
stake_keyed_account.initialize(&Authorized::default(), &Lockup::default()),
|
||||||
Err(InstructionError::InvalidAccountData)
|
Err(InstructionError::InvalidAccountData)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1087,10 +1144,7 @@ mod tests {
|
|||||||
let stake_lamports = 42;
|
let stake_lamports = 42;
|
||||||
let mut stake_account = Account::new_data_with_space(
|
let mut stake_account = Account::new_data_with_space(
|
||||||
stake_lamports,
|
stake_lamports,
|
||||||
&StakeState::Lockup(Lockup {
|
&StakeState::Initialized(Authorized::auto(&stake_pubkey), Lockup::default()),
|
||||||
authority: stake_pubkey,
|
|
||||||
..Lockup::default()
|
|
||||||
}),
|
|
||||||
std::mem::size_of::<StakeState>(),
|
std::mem::size_of::<StakeState>(),
|
||||||
&id(),
|
&id(),
|
||||||
)
|
)
|
||||||
@@ -1195,7 +1249,12 @@ mod tests {
|
|||||||
// lockup
|
// lockup
|
||||||
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account);
|
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account);
|
||||||
let custodian = Pubkey::new_rand();
|
let custodian = Pubkey::new_rand();
|
||||||
stake_keyed_account.lockup(0, &custodian).unwrap();
|
stake_keyed_account
|
||||||
|
.initialize(
|
||||||
|
&Authorized::auto(&stake_pubkey),
|
||||||
|
&Lockup { slot: 0, custodian },
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// signed keyed account and locked up, more than available should fail
|
// signed keyed account and locked up, more than available should fail
|
||||||
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account);
|
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account);
|
||||||
@@ -1297,10 +1356,7 @@ mod tests {
|
|||||||
let stake_lamports = 42;
|
let stake_lamports = 42;
|
||||||
let mut stake_account = Account::new_data_with_space(
|
let mut stake_account = Account::new_data_with_space(
|
||||||
total_lamports,
|
total_lamports,
|
||||||
&StakeState::Lockup(Lockup {
|
&StakeState::Initialized(Authorized::auto(&stake_pubkey), Lockup::default()),
|
||||||
authority: stake_pubkey,
|
|
||||||
..Lockup::default()
|
|
||||||
}),
|
|
||||||
std::mem::size_of::<StakeState>(),
|
std::mem::size_of::<StakeState>(),
|
||||||
&id(),
|
&id(),
|
||||||
)
|
)
|
||||||
@@ -1381,17 +1437,16 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_withdraw_lockout() {
|
fn test_withdraw_lockup() {
|
||||||
let stake_pubkey = Pubkey::new_rand();
|
let stake_pubkey = Pubkey::new_rand();
|
||||||
let custodian = Pubkey::new_rand();
|
let custodian = Pubkey::new_rand();
|
||||||
let total_lamports = 100;
|
let total_lamports = 100;
|
||||||
let mut stake_account = Account::new_data_with_space(
|
let mut stake_account = Account::new_data_with_space(
|
||||||
total_lamports,
|
total_lamports,
|
||||||
&StakeState::Lockup(Lockup {
|
&StakeState::Initialized(
|
||||||
slot: 1,
|
Authorized::auto(&stake_pubkey),
|
||||||
authority: stake_pubkey,
|
Lockup { slot: 1, custodian },
|
||||||
custodian,
|
),
|
||||||
}),
|
|
||||||
std::mem::size_of::<StakeState>(),
|
std::mem::size_of::<StakeState>(),
|
||||||
&id(),
|
&id(),
|
||||||
)
|
)
|
||||||
@@ -1542,10 +1597,7 @@ mod tests {
|
|||||||
let stake_lamports = 100;
|
let stake_lamports = 100;
|
||||||
let mut stake_account = Account::new_data_with_space(
|
let mut stake_account = Account::new_data_with_space(
|
||||||
stake_lamports,
|
stake_lamports,
|
||||||
&StakeState::Lockup(Lockup {
|
&StakeState::Initialized(Authorized::auto(&stake_pubkey), Lockup::default()),
|
||||||
authority: stake_pubkey,
|
|
||||||
..Lockup::default()
|
|
||||||
}),
|
|
||||||
std::mem::size_of::<StakeState>(),
|
std::mem::size_of::<StakeState>(),
|
||||||
&id(),
|
&id(),
|
||||||
)
|
)
|
||||||
@@ -1673,10 +1725,7 @@ mod tests {
|
|||||||
let stake_lamports = 42;
|
let stake_lamports = 42;
|
||||||
let mut stake_account = Account::new_data_with_space(
|
let mut stake_account = Account::new_data_with_space(
|
||||||
stake_lamports,
|
stake_lamports,
|
||||||
&StakeState::Lockup(Lockup {
|
&StakeState::Initialized(Authorized::auto(&stake_pubkey), Lockup::default()),
|
||||||
authority: stake_pubkey,
|
|
||||||
..Lockup::default()
|
|
||||||
}),
|
|
||||||
std::mem::size_of::<StakeState>(),
|
std::mem::size_of::<StakeState>(),
|
||||||
&id(),
|
&id(),
|
||||||
)
|
)
|
||||||
@@ -1690,16 +1739,27 @@ mod tests {
|
|||||||
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account);
|
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account);
|
||||||
|
|
||||||
let stake_pubkey0 = Pubkey::new_rand();
|
let stake_pubkey0 = Pubkey::new_rand();
|
||||||
assert_eq!(stake_keyed_account.authorize(&stake_pubkey0, &[]), Ok(()));
|
assert_eq!(
|
||||||
if let StakeState::Lockup(lockup) = StakeState::from(&stake_keyed_account.account).unwrap()
|
stake_keyed_account.authorize(&stake_pubkey0, StakeAuthorize::Staker, &[]),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
stake_keyed_account.authorize(&stake_pubkey0, StakeAuthorize::Withdrawer, &[]),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
if let StakeState::Initialized(authorized, _lockup) =
|
||||||
|
StakeState::from(&stake_keyed_account.account).unwrap()
|
||||||
{
|
{
|
||||||
assert_eq!(lockup.authority, stake_pubkey0);
|
assert_eq!(authorized.staker, stake_pubkey0);
|
||||||
|
assert_eq!(authorized.withdrawer, stake_pubkey0);
|
||||||
|
} else {
|
||||||
|
assert!(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A second authorization signed by the stake_keyed_account should fail
|
// A second authorization signed by the stake_keyed_account should fail
|
||||||
let stake_pubkey1 = Pubkey::new_rand();
|
let stake_pubkey1 = Pubkey::new_rand();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake_keyed_account.authorize(&stake_pubkey1, &[]),
|
stake_keyed_account.authorize(&stake_pubkey1, StakeAuthorize::Staker, &[]),
|
||||||
Err(InstructionError::MissingRequiredSignature)
|
Err(InstructionError::MissingRequiredSignature)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1709,18 +1769,38 @@ mod tests {
|
|||||||
// Test a second authorization by the newly authorized pubkey
|
// Test a second authorization by the newly authorized pubkey
|
||||||
let stake_pubkey2 = Pubkey::new_rand();
|
let stake_pubkey2 = Pubkey::new_rand();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake_keyed_account.authorize(&stake_pubkey2, &[staker_keyed_account0]),
|
stake_keyed_account.authorize(
|
||||||
|
&stake_pubkey2,
|
||||||
|
StakeAuthorize::Staker,
|
||||||
|
&[staker_keyed_account0]
|
||||||
|
),
|
||||||
Ok(())
|
Ok(())
|
||||||
);
|
);
|
||||||
if let StakeState::Lockup(lockup) = StakeState::from(&stake_keyed_account.account).unwrap()
|
if let StakeState::Initialized(authorized, _lockup) =
|
||||||
|
StakeState::from(&stake_keyed_account.account).unwrap()
|
||||||
{
|
{
|
||||||
assert_eq!(lockup.authority, stake_pubkey2);
|
assert_eq!(authorized.staker, stake_pubkey2);
|
||||||
|
}
|
||||||
|
|
||||||
|
let staker_keyed_account0 = KeyedAccount::new(&stake_pubkey0, true, &mut staker_account0);
|
||||||
|
assert_eq!(
|
||||||
|
stake_keyed_account.authorize(
|
||||||
|
&stake_pubkey2,
|
||||||
|
StakeAuthorize::Withdrawer,
|
||||||
|
&[staker_keyed_account0]
|
||||||
|
),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
if let StakeState::Initialized(authorized, _lockup) =
|
||||||
|
StakeState::from(&stake_keyed_account.account).unwrap()
|
||||||
|
{
|
||||||
|
assert_eq!(authorized.staker, stake_pubkey2);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut staker_account2 = Account::new(1, 0, &system_program::id());
|
let mut staker_account2 = Account::new(1, 0, &system_program::id());
|
||||||
let staker_keyed_account2 = KeyedAccount::new(&stake_pubkey2, true, &mut staker_account2);
|
let staker_keyed_account2 = KeyedAccount::new(&stake_pubkey2, true, &mut staker_account2);
|
||||||
|
|
||||||
// Test an action by the currently authorized pubkey
|
// Test an action by the currently authorized withdrawer
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake_keyed_account.withdraw(
|
stake_keyed_account.withdraw(
|
||||||
stake_lamports,
|
stake_lamports,
|
||||||
@@ -1739,10 +1819,7 @@ mod tests {
|
|||||||
let stake_lamports = 42;
|
let stake_lamports = 42;
|
||||||
let mut stake_account = Account::new_data_with_space(
|
let mut stake_account = Account::new_data_with_space(
|
||||||
stake_lamports,
|
stake_lamports,
|
||||||
&StakeState::Lockup(Lockup {
|
&StakeState::Initialized(Authorized::auto(&stake_pubkey), Lockup::default()),
|
||||||
authority: stake_pubkey,
|
|
||||||
..Lockup::default()
|
|
||||||
}),
|
|
||||||
std::mem::size_of::<StakeState>(),
|
std::mem::size_of::<StakeState>(),
|
||||||
&id(),
|
&id(),
|
||||||
)
|
)
|
||||||
@@ -1762,11 +1839,11 @@ mod tests {
|
|||||||
|
|
||||||
let new_staker_pubkey = Pubkey::new_rand();
|
let new_staker_pubkey = Pubkey::new_rand();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stake_keyed_account.authorize(&new_staker_pubkey, &[]),
|
stake_keyed_account.authorize(&new_staker_pubkey, StakeAuthorize::Staker, &[]),
|
||||||
Ok(())
|
Ok(())
|
||||||
);
|
);
|
||||||
let stake = StakeState::stake_from(&stake_keyed_account.account).unwrap();
|
let authorized = StakeState::authorized_from(&stake_keyed_account.account).unwrap();
|
||||||
assert_eq!(stake.lockup.authority, new_staker_pubkey);
|
assert_eq!(authorized.staker, new_staker_pubkey);
|
||||||
|
|
||||||
let other_pubkey = Pubkey::new_rand();
|
let other_pubkey = Pubkey::new_rand();
|
||||||
let mut other_account = Account::new(1, 0, &system_program::id());
|
let mut other_account = Account::new(1, 0, &system_program::id());
|
||||||
|
@@ -1,20 +1,27 @@
|
|||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
use solana_runtime::bank::Bank;
|
use solana_runtime::{
|
||||||
use solana_runtime::bank_client::BankClient;
|
bank::Bank,
|
||||||
use solana_runtime::genesis_utils::{create_genesis_block_with_leader, GenesisBlockInfo};
|
bank_client::BankClient,
|
||||||
use solana_sdk::account_utils::State;
|
genesis_utils::{create_genesis_block_with_leader, GenesisBlockInfo},
|
||||||
use solana_sdk::client::SyncClient;
|
};
|
||||||
use solana_sdk::message::Message;
|
use solana_sdk::{
|
||||||
use solana_sdk::pubkey::Pubkey;
|
account_utils::State,
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
client::SyncClient,
|
||||||
use solana_sdk::sysvar;
|
message::Message,
|
||||||
use solana_sdk::sysvar::rewards::Rewards;
|
pubkey::Pubkey,
|
||||||
use solana_stake_api::id;
|
signature::{Keypair, KeypairUtil},
|
||||||
use solana_stake_api::stake_instruction;
|
sysvar,
|
||||||
use solana_stake_api::stake_instruction::process_instruction;
|
sysvar::rewards::Rewards,
|
||||||
use solana_stake_api::stake_state::StakeState;
|
};
|
||||||
use solana_vote_api::vote_instruction;
|
use solana_stake_api::{
|
||||||
use solana_vote_api::vote_state::{Vote, VoteState};
|
id,
|
||||||
|
stake_instruction::{self, process_instruction},
|
||||||
|
stake_state::{self, StakeState},
|
||||||
|
};
|
||||||
|
use solana_vote_api::{
|
||||||
|
vote_instruction,
|
||||||
|
vote_state::{Vote, VoteInit, VoteState},
|
||||||
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
fn fill_epoch_with_votes(
|
fn fill_epoch_with_votes(
|
||||||
@@ -76,20 +83,26 @@ fn test_stake_account_delegate() {
|
|||||||
let message = Message::new(vote_instruction::create_account(
|
let message = Message::new(vote_instruction::create_account(
|
||||||
&mint_pubkey,
|
&mint_pubkey,
|
||||||
&vote_pubkey,
|
&vote_pubkey,
|
||||||
&node_pubkey,
|
&VoteInit {
|
||||||
std::u8::MAX / 2,
|
node_pubkey,
|
||||||
|
authorized_voter: vote_pubkey,
|
||||||
|
authorized_withdrawer: vote_pubkey,
|
||||||
|
commission: std::u8::MAX / 2,
|
||||||
|
},
|
||||||
10,
|
10,
|
||||||
));
|
));
|
||||||
bank_client
|
bank_client
|
||||||
.send_message(&[&mint_keypair], message)
|
.send_message(&[&mint_keypair], message)
|
||||||
.expect("failed to create vote account");
|
.expect("failed to create vote account");
|
||||||
|
|
||||||
|
let authorized = stake_state::Authorized::auto(&staker_pubkey);
|
||||||
// Create stake account and delegate to vote account
|
// Create stake account and delegate to vote account
|
||||||
let message = Message::new(stake_instruction::create_stake_account_and_delegate_stake(
|
let message = Message::new(stake_instruction::create_stake_account_and_delegate_stake(
|
||||||
&mint_pubkey,
|
&mint_pubkey,
|
||||||
&staker_pubkey,
|
&staker_pubkey,
|
||||||
&vote_pubkey,
|
&vote_pubkey,
|
||||||
20000,
|
20000,
|
||||||
|
&authorized,
|
||||||
));
|
));
|
||||||
bank_client
|
bank_client
|
||||||
.send_message(&[&mint_keypair, &staker_keypair], message)
|
.send_message(&[&mint_keypair, &staker_keypair], message)
|
||||||
@@ -98,7 +111,7 @@ fn test_stake_account_delegate() {
|
|||||||
// Test that correct lamports are staked
|
// Test that correct lamports are staked
|
||||||
let account = bank.get_account(&staker_pubkey).expect("account not found");
|
let account = bank.get_account(&staker_pubkey).expect("account not found");
|
||||||
let stake_state = account.state().expect("couldn't unpack account data");
|
let stake_state = account.state().expect("couldn't unpack account data");
|
||||||
if let StakeState::Stake(stake) = stake_state {
|
if let StakeState::Stake(_authorized, _lockup, stake) = stake_state {
|
||||||
assert_eq!(stake.stake, 20000);
|
assert_eq!(stake.stake, 20000);
|
||||||
} else {
|
} else {
|
||||||
assert!(false, "wrong account type found")
|
assert!(false, "wrong account type found")
|
||||||
@@ -120,7 +133,7 @@ fn test_stake_account_delegate() {
|
|||||||
// Test that lamports are still staked
|
// Test that lamports are still staked
|
||||||
let account = bank.get_account(&staker_pubkey).expect("account not found");
|
let account = bank.get_account(&staker_pubkey).expect("account not found");
|
||||||
let stake_state = account.state().expect("couldn't unpack account data");
|
let stake_state = account.state().expect("couldn't unpack account data");
|
||||||
if let StakeState::Stake(stake) = stake_state {
|
if let StakeState::Stake(_authorized, _lockup, stake) = stake_state {
|
||||||
assert_eq!(stake.stake, 20000);
|
assert_eq!(stake.stake, 20000);
|
||||||
} else {
|
} else {
|
||||||
assert!(false, "wrong account type found")
|
assert!(false, "wrong account type found")
|
||||||
@@ -164,7 +177,7 @@ fn test_stake_account_delegate() {
|
|||||||
let rewards;
|
let rewards;
|
||||||
let account = bank.get_account(&staker_pubkey).expect("account not found");
|
let account = bank.get_account(&staker_pubkey).expect("account not found");
|
||||||
let stake_state = account.state().expect("couldn't unpack account data");
|
let stake_state = account.state().expect("couldn't unpack account data");
|
||||||
if let StakeState::Stake(stake) = stake_state {
|
if let StakeState::Stake(_authorized, _lockup, stake) = stake_state {
|
||||||
assert!(account.lamports > 20000);
|
assert!(account.lamports > 20000);
|
||||||
assert_eq!(stake.stake, 20000);
|
assert_eq!(stake.stake, 20000);
|
||||||
rewards = account.lamports - 20000;
|
rewards = account.lamports - 20000;
|
||||||
@@ -247,7 +260,7 @@ fn test_stake_account_delegate() {
|
|||||||
// Test that balance and stake is updated correctly (we have withdrawn all lamports except rewards)
|
// Test that balance and stake is updated correctly (we have withdrawn all lamports except rewards)
|
||||||
let account = bank.get_account(&staker_pubkey).expect("account not found");
|
let account = bank.get_account(&staker_pubkey).expect("account not found");
|
||||||
let stake_state = account.state().expect("couldn't unpack account data");
|
let stake_state = account.state().expect("couldn't unpack account data");
|
||||||
if let StakeState::Stake(_stake) = stake_state {
|
if let StakeState::Stake(_, _, _stake) = stake_state {
|
||||||
assert_eq!(account.lamports, rewards);
|
assert_eq!(account.lamports, rewards);
|
||||||
} else {
|
} else {
|
||||||
assert!(false, "wrong account type found")
|
assert!(false, "wrong account type found")
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
id,
|
id,
|
||||||
vote_state::{self, Vote, VoteState},
|
vote_state::{self, Vote, VoteAuthorize, VoteInit, VoteState},
|
||||||
};
|
};
|
||||||
use bincode::deserialize;
|
use bincode::deserialize;
|
||||||
use log::*;
|
use log::*;
|
||||||
@@ -51,11 +51,11 @@ impl std::error::Error for VoteError {}
|
|||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum VoteInstruction {
|
pub enum VoteInstruction {
|
||||||
/// Initialize the VoteState for this `vote account`
|
/// Initialize the VoteState for this `vote account`
|
||||||
/// takes a node_pubkey and commission
|
InitializeAccount(VoteInit),
|
||||||
InitializeAccount(Pubkey, u8),
|
|
||||||
|
|
||||||
/// Authorize a voter to send signed votes.
|
/// Authorize a voter to send signed votes or a withdrawer
|
||||||
AuthorizeVoter(Pubkey),
|
/// to withdraw
|
||||||
|
Authorize(Pubkey, VoteAuthorize),
|
||||||
|
|
||||||
/// A Vote instruction with recent votes
|
/// A Vote instruction with recent votes
|
||||||
Vote(Vote),
|
Vote(Vote),
|
||||||
@@ -64,44 +64,38 @@ pub enum VoteInstruction {
|
|||||||
Withdraw(u64),
|
Withdraw(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_account(vote_pubkey: &Pubkey, node_pubkey: &Pubkey, commission: u8) -> Instruction {
|
fn initialize_account(vote_pubkey: &Pubkey, vote_init: &VoteInit) -> Instruction {
|
||||||
let account_metas = vec![AccountMeta::new(*vote_pubkey, false)];
|
let account_metas = vec![AccountMeta::new(*vote_pubkey, false)];
|
||||||
Instruction::new(
|
Instruction::new(
|
||||||
id(),
|
id(),
|
||||||
&VoteInstruction::InitializeAccount(*node_pubkey, commission),
|
&VoteInstruction::InitializeAccount(*vote_init),
|
||||||
account_metas,
|
account_metas,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn minimum_balance() -> u64 {
|
|
||||||
let rent_calculator = solana_sdk::rent_calculator::RentCalculator::default();
|
|
||||||
|
|
||||||
rent_calculator.minimum_balance(VoteState::size_of())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_account(
|
pub fn create_account(
|
||||||
from_pubkey: &Pubkey,
|
from_pubkey: &Pubkey,
|
||||||
vote_pubkey: &Pubkey,
|
vote_pubkey: &Pubkey,
|
||||||
node_pubkey: &Pubkey,
|
vote_init: &VoteInit,
|
||||||
commission: u8,
|
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
) -> Vec<Instruction> {
|
) -> Vec<Instruction> {
|
||||||
let space = VoteState::size_of() as u64;
|
let space = VoteState::size_of() as u64;
|
||||||
let create_ix =
|
let create_ix =
|
||||||
system_instruction::create_account(from_pubkey, vote_pubkey, lamports, space, &id());
|
system_instruction::create_account(from_pubkey, vote_pubkey, lamports, space, &id());
|
||||||
let init_ix = initialize_account(vote_pubkey, node_pubkey, commission);
|
let init_ix = initialize_account(vote_pubkey, vote_init);
|
||||||
vec![create_ix, init_ix]
|
vec![create_ix, init_ix]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for instructions that whose authorized signer may differ from the account's pubkey
|
||||||
fn metas_for_authorized_signer(
|
fn metas_for_authorized_signer(
|
||||||
vote_pubkey: &Pubkey,
|
account_pubkey: &Pubkey,
|
||||||
authorized_voter_pubkey: &Pubkey, // currently authorized
|
authorized_signer: &Pubkey, // currently authorized
|
||||||
other_params: &[AccountMeta],
|
other_params: &[AccountMeta],
|
||||||
) -> Vec<AccountMeta> {
|
) -> Vec<AccountMeta> {
|
||||||
let is_own_signer = authorized_voter_pubkey == vote_pubkey;
|
let is_own_signer = authorized_signer == account_pubkey;
|
||||||
|
|
||||||
// vote account
|
// vote account
|
||||||
let mut account_metas = vec![AccountMeta::new(*vote_pubkey, is_own_signer)];
|
let mut account_metas = vec![AccountMeta::new(*account_pubkey, is_own_signer)];
|
||||||
|
|
||||||
for meta in other_params {
|
for meta in other_params {
|
||||||
account_metas.push(meta.clone());
|
account_metas.push(meta.clone());
|
||||||
@@ -109,22 +103,23 @@ fn metas_for_authorized_signer(
|
|||||||
|
|
||||||
// append signer at the end
|
// append signer at the end
|
||||||
if !is_own_signer {
|
if !is_own_signer {
|
||||||
account_metas.push(AccountMeta::new_credit_only(*authorized_voter_pubkey, true)) // signer
|
account_metas.push(AccountMeta::new_credit_only(*authorized_signer, true)) // signer
|
||||||
}
|
}
|
||||||
|
|
||||||
account_metas
|
account_metas
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authorize_voter(
|
pub fn authorize(
|
||||||
vote_pubkey: &Pubkey,
|
vote_pubkey: &Pubkey,
|
||||||
authorized_voter_pubkey: &Pubkey, // currently authorized
|
authorized_pubkey: &Pubkey, // currently authorized
|
||||||
new_authorized_voter_pubkey: &Pubkey,
|
new_authorized_pubkey: &Pubkey,
|
||||||
|
vote_authorize: VoteAuthorize,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
let account_metas = metas_for_authorized_signer(vote_pubkey, authorized_voter_pubkey, &[]);
|
let account_metas = metas_for_authorized_signer(vote_pubkey, authorized_pubkey, &[]);
|
||||||
|
|
||||||
Instruction::new(
|
Instruction::new(
|
||||||
id(),
|
id(),
|
||||||
&VoteInstruction::AuthorizeVoter(*new_authorized_voter_pubkey),
|
&VoteInstruction::Authorize(*new_authorized_pubkey, vote_authorize),
|
||||||
account_metas,
|
account_metas,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -144,11 +139,17 @@ pub fn vote(vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, vote: Vote)
|
|||||||
Instruction::new(id(), &VoteInstruction::Vote(vote), account_metas)
|
Instruction::new(id(), &VoteInstruction::Vote(vote), account_metas)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn withdraw(vote_pubkey: &Pubkey, lamports: u64, to_pubkey: &Pubkey) -> Instruction {
|
pub fn withdraw(
|
||||||
let account_metas = vec![
|
vote_pubkey: &Pubkey,
|
||||||
AccountMeta::new(*vote_pubkey, true),
|
withdrawer_pubkey: &Pubkey,
|
||||||
AccountMeta::new_credit_only(*to_pubkey, false),
|
lamports: u64,
|
||||||
];
|
to_pubkey: &Pubkey,
|
||||||
|
) -> Instruction {
|
||||||
|
let account_metas = metas_for_authorized_signer(
|
||||||
|
vote_pubkey,
|
||||||
|
withdrawer_pubkey,
|
||||||
|
&[AccountMeta::new_credit_only(*to_pubkey, false)],
|
||||||
|
);
|
||||||
|
|
||||||
Instruction::new(id(), &VoteInstruction::Withdraw(lamports), account_metas)
|
Instruction::new(id(), &VoteInstruction::Withdraw(lamports), account_metas)
|
||||||
}
|
}
|
||||||
@@ -173,11 +174,11 @@ pub fn process_instruction(
|
|||||||
|
|
||||||
// TODO: data-driven unpack and dispatch of KeyedAccounts
|
// TODO: data-driven unpack and dispatch of KeyedAccounts
|
||||||
match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
|
match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
|
||||||
VoteInstruction::InitializeAccount(node_pubkey, commission) => {
|
VoteInstruction::InitializeAccount(vote_init) => {
|
||||||
vote_state::initialize_account(me, &node_pubkey, commission)
|
vote_state::initialize_account(me, &vote_init)
|
||||||
}
|
}
|
||||||
VoteInstruction::AuthorizeVoter(voter_pubkey) => {
|
VoteInstruction::Authorize(voter_pubkey, vote_authorize) => {
|
||||||
vote_state::authorize_voter(me, rest, &voter_pubkey)
|
vote_state::authorize(me, rest, &voter_pubkey, vote_authorize)
|
||||||
}
|
}
|
||||||
VoteInstruction::Vote(vote) => {
|
VoteInstruction::Vote(vote) => {
|
||||||
datapoint_info!("vote-native", ("count", 1, i64));
|
datapoint_info!("vote-native", ("count", 1, i64));
|
||||||
@@ -198,7 +199,10 @@ pub fn process_instruction(
|
|||||||
if rest.is_empty() {
|
if rest.is_empty() {
|
||||||
Err(InstructionError::InvalidInstructionData)?;
|
Err(InstructionError::InvalidInstructionData)?;
|
||||||
}
|
}
|
||||||
vote_state::withdraw(me, lamports, &mut rest[0])
|
let (to, rest) = rest.split_at_mut(1);
|
||||||
|
let to = &mut to[0];
|
||||||
|
|
||||||
|
vote_state::withdraw(me, rest, lamports, to)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -251,8 +255,7 @@ mod tests {
|
|||||||
let instructions = create_account(
|
let instructions = create_account(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
&Pubkey::default(),
|
&VoteInit::default(),
|
||||||
0,
|
|
||||||
100,
|
100,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -268,10 +271,20 @@ mod tests {
|
|||||||
Err(InstructionError::InvalidAccountData),
|
Err(InstructionError::InvalidAccountData),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
process_instruction(&authorize_voter(
|
process_instruction(&authorize(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
|
VoteAuthorize::Voter,
|
||||||
|
)),
|
||||||
|
Err(InstructionError::InvalidAccountData),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
process_instruction(&withdraw(
|
||||||
|
&Pubkey::default(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
0,
|
||||||
|
&Pubkey::default()
|
||||||
)),
|
)),
|
||||||
Err(InstructionError::InvalidAccountData),
|
Err(InstructionError::InvalidAccountData),
|
||||||
);
|
);
|
||||||
@@ -285,4 +298,44 @@ mod tests {
|
|||||||
assert!(minimum_balance as f64 / 2f64.powf(34.0) < 0.02)
|
assert!(minimum_balance as f64 / 2f64.powf(34.0) < 0.02)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_metas_for_authorized_signer() {
|
||||||
|
let account_pubkey = Pubkey::new_rand();
|
||||||
|
let authorized_signer = Pubkey::new_rand();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
metas_for_authorized_signer(&account_pubkey, &authorized_signer, &[]).len(),
|
||||||
|
2
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
metas_for_authorized_signer(&account_pubkey, &account_pubkey, &[]).len(),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_custom_error_decode() {
|
||||||
|
use num_traits::FromPrimitive;
|
||||||
|
fn pretty_err<T>(err: InstructionError) -> String
|
||||||
|
where
|
||||||
|
T: 'static + std::error::Error + DecodeError<T> + FromPrimitive,
|
||||||
|
{
|
||||||
|
if let InstructionError::CustomError(code) = err {
|
||||||
|
let specific_error: T = T::decode_custom_error_to_enum(code).unwrap();
|
||||||
|
format!(
|
||||||
|
"{:?}: {}::{:?} - {}",
|
||||||
|
err,
|
||||||
|
T::type_of(),
|
||||||
|
specific_error,
|
||||||
|
specific_error,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
"CustomError(0): VoteError::VoteTooOld - vote already recorded or not in slot hashes history",
|
||||||
|
pretty_err::<VoteError>(VoteError::VoteTooOld.into())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -67,14 +67,33 @@ impl Lockout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub struct VoteInit {
|
||||||
|
pub node_pubkey: Pubkey,
|
||||||
|
pub authorized_voter: Pubkey,
|
||||||
|
pub authorized_withdrawer: Pubkey,
|
||||||
|
pub commission: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum VoteAuthorize {
|
||||||
|
Voter,
|
||||||
|
Withdrawer,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
pub struct VoteState {
|
pub struct VoteState {
|
||||||
pub votes: VecDeque<Lockout>,
|
/// the node that votes in this account
|
||||||
pub node_pubkey: Pubkey,
|
pub node_pubkey: Pubkey,
|
||||||
pub authorized_voter_pubkey: Pubkey,
|
/// the signer for vote transactions
|
||||||
|
pub authorized_voter: Pubkey,
|
||||||
|
/// the signer for withdrawals
|
||||||
|
pub authorized_withdrawer: Pubkey,
|
||||||
/// fraction of std::u8::MAX that represents what part of a rewards
|
/// fraction of std::u8::MAX that represents what part of a rewards
|
||||||
/// payout should be given to this VoteAccount
|
/// payout should be given to this VoteAccount
|
||||||
pub commission: u8,
|
pub commission: u8,
|
||||||
|
|
||||||
|
pub votes: VecDeque<Lockout>,
|
||||||
pub root_slot: Option<u64>,
|
pub root_slot: Option<u64>,
|
||||||
|
|
||||||
/// clock epoch
|
/// clock epoch
|
||||||
@@ -91,11 +110,12 @@ pub struct VoteState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VoteState {
|
impl VoteState {
|
||||||
pub fn new(vote_pubkey: &Pubkey, node_pubkey: &Pubkey, commission: u8) -> Self {
|
pub fn new(vote_init: &VoteInit) -> Self {
|
||||||
Self {
|
Self {
|
||||||
node_pubkey: *node_pubkey,
|
node_pubkey: vote_init.node_pubkey,
|
||||||
authorized_voter_pubkey: *vote_pubkey,
|
authorized_voter: vote_init.authorized_voter,
|
||||||
commission,
|
authorized_withdrawer: vote_init.authorized_withdrawer,
|
||||||
|
commission: vote_init.commission,
|
||||||
..VoteState::default()
|
..VoteState::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -313,39 +333,69 @@ impl VoteState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Authorize the given pubkey to sign votes. This may be called multiple times,
|
/// Authorize the given pubkey to withdraw or sign votes. This may be called multiple times,
|
||||||
/// but will implicitly withdraw authorization from the previously authorized
|
/// but will implicitly withdraw authorization from the previously authorized
|
||||||
/// voter. The default voter is the owner of the vote account's pubkey.
|
/// key
|
||||||
pub fn authorize_voter(
|
pub fn authorize(
|
||||||
vote_account: &mut KeyedAccount,
|
vote_account: &mut KeyedAccount,
|
||||||
other_signers: &[KeyedAccount],
|
other_signers: &[KeyedAccount],
|
||||||
authorized_voter_pubkey: &Pubkey,
|
authorized: &Pubkey,
|
||||||
|
vote_authorize: VoteAuthorize,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let mut vote_state: VoteState = vote_account.state()?;
|
let mut vote_state: VoteState = vote_account.state()?;
|
||||||
|
|
||||||
// clock authorized signer must say "yay"
|
// current authorized signer must say "yay"
|
||||||
let authorized = Some(&vote_state.authorized_voter_pubkey);
|
match vote_authorize {
|
||||||
if vote_account.signer_key() != authorized
|
VoteAuthorize::Voter => {
|
||||||
|
verify_authorized_signer(&vote_state.authorized_voter, vote_account, other_signers)?;
|
||||||
|
vote_state.authorized_voter = *authorized;
|
||||||
|
}
|
||||||
|
VoteAuthorize::Withdrawer => {
|
||||||
|
verify_authorized_signer(
|
||||||
|
&vote_state.authorized_withdrawer,
|
||||||
|
vote_account,
|
||||||
|
other_signers,
|
||||||
|
)?;
|
||||||
|
vote_state.authorized_withdrawer = *authorized;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vote_account.set_state(&vote_state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_authorized_signer(
|
||||||
|
authorized: &Pubkey,
|
||||||
|
account: &KeyedAccount,
|
||||||
|
other_signers: &[KeyedAccount],
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
|
let authorized = Some(authorized);
|
||||||
|
|
||||||
|
// find a signer that matches authorized
|
||||||
|
if account.signer_key() != authorized
|
||||||
&& other_signers
|
&& other_signers
|
||||||
.iter()
|
.iter()
|
||||||
.all(|account| account.signer_key() != authorized)
|
.all(|account| account.signer_key() != authorized)
|
||||||
{
|
{
|
||||||
return Err(InstructionError::MissingRequiredSignature);
|
return Err(InstructionError::MissingRequiredSignature);
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
vote_state.authorized_voter_pubkey = *authorized_voter_pubkey;
|
|
||||||
vote_account.set_state(&vote_state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Withdraw funds from the vote account
|
/// Withdraw funds from the vote account
|
||||||
pub fn withdraw(
|
pub fn withdraw(
|
||||||
vote_account: &mut KeyedAccount,
|
vote_account: &mut KeyedAccount,
|
||||||
|
other_signers: &[KeyedAccount],
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
to_account: &mut KeyedAccount,
|
to_account: &mut KeyedAccount,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
if vote_account.signer_key().is_none() {
|
let vote_state: VoteState = vote_account.state()?;
|
||||||
return Err(InstructionError::MissingRequiredSignature);
|
|
||||||
}
|
verify_authorized_signer(
|
||||||
|
&vote_state.authorized_withdrawer,
|
||||||
|
vote_account,
|
||||||
|
other_signers,
|
||||||
|
)?;
|
||||||
|
|
||||||
if vote_account.account.lamports < lamports {
|
if vote_account.account.lamports < lamports {
|
||||||
return Err(InstructionError::InsufficientFunds);
|
return Err(InstructionError::InsufficientFunds);
|
||||||
}
|
}
|
||||||
@@ -359,19 +409,14 @@ pub fn withdraw(
|
|||||||
/// that the transaction must be signed by the staker's keys
|
/// that the transaction must be signed by the staker's keys
|
||||||
pub fn initialize_account(
|
pub fn initialize_account(
|
||||||
vote_account: &mut KeyedAccount,
|
vote_account: &mut KeyedAccount,
|
||||||
node_pubkey: &Pubkey,
|
vote_init: &VoteInit,
|
||||||
commission: u8,
|
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let vote_state: VoteState = vote_account.state()?;
|
let vote_state: VoteState = vote_account.state()?;
|
||||||
|
|
||||||
if vote_state.authorized_voter_pubkey != Pubkey::default() {
|
if vote_state.authorized_voter != Pubkey::default() {
|
||||||
return Err(InstructionError::AccountAlreadyInitialized);
|
return Err(InstructionError::AccountAlreadyInitialized);
|
||||||
}
|
}
|
||||||
vote_account.set_state(&VoteState::new(
|
vote_account.set_state(&VoteState::new(vote_init))
|
||||||
vote_account.unsigned_key(),
|
|
||||||
node_pubkey,
|
|
||||||
commission,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_vote(
|
pub fn process_vote(
|
||||||
@@ -383,19 +428,11 @@ pub fn process_vote(
|
|||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let mut vote_state: VoteState = vote_account.state()?;
|
let mut vote_state: VoteState = vote_account.state()?;
|
||||||
|
|
||||||
if vote_state.authorized_voter_pubkey == Pubkey::default() {
|
if vote_state.authorized_voter == Pubkey::default() {
|
||||||
return Err(InstructionError::UninitializedAccount);
|
return Err(InstructionError::UninitializedAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
let authorized = Some(&vote_state.authorized_voter_pubkey);
|
verify_authorized_signer(&vote_state.authorized_voter, vote_account, other_signers)?;
|
||||||
// find a signer that matches the authorized_voter_pubkey
|
|
||||||
if vote_account.signer_key() != authorized
|
|
||||||
&& other_signers
|
|
||||||
.iter()
|
|
||||||
.all(|account| account.signer_key() != authorized)
|
|
||||||
{
|
|
||||||
return Err(InstructionError::MissingRequiredSignature);
|
|
||||||
}
|
|
||||||
|
|
||||||
vote_state.process_vote(vote, slot_hashes, clock.epoch)?;
|
vote_state.process_vote(vote, slot_hashes, clock.epoch)?;
|
||||||
vote_account.set_state(&vote_state)
|
vote_account.set_state(&vote_state)
|
||||||
@@ -410,7 +447,12 @@ pub fn create_account(
|
|||||||
) -> Account {
|
) -> Account {
|
||||||
let mut vote_account = Account::new(lamports, VoteState::size_of(), &id());
|
let mut vote_account = Account::new(lamports, VoteState::size_of(), &id());
|
||||||
|
|
||||||
VoteState::new(vote_pubkey, node_pubkey, commission)
|
VoteState::new(&VoteInit {
|
||||||
|
node_pubkey: *node_pubkey,
|
||||||
|
authorized_voter: *vote_pubkey,
|
||||||
|
authorized_withdrawer: *vote_pubkey,
|
||||||
|
commission,
|
||||||
|
})
|
||||||
.to(&mut vote_account)
|
.to(&mut vote_account)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -427,6 +469,17 @@ mod tests {
|
|||||||
|
|
||||||
const MAX_RECENT_VOTES: usize = 16;
|
const MAX_RECENT_VOTES: usize = 16;
|
||||||
|
|
||||||
|
impl VoteState {
|
||||||
|
pub fn new_for_test(auth_pubkey: &Pubkey) -> Self {
|
||||||
|
Self::new(&VoteInit {
|
||||||
|
node_pubkey: Pubkey::new_rand(),
|
||||||
|
authorized_voter: *auth_pubkey,
|
||||||
|
authorized_withdrawer: *auth_pubkey,
|
||||||
|
commission: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_initialize_vote_account() {
|
fn test_initialize_vote_account() {
|
||||||
let vote_account_pubkey = Pubkey::new_rand();
|
let vote_account_pubkey = Pubkey::new_rand();
|
||||||
@@ -436,11 +489,27 @@ mod tests {
|
|||||||
|
|
||||||
//init should pass
|
//init should pass
|
||||||
let mut vote_account = KeyedAccount::new(&vote_account_pubkey, false, &mut vote_account);
|
let mut vote_account = KeyedAccount::new(&vote_account_pubkey, false, &mut vote_account);
|
||||||
let res = initialize_account(&mut vote_account, &node_pubkey, 0);
|
let res = initialize_account(
|
||||||
|
&mut vote_account,
|
||||||
|
&VoteInit {
|
||||||
|
node_pubkey,
|
||||||
|
authorized_voter: vote_account_pubkey,
|
||||||
|
authorized_withdrawer: vote_account_pubkey,
|
||||||
|
commission: 0,
|
||||||
|
},
|
||||||
|
);
|
||||||
assert_eq!(res, Ok(()));
|
assert_eq!(res, Ok(()));
|
||||||
|
|
||||||
// reinit should fail
|
// reinit should fail
|
||||||
let res = initialize_account(&mut vote_account, &node_pubkey, 0);
|
let res = initialize_account(
|
||||||
|
&mut vote_account,
|
||||||
|
&VoteInit {
|
||||||
|
node_pubkey,
|
||||||
|
authorized_voter: vote_account_pubkey,
|
||||||
|
authorized_withdrawer: vote_account_pubkey,
|
||||||
|
commission: 0,
|
||||||
|
},
|
||||||
|
);
|
||||||
assert_eq!(res, Err(InstructionError::AccountAlreadyInitialized));
|
assert_eq!(res, Err(InstructionError::AccountAlreadyInitialized));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,7 +573,7 @@ mod tests {
|
|||||||
let (vote_pubkey, vote_account) = create_test_account();
|
let (vote_pubkey, vote_account) = create_test_account();
|
||||||
|
|
||||||
let vote_state: VoteState = vote_account.state().unwrap();
|
let vote_state: VoteState = vote_account.state().unwrap();
|
||||||
assert_eq!(vote_state.authorized_voter_pubkey, vote_pubkey);
|
assert_eq!(vote_state.authorized_voter, vote_pubkey);
|
||||||
assert!(vote_state.votes.is_empty());
|
assert!(vote_state.votes.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,21 +651,23 @@ mod tests {
|
|||||||
|
|
||||||
// another voter
|
// another voter
|
||||||
let authorized_voter_pubkey = Pubkey::new_rand();
|
let authorized_voter_pubkey = Pubkey::new_rand();
|
||||||
let res = authorize_voter(
|
let res = authorize(
|
||||||
&mut KeyedAccount::new(&vote_pubkey, false, &mut vote_account),
|
&mut KeyedAccount::new(&vote_pubkey, false, &mut vote_account),
|
||||||
&[],
|
&[],
|
||||||
&authorized_voter_pubkey,
|
&authorized_voter_pubkey,
|
||||||
|
VoteAuthorize::Voter,
|
||||||
);
|
);
|
||||||
assert_eq!(res, Err(InstructionError::MissingRequiredSignature));
|
assert_eq!(res, Err(InstructionError::MissingRequiredSignature));
|
||||||
|
|
||||||
let res = authorize_voter(
|
let res = authorize(
|
||||||
&mut KeyedAccount::new(&vote_pubkey, true, &mut vote_account),
|
&mut KeyedAccount::new(&vote_pubkey, true, &mut vote_account),
|
||||||
&[],
|
&[],
|
||||||
&authorized_voter_pubkey,
|
&authorized_voter_pubkey,
|
||||||
|
VoteAuthorize::Voter,
|
||||||
);
|
);
|
||||||
assert_eq!(res, Ok(()));
|
assert_eq!(res, Ok(()));
|
||||||
// verify authorized_voter_pubkey can authorize authorized_voter_pubkey ;)
|
// verify authorized_voter_pubkey can authorize authorized_voter_pubkey ;)
|
||||||
let res = authorize_voter(
|
let res = authorize(
|
||||||
&mut KeyedAccount::new(&vote_pubkey, false, &mut vote_account),
|
&mut KeyedAccount::new(&vote_pubkey, false, &mut vote_account),
|
||||||
&[KeyedAccount::new(
|
&[KeyedAccount::new(
|
||||||
&authorized_voter_pubkey,
|
&authorized_voter_pubkey,
|
||||||
@@ -604,6 +675,31 @@ mod tests {
|
|||||||
&mut Account::default(),
|
&mut Account::default(),
|
||||||
)],
|
)],
|
||||||
&authorized_voter_pubkey,
|
&authorized_voter_pubkey,
|
||||||
|
VoteAuthorize::Voter,
|
||||||
|
);
|
||||||
|
assert_eq!(res, Ok(()));
|
||||||
|
|
||||||
|
// authorize another withdrawer
|
||||||
|
// another voter
|
||||||
|
let authorized_withdrawer_pubkey = Pubkey::new_rand();
|
||||||
|
let res = authorize(
|
||||||
|
&mut KeyedAccount::new(&vote_pubkey, true, &mut vote_account),
|
||||||
|
&[],
|
||||||
|
&authorized_withdrawer_pubkey,
|
||||||
|
VoteAuthorize::Withdrawer,
|
||||||
|
);
|
||||||
|
assert_eq!(res, Ok(()));
|
||||||
|
|
||||||
|
// verify authorized_withdrawer can authorize authorized_withdrawer ;)
|
||||||
|
let res = authorize(
|
||||||
|
&mut KeyedAccount::new(&vote_pubkey, false, &mut vote_account),
|
||||||
|
&[KeyedAccount::new(
|
||||||
|
&authorized_withdrawer_pubkey,
|
||||||
|
true,
|
||||||
|
&mut Account::default(),
|
||||||
|
)],
|
||||||
|
&authorized_withdrawer_pubkey,
|
||||||
|
VoteAuthorize::Withdrawer,
|
||||||
);
|
);
|
||||||
assert_eq!(res, Ok(()));
|
assert_eq!(res, Ok(()));
|
||||||
|
|
||||||
@@ -678,7 +774,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_vote_double_lockout_after_expiration() {
|
fn test_vote_double_lockout_after_expiration() {
|
||||||
let voter_pubkey = Pubkey::new_rand();
|
let voter_pubkey = Pubkey::new_rand();
|
||||||
let mut vote_state = VoteState::new(&voter_pubkey, &Pubkey::new_rand(), 0);
|
let mut vote_state = VoteState::new_for_test(&voter_pubkey);
|
||||||
|
|
||||||
for i in 0..3 {
|
for i in 0..3 {
|
||||||
vote_state.process_slot_vote_unchecked(i as u64);
|
vote_state.process_slot_vote_unchecked(i as u64);
|
||||||
@@ -706,7 +802,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_expire_multiple_votes() {
|
fn test_expire_multiple_votes() {
|
||||||
let voter_pubkey = Pubkey::new_rand();
|
let voter_pubkey = Pubkey::new_rand();
|
||||||
let mut vote_state = VoteState::new(&voter_pubkey, &Pubkey::new_rand(), 0);
|
let mut vote_state = VoteState::new_for_test(&voter_pubkey);
|
||||||
|
|
||||||
for i in 0..3 {
|
for i in 0..3 {
|
||||||
vote_state.process_slot_vote_unchecked(i as u64);
|
vote_state.process_slot_vote_unchecked(i as u64);
|
||||||
@@ -737,7 +833,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_vote_credits() {
|
fn test_vote_credits() {
|
||||||
let voter_pubkey = Pubkey::new_rand();
|
let voter_pubkey = Pubkey::new_rand();
|
||||||
let mut vote_state = VoteState::new(&voter_pubkey, &Pubkey::new_rand(), 0);
|
let mut vote_state = VoteState::new_for_test(&voter_pubkey);
|
||||||
|
|
||||||
for i in 0..MAX_LOCKOUT_HISTORY {
|
for i in 0..MAX_LOCKOUT_HISTORY {
|
||||||
vote_state.process_slot_vote_unchecked(i as u64);
|
vote_state.process_slot_vote_unchecked(i as u64);
|
||||||
@@ -756,7 +852,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_duplicate_vote() {
|
fn test_duplicate_vote() {
|
||||||
let voter_pubkey = Pubkey::new_rand();
|
let voter_pubkey = Pubkey::new_rand();
|
||||||
let mut vote_state = VoteState::new(&voter_pubkey, &Pubkey::new_rand(), 0);
|
let mut vote_state = VoteState::new_for_test(&voter_pubkey);
|
||||||
vote_state.process_slot_vote_unchecked(0);
|
vote_state.process_slot_vote_unchecked(0);
|
||||||
vote_state.process_slot_vote_unchecked(1);
|
vote_state.process_slot_vote_unchecked(1);
|
||||||
vote_state.process_slot_vote_unchecked(0);
|
vote_state.process_slot_vote_unchecked(0);
|
||||||
@@ -768,7 +864,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_nth_recent_vote() {
|
fn test_nth_recent_vote() {
|
||||||
let voter_pubkey = Pubkey::new_rand();
|
let voter_pubkey = Pubkey::new_rand();
|
||||||
let mut vote_state = VoteState::new(&voter_pubkey, &Pubkey::new_rand(), 0);
|
let mut vote_state = VoteState::new_for_test(&voter_pubkey);
|
||||||
for i in 0..MAX_LOCKOUT_HISTORY {
|
for i in 0..MAX_LOCKOUT_HISTORY {
|
||||||
vote_state.process_slot_vote_unchecked(i as u64);
|
vote_state.process_slot_vote_unchecked(i as u64);
|
||||||
}
|
}
|
||||||
@@ -799,9 +895,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_process_missed_votes() {
|
fn test_process_missed_votes() {
|
||||||
let account_a = Pubkey::new_rand();
|
let account_a = Pubkey::new_rand();
|
||||||
let mut vote_state_a = VoteState::new(&account_a, &Pubkey::new_rand(), 0);
|
let mut vote_state_a = VoteState::new_for_test(&account_a);
|
||||||
let account_b = Pubkey::new_rand();
|
let account_b = Pubkey::new_rand();
|
||||||
let mut vote_state_b = VoteState::new(&account_b, &Pubkey::new_rand(), 0);
|
let mut vote_state_b = VoteState::new_for_test(&account_b);
|
||||||
|
|
||||||
// process some votes on account a
|
// process some votes on account a
|
||||||
(0..5)
|
(0..5)
|
||||||
@@ -821,7 +917,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_vote_skips_old_vote() {
|
fn test_process_vote_skips_old_vote() {
|
||||||
let mut vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
|
let mut vote_state = VoteState::default();
|
||||||
|
|
||||||
let vote = Vote::new(vec![0], Hash::default());
|
let vote = Vote::new(vec![0], Hash::default());
|
||||||
let slot_hashes: Vec<_> = vec![(0, vote.hash)];
|
let slot_hashes: Vec<_> = vec![(0, vote.hash)];
|
||||||
@@ -836,7 +932,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_slots_are_valid_vote_empty_slot_hashes() {
|
fn test_check_slots_are_valid_vote_empty_slot_hashes() {
|
||||||
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
|
let vote_state = VoteState::default();
|
||||||
|
|
||||||
let vote = Vote::new(vec![0], Hash::default());
|
let vote = Vote::new(vec![0], Hash::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -847,7 +943,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_slots_are_valid_new_vote() {
|
fn test_check_slots_are_valid_new_vote() {
|
||||||
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
|
let vote_state = VoteState::default();
|
||||||
|
|
||||||
let vote = Vote::new(vec![0], Hash::default());
|
let vote = Vote::new(vec![0], Hash::default());
|
||||||
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
|
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
|
||||||
@@ -859,7 +955,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_slots_are_valid_bad_hash() {
|
fn test_check_slots_are_valid_bad_hash() {
|
||||||
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
|
let vote_state = VoteState::default();
|
||||||
|
|
||||||
let vote = Vote::new(vec![0], Hash::default());
|
let vote = Vote::new(vec![0], Hash::default());
|
||||||
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), hash(vote.hash.as_ref()))];
|
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), hash(vote.hash.as_ref()))];
|
||||||
@@ -871,7 +967,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_slots_are_valid_bad_slot() {
|
fn test_check_slots_are_valid_bad_slot() {
|
||||||
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
|
let vote_state = VoteState::default();
|
||||||
|
|
||||||
let vote = Vote::new(vec![1], Hash::default());
|
let vote = Vote::new(vec![1], Hash::default());
|
||||||
let slot_hashes: Vec<_> = vec![(0, vote.hash)];
|
let slot_hashes: Vec<_> = vec![(0, vote.hash)];
|
||||||
@@ -883,7 +979,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_slots_are_valid_duplicate_vote() {
|
fn test_check_slots_are_valid_duplicate_vote() {
|
||||||
let mut vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
|
let mut vote_state = VoteState::default();
|
||||||
|
|
||||||
let vote = Vote::new(vec![0], Hash::default());
|
let vote = Vote::new(vec![0], Hash::default());
|
||||||
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
|
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
|
||||||
@@ -896,7 +992,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_slots_are_valid_next_vote() {
|
fn test_check_slots_are_valid_next_vote() {
|
||||||
let mut vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
|
let mut vote_state = VoteState::default();
|
||||||
|
|
||||||
let vote = Vote::new(vec![0], Hash::default());
|
let vote = Vote::new(vec![0], Hash::default());
|
||||||
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
|
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
|
||||||
@@ -912,7 +1008,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_slots_are_valid_next_vote_only() {
|
fn test_check_slots_are_valid_next_vote_only() {
|
||||||
let mut vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
|
let mut vote_state = VoteState::default();
|
||||||
|
|
||||||
let vote = Vote::new(vec![0], Hash::default());
|
let vote = Vote::new(vec![0], Hash::default());
|
||||||
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
|
let slot_hashes: Vec<_> = vec![(*vote.slots.last().unwrap(), vote.hash)];
|
||||||
@@ -925,17 +1021,28 @@ mod tests {
|
|||||||
Ok(())
|
Ok(())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_process_vote_empty_slots() {
|
||||||
|
let mut vote_state = VoteState::default();
|
||||||
|
|
||||||
|
let vote = Vote::new(vec![], Hash::default());
|
||||||
|
assert_eq!(
|
||||||
|
vote_state.process_vote(&vote, &[], 0),
|
||||||
|
Err(VoteError::EmptySlots)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vote_state_commission_split() {
|
fn test_vote_state_commission_split() {
|
||||||
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), 0);
|
let vote_state = VoteState::default();
|
||||||
|
|
||||||
assert_eq!(vote_state.commission_split(1.0), (0.0, 1.0, false));
|
assert_eq!(vote_state.commission_split(1.0), (0.0, 1.0, false));
|
||||||
|
|
||||||
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), std::u8::MAX);
|
let mut vote_state = VoteState::default();
|
||||||
|
vote_state.commission = std::u8::MAX;
|
||||||
assert_eq!(vote_state.commission_split(1.0), (1.0, 0.0, false));
|
assert_eq!(vote_state.commission_split(1.0), (1.0, 0.0, false));
|
||||||
|
|
||||||
let vote_state = VoteState::new(&Pubkey::default(), &Pubkey::default(), std::u8::MAX / 2);
|
vote_state.commission = std::u8::MAX / 2;
|
||||||
let (voter_portion, staker_portion, was_split) = vote_state.commission_split(10.0);
|
let (voter_portion, staker_portion, was_split) = vote_state.commission_split(10.0);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -948,9 +1055,10 @@ mod tests {
|
|||||||
fn test_vote_state_withdraw() {
|
fn test_vote_state_withdraw() {
|
||||||
let (vote_pubkey, mut vote_account) = create_test_account();
|
let (vote_pubkey, mut vote_account) = create_test_account();
|
||||||
|
|
||||||
// unsigned
|
// unsigned request
|
||||||
let res = withdraw(
|
let res = withdraw(
|
||||||
&mut KeyedAccount::new(&vote_pubkey, false, &mut vote_account),
|
&mut KeyedAccount::new(&vote_pubkey, false, &mut vote_account),
|
||||||
|
&[],
|
||||||
0,
|
0,
|
||||||
&mut KeyedAccount::new(&Pubkey::new_rand(), false, &mut Account::default()),
|
&mut KeyedAccount::new(&Pubkey::new_rand(), false, &mut Account::default()),
|
||||||
);
|
);
|
||||||
@@ -959,6 +1067,7 @@ mod tests {
|
|||||||
// insufficient funds
|
// insufficient funds
|
||||||
let res = withdraw(
|
let res = withdraw(
|
||||||
&mut KeyedAccount::new(&vote_pubkey, true, &mut vote_account),
|
&mut KeyedAccount::new(&vote_pubkey, true, &mut vote_account),
|
||||||
|
&[],
|
||||||
101,
|
101,
|
||||||
&mut KeyedAccount::new(&Pubkey::new_rand(), false, &mut Account::default()),
|
&mut KeyedAccount::new(&Pubkey::new_rand(), false, &mut Account::default()),
|
||||||
);
|
);
|
||||||
@@ -969,6 +1078,25 @@ mod tests {
|
|||||||
let lamports = vote_account.lamports;
|
let lamports = vote_account.lamports;
|
||||||
let res = withdraw(
|
let res = withdraw(
|
||||||
&mut KeyedAccount::new(&vote_pubkey, true, &mut vote_account),
|
&mut KeyedAccount::new(&vote_pubkey, true, &mut vote_account),
|
||||||
|
&[],
|
||||||
|
lamports,
|
||||||
|
&mut KeyedAccount::new(&Pubkey::new_rand(), false, &mut to_account),
|
||||||
|
);
|
||||||
|
assert_eq!(res, Ok(()));
|
||||||
|
assert_eq!(vote_account.lamports, 0);
|
||||||
|
assert_eq!(to_account.lamports, lamports);
|
||||||
|
|
||||||
|
// reset balance, verify that authorized_withdrawer works
|
||||||
|
vote_account.lamports = lamports;
|
||||||
|
to_account.lamports = 0;
|
||||||
|
let mut authorized_withdrawer_account = Account::new(0, 0, &vote_pubkey);
|
||||||
|
let res = withdraw(
|
||||||
|
&mut KeyedAccount::new(&vote_pubkey, false, &mut vote_account),
|
||||||
|
&[KeyedAccount::new(
|
||||||
|
&vote_pubkey,
|
||||||
|
true,
|
||||||
|
&mut authorized_withdrawer_account,
|
||||||
|
)],
|
||||||
lamports,
|
lamports,
|
||||||
&mut KeyedAccount::new(&Pubkey::new_rand(), false, &mut to_account),
|
&mut KeyedAccount::new(&Pubkey::new_rand(), false, &mut to_account),
|
||||||
);
|
);
|
||||||
|
@@ -1584,7 +1584,7 @@ mod tests {
|
|||||||
use solana_sdk::sysvar::{fees::Fees, rewards::Rewards};
|
use solana_sdk::sysvar::{fees::Fees, rewards::Rewards};
|
||||||
use solana_stake_api::stake_state::Stake;
|
use solana_stake_api::stake_state::Stake;
|
||||||
use solana_vote_api::vote_instruction;
|
use solana_vote_api::vote_instruction;
|
||||||
use solana_vote_api::vote_state::{VoteState, MAX_LOCKOUT_HISTORY};
|
use solana_vote_api::vote_state::{VoteInit, VoteState, MAX_LOCKOUT_HISTORY};
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
@@ -2861,8 +2861,12 @@ mod tests {
|
|||||||
let instructions = vote_instruction::create_account(
|
let instructions = vote_instruction::create_account(
|
||||||
&mint_keypair.pubkey(),
|
&mint_keypair.pubkey(),
|
||||||
&vote_keypair.pubkey(),
|
&vote_keypair.pubkey(),
|
||||||
&mint_keypair.pubkey(),
|
&VoteInit {
|
||||||
0,
|
node_pubkey: mint_keypair.pubkey(),
|
||||||
|
authorized_voter: vote_keypair.pubkey(),
|
||||||
|
authorized_withdrawer: vote_keypair.pubkey(),
|
||||||
|
commission: 0,
|
||||||
|
},
|
||||||
10,
|
10,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -41,6 +41,7 @@ pub fn create_genesis_block_with_leader(
|
|||||||
);
|
);
|
||||||
|
|
||||||
let stake_account = stake_state::create_account(
|
let stake_account = stake_state::create_account(
|
||||||
|
&staking_keypair.pubkey(),
|
||||||
&voting_keypair.pubkey(),
|
&voting_keypair.pubkey(),
|
||||||
&vote_account,
|
&vote_account,
|
||||||
bootstrap_leader_stake_lamports,
|
bootstrap_leader_stake_lamports,
|
||||||
|
@@ -220,9 +220,11 @@ pub mod tests {
|
|||||||
|
|
||||||
// add stake to a vote_pubkey ( stake )
|
// add stake to a vote_pubkey ( stake )
|
||||||
pub fn create_stake_account(stake: u64, vote_pubkey: &Pubkey) -> (Pubkey, Account) {
|
pub fn create_stake_account(stake: u64, vote_pubkey: &Pubkey) -> (Pubkey, Account) {
|
||||||
|
let stake_pubkey = Pubkey::new_rand();
|
||||||
(
|
(
|
||||||
Pubkey::new_rand(),
|
stake_pubkey,
|
||||||
stake_state::create_account(
|
stake_state::create_account(
|
||||||
|
&stake_pubkey,
|
||||||
&vote_pubkey,
|
&vote_pubkey,
|
||||||
&vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 1),
|
&vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 1),
|
||||||
stake,
|
stake,
|
||||||
|
Reference in New Issue
Block a user