From d8d8f0bfc874c5c47da08d23136e3ca62ed8615c Mon Sep 17 00:00:00 2001 From: anatoly yakovenko Date: Fri, 5 Oct 2018 16:45:27 -0700 Subject: [PATCH] Fund all the keys with move many transactions (#1436) * Fund all the keys with move many transactions * logs --- src/bin/bench-tps.rs | 96 ++++++++++++++++++++++++++++++++------- src/system_transaction.rs | 31 ++++++++++++- 2 files changed, 110 insertions(+), 17 deletions(-) diff --git a/src/bin/bench-tps.rs b/src/bin/bench-tps.rs index 97fb238c71..e10bd19bda 100644 --- a/src/bin/bench-tps.rs +++ b/src/bin/bench-tps.rs @@ -25,6 +25,7 @@ use solana::timing::{duration_as_ms, duration_as_s}; use solana::transaction::Transaction; use solana::wallet::request_airdrop; use solana::window::default_window; +use std::cmp; use std::collections::VecDeque; use std::net::SocketAddr; use std::process::exit; @@ -174,21 +175,22 @@ fn send_barrier_transaction(barrier_client: &mut ThinClient, last_id: &mut Hash, fn generate_txs( shared_txs: &Arc>>>, - id: &Keypair, - keypairs: &[Keypair], + source: &[Keypair], + dest: &[Keypair], last_id: &Hash, threads: usize, reclaim: bool, ) { - let tx_count = keypairs.len(); + let tx_count = source.len(); println!("Signing transactions... {} (reclaim={})", tx_count, reclaim); let signing_start = Instant::now(); - let transactions: Vec<_> = keypairs + let pairs: Vec<_> = source.iter().zip(dest.iter()).collect(); + let transactions: Vec<_> = pairs .par_iter() - .map(|keypair| { + .map(|(id, keypair)| { if !reclaim { - Transaction::system_new(&id, keypair.pubkey(), 1, *last_id) + Transaction::system_new(id, keypair.pubkey(), 1, *last_id) } else { Transaction::system_new(keypair, id.pubkey(), 1, *last_id) } @@ -272,6 +274,61 @@ fn do_tx_transfers( } } +fn split_tokens(tokens: i64, per_unit: i64, max_units: usize) -> (usize, i64) { + let total_blocks = tokens / per_unit; + let max_keys_to_fund = cmp::min(total_blocks - 1, max_units as i64); + let blocks_per_unit = total_blocks / (max_keys_to_fund + 1); + (max_keys_to_fund as usize, blocks_per_unit * per_unit) +} + +fn fund_keys(client: &mut ThinClient, source: &Keypair, dests: &[Keypair], tokens: i64) { + let max_per_move = 5; + let total = tokens * (dests.len() as i64 + 1); + let mut funded: Vec<(&Keypair, i64)> = vec![(source, total)]; + let mut notfunded: Vec<&Keypair> = dests.iter().collect(); + println!("funding keys {}", dests.len()); + while !notfunded.is_empty() { + let last_id = client.get_last_id(); + let mut new_funded: Vec<(&Keypair, i64)> = vec![]; + let mut to_fund = vec![]; + println!("creating from... {}", funded.len()); + for f in &mut funded { + let max_units = cmp::min(notfunded.len(), max_per_move); + let (num, per_unit) = split_tokens(f.1, tokens, max_units); + let start = notfunded.len() - num; + let moves: Vec<_> = notfunded[start..] + .iter() + .map(|k| (k.pubkey(), per_unit)) + .collect(); + notfunded[start..] + .iter() + .for_each(|k| new_funded.push((k, per_unit))); + notfunded.truncate(start); + if !moves.is_empty() { + to_fund.push((f.0, moves)); + } + f.1 -= per_unit * (num as i64); + assert!(f.1 >= per_unit); + } + println!("generating... {}", to_fund.len()); + let to_fund_txs: Vec<_> = to_fund + .par_iter() + .map(|(k, m)| Transaction::system_move_many(k, &m, last_id, 0)) + .collect(); + println!("transfering... {}", to_fund.len()); + to_fund_txs.iter().for_each(|tx| { + let _ = client.transfer_signed(&tx).expect("transfer"); + }); + println!( + "funded {} total: {} left: {}", + new_funded.len(), + funded.len() + new_funded.len(), + notfunded.len() + ); + funded.append(&mut new_funded); + } +} + fn airdrop_tokens(client: &mut ThinClient, leader: &NodeInfo, id: &Keypair, tx_count: i64) { let mut drone_addr = leader.contact_info.tpu; drone_addr.set_port(DRONE_PORT); @@ -542,8 +599,8 @@ fn main() { seed.copy_from_slice(&id.public_key_bytes()[..32]); let mut rnd = GenKeys::new(seed); - println!("Creating {} keypairs...", tx_count / 2); - let keypairs = rnd.gen_n_keypairs(tx_count / 2); + println!("Creating {} keypairs...", tx_count * 2); + let keypairs = rnd.gen_n_keypairs(tx_count * 2); let barrier_id = rnd.gen_n_keypairs(1).pop().unwrap(); println!("Get tokens..."); @@ -554,12 +611,9 @@ fn main() { let keypair0_balance = client.poll_get_balance(&keypairs[0].pubkey()).unwrap_or(0); if num_tokens_per_account > keypair0_balance { - airdrop_tokens( - &mut client, - &leader, - &id, - (num_tokens_per_account - keypair0_balance) * tx_count, - ); + let extra = (num_tokens_per_account - keypair0_balance) * (keypairs.len() as i64); + airdrop_tokens(&mut client, &leader, &id, extra); + fund_keys(&mut client, &id, &keypairs, num_tokens_per_account); } airdrop_tokens(&mut barrier_client, &leader, &barrier_id, 1); @@ -624,10 +678,11 @@ fn main() { // ping-pong between source and destination accounts for each loop iteration // this seems to be faster than trying to determine the balance of individual // accounts + let len = tx_count as usize; generate_txs( &shared_txs, - &id, - &keypairs, + &keypairs[..len], + &keypairs[len..], &last_id, threads, reclaim_tokens_back_to_source_account, @@ -733,6 +788,15 @@ fn converge( #[cfg(test)] mod tests { use super::*; + #[test] + fn test_split_tokens() { + assert_eq!(split_tokens(3, 2, 5), (0, 2)); + assert_eq!(split_tokens(4, 2, 5), (1, 2)); + assert_eq!(split_tokens(5, 2, 5), (1, 2)); + assert_eq!(split_tokens(6, 2, 5), (2, 2)); + assert_eq!(split_tokens(20, 2, 5), (5, 2)); + assert_eq!(split_tokens(30, 2, 5), (5, 4)); + } #[test] fn test_switch_directions() { diff --git a/src/system_transaction.rs b/src/system_transaction.rs index 2e3135a9f3..2e71966424 100644 --- a/src/system_transaction.rs +++ b/src/system_transaction.rs @@ -5,7 +5,7 @@ use hash::Hash; use signature::{Keypair, KeypairUtil}; use solana_program_interface::pubkey::Pubkey; use system_program::SystemProgram; -use transaction::Transaction; +use transaction::{Instruction, Transaction}; pub trait SystemTransaction { fn system_create( @@ -37,6 +37,12 @@ pub trait SystemTransaction { program_id: Pubkey, name: String, ) -> Self; + fn system_move_many( + from_keypair: &Keypair, + moves: &[(Pubkey, i64)], + last_id: Hash, + fee: i64, + ) -> Self; } impl SystemTransaction for Transaction { @@ -120,6 +126,29 @@ impl SystemTransaction for Transaction { fee, ) } + fn system_move_many(from: &Keypair, moves: &[(Pubkey, i64)], last_id: Hash, fee: i64) -> Self { + let instructions: Vec<_> = moves + .iter() + .enumerate() + .map(|(i, (_, amount))| { + let spend = SystemProgram::Move { tokens: *amount }; + Instruction { + program_ids_index: 0, + userdata: serialize(&spend).unwrap(), + accounts: vec![0, i as u8], + } + }).collect(); + let to_keys: Vec<_> = moves.iter().map(|(to_key, _)| *to_key).collect(); + + Transaction::new_with_instructions( + from, + &to_keys, + last_id, + fee, + vec![SystemProgram::id()], + instructions, + ) + } } pub fn test_tx() -> Transaction {