Skip to content

Commit

Permalink
cleanup all tx retry logic
Browse files Browse the repository at this point in the history
  • Loading branch information
HardhatChad committed Apr 2, 2024
1 parent d794a03 commit 145807a
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 54 deletions.
13 changes: 8 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ admin = []

[dependencies]
bincode = "1.3.3"
bs58 = "0.5.1"
cached = "0.46.1"
chrono = "0.4.34"
clap = { version = "4.4.12", features = ["derive"] }
futures = "0.3.30"
log = "0.4"
ore = { version = "1.1.0", package = "ore-program" }
ore = { version = "1.2.0", package = "ore-program" }
solana-cli-config = "1.18.5"
solana-client = "^1.16"
solana-program = "^1.16"
Expand Down
44 changes: 20 additions & 24 deletions src/mine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ impl Miner {
// Submit mine tx.
let mut bus_id = 0;
let mut invalid_busses: Vec<u8> = vec![];
let mut needs_reset = false;
'submit: loop {
// Find a valid bus.
if invalid_busses.len().eq(&(BUS_COUNT as usize)) {
Expand All @@ -75,6 +76,7 @@ impl Miner {
println!("Bus {} is empty... ", bus_id);
bus_id += 1;
if bus_id.ge(&(BUS_COUNT as u8)) {
std::thread:sleep(Duration::from_secs(1));
bus_id = 0;
}
}
Expand All @@ -83,11 +85,12 @@ impl Miner {
let treasury = get_treasury(self.cluster.clone()).await;
let clock = get_clock_account(self.cluster.clone()).await;
let threshold = treasury.last_reset_at.saturating_add(EPOCH_DURATION);
if clock.unix_timestamp.ge(&threshold) {
if clock.unix_timestamp.ge(&threshold) || needs_reset {
let reset_ix = ore::instruction::reset(signer.pubkey());
self.send_and_confirm(&[reset_ix])
.await
.expect("Transaction failed");
needs_reset = false;
}

// Submit request.
Expand All @@ -102,33 +105,26 @@ impl Miner {
stdout.write(format!("Success: {}", sig).as_bytes()).ok();
break;
}
Err(err) => {
match err.kind {
ClientErrorKind::RpcError(err) => {
// TODO Why is BusInsufficientFunds an RpcError but EpochNeedsReset is a TransactionError ?
// Unhandled error Error { request: None, kind: TransactionError(InstructionError(0, Custom(6003))) }
// thread 'main' panicked at 'Failed to submit transaction: SolanaClientError(Error { request: None, kind: TransactionError(InstructionError(0, Custom(6000))) })', src/main.rs:193:26
if err.to_string().contains("custom program error: 0x5") {
// Bus has no remaining funds. Use a different one.
invalid_busses.push(bus_id);
} else if err
.to_string()
.contains("This transaction has already been processed")
{
break 'submit;
} else {
stdout
.write_all(format!("\n{:?} \n", err.to_string()).as_bytes())
.ok();
}
}
_ => {
Err(err) => match err.kind {
ClientErrorKind::Custom(msg) => {
if msg.contains("Bus insufficient") {
invalid_busses.push(bus_id);
} else if msg.contains("Needs reset") {
needs_reset = true;
} else if msg.contains("Hash invalid") {
break 'submit;
} else {
stdout
.write_all(format!("\nUnhandled error {:?} \n", err).as_bytes())
.write_all(format!("\n{:?} \n", msg.to_string()).as_bytes())
.ok();
}
}
}
_ => {
stdout
.write_all(format!("\nUnhandled error {:?} \n", err).as_bytes())
.ok();
}
},
}
}
}
Expand Down
147 changes: 125 additions & 22 deletions src/send_and_confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@ use std::{
use solana_client::{
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
nonblocking::rpc_client::RpcClient,
rpc_config::{RpcSendTransactionConfig, RpcSimulateTransactionConfig},
};
use solana_program::instruction::Instruction;
use solana_program::instruction::{Instruction, InstructionError};
use solana_sdk::{
commitment_config::CommitmentConfig,
commitment_config::{CommitmentConfig, CommitmentLevel},
compute_budget::ComputeBudgetInstruction,
signature::{Signature, Signer},
transaction::Transaction,
transaction::{Transaction, TransactionError},
};
use solana_transaction_status::UiTransactionEncoding;

use crate::Miner;

const RPC_RETRIES: usize = 1;
const GATEWAY_RETRIES: usize = 10;
const CONFIRM_RETRIES: usize = 10;

impl Miner {
pub async fn send_and_confirm(&self, ixs: &[Instruction]) -> ClientResult<Signature> {
Expand All @@ -27,40 +31,129 @@ impl Miner {
RpcClient::new_with_commitment(self.cluster.clone(), CommitmentConfig::confirmed());

// Build tx
let mut hash = client.get_latest_blockhash().await.unwrap();
let (mut hash, mut slot) = client
.get_latest_blockhash_with_commitment(CommitmentConfig::confirmed())
.await
.unwrap();
let mut send_cfg = RpcSendTransactionConfig {
skip_preflight: true,
preflight_commitment: Some(CommitmentLevel::Confirmed),
encoding: Some(UiTransactionEncoding::Base64),
max_retries: Some(RPC_RETRIES),
min_context_slot: Some(slot),
};

let mut tx = Transaction::new_with_payer(ixs, Some(&signer.pubkey()));
tx.sign(&[&signer], hash);

// Sim and prepend cu ixs
let sim_res = client.simulate_transaction(&tx).await;
let final_ixs = if let Ok(sim_res) = sim_res {
let cu_budget_ix = ComputeBudgetInstruction::set_compute_unit_limit(
sim_res.value.units_consumed.unwrap() as u32 + 1000,
);
let cu_price_ix = ComputeBudgetInstruction::set_compute_unit_price(self.priority_fee);
let mut final_ixs = vec![];
final_ixs.extend_from_slice(&[cu_budget_ix, cu_price_ix]);
final_ixs.extend_from_slice(ixs);
final_ixs
let sim_res = client
.simulate_transaction_with_config(
&tx,
RpcSimulateTransactionConfig {
sig_verify: false,
replace_recent_blockhash: false,
commitment: Some(CommitmentConfig::confirmed()),
encoding: Some(UiTransactionEncoding::Base64),
accounts: None,
min_context_slot: Some(slot),
inner_instructions: false,
},
)
.await;
if let Ok(sim_res) = sim_res {
match sim_res.value.err {
Some(err) => match err {
TransactionError::InstructionError(_, InstructionError::Custom(e)) => {
if e == 1 {
log::info!("Needs reset!");
return Err(ClientError {
request: None,
kind: ClientErrorKind::Custom("Needs reset".into()),
});
} else if e == 3 {
log::info!("Hash invalid!");
return Err(ClientError {
request: None,
kind: ClientErrorKind::Custom("Hash invalid".into()),
});
} else if e == 5 {
return Err(ClientError {
request: None,
kind: ClientErrorKind::Custom("Bus insufficient".into()),
});
} else {
return Err(ClientError {
request: None,
kind: ClientErrorKind::Custom("Sim failed".into()),
});
}
}
_ => {
return Err(ClientError {
request: None,
kind: ClientErrorKind::Custom("Sim failed".into()),
})
}
},
None => {
let cu_budget_ix = ComputeBudgetInstruction::set_compute_unit_limit(
sim_res.value.units_consumed.unwrap() as u32 + 1000,
);
let cu_price_ix =
ComputeBudgetInstruction::set_compute_unit_price(self.priority_fee);
let mut final_ixs = vec![];
final_ixs.extend_from_slice(&[cu_budget_ix, cu_price_ix]);
final_ixs.extend_from_slice(ixs);
tx = Transaction::new_with_payer(&final_ixs, Some(&signer.pubkey()));
tx.sign(&[&signer], hash);
}
}
} else {
return Err(ClientError {
request: None,
kind: ClientErrorKind::Custom("Failed simulation".into()),
});
};

// Rebuild tx with cu ixs
tx = Transaction::new_with_payer(&final_ixs, Some(&signer.pubkey()));
tx.sign(&[&signer], hash);

// Loop
let mut attempts = 0;
loop {
println!("Attempt: {:?}", attempts);
match client.send_and_confirm_transaction(&tx).await {
match client.send_transaction_with_config(&tx, send_cfg).await {
Ok(sig) => {
println!("Confirmed: {:?}", sig);
return Ok(sig);
log::info!("{:?}", sig);
let mut confirm_check = 0;
'confirm: loop {
match client
.confirm_transaction_with_commitment(
&sig,
CommitmentConfig::confirmed(),
)
.await
{
Ok(confirmed) => {
log::info!(
"Confirm check {:?}: {:?}",
confirm_check,
confirmed.value
);
if confirmed.value {
return Ok(sig);
}
}
Err(err) => {
log::error!("Err: {:?}", err);
}
}

// Retry confirm
std::thread::sleep(Duration::from_millis(500));
confirm_check += 1;
if confirm_check.gt(&CONFIRM_RETRIES) {
break 'confirm;
}
}
}
Err(err) => {
println!("Error {:?}", err);
Expand All @@ -70,7 +163,17 @@ impl Miner {

// Retry with new hash
std::thread::sleep(Duration::from_millis(1000));
hash = client.get_latest_blockhash().await.unwrap();
(hash, slot) = client
.get_latest_blockhash_with_commitment(CommitmentConfig::confirmed())
.await
.unwrap();
send_cfg = RpcSendTransactionConfig {
skip_preflight: true,
preflight_commitment: Some(CommitmentLevel::Confirmed),
encoding: Some(UiTransactionEncoding::Base64),
max_retries: Some(RPC_RETRIES),
min_context_slot: Some(slot),
};
tx.sign(&[&signer], hash);
attempts += 1;
if attempts > GATEWAY_RETRIES {
Expand Down
10 changes: 8 additions & 2 deletions src/update_difficulty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@ use crate::Miner;
impl Miner {
pub async fn update_difficulty(&self) {
let signer = self.signer();
// let new_difficulty = KeccakHash::new_from_array([
// 0, 0, 0, 64, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
// 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
// ]);
let new_difficulty = KeccakHash::new_from_array([
0, 0, 0, 32, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
]);
let ix = ore::instruction::update_difficulty(signer.pubkey(), new_difficulty.into());
// let bs58data = bs58::encode(ix.data).into_string();
// println!("Data: {:?}", bs58data);
self.send_and_confirm(&[ix])
.await
.expect("Transaction failed");
Expand Down

0 comments on commit 145807a

Please sign in to comment.