Move vote_program out of src/
This commit is contained in:
parent
a594f56c02
commit
ea6e042a6f
|
@ -1774,6 +1774,7 @@ dependencies = [
|
||||||
"solana-noop 0.11.0",
|
"solana-noop 0.11.0",
|
||||||
"solana-sdk 0.11.0",
|
"solana-sdk 0.11.0",
|
||||||
"solana-system-program 0.11.0",
|
"solana-system-program 0.11.0",
|
||||||
|
"solana-vote-program 0.11.0",
|
||||||
"solana-vote-signer 0.0.1",
|
"solana-vote-signer 0.0.1",
|
||||||
"sys-info 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1946,6 +1947,7 @@ version = "0.11.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1967,6 +1969,17 @@ dependencies = [
|
||||||
"solana-sdk 0.11.0",
|
"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]]
|
[[package]]
|
||||||
name = "solana-vote-signer"
|
name = "solana-vote-signer"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
|
|
@ -110,6 +110,7 @@ solana-metrics = { path = "metrics", version = "0.11.0" }
|
||||||
solana-noop = { path = "programs/native/noop", version = "0.11.0" }
|
solana-noop = { path = "programs/native/noop", version = "0.11.0" }
|
||||||
solana-sdk = { path = "sdk", version = "0.11.0" }
|
solana-sdk = { path = "sdk", version = "0.11.0" }
|
||||||
solana-system-program = { path = "programs/native/system", 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" }
|
solana-vote-signer = { path = "vote-signer", version = "0.0.1" }
|
||||||
sys-info = "0.5.6"
|
sys-info = "0.5.6"
|
||||||
tokio = "0.1"
|
tokio = "0.1"
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ license = "Apache-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bincode = "1.0.0"
|
bincode = "1.0.0"
|
||||||
|
byteorder = "1.2.1"
|
||||||
bs58 = "0.2.0"
|
bs58 = "0.2.0"
|
||||||
generic-array = { version = "0.12.0", default-features = false, features = ["serde"] }
|
generic-array = { version = "0.12.0", default-features = false, features = ["serde"] }
|
||||||
log = "0.4.2"
|
log = "0.4.2"
|
||||||
|
|
|
@ -12,9 +12,11 @@ pub mod system_program;
|
||||||
pub mod timing;
|
pub mod timing;
|
||||||
pub mod token_program;
|
pub mod token_program;
|
||||||
pub mod transaction;
|
pub mod transaction;
|
||||||
|
pub mod vote_program;
|
||||||
|
|
||||||
extern crate bincode;
|
extern crate bincode;
|
||||||
extern crate bs58;
|
extern crate bs58;
|
||||||
|
extern crate byteorder;
|
||||||
extern crate generic_array;
|
extern crate generic_array;
|
||||||
extern crate log;
|
extern crate log;
|
||||||
extern crate ring;
|
extern crate ring;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
16
src/bank.rs
16
src/bank.rs
|
@ -41,6 +41,7 @@ use std::sync::{Arc, Mutex, RwLock};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use system_transaction::SystemTransaction;
|
use system_transaction::SystemTransaction;
|
||||||
use tokio::prelude::Future;
|
use tokio::prelude::Future;
|
||||||
|
use solana_sdk::vote_program;
|
||||||
|
|
||||||
/// The number of most recent `last_id` values that the bank will track the signatures
|
/// 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
|
/// 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);
|
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) {
|
fn add_builtin_programs(&self) {
|
||||||
self.add_system_program();
|
self.add_system_program();
|
||||||
|
self.add_vote_program();
|
||||||
let mut accounts = self.accounts.write().unwrap();
|
let mut accounts = self.accounts.write().unwrap();
|
||||||
|
|
||||||
// Bpf Loader
|
// Bpf Loader
|
||||||
|
@ -1406,7 +1421,6 @@ mod tests {
|
||||||
use storage_program;
|
use storage_program;
|
||||||
use system_transaction::SystemTransaction;
|
use system_transaction::SystemTransaction;
|
||||||
use tokio::prelude::{Async, Stream};
|
use tokio::prelude::{Async, Stream};
|
||||||
use vote_program;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bank_new() {
|
fn test_bank_new() {
|
||||||
|
|
|
@ -17,9 +17,9 @@ use solana::leader_scheduler::LeaderScheduler;
|
||||||
use solana::logger;
|
use solana::logger;
|
||||||
use solana::netutil::find_available_port_in_range;
|
use solana::netutil::find_available_port_in_range;
|
||||||
use solana::thin_client::poll_gossip_for_leader;
|
use solana::thin_client::poll_gossip_for_leader;
|
||||||
use solana::vote_program::VoteProgram;
|
|
||||||
use solana::vote_transaction::VoteTransaction;
|
use solana::vote_transaction::VoteTransaction;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||||
|
use solana_sdk::vote_program::VoteProgram;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::net::{Ipv4Addr, SocketAddr};
|
use std::net::{Ipv4Addr, SocketAddr};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
|
@ -13,7 +13,7 @@ use std::sync::Arc;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::thread::{self, Builder, JoinHandle};
|
use std::thread::{self, Builder, JoinHandle};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use vote_program::{self, VoteProgram};
|
use solana_sdk::vote_program::{self, VoteProgram};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum FinalityError {
|
pub enum FinalityError {
|
||||||
|
@ -144,7 +144,7 @@ pub mod tests {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use vote_program::Vote;
|
use solana_sdk::vote_program::Vote;
|
||||||
use vote_transaction::{create_vote_account, VoteTransaction};
|
use vote_transaction::{create_vote_account, VoteTransaction};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -14,7 +14,7 @@ use solana_sdk::transaction::Transaction;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use system_transaction::SystemTransaction;
|
use system_transaction::SystemTransaction;
|
||||||
use vote_program::{self, Vote, VoteProgram};
|
use solana_sdk::vote_program::{self, Vote, VoteProgram};
|
||||||
use vote_transaction::VoteTransaction;
|
use vote_transaction::VoteTransaction;
|
||||||
|
|
||||||
pub const DEFAULT_BOOTSTRAP_HEIGHT: u64 = 1000;
|
pub const DEFAULT_BOOTSTRAP_HEIGHT: u64 = 1000;
|
||||||
|
@ -518,7 +518,7 @@ mod tests {
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::hash::Hash as StdHash;
|
use std::hash::Hash as StdHash;
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use vote_program::Vote;
|
use solana_sdk::vote_program::Vote;
|
||||||
use vote_transaction::{create_vote_account, VoteTransaction};
|
use vote_transaction::{create_vote_account, VoteTransaction};
|
||||||
|
|
||||||
fn to_hashset_owned<T>(slice: &[T]) -> HashSet<T>
|
fn to_hashset_owned<T>(slice: &[T]) -> HashSet<T>
|
||||||
|
|
|
@ -20,7 +20,7 @@ use std::io::{self, BufReader, BufWriter, Seek, SeekFrom};
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use vote_program::Vote;
|
use solana_sdk::vote_program::Vote;
|
||||||
use vote_transaction::VoteTransaction;
|
use vote_transaction::VoteTransaction;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -709,7 +709,7 @@ mod tests {
|
||||||
use solana_sdk::transaction::Transaction;
|
use solana_sdk::transaction::Transaction;
|
||||||
use std;
|
use std;
|
||||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||||
use vote_program::Vote;
|
use solana_sdk::vote_program::Vote;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_verify_slice() {
|
fn test_verify_slice() {
|
||||||
|
|
|
@ -78,7 +78,6 @@ pub mod thin_client;
|
||||||
pub mod tpu;
|
pub mod tpu;
|
||||||
pub mod tpu_forwarder;
|
pub mod tpu_forwarder;
|
||||||
pub mod tvu;
|
pub mod tvu;
|
||||||
pub mod vote_program;
|
|
||||||
pub mod vote_stage;
|
pub mod vote_stage;
|
||||||
pub mod vote_transaction;
|
pub mod vote_transaction;
|
||||||
pub mod wallet;
|
pub mod wallet;
|
||||||
|
|
|
@ -6,7 +6,6 @@ use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_sdk::system_program;
|
use solana_sdk::system_program;
|
||||||
use solana_sdk::transaction::Transaction;
|
use solana_sdk::transaction::Transaction;
|
||||||
use storage_program;
|
use storage_program;
|
||||||
use vote_program;
|
|
||||||
|
|
||||||
/// Reasons the runtime might have rejected a transaction.
|
/// Reasons the runtime might have rejected a transaction.
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
@ -16,9 +15,7 @@ pub enum RuntimeError {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_legacy_program(program_id: &Pubkey) -> bool {
|
pub fn is_legacy_program(program_id: &Pubkey) -> bool {
|
||||||
budget_program::check_id(program_id)
|
budget_program::check_id(program_id) || storage_program::check_id(program_id)
|
||||||
|| storage_program::check_id(program_id)
|
|
||||||
|| vote_program::check_id(program_id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process an instruction
|
/// Process an instruction
|
||||||
|
@ -39,8 +36,6 @@ fn process_instruction(
|
||||||
budget_program::process(&tx, instruction_index, program_accounts)?;
|
budget_program::process(&tx, instruction_index, program_accounts)?;
|
||||||
} else if storage_program::check_id(&program_id) {
|
} else if storage_program::check_id(&program_id) {
|
||||||
storage_program::process(&tx, instruction_index, program_accounts)?;
|
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 {
|
} else {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,7 +18,7 @@ use std::sync::mpsc::RecvTimeoutError;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::thread::{self, Builder, JoinHandle};
|
use std::thread::{self, Builder, JoinHandle};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use vote_program;
|
use solana_sdk::vote_program;
|
||||||
|
|
||||||
// Block of hash answers to validate against
|
// Block of hash answers to validate against
|
||||||
// Vec of [ledger blocks] x [keys]
|
// Vec of [ledger blocks] x [keys]
|
||||||
|
@ -278,7 +278,7 @@ mod tests {
|
||||||
use storage_stage::StorageState;
|
use storage_stage::StorageState;
|
||||||
use storage_stage::NUM_IDENTITIES;
|
use storage_stage::NUM_IDENTITIES;
|
||||||
use storage_stage::{get_identity_index_from_pubkey, StorageStage};
|
use storage_stage::{get_identity_index_from_pubkey, StorageStage};
|
||||||
use vote_program::Vote;
|
use solana_sdk::vote_program::Vote;
|
||||||
use vote_transaction::VoteTransaction;
|
use vote_transaction::VoteTransaction;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -439,7 +439,7 @@ mod tests {
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||||
use solana_sdk::system_instruction::SystemInstruction;
|
use solana_sdk::system_instruction::SystemInstruction;
|
||||||
use std::fs::remove_dir_all;
|
use std::fs::remove_dir_all;
|
||||||
use vote_program::VoteProgram;
|
use solana_sdk::vote_program::VoteProgram;
|
||||||
use vote_transaction::VoteTransaction;
|
use vote_transaction::VoteTransaction;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -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(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -14,7 +14,7 @@ use std::net::SocketAddr;
|
||||||
use std::sync::atomic::AtomicUsize;
|
use std::sync::atomic::AtomicUsize;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use streamer::BlobSender;
|
use streamer::BlobSender;
|
||||||
use vote_program::Vote;
|
use solana_sdk::vote_program::Vote;
|
||||||
use vote_transaction::VoteTransaction;
|
use vote_transaction::VoteTransaction;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
|
|
@ -13,7 +13,7 @@ use solana_sdk::signature::KeypairUtil;
|
||||||
use solana_sdk::system_instruction::SystemInstruction;
|
use solana_sdk::system_instruction::SystemInstruction;
|
||||||
use solana_sdk::system_program;
|
use solana_sdk::system_program;
|
||||||
use solana_sdk::transaction::{Instruction, Transaction};
|
use solana_sdk::transaction::{Instruction, Transaction};
|
||||||
use vote_program::{self, Vote, VoteInstruction};
|
use solana_sdk::vote_program::{self, Vote, VoteInstruction};
|
||||||
|
|
||||||
pub trait VoteTransaction {
|
pub trait VoteTransaction {
|
||||||
fn vote_new(vote_account: &Keypair, vote: Vote, last_id: Hash, fee: u64) -> Self;
|
fn vote_new(vote_account: &Keypair, vote: Vote, last_id: Hash, fee: u64) -> Self;
|
||||||
|
|
|
@ -2,7 +2,6 @@ extern crate bincode;
|
||||||
extern crate elf;
|
extern crate elf;
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
extern crate solana;
|
extern crate solana;
|
||||||
#[cfg(feature = "bpf_c")]
|
|
||||||
extern crate solana_sdk;
|
extern crate solana_sdk;
|
||||||
|
|
||||||
use solana::bank::Bank;
|
use solana::bank::Bank;
|
||||||
|
|
Loading…
Reference in New Issue