From a0dd8617bea8b280d24c16354181d47ce36caf8f Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 15 Nov 2018 17:05:31 -0800 Subject: [PATCH] Remove airdrop from fullnode --- multinode-demo/validator.sh | 17 +++++++ src/bin/fullnode-config.rs | 8 +++- src/bin/fullnode.rs | 95 +++++++++++++++---------------------- src/bin/genesis.rs | 14 +++++- src/fullnode.rs | 13 ++++- src/thin_client.rs | 38 ++++----------- 6 files changed, 94 insertions(+), 91 deletions(-) diff --git a/multinode-demo/validator.sh b/multinode-demo/validator.sh index 8e51079277..76ce67dde2 100755 --- a/multinode-demo/validator.sh +++ b/multinode-demo/validator.sh @@ -60,6 +60,7 @@ if ((!self_setup)); then echo " ${here}/setup.sh" exit 1 } + validator_id_path=$SOLANA_CONFIG_PRIVATE_DIR/validator-id.json validator_json_path=$SOLANA_CONFIG_VALIDATOR_DIR/validator.json SOLANA_LEADER_CONFIG_DIR=$SOLANA_CONFIG_VALIDATOR_DIR/leader-config else @@ -78,6 +79,22 @@ else SOLANA_LEADER_CONFIG_DIR=$SOLANA_CONFIG_VALIDATOR_DIR/leader-config-x$$ fi +[[ -r $validator_id_path ]] || { + echo "$validator_id_path does not exist" + exit 1 +} + +# A fullnode requires 2 tokens to function: +# - one token to create an instance of the vote_program with +# - one second token to keep the node identity public key valid. +( + set -x + $solana_wallet \ + --keypair "$validator_id_path" \ + --network "$leader_address" \ + airdrop 2 +) + rsync_leader_url=$(rsync_url "$leader") tune_networking diff --git a/src/bin/fullnode-config.rs b/src/bin/fullnode-config.rs index 9bd6961914..26ad46e352 100644 --- a/src/bin/fullnode-config.rs +++ b/src/bin/fullnode-config.rs @@ -1,10 +1,13 @@ #[macro_use] extern crate clap; extern crate dirs; +extern crate ring; extern crate serde_json; extern crate solana; use clap::{App, Arg}; +use ring::rand::SystemRandom; +use ring::signature::Ed25519KeyPair; use solana::cluster_info::FULLNODE_PORT_RANGE; use solana::fullnode::Config; use solana::logger; @@ -67,9 +70,12 @@ fn main() { }; let pkcs8 = read_pkcs8(id_path).expect("client keypair"); + let rnd = SystemRandom::new(); + let vote_account_pkcs8 = Ed25519KeyPair::generate_pkcs8(&rnd).unwrap(); + // we need all the receiving sockets to be bound within the expected // port range that we open on aws - let config = Config::new(&bind_addr, pkcs8); + let config = Config::new(&bind_addr, pkcs8, vote_account_pkcs8.to_vec()); let stdout = io::stdout(); serde_json::to_writer(stdout, &config).expect("serialize"); } diff --git a/src/bin/fullnode.rs b/src/bin/fullnode.rs index a9b96c561c..f1e6abed0f 100644 --- a/src/bin/fullnode.rs +++ b/src/bin/fullnode.rs @@ -10,7 +10,6 @@ extern crate solana; use clap::{App, Arg}; use solana::client::mk_client; use solana::cluster_info::{Node, FULLNODE_PORT_RANGE}; -use solana::drone::{request_airdrop_transaction, DRONE_PORT}; use solana::fullnode::{Config, Fullnode, FullnodeReturnType}; use solana::leader_scheduler::LeaderScheduler; use solana::logger; @@ -19,6 +18,7 @@ use solana::netutil::find_available_port_in_range; use solana::signature::{Keypair, KeypairUtil}; use solana::thin_client::poll_gossip_for_leader; use solana::vote_program::VoteProgram; +use solana::vote_transaction::VoteTransaction; use std::fs::File; use std::net::{Ipv4Addr, SocketAddr}; use std::process::exit; @@ -61,12 +61,16 @@ fn main() { .help("Custom RPC port for this node"), ).get_matches(); - let (keypair, ncp) = if let Some(i) = matches.value_of("identity") { + let (keypair, vote_account_keypair, ncp) = 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 = serde_json::from_reader(file); if let Ok(data) = parse { - (data.keypair(), data.node_info.ncp) + ( + data.keypair(), + data.vote_account_keypair(), + data.node_info.ncp, + ) } else { eprintln!("failed to parse {}", path); exit(1); @@ -76,7 +80,7 @@ fn main() { exit(1); } } else { - (Keypair::new(), socketaddr!(0, 8000)) + (Keypair::new(), Keypair::new(), socketaddr!(0, 8000)) }; let ledger_path = matches.value_of("ledger").unwrap(); @@ -91,7 +95,7 @@ fn main() { // save off some stuff for airdrop let mut node_info = node.info.clone(); - let vote_account_keypair = Arc::new(Keypair::new()); + let vote_account_keypair = Arc::new(vote_account_keypair); let vote_account_id = vote_account_keypair.pubkey(); let keypair = Arc::new(keypair); let pubkey = keypair.pubkey(); @@ -133,7 +137,7 @@ fn main() { node, ledger_path, keypair.clone(), - vote_account_keypair, + vote_account_keypair.clone(), network, false, leader_scheduler, @@ -141,66 +145,43 @@ fn main() { ); let mut client = mk_client(&leader); - // TODO: maybe have the drone put itself in gossip somewhere instead of hardcoding? - let drone_addr = match network { - Some(network) => SocketAddr::new(network.ip(), DRONE_PORT), - None => SocketAddr::new(ncp.ip(), DRONE_PORT), - }; - - loop { - let balance = client.poll_get_balance(&pubkey).unwrap_or(0); - info!("balance is {}", balance); - - if balance >= 50 { - info!("good to go!"); - break; - } - - info!("requesting airdrop from {}", drone_addr); - loop { - let last_id = client.get_last_id(); - if let Ok(transaction) = request_airdrop_transaction(&drone_addr, &pubkey, 50, last_id) - { - let signature = client.transfer_signed(&transaction).unwrap(); - if client.poll_for_signature(&signature).is_ok() { - break; - } - } - info!( - "airdrop request, is the drone address correct {:?}, drone running?", - drone_addr - ); - sleep(Duration::from_secs(2)); - } + let balance = client.poll_get_balance(&pubkey).unwrap_or(0); + info!("balance is {}", balance); + if balance < 1 { + error!("insufficient tokens"); + exit(1); } - // Create the vote account - loop { - let last_id = client.get_last_id(); - if client - .create_vote_account(&keypair, vote_account_id, &last_id, 1) - .is_err() - { + // Create the vote account if necessary + if client.poll_get_balance(&vote_account_id).unwrap_or(0) == 0 { + // Need at least two tokens as one token will be spent on a vote_account_new() transaction + if balance < 2 { + error!("insufficient tokens"); + exit(1); + } + loop { + let last_id = client.get_last_id(); + let transaction = + VoteTransaction::vote_account_new(&keypair, vote_account_id, last_id, 1); + if client.transfer_signed(&transaction).is_err() { + sleep(Duration::from_secs(2)); + continue; + } + + let balance = client.poll_get_balance(&vote_account_id).unwrap_or(0); + if balance > 0 { + break; + } sleep(Duration::from_secs(2)); - continue; } - - let balance = client.poll_get_balance(&vote_account_id).unwrap_or(0); - - if balance > 0 { - break; - } - - sleep(Duration::from_secs(2)); } // Register the vote account to this node loop { let last_id = client.get_last_id(); - if client - .register_vote_account(&keypair, vote_account_id, &last_id) - .is_err() - { + let transaction = + VoteTransaction::vote_account_register(&keypair, vote_account_id, last_id, 0); + if client.transfer_signed(&transaction).is_err() { sleep(Duration::from_secs(2)); continue; } diff --git a/src/bin/genesis.rs b/src/bin/genesis.rs index a60dff0e70..e299b94ae4 100644 --- a/src/bin/genesis.rs +++ b/src/bin/genesis.rs @@ -16,6 +16,13 @@ use std::error; use std::fs::File; use std::path::Path; +/** + * Bootstrap leader gets two tokens: + * - one token to create an instance of the vote_program with + * - one second token to keep the node identity public key valid + */ +pub const BOOTSTRAP_LEADER_TOKENS: u64 = 2; + fn main() -> Result<(), Box> { let matches = App::new("solana-genesis") .version(crate_version!()) @@ -62,7 +69,12 @@ fn main() -> Result<(), Box> { let num_tokens = value_t_or_exit!(matches, "num_tokens", u64); let file = File::open(Path::new(&matches.value_of("mint").unwrap())).unwrap(); let pkcs8: Vec = serde_json::from_reader(&file)?; - let mint = Mint::new_with_pkcs8(num_tokens, pkcs8, leader_keypair.pubkey(), 1); + let mint = Mint::new_with_pkcs8( + num_tokens, + pkcs8, + leader_keypair.pubkey(), + BOOTSTRAP_LEADER_TOKENS, + ); // Write the ledger entries let ledger_path = matches.value_of("ledger").unwrap(); diff --git a/src/fullnode.rs b/src/fullnode.rs index 20a17e9632..59251a16f2 100644 --- a/src/fullnode.rs +++ b/src/fullnode.rs @@ -110,20 +110,29 @@ pub struct Fullnode { pub struct Config { pub node_info: NodeInfo, pkcs8: Vec, + vote_account_pkcs8: Vec, } impl Config { - pub fn new(bind_addr: &SocketAddr, pkcs8: Vec) -> Self { + pub fn new(bind_addr: &SocketAddr, pkcs8: Vec, vote_account_pkcs8: Vec) -> Self { let keypair = Keypair::from_pkcs8(Input::from(&pkcs8)).expect("from_pkcs8 in fullnode::Config new"); let pubkey = keypair.pubkey(); let node_info = NodeInfo::new_with_pubkey_socketaddr(pubkey, bind_addr); - Config { node_info, pkcs8 } + Config { + node_info, + pkcs8, + vote_account_pkcs8, + } } pub fn keypair(&self) -> Keypair { Keypair::from_pkcs8(Input::from(&self.pkcs8)) .expect("from_pkcs8 in fullnode::Config keypair") } + pub fn vote_account_keypair(&self) -> Keypair { + Keypair::from_pkcs8(Input::from(&self.vote_account_pkcs8)) + .expect("from_pkcs8 in fullnode::Config vote_account_keypair") + } } impl Fullnode { diff --git a/src/thin_client.rs b/src/thin_client.rs index ce95da68f6..7665ff496f 100644 --- a/src/thin_client.rs +++ b/src/thin_client.rs @@ -29,7 +29,6 @@ use std::time::Instant; use system_transaction::SystemTransaction; use timing; use transaction::Transaction; -use vote_transaction::VoteTransaction; use influx_db_client as influxdb; use metrics; @@ -99,29 +98,6 @@ impl ThinClient { )) } - pub fn create_vote_account( - &self, - node_keypair: &Keypair, - vote_account_id: Pubkey, - last_id: &Hash, - num_tokens: u64, - ) -> io::Result { - let tx = - Transaction::vote_account_new(&node_keypair, vote_account_id, *last_id, num_tokens); - self.transfer_signed(&tx) - } - - /// Creates, signs, and processes a vote Transaction. Useful for writing unit-tests. - pub fn register_vote_account( - &self, - node_keypair: &Keypair, - vote_account_id: Pubkey, - last_id: &Hash, - ) -> io::Result { - let tx = Transaction::vote_account_register(node_keypair, vote_account_id, *last_id, 0); - self.transfer_signed(&tx) - } - /// Creates, signs, and processes a Transaction. Useful for writing unit-tests. pub fn transfer( &self, @@ -445,6 +421,7 @@ mod tests { use std::fs::remove_dir_all; use system_program::SystemProgram; use vote_program::VoteProgram; + use vote_transaction::VoteTransaction; #[test] fn test_thin_client() { @@ -650,20 +627,21 @@ mod tests { let validator_vote_account_keypair = Keypair::new(); let vote_account_id = validator_vote_account_keypair.pubkey(); let last_id = client.get_last_id(); - let signature = client - .create_vote_account(&validator_keypair, vote_account_id, &last_id, 1) - .unwrap(); + let transaction = + VoteTransaction::vote_account_new(&validator_keypair, vote_account_id, last_id, 1); + let signature = client.transfer_signed(&transaction).unwrap(); assert!(client.poll_for_signature(&signature).is_ok()); + let balance = retry_get_balance(&mut client, &vote_account_id, Some(1)) .expect("Expected balance for new account to exist"); assert_eq!(balance, 1); // Register the vote account to the validator let last_id = client.get_last_id(); - let signature = client - .register_vote_account(&validator_keypair, vote_account_id, &last_id) - .unwrap(); + let transaction = + VoteTransaction::vote_account_register(&validator_keypair, vote_account_id, last_id, 0); + let signature = client.transfer_signed(&transaction).unwrap(); assert!(client.poll_for_signature(&signature).is_ok()); const LAST: usize = 30;