Add retries to Wallet deploy
This commit is contained in:
parent
d5f20980eb
commit
2f2531d921
15
src/rpc.rs
15
src/rpc.rs
|
@ -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 {
|
||||||
|
|
|
@ -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",
|
||||||
|
|
138
src/wallet.rs
138
src/wallet.rs
|
@ -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,57 +414,43 @@ 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)
|
let tx = Transaction::write(
|
||||||
.map(|chunk| {
|
&program,
|
||||||
let tx = Transaction::write(
|
bpf_loader::id(),
|
||||||
&program,
|
offset,
|
||||||
bpf_loader::id(),
|
chunk.to_vec(),
|
||||||
offset,
|
last_id,
|
||||||
chunk.to_vec(),
|
0,
|
||||||
last_id,
|
);
|
||||||
0,
|
send_and_confirm_tx(&config, &tx).map_err(|_| {
|
||||||
);
|
WalletError::DynamicProgramError(format!(
|
||||||
let signature_str = serialize_and_send_tx(&config, &tx).unwrap();
|
"Program write failed at offset {:?}",
|
||||||
offset += USERDATA_CHUNK_SIZE as u32;
|
offset
|
||||||
signature_str
|
))
|
||||||
}).map(|signature| confirm_tx(&config, &signature).unwrap())
|
})?;
|
||||||
.all(|status| status);
|
offset += USERDATA_CHUNK_SIZE as u32;
|
||||||
|
|
||||||
if write_result {
|
|
||||||
let last_id = get_last_id(&config)?;
|
|
||||||
let tx = Transaction::finalize(&program, bpf_loader::id(), last_id, 0);
|
|
||||||
let signature_str = serialize_and_send_tx(&config, &tx)?;
|
|
||||||
if !confirm_tx(&config, &signature_str)? {
|
|
||||||
Err(WalletError::DynamicProgramError(
|
|
||||||
"Program finalize transaction failed".to_string(),
|
|
||||||
))?
|
|
||||||
}
|
|
||||||
|
|
||||||
let tx = Transaction::system_spawn(&program, last_id, 0);
|
|
||||||
let signature_str = serialize_and_send_tx(&config, &tx)?;
|
|
||||||
if !confirm_tx(&config, &signature_str)? {
|
|
||||||
Err(WalletError::DynamicProgramError(
|
|
||||||
"Program spawn failed".to_string(),
|
|
||||||
))?
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(json!({
|
|
||||||
"programId": format!("{}", program.pubkey()),
|
|
||||||
}).to_string())
|
|
||||||
} else {
|
|
||||||
Err(WalletError::DynamicProgramError(
|
|
||||||
"Program chunks write error".to_string(),
|
|
||||||
))?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let last_id = get_last_id(&config)?;
|
||||||
|
let tx = Transaction::finalize(&program, bpf_loader::id(), last_id, 0);
|
||||||
|
send_and_confirm_tx(&config, &tx).map_err(|_| {
|
||||||
|
WalletError::DynamicProgramError("Program finalize transaction failed".to_string())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let tx = Transaction::system_spawn(&program, last_id, 0);
|
||||||
|
send_and_confirm_tx(&config, &tx).map_err(|_| {
|
||||||
|
WalletError::DynamicProgramError("Program spawn failed".to_string())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(json!({
|
||||||
|
"programId": format!("{}", program.pubkey()),
|
||||||
|
}).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)]
|
||||||
|
|
Loading…
Reference in New Issue