Move vote_program out of src/

This commit is contained in:
Michael Vines 2018-12-04 07:45:32 -08:00
parent a594f56c02
commit ea6e042a6f
20 changed files with 241 additions and 193 deletions

13
Cargo.lock generated
View File

@ -1774,6 +1774,7 @@ dependencies = [
"solana-noop 0.11.0",
"solana-sdk 0.11.0",
"solana-system-program 0.11.0",
"solana-vote-program 0.11.0",
"solana-vote-signer 0.0.1",
"sys-info 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1946,6 +1947,7 @@ version = "0.11.0"
dependencies = [
"bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1967,6 +1969,17 @@ dependencies = [
"solana-sdk 0.11.0",
]
[[package]]
name = "solana-vote-program"
version = "0.11.0"
dependencies = [
"bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
"solana-sdk 0.11.0",
]
[[package]]
name = "solana-vote-signer"
version = "0.0.1"

View File

@ -110,6 +110,7 @@ solana-metrics = { path = "metrics", version = "0.11.0" }
solana-noop = { path = "programs/native/noop", version = "0.11.0" }
solana-sdk = { path = "sdk", version = "0.11.0" }
solana-system-program = { path = "programs/native/system", version = "0.11.0" }
solana-vote-program = { path = "programs/native/vote", version = "0.11.0" }
solana-vote-signer = { path = "vote-signer", version = "0.0.1" }
sys-info = "0.5.6"
tokio = "0.1"

View File

@ -0,0 +1,20 @@
[package]
name = "solana-vote-program"
version = "0.11.0"
description = "Solana vote program"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
[dependencies]
bincode = "1.0.0"
env_logger = "0.6.0"
log = "0.4.2"
serde = "1.0.27"
serde_derive = "1.0.27"
solana-sdk = { path = "../../../sdk", version = "0.11.0" }
[lib]
name = "solana_vote_program"
crate-type = ["cdylib"]

View File

@ -0,0 +1,78 @@
//! Vote program
//! Receive and processes votes from validators
extern crate bincode;
#[macro_use]
extern crate log;
#[macro_use]
extern crate solana_sdk;
use bincode::deserialize;
use solana_sdk::account::KeyedAccount;
use solana_sdk::native_program::ProgramError;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::vote_program::*;
use std::collections::VecDeque;
solana_entrypoint!(entrypoint);
fn entrypoint(
_program_id: &Pubkey,
keyed_accounts: &mut [KeyedAccount],
data: &[u8],
_tick_height: u64,
) -> Result<(), ProgramError> {
trace!("process_instruction: {:?}", data);
trace!("keyed_accounts: {:?}", keyed_accounts);
// all vote instructions require that accounts_keys[0] be a signer
if keyed_accounts[0].signer_key().is_none() {
error!("account[0] is unsigned");
Err(ProgramError::InvalidArgument)?;
}
match deserialize(data) {
Ok(VoteInstruction::RegisterAccount) => {
if !check_id(&keyed_accounts[1].account.owner) {
error!("account[1] is not assigned to the VOTE_PROGRAM");
Err(ProgramError::InvalidArgument)?;
}
// TODO: a single validator could register multiple "vote accounts"
// which would clutter the "accounts" structure. See github issue 1654.
let mut vote_state = VoteProgram {
votes: VecDeque::new(),
node_id: *keyed_accounts[0].signer_key().unwrap(),
};
vote_state.serialize(&mut keyed_accounts[1].account.userdata)?;
Ok(())
}
Ok(VoteInstruction::NewVote(vote)) => {
if !check_id(&keyed_accounts[0].account.owner) {
error!("account[0] is not assigned to the VOTE_PROGRAM");
Err(ProgramError::InvalidArgument)?;
}
let mut vote_state = VoteProgram::deserialize(&keyed_accounts[0].account.userdata)?;
// TODO: Integrity checks
// a) Verify the vote's bank hash matches what is expected
// b) Verify vote is older than previous votes
// Only keep around the most recent MAX_VOTE_HISTORY votes
if vote_state.votes.len() == MAX_VOTE_HISTORY {
vote_state.votes.pop_front();
}
vote_state.votes.push_back(vote);
vote_state.serialize(&mut keyed_accounts[0].account.userdata)?;
Ok(())
}
Err(_) => {
info!("Invalid transaction instruction userdata: {:?}", data);
Err(ProgramError::InvalidUserdata)
}
}
}

View File

@ -8,6 +8,7 @@ license = "Apache-2.0"
[dependencies]
bincode = "1.0.0"
byteorder = "1.2.1"
bs58 = "0.2.0"
generic-array = { version = "0.12.0", default-features = false, features = ["serde"] }
log = "0.4.2"

View File

@ -12,9 +12,11 @@ pub mod system_program;
pub mod timing;
pub mod token_program;
pub mod transaction;
pub mod vote_program;
extern crate bincode;
extern crate bs58;
extern crate byteorder;
extern crate generic_array;
extern crate log;
extern crate ring;

98
sdk/src/vote_program.rs Normal file
View File

@ -0,0 +1,98 @@
//! Vote program
//! Receive and processes votes from validators
use bincode::{deserialize, serialize};
use byteorder::{ByteOrder, LittleEndian};
use native_program::ProgramError;
use pubkey::Pubkey;
use std::collections::VecDeque;
use std::mem;
pub const VOTE_PROGRAM_ID: [u8; 32] = [
132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
];
pub fn check_id(program_id: &Pubkey) -> bool {
program_id.as_ref() == VOTE_PROGRAM_ID
}
pub fn id() -> Pubkey {
Pubkey::new(&VOTE_PROGRAM_ID)
}
// Maximum number of votes to keep around
pub const MAX_VOTE_HISTORY: usize = 32;
#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Vote {
// TODO: add signature of the state here as well
/// A vote for height tick_height
pub tick_height: u64,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum VoteInstruction {
/// Register a new "vote account" to represent a particular validator in the Vote Contract,
/// and initialize the VoteState for this "vote account"
/// * Transaction::keys[0] - the validator id
/// * Transaction::keys[1] - the new "vote account" to be associated with the validator
/// identified by keys[0] for voting
RegisterAccount,
NewVote(Vote),
}
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct VoteProgram {
pub votes: VecDeque<Vote>,
pub node_id: Pubkey,
}
pub fn get_max_size() -> usize {
// Upper limit on the size of the Vote State. Equal to
// sizeof(VoteProgram) + MAX_VOTE_HISTORY * sizeof(Vote) +
// 32 (the size of the Pubkey) + 2 (2 bytes for the size)
mem::size_of::<VoteProgram>()
+ MAX_VOTE_HISTORY * mem::size_of::<Vote>()
+ mem::size_of::<Pubkey>()
+ mem::size_of::<u16>()
}
impl VoteProgram {
pub fn deserialize(input: &[u8]) -> Result<VoteProgram, ProgramError> {
let len = LittleEndian::read_u16(&input[0..2]) as usize;
if len == 0 || input.len() < len + 2 {
Err(ProgramError::InvalidUserdata)
} else {
deserialize(&input[2..=len + 1]).map_err(|_| ProgramError::InvalidUserdata)
}
}
pub fn serialize(self: &VoteProgram, output: &mut [u8]) -> Result<(), ProgramError> {
let self_serialized = serialize(self).unwrap();
if output.len() + 2 < self_serialized.len() {
return Err(ProgramError::UserdataTooSmall);
}
let serialized_len = self_serialized.len() as u16;
LittleEndian::write_u16(&mut output[0..2], serialized_len);
output[2..=serialized_len as usize + 1].clone_from_slice(&self_serialized);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_serde() {
let mut buffer: Vec<u8> = vec![0; get_max_size()];
let mut vote_program = VoteProgram::default();
vote_program.votes = (0..MAX_VOTE_HISTORY).map(|_| Vote::default()).collect();
vote_program.serialize(&mut buffer).unwrap();
assert_eq!(VoteProgram::deserialize(&buffer).unwrap(), vote_program);
}
}

View File

@ -41,6 +41,7 @@ use std::sync::{Arc, Mutex, RwLock};
use std::time::Instant;
use system_transaction::SystemTransaction;
use tokio::prelude::Future;
use solana_sdk::vote_program;
/// The number of most recent `last_id` values that the bank will track the signatures
/// of. Once the bank discards a `last_id`, it will reject any transactions that use
@ -411,8 +412,22 @@ impl Bank {
accounts.store(&system_program::id(), &system_program_account);
}
fn add_vote_program(&self) {
let mut accounts = self.accounts.write().unwrap();
let vote_program_account = Account {
tokens: 1,
owner: vote_program::id(),
userdata: b"solana_vote_program".to_vec(),
executable: true,
loader: native_loader::id(),
};
accounts.store(&vote_program::id(), &vote_program_account);
}
fn add_builtin_programs(&self) {
self.add_system_program();
self.add_vote_program();
let mut accounts = self.accounts.write().unwrap();
// Bpf Loader
@ -1406,7 +1421,6 @@ mod tests {
use storage_program;
use system_transaction::SystemTransaction;
use tokio::prelude::{Async, Stream};
use vote_program;
#[test]
fn test_bank_new() {

View File

@ -17,9 +17,9 @@ use solana::leader_scheduler::LeaderScheduler;
use solana::logger;
use solana::netutil::find_available_port_in_range;
use solana::thin_client::poll_gossip_for_leader;
use solana::vote_program::VoteProgram;
use solana::vote_transaction::VoteTransaction;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::vote_program::VoteProgram;
use std::fs::File;
use std::net::{Ipv4Addr, SocketAddr};
use std::process::exit;

View File

@ -13,7 +13,7 @@ use std::sync::Arc;
use std::thread::sleep;
use std::thread::{self, Builder, JoinHandle};
use std::time::Duration;
use vote_program::{self, VoteProgram};
use solana_sdk::vote_program::{self, VoteProgram};
#[derive(Debug, PartialEq, Eq)]
pub enum FinalityError {
@ -144,7 +144,7 @@ pub mod tests {
use std::sync::Arc;
use std::thread::sleep;
use std::time::Duration;
use vote_program::Vote;
use solana_sdk::vote_program::Vote;
use vote_transaction::{create_vote_account, VoteTransaction};
#[test]

View File

@ -14,7 +14,7 @@ use solana_sdk::transaction::Transaction;
use std::collections::HashSet;
use std::io::Cursor;
use system_transaction::SystemTransaction;
use vote_program::{self, Vote, VoteProgram};
use solana_sdk::vote_program::{self, Vote, VoteProgram};
use vote_transaction::VoteTransaction;
pub const DEFAULT_BOOTSTRAP_HEIGHT: u64 = 1000;
@ -518,7 +518,7 @@ mod tests {
use std::collections::HashSet;
use std::hash::Hash as StdHash;
use std::iter::FromIterator;
use vote_program::Vote;
use solana_sdk::vote_program::Vote;
use vote_transaction::{create_vote_account, VoteTransaction};
fn to_hashset_owned<T>(slice: &[T]) -> HashSet<T>

View File

@ -20,7 +20,7 @@ use std::io::{self, BufReader, BufWriter, Seek, SeekFrom};
use std::mem::size_of;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::Path;
use vote_program::Vote;
use solana_sdk::vote_program::Vote;
use vote_transaction::VoteTransaction;
//
@ -709,7 +709,7 @@ mod tests {
use solana_sdk::transaction::Transaction;
use std;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use vote_program::Vote;
use solana_sdk::vote_program::Vote;
#[test]
fn test_verify_slice() {

View File

@ -78,7 +78,6 @@ pub mod thin_client;
pub mod tpu;
pub mod tpu_forwarder;
pub mod tvu;
pub mod vote_program;
pub mod vote_stage;
pub mod vote_transaction;
pub mod wallet;

View File

@ -6,7 +6,6 @@ use solana_sdk::pubkey::Pubkey;
use solana_sdk::system_program;
use solana_sdk::transaction::Transaction;
use storage_program;
use vote_program;
/// Reasons the runtime might have rejected a transaction.
#[derive(Debug, PartialEq, Eq, Clone)]
@ -16,9 +15,7 @@ pub enum RuntimeError {
}
pub fn is_legacy_program(program_id: &Pubkey) -> bool {
budget_program::check_id(program_id)
|| storage_program::check_id(program_id)
|| vote_program::check_id(program_id)
budget_program::check_id(program_id) || storage_program::check_id(program_id)
}
/// Process an instruction
@ -39,8 +36,6 @@ fn process_instruction(
budget_program::process(&tx, instruction_index, program_accounts)?;
} else if storage_program::check_id(&program_id) {
storage_program::process(&tx, instruction_index, program_accounts)?;
} else if vote_program::check_id(&program_id) {
vote_program::process(&tx, instruction_index, program_accounts)?;
} else {
unreachable!();
};

View File

@ -18,7 +18,7 @@ use std::sync::mpsc::RecvTimeoutError;
use std::sync::{Arc, RwLock};
use std::thread::{self, Builder, JoinHandle};
use std::time::Duration;
use vote_program;
use solana_sdk::vote_program;
// Block of hash answers to validate against
// Vec of [ledger blocks] x [keys]
@ -278,7 +278,7 @@ mod tests {
use storage_stage::StorageState;
use storage_stage::NUM_IDENTITIES;
use storage_stage::{get_identity_index_from_pubkey, StorageStage};
use vote_program::Vote;
use solana_sdk::vote_program::Vote;
use vote_transaction::VoteTransaction;
#[test]

View File

@ -439,7 +439,7 @@ mod tests {
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::system_instruction::SystemInstruction;
use std::fs::remove_dir_all;
use vote_program::VoteProgram;
use solana_sdk::vote_program::VoteProgram;
use vote_transaction::VoteTransaction;
#[test]

View File

@ -1,172 +0,0 @@
//! Vote program
//! Receive and processes votes from validators
use bincode::{deserialize, serialize};
use byteorder::{ByteOrder, LittleEndian};
use solana_sdk::account::Account;
use solana_sdk::native_program::ProgramError;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::transaction::Transaction;
use std;
use std::collections::VecDeque;
use std::mem;
// Maximum number of votes to keep around
const MAX_VOTE_HISTORY: usize = 32;
pub type Result<T> = std::result::Result<T, ProgramError>;
#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Vote {
// TODO: add signature of the state here as well
/// A vote for height tick_height
pub tick_height: u64,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum VoteInstruction {
/// Register a new "vote account" to represent a particular validator in the Vote Contract,
/// and initialize the VoteState for this "vote account"
/// * Transaction::keys[0] - the validator id
/// * Transaction::keys[1] - the new "vote account" to be associated with the validator
/// identified by keys[0] for voting
RegisterAccount,
NewVote(Vote),
}
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct VoteProgram {
pub votes: VecDeque<Vote>,
pub node_id: Pubkey,
}
const VOTE_PROGRAM_ID: [u8; 32] = [
132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
];
pub fn check_id(program_id: &Pubkey) -> bool {
program_id.as_ref() == VOTE_PROGRAM_ID
}
pub fn id() -> Pubkey {
Pubkey::new(&VOTE_PROGRAM_ID)
}
pub fn process(
tx: &Transaction,
instruction_index: usize,
accounts: &mut [&mut Account],
) -> Result<()> {
// all vote instructions require that accounts_keys[0] be a signer
if tx.signer_key(instruction_index, 0).is_none() {
Err(ProgramError::InvalidArgument)?;
}
match deserialize(tx.userdata(instruction_index)) {
Ok(VoteInstruction::RegisterAccount) => {
if !check_id(&accounts[1].owner) {
error!("accounts[1] is not assigned to the VOTE_PROGRAM");
Err(ProgramError::InvalidArgument)?;
}
// TODO: a single validator could register multiple "vote accounts"
// which would clutter the "accounts" structure. See github issue 1654.
let mut vote_state = VoteProgram {
votes: VecDeque::new(),
node_id: *tx.from(),
};
vote_state.serialize(&mut accounts[1].userdata)?;
Ok(())
}
Ok(VoteInstruction::NewVote(vote)) => {
if !check_id(&accounts[0].owner) {
error!("accounts[0] is not assigned to the VOTE_PROGRAM");
Err(ProgramError::InvalidArgument)?;
}
let mut vote_state = VoteProgram::deserialize(&accounts[0].userdata)?;
// TODO: Integrity checks
// a) Verify the vote's bank hash matches what is expected
// b) Verify vote is older than previous votes
// Only keep around the most recent MAX_VOTE_HISTORY votes
if vote_state.votes.len() == MAX_VOTE_HISTORY {
vote_state.votes.pop_front();
}
vote_state.votes.push_back(vote);
vote_state.serialize(&mut accounts[0].userdata)?;
Ok(())
}
Err(_) => {
info!(
"Invalid vote transaction userdata: {:?}",
tx.userdata(instruction_index)
);
Err(ProgramError::InvalidUserdata)
}
}
}
pub fn get_max_size() -> usize {
// Upper limit on the size of the Vote State. Equal to
// sizeof(VoteProgram) + MAX_VOTE_HISTORY * sizeof(Vote) +
// 32 (the size of the Pubkey) + 2 (2 bytes for the size)
mem::size_of::<VoteProgram>()
+ MAX_VOTE_HISTORY * mem::size_of::<Vote>()
+ mem::size_of::<Pubkey>()
+ mem::size_of::<u16>()
}
impl VoteProgram {
pub fn deserialize(input: &[u8]) -> Result<VoteProgram> {
let len = LittleEndian::read_u16(&input[0..2]) as usize;
if len == 0 || input.len() < len + 2 {
Err(ProgramError::InvalidUserdata)
} else {
deserialize(&input[2..=len + 1]).map_err(|err| {
error!("Unable to deserialize vote state: {:?}", err);
ProgramError::InvalidUserdata
})
}
}
pub fn serialize(self: &VoteProgram, output: &mut [u8]) -> Result<()> {
let self_serialized = serialize(self).unwrap();
if output.len() + 2 < self_serialized.len() {
warn!(
"{} bytes required to serialize but only have {} bytes",
self_serialized.len(),
output.len() + 2,
);
return Err(ProgramError::UserdataTooSmall);
}
let serialized_len = self_serialized.len() as u16;
LittleEndian::write_u16(&mut output[0..2], serialized_len);
output[2..=serialized_len as usize + 1].clone_from_slice(&self_serialized);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_serde() -> Result<()> {
let mut buffer: Vec<u8> = vec![0; get_max_size()];
let mut vote_program = VoteProgram::default();
vote_program.votes = (0..MAX_VOTE_HISTORY).map(|_| Vote::default()).collect();
vote_program.serialize(&mut buffer).unwrap();
assert_eq!(VoteProgram::deserialize(&buffer).unwrap(), vote_program);
Ok(())
}
}

View File

@ -14,7 +14,7 @@ use std::net::SocketAddr;
use std::sync::atomic::AtomicUsize;
use std::sync::{Arc, RwLock};
use streamer::BlobSender;
use vote_program::Vote;
use solana_sdk::vote_program::Vote;
use vote_transaction::VoteTransaction;
#[derive(Debug, PartialEq, Eq)]

View File

@ -13,7 +13,7 @@ use solana_sdk::signature::KeypairUtil;
use solana_sdk::system_instruction::SystemInstruction;
use solana_sdk::system_program;
use solana_sdk::transaction::{Instruction, Transaction};
use vote_program::{self, Vote, VoteInstruction};
use solana_sdk::vote_program::{self, Vote, VoteInstruction};
pub trait VoteTransaction {
fn vote_new(vote_account: &Keypair, vote: Vote, last_id: Hash, fee: u64) -> Self;

View File

@ -2,7 +2,6 @@ extern crate bincode;
extern crate elf;
extern crate serde_derive;
extern crate solana;
#[cfg(feature = "bpf_c")]
extern crate solana_sdk;
use solana::bank::Bank;