From 4481efd51e8eb8e5f5155dc8ffd3a2cbefbc3d74 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Mon, 10 Dec 2018 12:20:55 -0700 Subject: [PATCH] Merge pull request #2084 from CriesofCarrots/fix-wallet-accountinuse Fix wallet accountinuse --- src/wallet.rs | 152 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 124 insertions(+), 28 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 2f6bf6a35..c609c2f72 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -340,10 +340,8 @@ pub fn process_command(config: &WalletConfig) -> Result Result Result Result Result Result<(), Box> { let mut send_retries = 3; while send_retries > 0 { @@ -749,6 +748,64 @@ fn send_and_confirm_tx( }; match status { RpcSignatureStatus::AccountInUse => { + resign_tx(rpc_client, tx, signer)?; + send_retries -= 1; + } + RpcSignatureStatus::Confirmed => { + return Ok(()); + } + _ => { + return Err(WalletError::RpcRequestError(format!( + "Transaction {:?} failed: {:?}", + signature_str, status + )))?; + } + } + } + Err(WalletError::RpcRequestError(format!( + "AccountInUse after 3 retries: {:?}", + tx.account_keys[0] + )))? +} + +fn resign_tx( + rpc_client: &RpcClient, + tx: &mut Transaction, + signer_key: &Keypair, +) -> Result<(), Box> { + let last_id = get_last_id(rpc_client)?; + tx.sign(&[signer_key], last_id); + Ok(()) +} + +fn request_and_confirm_airdrop( + rpc_client: &RpcClient, + drone_addr: &SocketAddr, + id: &Pubkey, + tokens: u64, +) -> Result<(), Box> { + let mut last_id = get_last_id(rpc_client)?; + let mut tx = request_airdrop_transaction(drone_addr, id, tokens, last_id)?; + let mut send_retries = 3; + while send_retries > 0 { + let mut status_retries = 4; + let signature_str = send_tx(rpc_client, &tx)?; + let status = loop { + let status = confirm_tx(rpc_client, &signature_str)?; + if status == RpcSignatureStatus::SignatureNotFound { + status_retries -= 1; + if status_retries == 0 { + break status; + } + } else { + break status; + } + sleep(Duration::from_secs(1)); + }; + match status { + RpcSignatureStatus::AccountInUse => { + last_id = get_last_id(rpc_client)?; + tx = request_airdrop_transaction(drone_addr, id, tokens, last_id)?; send_retries -= 1; } RpcSignatureStatus::Confirmed => { @@ -786,6 +843,54 @@ mod tests { use std::thread::sleep; use std::time::Duration; + #[test] + fn test_resign_tx() { + let leader_keypair = Arc::new(Keypair::new()); + let leader = Node::new_localhost_with_pubkey(leader_keypair.pubkey()); + let leader_data = leader.info.clone(); + let (alice, ledger_path) = + create_tmp_genesis("wallet_request_airdrop", 10_000_000, leader_data.id, 1000); + let mut bank = Bank::new(&alice); + + let leader_scheduler = Arc::new(RwLock::new(LeaderScheduler::from_bootstrap_leader( + leader_data.id, + ))); + bank.leader_scheduler = leader_scheduler; + let vote_account_keypair = Arc::new(Keypair::new()); + let last_id = bank.last_id(); + let entry_height = alice.create_entries().len() as u64; + let _server = Fullnode::new_with_bank( + leader_keypair, + vote_account_keypair, + bank, + entry_height, + &last_id, + leader, + None, + &ledger_path, + false, + None, + ); + sleep(Duration::from_millis(900)); + + let rpc_client = RpcClient::new_from_socket(leader_data.rpc); + + let key = Keypair::new(); + let to = Keypair::new().pubkey(); + let last_id = Hash::default(); + let prev_tx = Transaction::system_new(&key, to, 50, last_id); + let mut tx = Transaction::system_new(&key, to, 50, last_id); + + resign_tx(&rpc_client, &mut tx, &key).unwrap(); + + assert_ne!(prev_tx, tx); + assert_ne!(prev_tx.signatures, tx.signatures); + assert_ne!(prev_tx.last_id, tx.last_id); + assert_eq!(prev_tx.fee, tx.fee); + assert_eq!(prev_tx.account_keys, tx.account_keys); + assert_eq!(prev_tx.instructions, tx.instructions); + } + #[test] fn test_wallet_parse_command() { let test_commands = App::new("test") @@ -1314,11 +1419,8 @@ mod tests { assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey()); - let last_id = get_last_id(&rpc_client).unwrap(); - let transaction = - request_airdrop_transaction(&drone_addr, &config_payer.id.pubkey(), 50, last_id) - .unwrap(); - send_and_confirm_tx(&rpc_client, &transaction).unwrap(); + request_and_confirm_airdrop(&rpc_client, &drone_addr, &config_payer.id.pubkey(), 50) + .unwrap(); // Make transaction (from config_payer to bob_pubkey) requiring timestamp from config_witness let date_string = "\"2018-09-19T17:30:59Z\""; @@ -1434,11 +1536,8 @@ mod tests { assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey()); - let last_id = get_last_id(&rpc_client).unwrap(); - let transaction = - request_airdrop_transaction(&drone_addr, &config_payer.id.pubkey(), 50, last_id) - .unwrap(); - send_and_confirm_tx(&rpc_client, &transaction).unwrap(); + request_and_confirm_airdrop(&rpc_client, &drone_addr, &config_payer.id.pubkey(), 50) + .unwrap(); // Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness config_payer.command = WalletCommand::Pay( @@ -1559,11 +1658,8 @@ mod tests { assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey()); - let last_id = get_last_id(&rpc_client).unwrap(); - let transaction = - request_airdrop_transaction(&drone_addr, &config_payer.id.pubkey(), 50, last_id) - .unwrap(); - send_and_confirm_tx(&rpc_client, &transaction).unwrap(); + request_and_confirm_airdrop(&rpc_client, &drone_addr, &config_payer.id.pubkey(), 50) + .unwrap(); // Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness config_payer.command = WalletCommand::Pay(