Add retries to Wallet deploy

This commit is contained in:
Tyera Eulberg 2018-10-24 10:01:19 -06:00 committed by Tyera Eulberg
parent d5f20980eb
commit 2f2531d921
3 changed files with 102 additions and 53 deletions

View File

@ -14,6 +14,7 @@ use solana_program_interface::pubkey::Pubkey;
use std::mem; use std::mem;
use std::net::{SocketAddr, UdpSocket}; use std::net::{SocketAddr, UdpSocket};
use std::result; use std::result;
use std::str::FromStr;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use std::thread::{self, sleep, Builder, JoinHandle}; use std::thread::{self, sleep, Builder, JoinHandle};
@ -96,6 +97,20 @@ pub enum RpcSignatureStatus {
ProgramRuntimeError, ProgramRuntimeError,
SignatureNotFound, SignatureNotFound,
} }
impl FromStr for RpcSignatureStatus {
type Err = Error;
fn from_str(s: &str) -> Result<RpcSignatureStatus> {
match s {
"AccountInUse" => Ok(RpcSignatureStatus::AccountInUse),
"Confirmed" => Ok(RpcSignatureStatus::Confirmed),
"GenericFailure" => Ok(RpcSignatureStatus::GenericFailure),
"ProgramRuntimeError" => Ok(RpcSignatureStatus::ProgramRuntimeError),
"SignatureNotFound" => Ok(RpcSignatureStatus::SignatureNotFound),
_ => Err(Error::parse_error()),
}
}
}
build_rpc_trait! { build_rpc_trait! {
pub trait RpcSol { pub trait RpcSol {

View File

@ -9,6 +9,7 @@ pub enum RpcRequest {
GetBalance, GetBalance,
GetFinality, GetFinality,
GetLastId, GetLastId,
GetSignatureStatus,
GetTransactionCount, GetTransactionCount,
RequestAirdrop, RequestAirdrop,
SendTransaction, SendTransaction,
@ -44,6 +45,7 @@ impl RpcRequest {
RpcRequest::GetBalance => "getBalance", RpcRequest::GetBalance => "getBalance",
RpcRequest::GetFinality => "getFinality", RpcRequest::GetFinality => "getFinality",
RpcRequest::GetLastId => "getLastId", RpcRequest::GetLastId => "getLastId",
RpcRequest::GetSignatureStatus => "getSignatureStatus",
RpcRequest::GetTransactionCount => "getTransactionCount", RpcRequest::GetTransactionCount => "getTransactionCount",
RpcRequest::RequestAirdrop => "requestAirdrop", RpcRequest::RequestAirdrop => "requestAirdrop",
RpcRequest::SendTransaction => "sendTransaction", RpcRequest::SendTransaction => "sendTransaction",

View File

@ -13,6 +13,7 @@ use hash::Hash;
use loader_transaction::LoaderTransaction; use loader_transaction::LoaderTransaction;
use ring::rand::SystemRandom; use ring::rand::SystemRandom;
use ring::signature::Ed25519KeyPair; use ring::signature::Ed25519KeyPair;
use rpc::RpcSignatureStatus;
use rpc_request::RpcRequest; use rpc_request::RpcRequest;
use serde_json; use serde_json;
use signature::{Keypair, KeypairUtil, Signature}; use signature::{Keypair, KeypairUtil, Signature};
@ -23,6 +24,7 @@ use std::io::{Error, ErrorKind, Write};
use std::mem::size_of; use std::mem::size_of;
use std::net::{Ipv4Addr, SocketAddr, TcpStream}; use std::net::{Ipv4Addr, SocketAddr, TcpStream};
use std::path::Path; use std::path::Path;
use std::str::FromStr;
use std::thread::sleep; use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use std::{error, fmt, mem}; use std::{error, fmt, mem};
@ -412,17 +414,12 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
bpf_loader::id(), bpf_loader::id(),
0, 0,
); );
let signature_str = serialize_and_send_tx(&config, &tx)?; send_and_confirm_tx(&config, &tx).map_err(|_| {
if !confirm_tx(&config, &signature_str)? { WalletError::DynamicProgramError("Program allocate space failed".to_string())
Err(WalletError::DynamicProgramError( })?;
"Program allocate space failed".to_string(),
))?
}
let mut offset = 0; let mut offset = 0;
let write_result = program_userdata for chunk in program_userdata.chunks(USERDATA_CHUNK_SIZE) {
.chunks(USERDATA_CHUNK_SIZE)
.map(|chunk| {
let tx = Transaction::write( let tx = Transaction::write(
&program, &program,
bpf_loader::id(), bpf_loader::id(),
@ -431,38 +428,29 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
last_id, last_id,
0, 0,
); );
let signature_str = serialize_and_send_tx(&config, &tx).unwrap(); send_and_confirm_tx(&config, &tx).map_err(|_| {
WalletError::DynamicProgramError(format!(
"Program write failed at offset {:?}",
offset
))
})?;
offset += USERDATA_CHUNK_SIZE as u32; offset += USERDATA_CHUNK_SIZE as u32;
signature_str }
}).map(|signature| confirm_tx(&config, &signature).unwrap())
.all(|status| status);
if write_result {
let last_id = get_last_id(&config)?; let last_id = get_last_id(&config)?;
let tx = Transaction::finalize(&program, bpf_loader::id(), last_id, 0); let tx = Transaction::finalize(&program, bpf_loader::id(), last_id, 0);
let signature_str = serialize_and_send_tx(&config, &tx)?; send_and_confirm_tx(&config, &tx).map_err(|_| {
if !confirm_tx(&config, &signature_str)? { WalletError::DynamicProgramError("Program finalize transaction failed".to_string())
Err(WalletError::DynamicProgramError( })?;
"Program finalize transaction failed".to_string(),
))?
}
let tx = Transaction::system_spawn(&program, last_id, 0); let tx = Transaction::system_spawn(&program, last_id, 0);
let signature_str = serialize_and_send_tx(&config, &tx)?; send_and_confirm_tx(&config, &tx).map_err(|_| {
if !confirm_tx(&config, &signature_str)? { WalletError::DynamicProgramError("Program spawn failed".to_string())
Err(WalletError::DynamicProgramError( })?;
"Program spawn failed".to_string(),
))?
}
Ok(json!({ Ok(json!({
"programId": format!("{}", program.pubkey()), "programId": format!("{}", program.pubkey()),
}).to_string()) }).to_string())
} else {
Err(WalletError::DynamicProgramError(
"Program chunks write error".to_string(),
))?
}
} }
// If client has positive balance, pay tokens to another address // If client has positive balance, pay tokens to another address
WalletCommand::Pay(tokens, to, timestamp, timestamp_pubkey, ref witnesses, cancelable) => { WalletCommand::Pay(tokens, to, timestamp, timestamp_pubkey, ref witnesses, cancelable) => {
@ -709,16 +697,60 @@ fn serialize_and_send_tx(
Ok(signature.as_str().unwrap().to_string()) Ok(signature.as_str().unwrap().to_string())
} }
fn confirm_tx(config: &WalletConfig, signature: &str) -> Result<bool, Box<error::Error>> { fn confirm_tx(
config: &WalletConfig,
signature: &str,
) -> Result<RpcSignatureStatus, Box<error::Error>> {
let params = json!(signature.to_string()); let params = json!(signature.to_string());
let status = let signature_status =
RpcRequest::ConfirmTransaction.make_rpc_request(&config.rpc_addr, 1, Some(params))?; RpcRequest::GetSignatureStatus.make_rpc_request(&config.rpc_addr, 1, Some(params))?;
if status.as_bool().is_none() { if let Some(status) = signature_status.as_str() {
let rpc_status = RpcSignatureStatus::from_str(status).map_err(|_| {
WalletError::RpcRequestError("Unable to parse signature status".to_string())
})?;
Ok(rpc_status)
} else {
Err(WalletError::RpcRequestError( Err(WalletError::RpcRequestError(
"Received result of an unexpected type".to_string(), "Received result of an unexpected type".to_string(),
))? ))?
} }
Ok(status.as_bool().unwrap()) }
fn send_and_confirm_tx(config: &WalletConfig, tx: &Transaction) -> Result<(), Box<error::Error>> {
let mut send_retries = 3;
while send_retries > 0 {
let mut status_retries = 4;
let signature_str = serialize_and_send_tx(config, tx)?;
let status = loop {
let status = confirm_tx(config, &signature_str)?;
if status == RpcSignatureStatus::SignatureNotFound {
status_retries -= 1;
if status_retries == 0 {
break status;
}
} else {
break status;
}
};
match status {
RpcSignatureStatus::AccountInUse => {
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]
)))?
} }
#[cfg(test)] #[cfg(test)]