diff --git a/Cargo.lock b/Cargo.lock index 8a4209eec..26bbf4383 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1806,6 +1806,7 @@ dependencies = [ "solana-netutil 0.12.0", "solana-sdk 0.12.0", "solana-system-program 0.12.0", + "solana-vote-signer 0.12.0", "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-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index f53ccf5bc..b22e014ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ solana-native-loader = { path = "programs/native/native_loader", version = "0.12 solana-netutil = { path = "netutil", version = "0.12.0" } solana-sdk = { path = "sdk", version = "0.12.0" } solana-system-program = { path = "programs/native/system", version = "0.12.0" } +solana-vote-signer = { path = "vote-signer", version = "0.12.0" } sys-info = "0.5.6" tokio = "0.1" tokio-codec = "0.1" diff --git a/fullnode/src/main.rs b/fullnode/src/main.rs index 4bb3d3514..c6ee2bedf 100644 --- a/fullnode/src/main.rs +++ b/fullnode/src/main.rs @@ -1,12 +1,19 @@ +#[macro_use] +extern crate serde_json; + use clap::{crate_version, App, Arg}; use log::*; + use solana::client::mk_client; use solana::cluster_info::{Node, NodeInfo, FULLNODE_PORT_RANGE}; +use solana::create_vote_account; use solana::fullnode::{Fullnode, FullnodeReturnType}; use solana::leader_scheduler::LeaderScheduler; +use solana::rpc_request::{RpcClient, RpcRequest}; use solana::socketaddr; use solana::thin_client::poll_gossip_for_leader; -use solana_sdk::signature::{Keypair, KeypairUtil}; +use solana_sdk::pubkey::Pubkey; +use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use solana_sdk::vote_program::VoteProgram; use solana_sdk::vote_transaction::VoteTransaction; use std::fs::File; @@ -16,6 +23,7 @@ use std::sync::Arc; use std::thread::sleep; use std::time::Duration; +#[allow(clippy::cyclomatic_complexity)] fn main() { solana_logger::setup(); solana_metrics::set_panic_hook("fullnode"); @@ -48,6 +56,14 @@ fn main() { .takes_value(true) .help("Rendezvous with the network at this gossip entry point"), ) + .arg( + Arg::with_name("signer") + .short("s") + .long("signer") + .value_name("HOST:PORT") + .takes_value(true) + .help("Rendezvous with the vote signer at this RPC end point"), + ) .arg( Arg::with_name("ledger") .short("l") @@ -69,7 +85,7 @@ fn main() { let nosigverify = matches.is_present("nosigverify"); let use_only_bootstrap_leader = matches.is_present("no-leader-rotation"); - let (keypair, vote_account_keypair, gossip) = if let Some(i) = matches.value_of("identity") { + let (keypair, _vote_account_keypair, gossip) = if let Some(i) = matches.value_of("identity") { let path = i.to_string(); if let Ok(file) = File::open(path.clone()) { let parse: serde_json::Result = @@ -106,13 +122,35 @@ fn main() { .value_of("network") .map(|network| network.parse().expect("failed to parse network address")); + let (signer, t_signer, signer_exit) = if let Some(signer_addr) = matches.value_of("signer") { + ( + signer_addr.to_string().parse().expect("Signer IP Address"), + None, + None, + ) + } else { + // If a remote vote-signer service is not provided, run a local instance + let (signer, t_signer, signer_exit) = create_vote_account::local_vote_signer_service() + .expect("Failed to start vote signer service"); + (signer, Some(t_signer), Some(signer_exit)) + }; + let node = Node::new_with_external_ip(keypair.pubkey(), &gossip); // save off some stuff for airdrop let mut node_info = node.info.clone(); - let vote_account_keypair = Arc::new(vote_account_keypair); - let vote_account_id = vote_account_keypair.pubkey(); + let rpc_client = RpcClient::new_from_socket(signer); + + let msg = "Registering a new node"; + let sig = Signature::new(&keypair.sign(msg.as_bytes()).as_ref()); + + let params = json!([keypair.pubkey(), sig, msg.as_bytes()]); + let resp = RpcRequest::RegisterNode + .make_rpc_request(&rpc_client, 1, Some(params)) + .unwrap(); + let vote_account_id: Pubkey = serde_json::from_value(resp).unwrap(); + info!("New vote account ID is {:?}", vote_account_id); let keypair = Arc::new(keypair); let pubkey = keypair.pubkey(); @@ -125,6 +163,11 @@ fn main() { let port_number = port.to_string().parse().expect("integer"); if port_number == 0 { eprintln!("Invalid RPC port requested: {:?}", port); + if let Some(t) = t_signer { + if let Some(exit) = signer_exit { + create_vote_account::stop_local_vote_signer_service(t, &exit); + } + } exit(1); } Some(port_number) @@ -153,7 +196,8 @@ fn main() { node, ledger_path, keypair.clone(), - vote_account_keypair.clone(), + &vote_account_id, + &signer, network, nosigverify, leader_scheduler, @@ -165,6 +209,11 @@ fn main() { info!("balance is {}", balance); if balance < 1 { error!("insufficient tokens"); + if let Some(t) = t_signer { + if let Some(exit) = signer_exit { + create_vote_account::stop_local_vote_signer_service(t, &exit); + } + } exit(1); } @@ -173,6 +222,11 @@ fn main() { // Need at least two tokens as one token will be spent on a vote_account_new() transaction if balance < 2 { error!("insufficient tokens"); + if let Some(t) = t_signer { + if let Some(exit) = signer_exit { + create_vote_account::stop_local_vote_signer_service(t, &exit); + } + } exit(1); } loop { @@ -212,6 +266,11 @@ fn main() { _ => { // Fullnode tpu/tvu exited for some unexpected // reason, so exit + if let Some(t) = t_signer { + if let Some(exit) = signer_exit { + create_vote_account::stop_local_vote_signer_service(t, &exit); + } + } exit(1); } } diff --git a/sdk/src/transaction.rs b/sdk/src/transaction.rs index b6dac7c36..3f99c4928 100644 --- a/sdk/src/transaction.rs +++ b/sdk/src/transaction.rs @@ -71,6 +71,21 @@ impl Transaction { instructions, ) } + pub fn new_unsigned( + from_pubkey: &Pubkey, + transaction_keys: &[Pubkey], + program_id: Pubkey, + userdata: &T, + last_id: Hash, + fee: u64, + ) -> Self { + let program_ids = vec![program_id]; + let accounts = (0..=transaction_keys.len() as u8).collect(); + let instructions = vec![Instruction::new(0, userdata, accounts)]; + let mut keys = vec![*from_pubkey]; + keys.extend_from_slice(transaction_keys); + Self::new_with_instructions(&[], &keys[..], last_id, fee, program_ids, instructions) + } /// Create a signed transaction /// * `from_keypair` - The key used to sign the transaction. This key is stored as keys[0] /// * `account_keys` - The keys for the transaction. These are the program state diff --git a/sdk/src/vote_transaction.rs b/sdk/src/vote_transaction.rs index 1ea07f283..57db2fe6a 100644 --- a/sdk/src/vote_transaction.rs +++ b/sdk/src/vote_transaction.rs @@ -10,7 +10,7 @@ use crate::vote_program::{self, Vote, VoteInstruction}; use bincode::deserialize; pub trait VoteTransaction { - fn vote_new(vote_account: &Keypair, vote: Vote, last_id: Hash, fee: u64) -> Self; + fn vote_new(vote_account: &Pubkey, vote: Vote, last_id: Hash, fee: u64) -> Self; fn vote_account_new( validator_id: &Keypair, vote_account_id: Pubkey, @@ -23,9 +23,9 @@ pub trait VoteTransaction { } impl VoteTransaction for Transaction { - fn vote_new(vote_account: &Keypair, vote: Vote, last_id: Hash, fee: u64) -> Self { + fn vote_new(vote_account: &Pubkey, vote: Vote, last_id: Hash, fee: u64) -> Self { let instruction = VoteInstruction::NewVote(vote); - Transaction::new( + Transaction::new_unsigned( vote_account, &[], vote_program::id(), diff --git a/src/compute_leader_confirmation_service.rs b/src/compute_leader_confirmation_service.rs old mode 100755 new mode 100644 index 62c868374..41da86a9f --- a/src/compute_leader_confirmation_service.rs +++ b/src/compute_leader_confirmation_service.rs @@ -160,12 +160,11 @@ pub mod tests { use crate::create_vote_account::*; use crate::mint::Mint; + use crate::rpc_request::RpcClient; + use crate::vote_stage::create_new_signed_vote_transaction; use bincode::serialize; use solana_sdk::hash::hash; use solana_sdk::signature::{Keypair, KeypairUtil}; - use solana_sdk::transaction::Transaction; - use solana_sdk::vote_program::Vote; - use solana_sdk::vote_transaction::VoteTransaction; use std::sync::Arc; use std::thread::sleep; use std::time::Duration; @@ -188,6 +187,8 @@ pub mod tests { }) .collect(); + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let rpc_client = RpcClient::new_from_socket(signer); // Create a total of 10 vote accounts, each will have a balance of 1 (after giving 1 to // their vote account), for a total staking pool of 10 tokens. let vote_accounts: Vec<_> = (0..10) @@ -199,17 +200,22 @@ pub mod tests { // Give the validator some tokens bank.transfer(2, &mint.keypair(), validator_keypair.pubkey(), last_id) .unwrap(); - let vote_account = create_vote_account(&validator_keypair, &bank, 1, last_id) - .expect("Expected successful creation of account"); + let vote_account = + create_vote_account(&validator_keypair, &bank, 1, last_id, &rpc_client) + .expect("Expected successful creation of account"); + let validator_keypair = Arc::new(validator_keypair); if i < 6 { - let vote = Vote { - tick_height: (i + 1) as u64, - }; - let vote_tx = Transaction::vote_new(&vote_account, vote, last_id, 0); + let vote_tx = create_new_signed_vote_transaction( + &last_id, + &validator_keypair, + (i + 1) as u64, + &vote_account, + &rpc_client, + ); bank.process_transaction(&vote_tx).unwrap(); } - vote_account + (vote_account, validator_keypair) }) .collect(); @@ -223,9 +229,14 @@ pub mod tests { assert_eq!(bank.confirmation_time(), std::usize::MAX); // Get another validator to vote, so we now have 2/3 consensus - let vote_account = &vote_accounts[7]; - let vote = Vote { tick_height: 7 }; - let vote_tx = Transaction::vote_new(&vote_account, vote, ids[6], 0); + let vote_account = &vote_accounts[7].0; + let vote_tx = create_new_signed_vote_transaction( + &ids[6], + &vote_accounts[7].1, + 7, + &vote_account, + &rpc_client, + ); bank.process_transaction(&vote_tx).unwrap(); ComputeLeaderConfirmationService::compute_confirmation( @@ -235,5 +246,6 @@ pub mod tests { ); assert!(bank.confirmation_time() != std::usize::MAX); assert!(last_confirmation_time > 0); + stop_local_vote_signer_service(t_signer, &signer_exit); } } diff --git a/src/create_vote_account.rs b/src/create_vote_account.rs index f1f0a5900..08600e0e6 100644 --- a/src/create_vote_account.rs +++ b/src/create_vote_account.rs @@ -1,26 +1,60 @@ use crate::bank::Bank; +use crate::cluster_info::FULLNODE_PORT_RANGE; use crate::result::Result; +use crate::rpc_request::{RpcClient, RpcRequest}; +use solana_netutil::find_available_port_in_range; use solana_sdk::hash::Hash; -use solana_sdk::signature::{Keypair, KeypairUtil}; +use solana_sdk::pubkey::Pubkey; +use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use solana_sdk::transaction::Transaction; use solana_sdk::vote_transaction::*; +use solana_vote_signer::rpc::VoteSignerRpcService; +use std::io; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use std::thread::{Builder, JoinHandle}; + +pub fn local_vote_signer_service() -> io::Result<(SocketAddr, JoinHandle<()>, Arc)> { + let addr = match find_available_port_in_range(FULLNODE_PORT_RANGE) { + Ok(port) => SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port), + Err(e) => return Err(e), + }; + let service_addr = addr; + let exit = Arc::new(AtomicBool::new(false)); + let thread_exit = exit.clone(); + let thread = Builder::new() + .name("solana-vote-signer".to_string()) + .spawn(move || { + let service = VoteSignerRpcService::new(service_addr, thread_exit); + service.join().unwrap(); + }) + .unwrap(); + Ok((addr, thread, exit)) +} + +pub fn stop_local_vote_signer_service(t: JoinHandle<()>, exit: &Arc) { + exit.store(true, Ordering::Relaxed); + t.join().unwrap(); +} pub fn create_vote_account( node_keypair: &Keypair, bank: &Bank, num_tokens: u64, last_id: Hash, -) -> Result { - let new_vote_account = Keypair::new(); + rpc_client: &RpcClient, +) -> Result { + let msg = "Registering a new node"; + let sig = Signature::new(&node_keypair.sign(msg.as_bytes()).as_ref()); + let params = json!([node_keypair.pubkey(), sig, msg.as_bytes()]); + let resp = RpcRequest::RegisterNode + .make_rpc_request(&rpc_client, 1, Some(params)) + .unwrap(); + let new_vote_account: Pubkey = serde_json::from_value(resp).unwrap(); // Create and register the new vote account - let tx = Transaction::vote_account_new( - node_keypair, - new_vote_account.pubkey(), - last_id, - num_tokens, - 0, - ); + let tx = Transaction::vote_account_new(node_keypair, new_vote_account, last_id, num_tokens, 0); bank.process_transaction(&tx)?; Ok(new_vote_account) diff --git a/src/fullnode.rs b/src/fullnode.rs index 5ae649859..9b4f06b32 100644 --- a/src/fullnode.rs +++ b/src/fullnode.rs @@ -16,6 +16,7 @@ use crate::tvu::{Sockets, Tvu, TvuReturnType}; use crate::window::{new_window, SharedWindow}; use log::Level; use solana_sdk::hash::Hash; +use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{Keypair, KeypairUtil}; use solana_sdk::timing::timestamp; use std::net::UdpSocket; @@ -90,7 +91,7 @@ pub enum FullnodeReturnType { pub struct Fullnode { pub node_role: Option, keypair: Arc, - vote_account_keypair: Arc, + vote_account_id: Pubkey, exit: Arc, rpc_service: Option, rpc_pubsub_service: Option, @@ -107,6 +108,7 @@ pub struct Fullnode { rpc_addr: SocketAddr, rpc_pubsub_addr: SocketAddr, drone_addr: SocketAddr, + vote_signer_addr: SocketAddr, db_ledger: Arc, } @@ -115,7 +117,8 @@ impl Fullnode { node: Node, ledger_path: &str, keypair: Arc, - vote_account_keypair: Arc, + vote_account_id: &Pubkey, + vote_signer_addr: &SocketAddr, leader_addr: Option, sigverify_disabled: bool, leader_scheduler: LeaderScheduler, @@ -144,7 +147,8 @@ impl Fullnode { let leader_info = leader_addr.map(|i| NodeInfo::new_entry_point(&i)); let server = Self::new_with_bank( keypair, - vote_account_keypair, + vote_account_id, + vote_signer_addr, bank, Some(db_ledger), entry_height, @@ -175,7 +179,8 @@ impl Fullnode { #[allow(clippy::too_many_arguments)] pub fn new_with_bank( keypair: Arc, - vote_account_keypair: Arc, + vote_account_id: &Pubkey, + vote_signer_addr: &SocketAddr, bank: Bank, db_ledger: Option>, entry_height: u64, @@ -266,7 +271,8 @@ impl Fullnode { }; let tvu = Tvu::new( - vote_account_keypair.clone(), + vote_account_id, + vote_signer_addr, &bank, entry_height, *last_entry_id, @@ -329,7 +335,7 @@ impl Fullnode { Fullnode { keypair, - vote_account_keypair, + vote_account_id: *vote_account_id, cluster_info, shared_window, bank, @@ -347,6 +353,7 @@ impl Fullnode { rpc_addr, rpc_pubsub_addr, drone_addr, + vote_signer_addr: *vote_signer_addr, db_ledger, } } @@ -430,7 +437,8 @@ impl Fullnode { }; let tvu = Tvu::new( - self.vote_account_keypair.clone(), + &self.vote_account_id, + &self.vote_signer_addr, &self.bank, entry_height, last_entry_id, @@ -644,6 +652,7 @@ impl Service for Fullnode { mod tests { use crate::bank::Bank; use crate::cluster_info::Node; + use crate::create_vote_account::*; use crate::db_ledger::*; use crate::fullnode::{Fullnode, FullnodeReturnType, NodeRole, TvuReturnType}; use crate::leader_scheduler::{ @@ -652,12 +661,15 @@ mod tests { use crate::ledger::{ create_tmp_genesis, create_tmp_sample_ledger, make_consecutive_blobs, tmp_copy_ledger, }; + use crate::rpc_request::{RpcClient, RpcRequest}; use crate::service::Service; use crate::streamer::responder; - use solana_sdk::signature::{Keypair, KeypairUtil}; + use solana_sdk::pubkey::Pubkey; + use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use std::cmp; use std::fs::remove_dir_all; use std::net::UdpSocket; + use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::sync::mpsc::channel; use std::sync::{Arc, RwLock}; @@ -680,7 +692,8 @@ mod tests { let last_id = bank.last_id(); let v = Fullnode::new_with_bank( Arc::new(keypair), - Arc::new(Keypair::new()), + &Keypair::new().pubkey(), + &SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), bank, None, entry_height, @@ -721,7 +734,8 @@ mod tests { let last_id = bank.last_id(); Fullnode::new_with_bank( Arc::new(keypair), - Arc::new(Keypair::new()), + &Keypair::new().pubkey(), + &SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), bank, None, entry_height, @@ -795,7 +809,8 @@ mod tests { bootstrap_leader_node, &bootstrap_leader_ledger_path, Arc::new(bootstrap_leader_keypair), - Arc::new(Keypair::new()), + &Keypair::new().pubkey(), + &SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), Some(bootstrap_leader_info.gossip), false, LeaderScheduler::new(&leader_scheduler_config), @@ -848,8 +863,10 @@ mod tests { // Write the entries to the ledger that will cause leader rotation // after the bootstrap height - let (active_set_entries, validator_vote_account_keypair) = make_active_set_entries( + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let (active_set_entries, validator_vote_account_id) = make_active_set_entries( &validator_keypair, + signer, &mint.keypair(), &last_id, &last_id, @@ -898,12 +915,13 @@ mod tests { { // Test that a node knows to transition to a validator based on parsing the ledger - let leader_vote_account_keypair = Arc::new(Keypair::new()); + let leader_vote_id = register_node(signer, bootstrap_leader_keypair.clone()); let bootstrap_leader = Fullnode::new( bootstrap_leader_node, &bootstrap_leader_ledger_path, bootstrap_leader_keypair, - leader_vote_account_keypair, + &leader_vote_id, + &signer, Some(bootstrap_leader_info.gossip), false, LeaderScheduler::new(&leader_scheduler_config), @@ -922,7 +940,8 @@ mod tests { validator_node, &validator_ledger_path, Arc::new(validator_keypair), - Arc::new(validator_vote_account_keypair), + &validator_vote_account_id, + &signer, Some(bootstrap_leader_info.gossip), false, LeaderScheduler::new(&leader_scheduler_config), @@ -945,6 +964,21 @@ mod tests { DbLedger::destroy(&path).expect("Expected successful database destruction"); let _ignored = remove_dir_all(&path); } + stop_local_vote_signer_service(t_signer, &signer_exit); + } + + fn register_node(signer: SocketAddr, keypair: Arc) -> Pubkey { + let rpc_client = RpcClient::new_from_socket(signer); + + let msg = "Registering a new node"; + let sig = Signature::new(&keypair.sign(msg.as_bytes()).as_ref()); + + let params = json!([keypair.pubkey(), sig, msg.as_bytes()]); + let resp = RpcRequest::RegisterNode + .make_rpc_request(&rpc_client, 1, Some(params)) + .unwrap(); + let vote_account_id: Pubkey = serde_json::from_value(resp).unwrap(); + vote_account_id } #[test] @@ -981,8 +1015,15 @@ mod tests { // after the bootstrap height // // 2) A vote from the validator - let (active_set_entries, validator_vote_account_keypair) = - make_active_set_entries(&validator_keypair, &mint.keypair(), &last_id, &last_id, 0); + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let (active_set_entries, _validator_vote_account_id) = make_active_set_entries( + &validator_keypair, + signer, + &mint.keypair(), + &last_id, + &last_id, + 0, + ); let initial_tick_height = genesis_entries .iter() .skip(2) @@ -1016,12 +1057,15 @@ mod tests { Some(bootstrap_height), ); + let validator_keypair = Arc::new(validator_keypair); + let vote_id = register_node(signer, validator_keypair.clone()); // Start the validator let mut validator = Fullnode::new( validator_node, &validator_ledger_path, - Arc::new(validator_keypair), - Arc::new(validator_vote_account_keypair), + validator_keypair, + &vote_id, + &signer, Some(leader_gossip), false, LeaderScheduler::new(&leader_scheduler_config), @@ -1090,6 +1134,7 @@ mod tests { ); // Shut down + stop_local_vote_signer_service(t_signer, &signer_exit); t_responder.join().expect("responder thread join"); validator.close().unwrap(); DbLedger::destroy(&validator_ledger_path) diff --git a/src/leader_scheduler.rs b/src/leader_scheduler.rs index 11c0d006b..294a3f866 100644 --- a/src/leader_scheduler.rs +++ b/src/leader_scheduler.rs @@ -5,17 +5,19 @@ use crate::bank::Bank; use crate::entry::Entry; use crate::ledger::create_ticks; +use crate::rpc_request::{RpcClient, RpcRequest}; use bincode::serialize; use byteorder::{LittleEndian, ReadBytesExt}; use hashbrown::HashSet; use solana_sdk::hash::{hash, Hash}; use solana_sdk::pubkey::Pubkey; -use solana_sdk::signature::{Keypair, KeypairUtil}; +use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use solana_sdk::system_transaction::SystemTransaction; use solana_sdk::transaction::Transaction; use solana_sdk::vote_program::{self, Vote, VoteProgram}; use solana_sdk::vote_transaction::VoteTransaction; use std::io::Cursor; +use std::net::SocketAddr; pub const DEFAULT_BOOTSTRAP_HEIGHT: u64 = 1000; pub const DEFAULT_LEADER_ROTATION_INTERVAL: u64 = 100; @@ -480,11 +482,12 @@ impl Default for LeaderScheduler { // 2) A vote from the validator pub fn make_active_set_entries( active_keypair: &Keypair, + signer: SocketAddr, token_source: &Keypair, last_entry_id: &Hash, last_tick_id: &Hash, num_ending_ticks: usize, -) -> (Vec, Keypair) { +) -> (Vec, Pubkey) { // 1) Create transfer token entry let transfer_tx = Transaction::system_new(&token_source, active_keypair.pubkey(), 3, *last_tick_id); @@ -492,15 +495,35 @@ pub fn make_active_set_entries( let mut last_entry_id = transfer_entry.id; // 2) Create and register the vote account - let vote_account = Keypair::new(); + let rpc_client = RpcClient::new_from_socket(signer); + + let msg = "Registering a new node"; + let sig = Signature::new(&active_keypair.sign(msg.as_bytes()).as_ref()); + + let params = json!([active_keypair.pubkey(), sig, msg.as_bytes()]); + let resp = RpcRequest::RegisterNode + .make_rpc_request(&rpc_client, 1, Some(params)) + .unwrap(); + let vote_account_id: Pubkey = serde_json::from_value(resp).unwrap(); + let new_vote_account_tx = - Transaction::vote_account_new(active_keypair, vote_account.pubkey(), *last_tick_id, 1, 1); + Transaction::vote_account_new(active_keypair, vote_account_id, *last_tick_id, 1, 1); let new_vote_account_entry = Entry::new(&last_entry_id, 0, 1, vec![new_vote_account_tx]); last_entry_id = new_vote_account_entry.id; // 3) Create vote entry let vote = Vote { tick_height: 1 }; - let vote_tx = Transaction::vote_new(&vote_account, vote, *last_tick_id, 0); + let tx = Transaction::vote_new(&vote_account_id, vote, *last_tick_id, 0); + let msg = tx.get_sign_data(); + let sig = Signature::new(&active_keypair.sign(&msg).as_ref()); + let vote_tx = Transaction { + signatures: vec![sig], + account_keys: tx.account_keys, + last_id: tx.last_id, + fee: tx.fee, + program_ids: tx.program_ids, + instructions: tx.instructions, + }; let vote_entry = Entry::new(&last_entry_id, 0, 1, vec![vote_tx]); last_entry_id = vote_entry.id; @@ -508,7 +531,7 @@ pub fn make_active_set_entries( let mut txs = vec![transfer_entry, new_vote_account_entry, vote_entry]; let empty_ticks = create_ticks(num_ending_ticks, last_entry_id); txs.extend(empty_ticks); - (txs, vote_account) + (txs, vote_account_id) } #[cfg(test)] @@ -520,15 +543,15 @@ mod tests { DEFAULT_LEADER_ROTATION_INTERVAL, DEFAULT_SEED_ROTATION_INTERVAL, }; use crate::mint::Mint; + use crate::rpc_request::RpcClient; + use crate::vote_stage::create_new_signed_vote_transaction; use hashbrown::HashSet; use solana_sdk::hash::Hash; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{Keypair, KeypairUtil}; - use solana_sdk::transaction::Transaction; - use solana_sdk::vote_program::Vote; - use solana_sdk::vote_transaction::VoteTransaction; use std::hash::Hash as StdHash; use std::iter::FromIterator; + use std::sync::Arc; fn to_hashset_owned(slice: &[T]) -> HashSet where @@ -537,12 +560,16 @@ mod tests { HashSet::from_iter(slice.iter().cloned()) } - fn push_vote(vote_account: &Keypair, bank: &Bank, height: u64, last_id: Hash) { - let vote = Vote { - tick_height: height, - }; - - let new_vote_tx = Transaction::vote_new(vote_account, vote, last_id, 0); + fn push_vote( + keypair: &Arc, + vote_account: &Pubkey, + bank: &Bank, + height: u64, + last_id: Hash, + rpc_client: &RpcClient, + ) { + let new_vote_tx = + create_new_signed_vote_transaction(&last_id, keypair, height, vote_account, rpc_client); bank.process_transaction(&new_vote_tx).unwrap(); } @@ -581,6 +608,8 @@ mod tests { .last() .expect("Mint should not create empty genesis entries") .id; + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let rpc_client = RpcClient::new_from_socket(signer); for i in 0..num_validators { let new_validator = Keypair::new(); let new_pubkey = new_validator.pubkey(); @@ -600,11 +629,19 @@ mod tests { &bank, num_vote_account_tokens as u64, mint.last_id(), + &rpc_client, ) .unwrap(); // Vote to make the validator part of the active set for the entire test // (we made the active_window_length large enough at the beginning of the test) - push_vote(&new_vote_account, &bank, 1, mint.last_id()); + push_vote( + &Arc::new(new_validator), + &new_vote_account, + &bank, + 1, + mint.last_id(), + &rpc_client, + ); } // The scheduled leader during the bootstrapping period (assuming a seed + schedule @@ -681,6 +718,7 @@ mod tests { Some((current_leader, slot)) ); } + stop_local_vote_signer_service(t_signer, &signer_exit); } #[test] @@ -700,6 +738,8 @@ mod tests { let start_height = 3; let num_old_ids = 20; let mut old_ids = HashSet::new(); + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let rpc_client = RpcClient::new_from_socket(signer); for _ in 0..num_old_ids { let new_keypair = Keypair::new(); let pk = new_keypair.pubkey(); @@ -711,10 +751,17 @@ mod tests { // Create a vote account let new_vote_account = - create_vote_account(&new_keypair, &bank, 1, mint.last_id()).unwrap(); + create_vote_account(&new_keypair, &bank, 1, mint.last_id(), &rpc_client).unwrap(); // Push a vote for the account - push_vote(&new_vote_account, &bank, start_height, mint.last_id()); + push_vote( + &Arc::new(new_keypair), + &new_vote_account, + &bank, + start_height, + mint.last_id(), + &rpc_client, + ); } // Insert a bunch of votes at height "start_height + active_window_length" @@ -730,13 +777,15 @@ mod tests { // Create a vote account let new_vote_account = - create_vote_account(&new_keypair, &bank, 1, mint.last_id()).unwrap(); + create_vote_account(&new_keypair, &bank, 1, mint.last_id(), &rpc_client).unwrap(); push_vote( + &Arc::new(new_keypair), &new_vote_account, &bank, start_height + active_window_length, mint.last_id(), + &rpc_client, ); } @@ -755,6 +804,7 @@ mod tests { let result = leader_scheduler.get_active_set(2 * active_window_length + start_height, &bank); assert!(result.is_empty()); + stop_local_vote_signer_service(t_signer, &signer_exit); } #[test] @@ -997,6 +1047,8 @@ mod tests { .last() .expect("Mint should not create empty genesis entries") .id; + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let rpc_client = RpcClient::new_from_socket(signer); for i in 0..num_validators { let new_validator = Keypair::new(); let new_pubkey = new_validator.pubkey(); @@ -1016,15 +1068,18 @@ mod tests { &bank, num_vote_account_tokens as u64, mint.last_id(), + &rpc_client, ) .unwrap(); // Vote at height i * active_window_length for validator i push_vote( + &Arc::new(new_validator), &new_vote_account, &bank, i * active_window_length + bootstrap_height, mint.last_id(), + &rpc_client, ); } @@ -1042,6 +1097,7 @@ mod tests { assert_eq!(vec![expected], *result); } + stop_local_vote_signer_service(t_signer, &signer_exit); } #[test] @@ -1062,22 +1118,28 @@ mod tests { // window let initial_vote_height = 1; + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let rpc_client = RpcClient::new_from_socket(signer); // Create a vote account let new_vote_account = - create_vote_account(&leader_keypair, &bank, 1, mint.last_id()).unwrap(); - + create_vote_account(&leader_keypair, &bank, 1, mint.last_id(), &rpc_client).unwrap(); + let leader_keypair = Arc::new(leader_keypair); // Vote twice push_vote( + &leader_keypair, &new_vote_account, &bank, initial_vote_height, mint.last_id(), + &rpc_client, ); push_vote( + &leader_keypair, &new_vote_account, &bank, initial_vote_height + 1, mint.last_id(), + &rpc_client, ); let result = @@ -1086,6 +1148,7 @@ mod tests { let result = leader_scheduler.get_active_set(initial_vote_height + active_window_length + 1, &bank); assert!(result.is_empty()); + stop_local_vote_signer_service(t_signer, &signer_exit); } #[test] @@ -1206,17 +1269,22 @@ mod tests { // Create and add validator to the active set let validator_keypair = Keypair::new(); let validator_id = validator_keypair.pubkey(); + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let rpc_client = RpcClient::new_from_socket(signer); if add_validator { bank.transfer(5, &mint.keypair(), validator_id, last_id) .unwrap(); // Create a vote account let new_vote_account = - create_vote_account(&validator_keypair, &bank, 1, mint.last_id()).unwrap(); + create_vote_account(&validator_keypair, &bank, 1, mint.last_id(), &rpc_client) + .unwrap(); push_vote( + &Arc::new(validator_keypair), &new_vote_account, &bank, initial_vote_height, mint.last_id(), + &rpc_client, ); } @@ -1249,15 +1317,18 @@ mod tests { &bank, vote_account_tokens, mint.last_id(), + &rpc_client, ) .unwrap(); // Add leader to the active set push_vote( + &Arc::new(bootstrap_leader_keypair), &new_vote_account, &bank, initial_vote_height, mint.last_id(), + &rpc_client, ); leader_scheduler.generate_schedule(bootstrap_height, &bank); @@ -1269,6 +1340,7 @@ mod tests { } else { assert!(leader_scheduler.leader_schedule[0] == bootstrap_leader_id); } + stop_local_vote_signer_service(t_signer, &signer_exit); } #[test] @@ -1373,27 +1445,39 @@ mod tests { // Create a vote account for the validator bank.transfer(5, &mint.keypair(), validator_id, last_id) .unwrap(); + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let rpc_client = RpcClient::new_from_socket(signer); let new_validator_vote_account = - create_vote_account(&validator_keypair, &bank, 1, mint.last_id()).unwrap(); + create_vote_account(&validator_keypair, &bank, 1, mint.last_id(), &rpc_client).unwrap(); push_vote( + &Arc::new(validator_keypair), &new_validator_vote_account, &bank, initial_vote_height, mint.last_id(), + &rpc_client, ); // Create a vote account for the leader bank.transfer(5, &mint.keypair(), bootstrap_leader_id, last_id) .unwrap(); - let new_leader_vote_account = - create_vote_account(&bootstrap_leader_keypair, &bank, 1, mint.last_id()).unwrap(); + let new_leader_vote_account = create_vote_account( + &bootstrap_leader_keypair, + &bank, + 1, + mint.last_id(), + &rpc_client, + ) + .unwrap(); // Add leader to the active set push_vote( + &Arc::new(bootstrap_leader_keypair), &new_leader_vote_account, &bank, initial_vote_height, mint.last_id(), + &rpc_client, ); // Generate the schedule @@ -1452,5 +1536,6 @@ mod tests { .max_height_for_leader(bootstrap_height + 2 * seed_rotation_interval + 1), None ); + stop_local_vote_signer_service(t_signer, &signer_exit); } } diff --git a/src/ledger.rs b/src/ledger.rs index c68f17a1f..f0209b545 100644 --- a/src/ledger.rs +++ b/src/ledger.rs @@ -734,7 +734,7 @@ mod tests { use crate::packet::{to_blobs, BLOB_DATA_SIZE, PACKET_DATA_SIZE}; use bincode::{deserialize, serialized_size}; use solana_sdk::hash::hash; - use solana_sdk::signature::{Keypair, KeypairUtil}; + use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use solana_sdk::transaction::Transaction; use solana_sdk::vote_program::Vote; use std; @@ -760,7 +760,17 @@ mod tests { let one = hash(&zero.as_ref()); let keypair = Keypair::new(); let vote_account = Keypair::new(); - let tx0 = Transaction::vote_new(&vote_account, Vote { tick_height: 1 }, one, 1); + let tx = Transaction::vote_new(&vote_account.pubkey(), Vote { tick_height: 1 }, one, 1); + let msg = tx.get_sign_data(); + let sig = Signature::new(&vote_account.sign(&msg).as_ref()); + let tx0 = Transaction { + signatures: vec![sig], + account_keys: tx.account_keys, + last_id: tx.last_id, + fee: tx.fee, + program_ids: tx.program_ids, + instructions: tx.instructions, + }; let tx1 = Transaction::budget_new_timestamp( &keypair, keypair.pubkey(), @@ -808,7 +818,17 @@ mod tests { let next_id = hash(&id.as_ref()); let keypair = Keypair::new(); let vote_account = Keypair::new(); - let tx_small = Transaction::vote_new(&vote_account, Vote { tick_height: 1 }, next_id, 2); + let tx = Transaction::vote_new(&vote_account.pubkey(), Vote { tick_height: 1 }, next_id, 2); + let msg = tx.get_sign_data(); + let sig = Signature::new(&vote_account.sign(&msg).as_ref()); + let tx_small = Transaction { + signatures: vec![sig], + account_keys: tx.account_keys, + last_id: tx.last_id, + fee: tx.fee, + program_ids: tx.program_ids, + instructions: tx.instructions, + }; let tx_large = Transaction::budget_new(&keypair, keypair.pubkey(), 1, next_id); let tx_small_size = serialized_size(&tx_small).unwrap() as usize; diff --git a/src/lib.rs b/src/lib.rs index 2087e937c..c614fbd75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,6 +93,7 @@ use solana_jsonrpc_http_server as jsonrpc_http_server; extern crate solana_jsonrpc_macros as jsonrpc_macros; use solana_jsonrpc_pubsub as jsonrpc_pubsub; use solana_jsonrpc_ws_server as jsonrpc_ws_server; +use solana_vote_signer; #[cfg(test)] #[macro_use] diff --git a/src/replay_stage.rs b/src/replay_stage.rs index d70ec6732..97610dd7b 100644 --- a/src/replay_stage.rs +++ b/src/replay_stage.rs @@ -9,14 +9,16 @@ use solana_sdk::hash::Hash; use crate::ledger::Block; use crate::packet::BlobError; use crate::result::{Error, Result}; +use crate::rpc_request::RpcClient; use crate::service::Service; use crate::streamer::{responder, BlobSender}; use crate::vote_stage::send_validator_vote; use log::Level; use solana_metrics::{influxdb, submit}; +use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{Keypair, KeypairUtil}; use solana_sdk::timing::duration_as_ms; -use std::net::UdpSocket; +use std::net::{SocketAddr, UdpSocket}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::mpsc::channel; use std::sync::mpsc::RecvTimeoutError; @@ -58,12 +60,14 @@ pub struct ReplayStage { impl ReplayStage { /// Process entry blobs, already in order + #[allow(clippy::too_many_arguments)] fn process_entries( bank: &Arc, cluster_info: &Arc>, window_receiver: &EntryReceiver, keypair: &Arc, - vote_account_keypair: &Arc, + vote_account_id: &Pubkey, + vote_signer_rpc: &RpcClient, vote_blob_sender: Option<&BlobSender>, ledger_entry_sender: &EntrySender, entry_height: &mut u64, @@ -137,8 +141,15 @@ impl ReplayStage { if 0 == num_ticks_to_next_vote { if let Some(sender) = vote_blob_sender { - send_validator_vote(bank, vote_account_keypair, &cluster_info, sender) - .unwrap(); + send_validator_vote( + bank, + &keypair, + &vote_account_id, + vote_signer_rpc, + &cluster_info, + sender, + ) + .unwrap(); } } let (scheduled_leader, _) = bank @@ -192,7 +203,8 @@ impl ReplayStage { #[allow(clippy::new_ret_no_self)] pub fn new( keypair: Arc, - vote_account_keypair: Arc, + vote_account_id: &Pubkey, + vote_signer_addr: &SocketAddr, bank: Arc, cluster_info: Arc>, window_receiver: EntryReceiver, @@ -206,7 +218,9 @@ impl ReplayStage { let t_responder = responder("replay_stage", Arc::new(send), vote_blob_receiver); let keypair = Arc::new(keypair); + let vote_account_id = *vote_account_id; + let rpc_client = RpcClient::new_from_socket(*vote_signer_addr); let t_replay = Builder::new() .name("solana-replay-stage".to_string()) .spawn(move || { @@ -235,7 +249,8 @@ impl ReplayStage { &cluster_info, &window_receiver, &keypair, - &vote_account_keypair, + &vote_account_id, + &rpc_client, Some(&vote_blob_sender), &ledger_entry_sender, &mut entry_height_, @@ -282,14 +297,18 @@ mod test { make_active_set_entries, LeaderScheduler, LeaderSchedulerConfig, }; use crate::ledger::{create_ticks, create_tmp_sample_ledger}; + + use crate::create_vote_account::*; use crate::packet::BlobError; use crate::replay_stage::{ReplayStage, ReplayStageReturnType}; use crate::result::Error; + use crate::rpc_request::RpcClient; use crate::service::Service; use crate::vote_stage::send_validator_vote; use solana_sdk::hash::Hash; use solana_sdk::signature::{Keypair, KeypairUtil}; use std::fs::remove_dir_all; + use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::channel; use std::sync::{Arc, RwLock}; @@ -324,8 +343,9 @@ mod test { // Write two entries to the ledger so that the validator is in the active set: // 1) Give the validator a nonzero number of tokens 2) A vote from the validator . // This will cause leader rotation after the bootstrap height - let (active_set_entries, vote_account_keypair) = - make_active_set_entries(&my_keypair, &mint.keypair(), &last_id, &last_id, 0); + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let (active_set_entries, vote_account_id) = + make_active_set_entries(&my_keypair, signer, &mint.keypair(), &last_id, &last_id, 0); last_id = active_set_entries.last().unwrap().id; let initial_tick_height = genesis_entries .iter() @@ -370,7 +390,8 @@ mod test { let exit = Arc::new(AtomicBool::new(false)); let (replay_stage, ledger_writer_recv) = ReplayStage::new( Arc::new(my_keypair), - Arc::new(vote_account_keypair), + &vote_account_id, + &signer, Arc::new(bank), Arc::new(RwLock::new(cluster_info_me)), entry_receiver, @@ -421,6 +442,8 @@ mod test { &entries_to_send[..leader_rotation_index + 1] ); + stop_local_vote_signer_service(t_signer, &signer_exit); + assert_eq!(exit.load(Ordering::Relaxed), true); let _ignored = remove_dir_all(&my_ledger_path); @@ -456,13 +479,16 @@ mod test { let cluster_info_me = Arc::new(RwLock::new(ClusterInfo::new(my_node.info.clone()))); // Set up the replay stage - let vote_account_keypair = Arc::new(Keypair::new()); + let vote_account_id = Keypair::new().pubkey(); let bank = Arc::new(bank); let (entry_sender, entry_receiver) = channel(); let exit = Arc::new(AtomicBool::new(false)); + let signer = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0); + let my_keypair = Arc::new(my_keypair); let (replay_stage, ledger_writer_recv) = ReplayStage::new( - Arc::new(my_keypair), - vote_account_keypair.clone(), + my_keypair.clone(), + &vote_account_id, + &signer, bank.clone(), cluster_info_me.clone(), entry_receiver, @@ -474,8 +500,14 @@ mod test { // Vote sender should error because no leader contact info is found in the // ClusterInfo let (mock_sender, _mock_receiver) = channel(); - let _vote_err = - send_validator_vote(&bank, &vote_account_keypair, &cluster_info_me, &mock_sender); + let _vote_err = send_validator_vote( + &bank, + &my_keypair, + &vote_account_id, + &RpcClient::new_from_socket(signer), + &cluster_info_me, + &mock_sender, + ); // Send ReplayStage an entry, should see it on the ledger writer receiver let next_tick = create_ticks( @@ -527,8 +559,9 @@ mod test { // Write two entries to the ledger so that the validator is in the active set: // 1) Give the validator a nonzero number of tokens 2) A vote from the validator. // This will cause leader rotation after the bootstrap height - let (active_set_entries, vote_account_keypair) = - make_active_set_entries(&my_keypair, &mint.keypair(), &last_id, &last_id, 0); + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let (active_set_entries, vote_account_id) = + make_active_set_entries(&my_keypair, signer, &mint.keypair(), &last_id, &last_id, 0); last_id = active_set_entries.last().unwrap().id; let initial_tick_height = genesis_entries .iter() @@ -572,13 +605,15 @@ mod test { let cluster_info_me = Arc::new(RwLock::new(ClusterInfo::new(my_node.info.clone()))); // Set up the replay stage - let vote_account_keypair = Arc::new(vote_account_keypair); + let vote_account_id = Arc::new(vote_account_id); let bank = Arc::new(bank); let (entry_sender, entry_receiver) = channel(); let exit = Arc::new(AtomicBool::new(false)); + let my_keypair = Arc::new(my_keypair); let (replay_stage, ledger_writer_recv) = ReplayStage::new( - Arc::new(my_keypair), - vote_account_keypair.clone(), + my_keypair.clone(), + &vote_account_id, + &signer, bank.clone(), cluster_info_me.clone(), entry_receiver, @@ -590,8 +625,14 @@ mod test { // Vote sender should error because no leader contact info is found in the // ClusterInfo let (mock_sender, _mock_receiver) = channel(); - let _vote_err = - send_validator_vote(&bank, &vote_account_keypair, &cluster_info_me, &mock_sender); + let _vote_err = send_validator_vote( + &bank, + &my_keypair, + &vote_account_id, + &RpcClient::new_from_socket(signer), + &cluster_info_me, + &mock_sender, + ); // Send enough ticks to trigger leader rotation let total_entries_to_send = (bootstrap_height - initial_tick_height) as usize; @@ -631,7 +672,7 @@ mod test { )), replay_stage.join().expect("replay stage join") ); - + stop_local_vote_signer_service(t_signer, &signer_exit); assert_eq!(exit.load(Ordering::Relaxed), true); let _ignored = remove_dir_all(&my_ledger_path); } @@ -671,12 +712,16 @@ mod test { .send(entries.clone()) .expect("Expected to err out"); + let signer = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0); + let rpc_client = RpcClient::new_from_socket(signer); + let my_keypair = Arc::new(my_keypair); let res = ReplayStage::process_entries( &Arc::new(Bank::default()), &cluster_info_me, &entry_receiver, - &Arc::new(my_keypair), - &Arc::new(vote_keypair), + &my_keypair, + &vote_keypair.pubkey(), + &rpc_client, None, &ledger_entry_sender, &mut entry_height, @@ -702,7 +747,8 @@ mod test { &cluster_info_me, &entry_receiver, &Arc::new(Keypair::new()), - &Arc::new(Keypair::new()), + &Keypair::new().pubkey(), + &rpc_client, None, &ledger_entry_sender, &mut entry_height, diff --git a/src/rpc.rs b/src/rpc.rs index 422718296..b4bf3ed80 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -689,7 +689,8 @@ mod tests { let entry_height = alice.create_entries().len() as u64; let server = Fullnode::new_with_bank( leader_keypair, - vote_account_keypair, + &vote_account_keypair.pubkey(), + &SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), bank, None, entry_height, diff --git a/src/storage_stage.rs b/src/storage_stage.rs index 7e8ce6c38..0fbe023d6 100644 --- a/src/storage_stage.rs +++ b/src/storage_stage.rs @@ -485,7 +485,17 @@ mod tests { tick_height: 123456, }; let keypair = Keypair::new(); - let vote_tx = VoteTransaction::vote_new(&keypair, vote, Hash::default(), 1); + let tx = Transaction::vote_new(&keypair.pubkey(), vote, Hash::default(), 1); + let msg = tx.get_sign_data(); + let sig = Signature::new(&keypair.sign(&msg).as_ref()); + let vote_tx = Transaction { + signatures: vec![sig], + account_keys: tx.account_keys, + last_id: tx.last_id, + fee: tx.fee, + program_ids: tx.program_ids, + instructions: tx.instructions, + }; vote_txs.push(vote_tx); let vote_entries = vec![Entry::new(&Hash::default(), 0, 1, vote_txs)]; storage_entry_sender.send(vote_entries).unwrap(); diff --git a/src/thin_client.rs b/src/thin_client.rs index 46ca4a502..32ed5f7e5 100644 --- a/src/thin_client.rs +++ b/src/thin_client.rs @@ -438,6 +438,7 @@ mod tests { use solana_sdk::vote_program::VoteProgram; use solana_sdk::vote_transaction::VoteTransaction; use std::fs::remove_dir_all; + use std::net::{IpAddr, Ipv4Addr, SocketAddr}; #[test] fn test_thin_client() { @@ -460,7 +461,8 @@ mod tests { let last_id = bank.last_id(); let server = Fullnode::new_with_bank( leader_keypair, - vote_account_keypair, + &vote_account_keypair.pubkey(), + &SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), bank, None, entry_height, @@ -514,7 +516,8 @@ mod tests { let last_id = bank.last_id(); let server = Fullnode::new_with_bank( leader_keypair, - vote_account_keypair, + &vote_account_keypair.pubkey(), + &SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), bank, None, 0, @@ -573,7 +576,8 @@ mod tests { let last_id = bank.last_id(); let server = Fullnode::new_with_bank( leader_keypair, - vote_account_keypair, + &vote_account_keypair.pubkey(), + &SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), bank, None, entry_height, @@ -619,7 +623,8 @@ mod tests { let leader_vote_account_keypair = Arc::new(Keypair::new()); let server = Fullnode::new_with_bank( leader_keypair, - leader_vote_account_keypair.clone(), + &leader_vote_account_keypair.pubkey(), + &SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), bank, None, entry_height, @@ -713,7 +718,8 @@ mod tests { let entry_height = alice.create_entries().len() as u64; let server = Fullnode::new_with_bank( leader_keypair, - vote_account_keypair, + &vote_account_keypair.pubkey(), + &SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), bank, None, entry_height, diff --git a/src/tvu.rs b/src/tvu.rs index 1bdba5494..06fdc645d 100644 --- a/src/tvu.rs +++ b/src/tvu.rs @@ -21,8 +21,9 @@ use crate::retransmit_stage::RetransmitStage; use crate::service::Service; use crate::storage_stage::StorageStage; use solana_sdk::hash::Hash; +use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::Keypair; -use std::net::UdpSocket; +use std::net::{SocketAddr, UdpSocket}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; use std::thread; @@ -50,7 +51,7 @@ impl Tvu { /// This service receives messages from a leader in the network and processes the transactions /// on the bank state. /// # Arguments - /// * `vote_account_keypair` - Vote key pair + /// * `vote_account_id` - Vote public key /// * `bank` - The bank state. /// * `entry_height` - Initial ledger height /// * `last_entry_id` - Hash of the last entry @@ -58,7 +59,8 @@ impl Tvu { /// * `sockets` - My fetch, repair, and restransmit sockets /// * `db_ledger` - the ledger itself pub fn new( - vote_account_keypair: Arc, + vote_account_id: &Pubkey, + vote_signer_addr: &SocketAddr, bank: &Arc, entry_height: u64, last_entry_id: Hash, @@ -103,7 +105,8 @@ impl Tvu { let (replay_stage, ledger_entry_receiver) = ReplayStage::new( keypair.clone(), - vote_account_keypair, + vote_account_id, + vote_signer_addr, bank.clone(), cluster_info.clone(), blob_window_receiver, @@ -189,6 +192,7 @@ pub mod tests { use solana_sdk::transaction::Transaction; use std::fs::remove_dir_all; use std::net::UdpSocket; + use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::channel; use std::sync::{Arc, RwLock}; @@ -264,13 +268,14 @@ pub mod tests { let cref1 = Arc::new(RwLock::new(cluster_info1)); let dr_1 = new_ncp(cref1.clone(), target1.sockets.gossip, exit.clone()); - let vote_account_keypair = Arc::new(Keypair::new()); + let vote_account_id = Keypair::new().pubkey(); let mut cur_hash = Hash::default(); let db_ledger_path = get_tmp_ledger_path("test_replay"); let db_ledger = DbLedger::open(&db_ledger_path).expect("Expected to successfully open ledger"); let tvu = Tvu::new( - vote_account_keypair, + &vote_account_id, + &SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), &bank, 0, cur_hash, diff --git a/src/vote_stage.rs b/src/vote_stage.rs index b3845f52d..8a8cea739 100644 --- a/src/vote_stage.rs +++ b/src/vote_stage.rs @@ -5,11 +5,13 @@ use crate::cluster_info::ClusterInfo; use crate::counter::Counter; use crate::packet::SharedBlob; use crate::result::{Error, Result}; +use crate::rpc_request::{RpcClient, RpcRequest}; use crate::streamer::BlobSender; use bincode::serialize; use log::Level; use solana_sdk::hash::Hash; -use solana_sdk::signature::Keypair; +use solana_sdk::pubkey::Pubkey; +use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use solana_sdk::transaction::Transaction; use solana_sdk::vote_program::Vote; use solana_sdk::vote_transaction::VoteTransaction; @@ -24,10 +26,41 @@ pub enum VoteError { LeaderInfoNotFound, } +pub fn create_new_signed_vote_transaction( + last_id: &Hash, + keypair: &Arc, + tick_height: u64, + vote_account: &Pubkey, + rpc_client: &RpcClient, +) -> Transaction { + let vote = Vote { tick_height }; + let tx = Transaction::vote_new(&vote_account, vote, *last_id, 0); + + let msg = tx.get_sign_data(); + let sig = Signature::new(&keypair.sign(&msg).as_ref()); + + let params = json!([keypair.pubkey(), sig, &msg]); + let resp = RpcRequest::SignVote + .make_rpc_request(&rpc_client, 1, Some(params)) + .unwrap(); + let vote_signature: Signature = serde_json::from_value(resp).unwrap(); + + Transaction { + signatures: vec![vote_signature], + account_keys: tx.account_keys, + last_id: tx.last_id, + fee: tx.fee, + program_ids: tx.program_ids, + instructions: tx.instructions, + } +} + // TODO: Change voting to be on fixed tick intervals based on bank state pub fn create_new_signed_vote_blob( last_id: &Hash, - vote_account: &Keypair, + keypair: &Arc, + vote_account: &Pubkey, + rpc_client: &RpcClient, bank: &Arc, cluster_info: &Arc>, ) -> Result { @@ -37,8 +70,9 @@ pub fn create_new_signed_vote_blob( let leader_tpu = get_leader_tpu(&bank, cluster_info)?; //TODO: doesn't seem like there is a synchronous call to get height and id debug!("voting on {:?}", &last_id.as_ref()[..8]); - let vote = Vote { tick_height }; - let tx = Transaction::vote_new(&vote_account, vote, *last_id, 0); + let tx = + create_new_signed_vote_transaction(last_id, keypair, tick_height, vote_account, rpc_client); + { let mut blob = shared_blob.write().unwrap(); let bytes = serialize(&tx)?; @@ -68,16 +102,25 @@ fn get_leader_tpu(bank: &Bank, cluster_info: &Arc>) -> Resul pub fn send_validator_vote( bank: &Arc, - vote_account: &Keypair, + keypair: &Arc, + vote_account: &Pubkey, + vote_signer_rpc: &RpcClient, cluster_info: &Arc>, vote_blob_sender: &BlobSender, ) -> Result<()> { let last_id = bank.last_id(); - if let Ok(shared_blob) = create_new_signed_vote_blob(&last_id, vote_account, bank, cluster_info) - { + if let Ok(shared_blob) = create_new_signed_vote_blob( + &last_id, + keypair, + vote_account, + vote_signer_rpc, + bank, + cluster_info, + ) { inc_new_counter_info!("validator-vote_sent", 1); vote_blob_sender.send(vec![shared_blob])?; } + Ok(()) } diff --git a/src/window_service.rs b/src/window_service.rs old mode 100755 new mode 100644 diff --git a/tests/multinode.rs b/tests/multinode.rs index da27c3dd8..538146170 100644 --- a/tests/multinode.rs +++ b/tests/multinode.rs @@ -1,11 +1,17 @@ #[macro_use] extern crate log; - -use solana; +extern crate bincode; +extern crate chrono; +#[macro_use] +extern crate serde_json; +extern crate solana; +extern crate solana_sdk; +extern crate solana_vote_signer; use solana::blob_fetch_stage::BlobFetchStage; use solana::cluster_info::{ClusterInfo, Node, NodeInfo}; use solana::contact_info::ContactInfo; +use solana::create_vote_account::*; use solana::db_ledger::{DbLedger, DEFAULT_SLOT_HEIGHT}; use solana::entry::{reconstruct_entries_from_blobs, Entry}; use solana::fullnode::{Fullnode, FullnodeReturnType}; @@ -17,17 +23,19 @@ use solana::mint::Mint; use solana::packet::SharedBlob; use solana::poh_service::NUM_TICKS_PER_SECOND; use solana::result; +use solana::rpc_request::{RpcClient, RpcRequest}; use solana::service::Service; use solana::thin_client::{retry_get_balance, ThinClient}; use solana_sdk::hash::Hash; use solana_sdk::pubkey::Pubkey; -use solana_sdk::signature::{Keypair, KeypairUtil}; +use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use solana_sdk::system_transaction::SystemTransaction; use solana_sdk::timing::{duration_as_ms, duration_as_s}; use solana_sdk::transaction::Transaction; use std::collections::{HashSet, VecDeque}; use std::env; use std::fs::remove_dir_all; +use std::net::SocketAddr; use std::net::UdpSocket; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; @@ -119,6 +127,20 @@ fn make_tiny_test_entries(start_hash: Hash, num: usize) -> Vec { .collect() } +fn register_node(signer: SocketAddr, keypair: Arc) -> Pubkey { + let rpc_client = RpcClient::new_from_socket(signer); + + let msg = "Registering a new node"; + let sig = Signature::new(&keypair.sign(msg.as_bytes()).as_ref()); + + let params = json!([keypair.pubkey(), sig, msg.as_bytes()]); + let resp = RpcRequest::RegisterNode + .make_rpc_request(&rpc_client, 1, Some(params)) + .unwrap(); + let vote_account_id: Pubkey = serde_json::from_value(resp).unwrap(); + vote_account_id +} + #[test] fn test_multi_node_ledger_window() -> result::Result<()> { solana_logger::setup(); @@ -152,11 +174,14 @@ fn test_multi_node_ledger_window() -> result::Result<()> { .unwrap(); } + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let vote_id = register_node(signer, leader_keypair.clone()); let leader = Fullnode::new( leader, &leader_ledger_path, leader_keypair, - Arc::new(Keypair::new()), + &vote_id, + &signer, None, false, LeaderScheduler::from_bootstrap_leader(leader_pubkey), @@ -169,11 +194,13 @@ fn test_multi_node_ledger_window() -> result::Result<()> { let validator_pubkey = keypair.pubkey().clone(); let validator = Node::new_localhost_with_pubkey(keypair.pubkey()); let validator_data = validator.info.clone(); + let validator_vote_id = register_node(signer, keypair.clone()); let validator = Fullnode::new( validator, &zero_ledger_path, keypair, - Arc::new(Keypair::new()), + &validator_vote_id, + &signer, Some(leader_data.gossip), false, LeaderScheduler::from_bootstrap_leader(leader_pubkey), @@ -212,6 +239,8 @@ fn test_multi_node_ledger_window() -> result::Result<()> { validator.close()?; leader.close()?; + stop_local_vote_signer_service(t_signer, &signer_exit); + for path in ledger_paths { remove_dir_all(path).unwrap(); } @@ -246,11 +275,14 @@ fn test_multi_node_validator_catchup_from_zero() -> result::Result<()> { ); ledger_paths.push(zero_ledger_path.clone()); + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let vote_id = register_node(signer, leader_keypair.clone()); let server = Fullnode::new( leader, &leader_ledger_path, leader_keypair, - Arc::new(Keypair::new()), + &vote_id, + &signer, None, false, LeaderScheduler::from_bootstrap_leader(leader_pubkey), @@ -274,11 +306,13 @@ fn test_multi_node_validator_catchup_from_zero() -> result::Result<()> { .unwrap(); info!("validator balance {}", validator_balance); + let vote_id = register_node(signer, keypair.clone()); let val = Fullnode::new( validator, &ledger_path, keypair, - Arc::new(Keypair::new()), + &vote_id, + &signer, Some(leader_data.gossip), false, LeaderScheduler::from_bootstrap_leader(leader_pubkey), @@ -312,11 +346,13 @@ fn test_multi_node_validator_catchup_from_zero() -> result::Result<()> { // balances let keypair = Arc::new(Keypair::new()); let validator = Node::new_localhost_with_pubkey(keypair.pubkey()); + let vote_id = register_node(signer, keypair.clone()); let val = Fullnode::new( validator, &zero_ledger_path, keypair, - Arc::new(Keypair::new()), + &vote_id, + &signer, Some(leader_data.gossip), false, LeaderScheduler::from_bootstrap_leader(leader_pubkey), @@ -358,6 +394,9 @@ fn test_multi_node_validator_catchup_from_zero() -> result::Result<()> { for node in nodes { node.close()?; } + + stop_local_vote_signer_service(t_signer, &signer_exit); + for path in ledger_paths { remove_dir_all(path).unwrap(); } @@ -381,11 +420,14 @@ fn test_multi_node_basic() { let (alice, leader_ledger_path) = create_tmp_genesis("multi_node_basic", 10_000, leader_data.id, 500); ledger_paths.push(leader_ledger_path.clone()); + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let vote_id = register_node(signer, leader_keypair.clone()); let server = Fullnode::new( leader, &leader_ledger_path, leader_keypair, - Arc::new(Keypair::new()), + &vote_id, + &signer, None, false, LeaderScheduler::from_bootstrap_leader(leader_pubkey), @@ -405,12 +447,13 @@ fn test_multi_node_basic() { send_tx_and_retry_get_balance(&leader_data, &alice, &validator_pubkey, 500, None) .unwrap(); info!("validator balance {}", validator_balance); - + let vote_id = register_node(signer, keypair.clone()); let val = Fullnode::new( validator, &ledger_path, keypair, - Arc::new(Keypair::new()), + &vote_id, + &signer, Some(leader_data.gossip), false, LeaderScheduler::from_bootstrap_leader(leader_pubkey), @@ -441,6 +484,8 @@ fn test_multi_node_basic() { for node in nodes { node.close().unwrap(); } + stop_local_vote_signer_service(t_signer, &signer_exit); + for path in ledger_paths { remove_dir_all(path).unwrap(); } @@ -460,11 +505,14 @@ fn test_boot_validator_from_file() -> result::Result<()> { ledger_paths.push(leader_ledger_path.clone()); let leader_data = leader.info.clone(); + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let vote_id = register_node(signer, leader_keypair.clone()); let leader_fullnode = Fullnode::new( leader, &leader_ledger_path, leader_keypair, - Arc::new(Keypair::new()), + &vote_id, + &signer, None, false, LeaderScheduler::from_bootstrap_leader(leader_pubkey), @@ -482,11 +530,13 @@ fn test_boot_validator_from_file() -> result::Result<()> { let validator_data = validator.info.clone(); let ledger_path = tmp_copy_ledger(&leader_ledger_path, "boot_validator_from_file"); ledger_paths.push(ledger_path.clone()); + let vote_id = register_node(signer, keypair.clone()); let val_fullnode = Fullnode::new( validator, &ledger_path, keypair, - Arc::new(Keypair::new()), + &vote_id, + &signer, Some(leader_data.gossip), false, LeaderScheduler::from_bootstrap_leader(leader_pubkey), @@ -498,6 +548,8 @@ fn test_boot_validator_from_file() -> result::Result<()> { val_fullnode.close()?; leader_fullnode.close()?; + stop_local_vote_signer_service(t_signer, &signer_exit); + for path in ledger_paths { remove_dir_all(path)?; } @@ -505,14 +557,20 @@ fn test_boot_validator_from_file() -> result::Result<()> { Ok(()) } -fn create_leader(ledger_path: &str, leader_keypair: Arc) -> (NodeInfo, Fullnode) { +fn create_leader( + ledger_path: &str, + leader_keypair: Arc, + vote_id: &Pubkey, + signer: &SocketAddr, +) -> (NodeInfo, Fullnode) { let leader = Node::new_localhost_with_pubkey(leader_keypair.pubkey()); let leader_data = leader.info.clone(); let leader_fullnode = Fullnode::new( leader, &ledger_path, leader_keypair, - Arc::new(Keypair::new()), + &vote_id, + &signer, None, false, LeaderScheduler::from_bootstrap_leader(leader_data.id), @@ -539,7 +597,10 @@ fn test_leader_restart_validator_start_from_old_ledger() -> result::Result<()> { ); let bob_pubkey = Keypair::new().pubkey(); - let (leader_data, leader_fullnode) = create_leader(&ledger_path, leader_keypair.clone()); + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let vote_id = register_node(signer, leader_keypair.clone()); + let (leader_data, leader_fullnode) = + create_leader(&ledger_path, leader_keypair.clone(), &vote_id, &signer); // lengthen the ledger let leader_balance = @@ -554,7 +615,8 @@ fn test_leader_restart_validator_start_from_old_ledger() -> result::Result<()> { // restart the leader leader_fullnode.close()?; - let (leader_data, leader_fullnode) = create_leader(&ledger_path, leader_keypair.clone()); + let (leader_data, leader_fullnode) = + create_leader(&ledger_path, leader_keypair.clone(), &vote_id, &signer); // lengthen the ledger let leader_balance = @@ -563,18 +625,21 @@ fn test_leader_restart_validator_start_from_old_ledger() -> result::Result<()> { // restart the leader leader_fullnode.close()?; - let (leader_data, leader_fullnode) = create_leader(&ledger_path, leader_keypair); + let (leader_data, leader_fullnode) = + create_leader(&ledger_path, leader_keypair, &vote_id, &signer); // start validator from old ledger let keypair = Arc::new(Keypair::new()); let validator = Node::new_localhost_with_pubkey(keypair.pubkey()); let validator_data = validator.info.clone(); + let vote_id = register_node(signer, keypair.clone()); let val_fullnode = Fullnode::new( validator, &stale_ledger_path, keypair, - Arc::new(Keypair::new()), + &vote_id, + &signer, Some(leader_data.gossip), false, LeaderScheduler::from_bootstrap_leader(leader_data.id), @@ -604,6 +669,7 @@ fn test_leader_restart_validator_start_from_old_ledger() -> result::Result<()> { val_fullnode.close()?; leader_fullnode.close()?; + stop_local_vote_signer_service(t_signer, &signer_exit); remove_dir_all(ledger_path)?; remove_dir_all(stale_ledger_path)?; @@ -637,11 +703,14 @@ fn test_multi_node_dynamic_network() { let alice_arc = Arc::new(RwLock::new(alice)); let leader_data = leader.info.clone(); + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let vote_id = register_node(signer, leader_keypair.clone()); let server = Fullnode::new( leader, &leader_ledger_path, leader_keypair, - Arc::new(Keypair::new()), + &vote_id, + &signer, None, true, LeaderScheduler::from_bootstrap_leader(leader_pubkey), @@ -705,11 +774,14 @@ fn test_multi_node_dynamic_network() { let validator = Node::new_localhost_with_pubkey(keypair.pubkey()); let rd = validator.info.clone(); info!("starting {} {}", keypair.pubkey(), rd.id); + let keypair = Arc::new(keypair); + let vote_id = register_node(signer, keypair.clone()); let val = Fullnode::new( validator, &ledger_path, - Arc::new(keypair), - Arc::new(Keypair::new()), + keypair, + &vote_id, + &signer, Some(leader_data.gossip), true, LeaderScheduler::from_bootstrap_leader(leader_pubkey), @@ -794,6 +866,7 @@ fn test_multi_node_dynamic_network() { } } + stop_local_vote_signer_service(t_signer, &signer_exit); assert_eq!(consecutive_success, 10); for (_, node) in &validators { node.exit(); @@ -840,8 +913,15 @@ fn test_leader_to_validator_transition() { // Write the bootstrap entries to the ledger that will cause leader rotation // after the bootstrap height - let (bootstrap_entries, _) = - make_active_set_entries(&validator_keypair, &mint.keypair(), &last_id, &last_id, 0); + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let (bootstrap_entries, _) = make_active_set_entries( + &validator_keypair, + signer, + &mint.keypair(), + &last_id, + &last_id, + 0, + ); { let db_ledger = DbLedger::open(&leader_ledger_path).unwrap(); db_ledger @@ -862,11 +942,13 @@ fn test_leader_to_validator_transition() { Some(bootstrap_height), ); + let vote_id = register_node(signer, leader_keypair.clone()); let mut leader = Fullnode::new( leader_node, &leader_ledger_path, leader_keypair, - Arc::new(Keypair::new()), + &vote_id, + &signer, Some(leader_info.gossip), false, LeaderScheduler::new(&leader_scheduler_config), @@ -942,6 +1024,7 @@ fn test_leader_to_validator_transition() { ); assert_eq!(bank.tick_height(), bootstrap_height); + stop_local_vote_signer_service(t_signer, &signer_exit); remove_dir_all(leader_ledger_path).unwrap(); } @@ -986,8 +1069,15 @@ fn test_leader_validator_basic() { // Write the bootstrap entries to the ledger that will cause leader rotation // after the bootstrap height - let (active_set_entries, vote_account_keypair) = - make_active_set_entries(&validator_keypair, &mint.keypair(), &last_id, &last_id, 0); + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let (active_set_entries, _vote_account_keypair) = make_active_set_entries( + &validator_keypair, + signer, + &mint.keypair(), + &last_id, + &last_id, + 0, + ); { let db_ledger = DbLedger::open(&leader_ledger_path).unwrap(); db_ledger @@ -1010,11 +1100,13 @@ fn test_leader_validator_basic() { ); // Start the validator node + let vote_id = register_node(signer, validator_keypair.clone()); let mut validator = Fullnode::new( validator_node, &validator_ledger_path, validator_keypair, - Arc::new(vote_account_keypair), + &vote_id, + &signer, Some(leader_info.gossip), false, LeaderScheduler::new(&leader_scheduler_config), @@ -1022,11 +1114,13 @@ fn test_leader_validator_basic() { ); // Start the leader fullnode + let vote_id = register_node(signer, leader_keypair.clone()); let mut leader = Fullnode::new( leader_node, &leader_ledger_path, leader_keypair, - Arc::new(Keypair::new()), + &vote_id, + &signer, Some(leader_info.gossip), false, LeaderScheduler::new(&leader_scheduler_config), @@ -1095,6 +1189,7 @@ fn test_leader_validator_basic() { for (v, l) in validator_entries.iter().zip(leader_entries) { assert_eq!(*v, l); } + stop_local_vote_signer_service(t_signer, &signer_exit); for path in ledger_paths { DbLedger::destroy(&path).expect("Expected successful database destruction"); @@ -1167,8 +1262,15 @@ fn test_dropped_handoff_recovery() { // Make the entries to give the next_leader validator some stake so that they will be in // leader election active set - let (active_set_entries, vote_account_keypair) = - make_active_set_entries(&next_leader_keypair, &mint.keypair(), &last_id, &last_id, 0); + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let (active_set_entries, _vote_account_keypair) = make_active_set_entries( + &next_leader_keypair, + signer, + &mint.keypair(), + &last_id, + &last_id, + 0, + ); // Write the entries { @@ -1204,12 +1306,14 @@ fn test_dropped_handoff_recovery() { Some(leader_rotation_interval), ); + let vote_id = register_node(signer, bootstrap_leader_keypair.clone()); // Start up the bootstrap leader fullnode let bootstrap_leader = Fullnode::new( bootstrap_leader_node, &bootstrap_leader_ledger_path, bootstrap_leader_keypair, - Arc::new(Keypair::new()), + &vote_id, + &signer, Some(bootstrap_leader_info.gossip), false, LeaderScheduler::new(&leader_scheduler_config), @@ -1228,11 +1332,13 @@ fn test_dropped_handoff_recovery() { ledger_paths.push(validator_ledger_path.clone()); let validator_id = kp.pubkey(); let validator_node = Node::new_localhost_with_pubkey(validator_id); + let vote_id = register_node(signer, kp.clone()); let validator = Fullnode::new( validator_node, &validator_ledger_path, kp, - Arc::new(Keypair::new()), + &vote_id, + &signer, Some(bootstrap_leader_info.gossip), false, LeaderScheduler::new(&leader_scheduler_config), @@ -1254,11 +1360,13 @@ fn test_dropped_handoff_recovery() { // Now start up the "next leader" node let next_leader_node = Node::new_localhost_with_pubkey(next_leader_keypair.pubkey()); + let vote_id = register_node(signer, next_leader_keypair.clone()); let mut next_leader = Fullnode::new( next_leader_node, &next_leader_ledger_path, next_leader_keypair, - Arc::new(vote_account_keypair), + &vote_id, + &signer, Some(bootstrap_leader_info.gossip), false, LeaderScheduler::new(&leader_scheduler_config), @@ -1273,6 +1381,7 @@ fn test_dropped_handoff_recovery() { nodes.push(next_leader); + stop_local_vote_signer_service(t_signer, &signer_exit); for node in nodes { node.close().unwrap(); } @@ -1333,12 +1442,14 @@ fn test_full_leader_validator_network() { let mut ledger_paths = Vec::new(); ledger_paths.push(bootstrap_leader_ledger_path.clone()); + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); let mut vote_account_keypairs = VecDeque::new(); for node_keypair in node_keypairs.iter() { // Make entries to give each node some stake so that they will be in the // leader election active set let (bootstrap_entries, vote_account_keypair) = make_active_set_entries( node_keypair, + signer, &mint.keypair(), &last_entry_id, &last_tick_id, @@ -1385,7 +1496,7 @@ fn test_full_leader_validator_network() { // 2) Modifying the leader ledger which validators are going to be copying // during startup let leader_keypair = node_keypairs.pop_front().unwrap(); - let leader_vote_keypair = vote_account_keypairs.pop_front().unwrap(); + let _leader_vote_keypair = vote_account_keypairs.pop_front().unwrap(); let mut nodes: Vec>> = vec![]; let mut t_nodes = vec![]; @@ -1400,11 +1511,14 @@ fn test_full_leader_validator_network() { let validator_id = kp.pubkey(); let validator_node = Node::new_localhost_with_pubkey(validator_id); + let kp = Arc::new(kp); + let vote_id = register_node(signer, kp.clone()); let validator = Arc::new(RwLock::new(Fullnode::new( validator_node, &validator_ledger_path, - Arc::new(kp), - Arc::new(vote_account_keypairs.pop_front().unwrap()), + kp, + &vote_id, + &signer, Some(bootstrap_leader_info.gossip), false, LeaderScheduler::new(&leader_scheduler_config), @@ -1416,11 +1530,14 @@ fn test_full_leader_validator_network() { } // Start up the bootstrap leader + let leader_keypair = Arc::new(leader_keypair); + let vote_id = register_node(signer, leader_keypair.clone()); let bootstrap_leader = Arc::new(RwLock::new(Fullnode::new( bootstrap_leader_node, &bootstrap_leader_ledger_path, - Arc::new(leader_keypair), - Arc::new(leader_vote_keypair), + leader_keypair, + &vote_id, + &signer, Some(bootstrap_leader_info.gossip), false, LeaderScheduler::new(&leader_scheduler_config), @@ -1534,6 +1651,7 @@ fn test_full_leader_validator_network() { assert!(shortest.unwrap() >= target_height); + stop_local_vote_signer_service(t_signer, &signer_exit); for path in ledger_paths { DbLedger::destroy(&path).expect("Expected successful database destruction"); remove_dir_all(path).unwrap(); @@ -1596,11 +1714,15 @@ fn test_broadcast_last_tick() { ); // Start up the bootstrap leader fullnode + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let bootstrap_leader_keypair = Arc::new(bootstrap_leader_keypair); + let vote_id = bootstrap_leader_keypair.pubkey(); let mut bootstrap_leader = Fullnode::new( bootstrap_leader_node, &bootstrap_leader_ledger_path, - Arc::new(bootstrap_leader_keypair), - Arc::new(Keypair::new()), + bootstrap_leader_keypair, + &vote_id, + &signer, Some(bootstrap_leader_info.gossip), false, LeaderScheduler::new(&leader_scheduler_config), @@ -1650,6 +1772,7 @@ fn test_broadcast_last_tick() { bf.join().unwrap(); } + stop_local_vote_signer_service(t_signer, &signer_exit); // Shut down the listeners for node in listening_nodes { node.0.close().unwrap(); diff --git a/tests/replicator.rs b/tests/replicator.rs index ed12feeb1..475c50c2c 100644 --- a/tests/replicator.rs +++ b/tests/replicator.rs @@ -1,18 +1,20 @@ #[macro_use] extern crate log; -#[cfg(feature = "chacha")] #[macro_use] extern crate serde_json; use solana::client::mk_client; use solana::cluster_info::{Node, NodeInfo}; +use solana::create_vote_account::*; use solana::db_ledger::DbLedger; use solana::fullnode::Fullnode; use solana::leader_scheduler::LeaderScheduler; use solana::ledger::{create_tmp_genesis, get_tmp_ledger_path, read_ledger, tmp_copy_ledger}; use solana::replicator::Replicator; -use solana_sdk::signature::{Keypair, KeypairUtil}; +use solana::rpc_request::{RpcClient, RpcRequest}; +use solana_sdk::pubkey::Pubkey; +use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use solana_sdk::system_transaction::SystemTransaction; use solana_sdk::transaction::Transaction; use std::fs::remove_dir_all; @@ -29,7 +31,6 @@ fn test_replicator_startup() { let leader_keypair = Arc::new(Keypair::new()); let leader_node = Node::new_localhost_with_pubkey(leader_keypair.pubkey()); let leader_info = leader_node.info.clone(); - let vote_account_keypair = Arc::new(Keypair::new()); let leader_ledger_path = "replicator_test_leader_ledger"; let (mint, leader_ledger_path) = create_tmp_genesis(leader_ledger_path, 100, leader_info.id, 1); @@ -38,11 +39,24 @@ fn test_replicator_startup() { tmp_copy_ledger(&leader_ledger_path, "replicator_test_validator_ledger"); { + let (signer, t_signer, signer_exit) = local_vote_signer_service().unwrap(); + let rpc_client = RpcClient::new_from_socket(signer); + + let msg = "Registering a new node"; + let sig = Signature::new(&leader_keypair.sign(msg.as_bytes()).as_ref()); + + let params = json!([leader_keypair.pubkey(), sig, msg.as_bytes()]); + let resp = RpcRequest::RegisterNode + .make_rpc_request(&rpc_client, 1, Some(params)) + .unwrap(); + let vote_account_id: Pubkey = serde_json::from_value(resp).unwrap(); + let leader = Fullnode::new( leader_node, &leader_ledger_path, leader_keypair, - vote_account_keypair.clone(), + &vote_account_id, + &signer, None, false, LeaderScheduler::from_bootstrap_leader(leader_info.id.clone()), @@ -52,6 +66,15 @@ fn test_replicator_startup() { let validator_keypair = Arc::new(Keypair::new()); let validator_node = Node::new_localhost_with_pubkey(validator_keypair.pubkey()); + let msg = "Registering a new node"; + let sig = Signature::new(&validator_keypair.sign(msg.as_bytes()).as_ref()); + + let params = json!([validator_keypair.pubkey(), sig, msg.as_bytes()]); + let resp = RpcRequest::RegisterNode + .make_rpc_request(&rpc_client, 1, Some(params)) + .unwrap(); + let vote_account_id: Pubkey = serde_json::from_value(resp).unwrap(); + #[cfg(feature = "chacha")] let validator_node_info = validator_node.info.clone(); @@ -59,7 +82,8 @@ fn test_replicator_startup() { validator_node, &validator_ledger_path, validator_keypair, - vote_account_keypair, + &vote_account_id, + &signer, Some(leader_info.gossip), false, LeaderScheduler::from_bootstrap_leader(leader_info.id), @@ -154,6 +178,7 @@ fn test_replicator_startup() { // Check that some ledger was downloaded assert!(num_entries > 0); + stop_local_vote_signer_service(t_signer, &signer_exit); replicator.close(); validator.exit(); diff --git a/vote-signer/src/bin/main.rs b/vote-signer/src/bin/main.rs index 5896e3815..9da4bfe83 100644 --- a/vote-signer/src/bin/main.rs +++ b/vote-signer/src/bin/main.rs @@ -9,7 +9,8 @@ use clap::{App, Arg}; use solana_vote_signer::rpc::VoteSignerRpcService; use std::error; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - +use std::sync::atomic::AtomicBool; +use std::sync::Arc; pub const RPC_PORT: u16 = 8989; fn main() -> Result<(), Box> { @@ -34,8 +35,11 @@ fn main() -> Result<(), Box> { RPC_PORT }; - let service = - VoteSignerRpcService::new(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port)); + let exit = Arc::new(AtomicBool::new(false)); + let service = VoteSignerRpcService::new( + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port), + exit, + ); service.join().unwrap(); diff --git a/vote-signer/src/rpc.rs b/vote-signer/src/rpc.rs index d89731e81..11655343a 100644 --- a/vote-signer/src/rpc.rs +++ b/vote-signer/src/rpc.rs @@ -1,12 +1,10 @@ //! The `rpc` module implements the Vote signing service RPC interface. -use bs58; use jsonrpc_core::*; use jsonrpc_http_server::*; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use std::collections::HashMap; -use std::mem; use std::net::SocketAddr; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; @@ -19,9 +17,8 @@ pub struct VoteSignerRpcService { } impl VoteSignerRpcService { - pub fn new(rpc_addr: SocketAddr) -> Self { + pub fn new(rpc_addr: SocketAddr, exit: Arc) -> Self { let request_processor = VoteSignRequestProcessor::default(); - let exit = Arc::new(AtomicBool::new(false)); let exit_ = exit.clone(); let thread_hdl = Builder::new() .name("solana-vote-signer-jsonrpc".to_string()) @@ -76,13 +73,13 @@ build_rpc_trait! { type Metadata; #[rpc(meta, name = "registerNode")] - fn register(&self, Self::Metadata, String, Signature, Vec) -> Result; + fn register(&self, Self::Metadata, Pubkey, Signature, Vec) -> Result; #[rpc(meta, name = "signVote")] - fn sign(&self, Self::Metadata, String, Signature, Vec) -> Result; + fn sign(&self, Self::Metadata, Pubkey, Signature, Vec) -> Result; #[rpc(meta, name = "deregisterNode")] - fn deregister(&self, Self::Metadata, String, Signature, Vec) -> Result<()>; + fn deregister(&self, Self::Metadata, Pubkey, Signature, Vec) -> Result<()>; } } @@ -93,40 +90,37 @@ impl VoteSignerRpc for VoteSignerRpcImpl { fn register( &self, meta: Self::Metadata, - id: String, + id: Pubkey, sig: Signature, signed_msg: Vec, ) -> Result { info!("register rpc request received: {:?}", id); - let pubkey = verify_pubkey(id)?; - verify_signature(&sig, &pubkey, &signed_msg)?; - meta.request_processor.register(pubkey) + verify_signature(&sig, &id, &signed_msg)?; + meta.request_processor.register(id) } fn sign( &self, meta: Self::Metadata, - id: String, + id: Pubkey, sig: Signature, signed_msg: Vec, ) -> Result { info!("sign rpc request received: {:?}", id); - let pubkey = verify_pubkey(id)?; - verify_signature(&sig, &pubkey, &signed_msg)?; - meta.request_processor.sign(pubkey, &signed_msg) + verify_signature(&sig, &id, &signed_msg)?; + meta.request_processor.sign(id, &signed_msg) } fn deregister( &self, meta: Self::Metadata, - id: String, + id: Pubkey, sig: Signature, signed_msg: Vec, ) -> Result<()> { info!("deregister rpc request received: {:?}", id); - let pubkey = verify_pubkey(id)?; - verify_signature(&sig, &pubkey, &signed_msg)?; - meta.request_processor.deregister(pubkey) + verify_signature(&sig, &id, &signed_msg)?; + meta.request_processor.deregister(id) } } @@ -154,6 +148,7 @@ impl VoteSignRequestProcessor { let voting_pubkey = voting_keypair.pubkey(); self.nodes.write().unwrap().insert(pubkey, voting_keypair); Ok(voting_pubkey) + //Ok(bs58::encode(voting_pubkey).into_string()) } pub fn sign(&self, pubkey: Pubkey, msg: &[u8]) -> Result { match self.nodes.read().unwrap().get(&pubkey) { @@ -178,27 +173,12 @@ impl Default for VoteSignRequestProcessor { } } -fn verify_pubkey(input: String) -> Result { - let pubkey_vec = bs58::decode(input).into_vec().map_err(|err| { - info!("verify_pubkey: invalid input: {:?}", err); - Error::invalid_request() - })?; - if pubkey_vec.len() != mem::size_of::() { - info!( - "verify_pubkey: invalid pubkey_vec length: {}", - pubkey_vec.len() - ); - Err(Error::invalid_request()) - } else { - Ok(Pubkey::new(&pubkey_vec)) - } -} - #[cfg(test)] mod tests { use super::*; use jsonrpc_core::Response; use solana_sdk::signature::{Keypair, KeypairUtil}; + use std::mem; fn start_rpc_handler() -> (MetaIoHandler, Meta) { let request_processor = VoteSignRequestProcessor::default(); @@ -221,7 +201,7 @@ mod tests { "jsonrpc": "2.0", "id": 1, "method": "registerNode", - "params": [node_pubkey.to_string(), sig, msg.as_bytes()], + "params": [node_pubkey, sig, msg.as_bytes()], }); let res = io.handle_request_sync(&req.to_string(), meta); @@ -232,11 +212,11 @@ mod tests { if let Output::Success(succ) = out { assert_eq!(succ.jsonrpc.unwrap(), Version::V2); assert_eq!(succ.id, Id::Num(1)); - assert!(succ.result.is_array()); assert_eq!( succ.result.as_array().unwrap().len(), mem::size_of::() ); + let _pk: Pubkey = serde_json::from_value(succ.result).unwrap(); } else { assert!(false); } @@ -258,7 +238,7 @@ mod tests { "jsonrpc": "2.0", "id": 1, "method": "registerNode", - "params": [node_pubkey.to_string(), sig, msg1.as_bytes()], + "params": [node_pubkey, sig, msg1.as_bytes()], }); let res = io.handle_request_sync(&req.to_string(), meta); @@ -289,7 +269,7 @@ mod tests { "jsonrpc": "2.0", "id": 1, "method": "deregisterNode", - "params": [node_pubkey.to_string(), sig, msg.as_bytes()], + "params": [node_pubkey, sig, msg.as_bytes()], }); let res = io.handle_request_sync(&req.to_string(), meta); @@ -321,7 +301,7 @@ mod tests { "jsonrpc": "2.0", "id": 1, "method": "deregisterNode", - "params": [node_pubkey.to_string(), sig, msg1.as_bytes()], + "params": [node_pubkey, sig, msg1.as_bytes()], }); let res = io.handle_request_sync(&req.to_string(), meta); @@ -353,15 +333,33 @@ mod tests { "jsonrpc": "2.0", "id": 1, "method": "registerNode", - "params": [node_pubkey.to_string(), sig, msg.as_bytes()], + "params": [node_pubkey, sig, msg.as_bytes()], }); - let _res = io.handle_request_sync(&req.to_string(), meta.clone()); + let res = io.handle_request_sync(&req.to_string(), meta.clone()); + let result: Response = serde_json::from_str(&res.expect("actual response")) + .expect("actual response deserialization"); + let mut vote_pubkey = Keypair::new().pubkey(); + if let Response::Single(out) = result { + if let Output::Success(succ) = out { + assert_eq!(succ.jsonrpc.unwrap(), Version::V2); + assert_eq!(succ.id, Id::Num(1)); + assert_eq!( + succ.result.as_array().unwrap().len(), + mem::size_of::() + ); + vote_pubkey = serde_json::from_value(succ.result).unwrap(); + } else { + assert!(false); + } + } else { + assert!(false); + } let req = json!({ "jsonrpc": "2.0", "id": 1, "method": "signVote", - "params": [node_pubkey.to_string(), sig, msg.as_bytes()], + "params": [node_pubkey, sig, msg.as_bytes()], }); let res = io.handle_request_sync(&req.to_string(), meta); @@ -372,6 +370,12 @@ mod tests { if let Output::Success(succ) = out { assert_eq!(succ.jsonrpc.unwrap(), Version::V2); assert_eq!(succ.id, Id::Num(1)); + assert_eq!( + succ.result.as_array().unwrap().len(), + mem::size_of::() + ); + let sig: Signature = serde_json::from_value(succ.result).unwrap(); + assert_eq!(verify_signature(&sig, &vote_pubkey, msg.as_bytes()), Ok(())); } else { assert!(false); } @@ -392,7 +396,7 @@ mod tests { "jsonrpc": "2.0", "id": 1, "method": "signVote", - "params": [node_pubkey.to_string(), sig, msg.as_bytes()], + "params": [node_pubkey, sig, msg.as_bytes()], }); let res = io.handle_request_sync(&req.to_string(), meta); @@ -424,7 +428,7 @@ mod tests { "jsonrpc": "2.0", "id": 1, "method": "registerNode", - "params": [node_pubkey.to_string(), sig, msg.as_bytes()], + "params": [node_pubkey, sig, msg.as_bytes()], }); let _res = io.handle_request_sync(&req.to_string(), meta.clone()); @@ -432,7 +436,7 @@ mod tests { "jsonrpc": "2.0", "id": 1, "method": "deregisterNode", - "params": [node_pubkey.to_string(), sig, msg.as_bytes()], + "params": [node_pubkey, sig, msg.as_bytes()], }); let _res = io.handle_request_sync(&req.to_string(), meta.clone()); @@ -440,7 +444,7 @@ mod tests { "jsonrpc": "2.0", "id": 1, "method": "signVote", - "params": [node_pubkey.to_string(), sig, msg.as_bytes()], + "params": [node_pubkey, sig, msg.as_bytes()], }); let res = io.handle_request_sync(&req.to_string(), meta); @@ -473,7 +477,7 @@ mod tests { "jsonrpc": "2.0", "id": 1, "method": "registerNode", - "params": [node_pubkey.to_string(), sig, msg.as_bytes()], + "params": [node_pubkey, sig, msg.as_bytes()], }); let _res = io.handle_request_sync(&req.to_string(), meta.clone()); @@ -481,7 +485,7 @@ mod tests { "jsonrpc": "2.0", "id": 1, "method": "signVote", - "params": [node_pubkey.to_string(), sig, msg1.as_bytes()], + "params": [node_pubkey, sig, msg1.as_bytes()], }); let res = io.handle_request_sync(&req.to_string(), meta); diff --git a/wallet/src/wallet.rs b/wallet/src/wallet.rs index c3f984722..f3a9a3b55 100644 --- a/wallet/src/wallet.rs +++ b/wallet/src/wallet.rs @@ -820,6 +820,7 @@ mod tests { use solana_sdk::signature::{gen_keypair_file, read_keypair, read_pkcs8, Keypair, KeypairUtil}; use std::fs; use std::fs::remove_dir_all; + use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::path::Path; use std::sync::mpsc::channel; use std::sync::{Arc, RwLock}; @@ -838,7 +839,8 @@ mod tests { leader, &ledger_path, leader_keypair, - Arc::new(Keypair::new()), + &leader_pubkey, + &SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), None, false, LeaderScheduler::from_bootstrap_leader(leader_pubkey), @@ -1205,7 +1207,8 @@ mod tests { leader, &ledger_path, leader_keypair, - Arc::new(Keypair::new()), + &leader_pubkey, + &SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), None, false, LeaderScheduler::from_bootstrap_leader(leader_pubkey), @@ -1265,7 +1268,8 @@ mod tests { leader, &ledger_path, leader_keypair, - Arc::new(Keypair::new()), + &leader_pubkey, + &SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), None, false, LeaderScheduler::from_bootstrap_leader(leader_pubkey), @@ -1341,7 +1345,8 @@ mod tests { let last_id = bank.last_id(); let server = Fullnode::new_with_bank( leader_keypair, - vote_account_keypair, + &vote_account_keypair.pubkey(), + &SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), bank, None, 0, @@ -1467,7 +1472,8 @@ mod tests { let last_id = bank.last_id(); let server = Fullnode::new_with_bank( leader_keypair, - vote_account_keypair, + &vote_account_keypair.pubkey(), + &SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), bank, None, 0, @@ -1582,7 +1588,8 @@ mod tests { let last_id = bank.last_id(); let server = Fullnode::new_with_bank( leader_keypair, - vote_account_keypair, + &vote_account_keypair.pubkey(), + &SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), bank, None, 0,