Merge pull request #2084 from CriesofCarrots/fix-wallet-accountinuse
Fix wallet accountinuse
This commit is contained in:
		
							
								
								
									
										146
									
								
								src/wallet.rs
									
									
									
									
									
								
							
							
						
						
									
										146
									
								
								src/wallet.rs
									
									
									
									
									
								
							| @@ -340,10 +340,8 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<dyn error::E | |||||||
|                 ))?, |                 ))?, | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|             let last_id = get_last_id(&rpc_client)?; |             request_and_confirm_airdrop(&rpc_client, &drone_addr, &config.id.pubkey(), tokens) | ||||||
|             let transaction = |                 .unwrap(); | ||||||
|                 request_airdrop_transaction(&drone_addr, &config.id.pubkey(), tokens, last_id)?; |  | ||||||
|             send_and_confirm_tx(&rpc_client, &transaction)?; |  | ||||||
|  |  | ||||||
|             let params = json!([format!("{}", config.id.pubkey())]); |             let params = json!([format!("{}", config.id.pubkey())]); | ||||||
|             let current_balance = RpcRequest::GetBalance |             let current_balance = RpcRequest::GetBalance | ||||||
| @@ -427,7 +425,7 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<dyn error::E | |||||||
|                 .data |                 .data | ||||||
|                 .clone(); |                 .clone(); | ||||||
|  |  | ||||||
|             let tx = Transaction::system_create( |             let mut tx = Transaction::system_create( | ||||||
|                 &config.id, |                 &config.id, | ||||||
|                 program.pubkey(), |                 program.pubkey(), | ||||||
|                 last_id, |                 last_id, | ||||||
| @@ -436,13 +434,13 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<dyn error::E | |||||||
|                 bpf_loader::id(), |                 bpf_loader::id(), | ||||||
|                 0, |                 0, | ||||||
|             ); |             ); | ||||||
|             send_and_confirm_tx(&rpc_client, &tx).map_err(|_| { |             send_and_confirm_tx(&rpc_client, &mut tx, &config.id).map_err(|_| { | ||||||
|                 WalletError::DynamicProgramError("Program allocate space failed".to_string()) |                 WalletError::DynamicProgramError("Program allocate space failed".to_string()) | ||||||
|             })?; |             })?; | ||||||
|  |  | ||||||
|             let mut offset = 0; |             let mut offset = 0; | ||||||
|             for chunk in program_userdata.chunks(USERDATA_CHUNK_SIZE) { |             for chunk in program_userdata.chunks(USERDATA_CHUNK_SIZE) { | ||||||
|                 let tx = Transaction::loader_write( |                 let mut tx = Transaction::loader_write( | ||||||
|                     &program, |                     &program, | ||||||
|                     bpf_loader::id(), |                     bpf_loader::id(), | ||||||
|                     offset, |                     offset, | ||||||
| @@ -450,7 +448,7 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<dyn error::E | |||||||
|                     last_id, |                     last_id, | ||||||
|                     0, |                     0, | ||||||
|                 ); |                 ); | ||||||
|                 send_and_confirm_tx(&rpc_client, &tx).map_err(|_| { |                 send_and_confirm_tx(&rpc_client, &mut tx, &program).map_err(|_| { | ||||||
|                     WalletError::DynamicProgramError(format!( |                     WalletError::DynamicProgramError(format!( | ||||||
|                         "Program write failed at offset {:?}", |                         "Program write failed at offset {:?}", | ||||||
|                         offset |                         offset | ||||||
| @@ -460,13 +458,13 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<dyn error::E | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             let last_id = get_last_id(&rpc_client)?; |             let last_id = get_last_id(&rpc_client)?; | ||||||
|             let tx = Transaction::loader_finalize(&program, bpf_loader::id(), last_id, 0); |             let mut tx = Transaction::loader_finalize(&program, bpf_loader::id(), last_id, 0); | ||||||
|             send_and_confirm_tx(&rpc_client, &tx).map_err(|_| { |             send_and_confirm_tx(&rpc_client, &mut tx, &program).map_err(|_| { | ||||||
|                 WalletError::DynamicProgramError("Program finalize transaction failed".to_string()) |                 WalletError::DynamicProgramError("Program finalize transaction failed".to_string()) | ||||||
|             })?; |             })?; | ||||||
|  |  | ||||||
|             let tx = Transaction::system_spawn(&program, last_id, 0); |             let mut tx = Transaction::system_spawn(&program, last_id, 0); | ||||||
|             send_and_confirm_tx(&rpc_client, &tx).map_err(|_| { |             send_and_confirm_tx(&rpc_client, &mut tx, &program).map_err(|_| { | ||||||
|                 WalletError::DynamicProgramError("Program spawn failed".to_string()) |                 WalletError::DynamicProgramError("Program spawn failed".to_string()) | ||||||
|             })?; |             })?; | ||||||
|  |  | ||||||
| @@ -729,7 +727,8 @@ fn confirm_tx( | |||||||
|  |  | ||||||
| fn send_and_confirm_tx( | fn send_and_confirm_tx( | ||||||
|     rpc_client: &RpcClient, |     rpc_client: &RpcClient, | ||||||
|     tx: &Transaction, |     tx: &mut Transaction, | ||||||
|  |     signer: &Keypair, | ||||||
| ) -> Result<(), Box<dyn error::Error>> { | ) -> Result<(), Box<dyn error::Error>> { | ||||||
|     let mut send_retries = 3; |     let mut send_retries = 3; | ||||||
|     while send_retries > 0 { |     while send_retries > 0 { | ||||||
| @@ -749,6 +748,64 @@ fn send_and_confirm_tx( | |||||||
|         }; |         }; | ||||||
|         match status { |         match status { | ||||||
|             RpcSignatureStatus::AccountInUse => { |             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<dyn error::Error>> { | ||||||
|  |     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<dyn error::Error>> { | ||||||
|  |     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; |                 send_retries -= 1; | ||||||
|             } |             } | ||||||
|             RpcSignatureStatus::Confirmed => { |             RpcSignatureStatus::Confirmed => { | ||||||
| @@ -786,6 +843,54 @@ mod tests { | |||||||
|     use std::thread::sleep; |     use std::thread::sleep; | ||||||
|     use std::time::Duration; |     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] |     #[test] | ||||||
|     fn test_wallet_parse_command() { |     fn test_wallet_parse_command() { | ||||||
|         let test_commands = App::new("test") |         let test_commands = App::new("test") | ||||||
| @@ -1314,11 +1419,8 @@ mod tests { | |||||||
|  |  | ||||||
|         assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey()); |         assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey()); | ||||||
|  |  | ||||||
|         let last_id = get_last_id(&rpc_client).unwrap(); |         request_and_confirm_airdrop(&rpc_client, &drone_addr, &config_payer.id.pubkey(), 50) | ||||||
|         let transaction = |  | ||||||
|             request_airdrop_transaction(&drone_addr, &config_payer.id.pubkey(), 50, last_id) |  | ||||||
|             .unwrap(); |             .unwrap(); | ||||||
|         send_and_confirm_tx(&rpc_client, &transaction).unwrap(); |  | ||||||
|  |  | ||||||
|         // Make transaction (from config_payer to bob_pubkey) requiring timestamp from config_witness |         // Make transaction (from config_payer to bob_pubkey) requiring timestamp from config_witness | ||||||
|         let date_string = "\"2018-09-19T17:30:59Z\""; |         let date_string = "\"2018-09-19T17:30:59Z\""; | ||||||
| @@ -1434,11 +1536,8 @@ mod tests { | |||||||
|  |  | ||||||
|         assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey()); |         assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey()); | ||||||
|  |  | ||||||
|         let last_id = get_last_id(&rpc_client).unwrap(); |         request_and_confirm_airdrop(&rpc_client, &drone_addr, &config_payer.id.pubkey(), 50) | ||||||
|         let transaction = |  | ||||||
|             request_airdrop_transaction(&drone_addr, &config_payer.id.pubkey(), 50, last_id) |  | ||||||
|             .unwrap(); |             .unwrap(); | ||||||
|         send_and_confirm_tx(&rpc_client, &transaction).unwrap(); |  | ||||||
|  |  | ||||||
|         // Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness |         // Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness | ||||||
|         config_payer.command = WalletCommand::Pay( |         config_payer.command = WalletCommand::Pay( | ||||||
| @@ -1559,11 +1658,8 @@ mod tests { | |||||||
|  |  | ||||||
|         assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey()); |         assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey()); | ||||||
|  |  | ||||||
|         let last_id = get_last_id(&rpc_client).unwrap(); |         request_and_confirm_airdrop(&rpc_client, &drone_addr, &config_payer.id.pubkey(), 50) | ||||||
|         let transaction = |  | ||||||
|             request_airdrop_transaction(&drone_addr, &config_payer.id.pubkey(), 50, last_id) |  | ||||||
|             .unwrap(); |             .unwrap(); | ||||||
|         send_and_confirm_tx(&rpc_client, &transaction).unwrap(); |  | ||||||
|  |  | ||||||
|         // Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness |         // Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness | ||||||
|         config_payer.command = WalletCommand::Pay( |         config_payer.command = WalletCommand::Pay( | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user