Skip to content

Commit f27dea7

Browse files
devin-ai-integration[bot]Jayant Krishnamurthy
andcommitted
fix(fortuna): Replace panic-causing constructs with safer alternatives
Co-Authored-By: Jayant Krishnamurthy <[email protected]>
1 parent d8c8a40 commit f27dea7

File tree

9 files changed

+53
-29
lines changed

9 files changed

+53
-29
lines changed

apps/fortuna/src/api/metrics.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ pub async fn metrics(State(state): State<crate::api::ApiState>) -> impl IntoResp
1111

1212
// Should not fail if the metrics are valid and there is memory available
1313
// to write to the buffer.
14-
encode(&mut buffer, &registry).unwrap();
14+
if let Err(e) = encode(&mut buffer, &registry) {
15+
tracing::error!("Failed to encode metrics: {}", e);
16+
return axum::http::StatusCode::INTERNAL_SERVER_ERROR.into_response();
17+
}
1518

16-
buffer
19+
buffer.into_response()
1720
}

apps/fortuna/src/chain/ethereum.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,10 @@ impl<T: JsonRpcClient + 'static + Clone> SignablePythContractInner<T> {
8282
.await?
8383
{
8484
// Extract Log from TransactionReceipt.
85-
let l: RawLog = r.logs[0].clone().into();
85+
let l: RawLog = r.logs.first()
86+
.ok_or_else(|| anyhow!("No logs in receipt"))?
87+
.clone()
88+
.into();
8689
if let PythRandomEvents::Requested1Filter(r) = PythRandomEvents::decode_log(&l)? {
8790
Ok(r.request.sequence_number)
8891
} else {
@@ -112,7 +115,10 @@ impl<T: JsonRpcClient + 'static + Clone> SignablePythContractInner<T> {
112115
.await?
113116
{
114117
// Extract Log from TransactionReceipt.
115-
let l: RawLog = r.logs[0].clone().into();
118+
let l: RawLog = r.logs.first()
119+
.ok_or_else(|| anyhow!("No logs in receipt"))?
120+
.clone()
121+
.into();
116122
if let PythRandomEvents::RequestedWithCallbackFilter(r) =
117123
PythRandomEvents::decode_log(&l)?
118124
{
@@ -148,7 +154,10 @@ impl<T: JsonRpcClient + 'static + Clone> SignablePythContractInner<T> {
148154
.await?
149155
{
150156
if let PythRandomEvents::Revealed1Filter(r) =
151-
PythRandomEvents::decode_log(&r.logs[0].clone().into())?
157+
PythRandomEvents::decode_log(&r.logs.first()
158+
.ok_or_else(|| anyhow!("No logs in receipt"))?
159+
.clone()
160+
.into())?
152161
{
153162
Ok(r.random_number)
154163
} else {

apps/fortuna/src/command/inspect.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ async fn inspect_chain(
3737
let multicall_exists = rpc_provider
3838
.get_code(ethers::contract::MULTICALL_ADDRESS, None)
3939
.await
40-
.expect("Failed to get code")
40+
.map_err(|e| anyhow::anyhow!("Failed to get code: {}", e))?
4141
.len()
4242
> 0;
4343

@@ -97,9 +97,9 @@ async fn process_request(
9797
let block = rpc_provider
9898
.get_block(request.block_number)
9999
.await?
100-
.expect("Block not found");
100+
.ok_or_else(|| anyhow::anyhow!("Block not found"))?;
101101
let datetime = chrono::DateTime::from_timestamp(block.timestamp.as_u64() as i64, 0)
102-
.expect("Invalid timestamp");
102+
.ok_or_else(|| anyhow::anyhow!("Invalid timestamp"))?;
103103
println!(
104104
"{} sequence_number:{} block_number:{} requester:{}",
105105
datetime, request.sequence_number, request.block_number, request.requester

apps/fortuna/src/command/run.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@ pub async fn run(opts: &RunOptions) -> Result<()> {
135135
// Listen for Ctrl+C so we can set the exit flag and wait for a graceful shutdown.
136136
spawn(async move {
137137
tracing::info!("Registered shutdown signal handler...");
138-
tokio::signal::ctrl_c().await.unwrap();
138+
tokio::signal::ctrl_c().await.map_err(|e| {
139+
tracing::error!("Failed to register ctrl-c handler: {}", e);
140+
Error::msg(format!("Failed to register ctrl-c handler: {}", e))
141+
})?;
139142
tracing::info!("Shut down signal received, waiting for tasks...");
140143
// no need to handle error here, as it will only occur when all the
141144
// receiver has been dropped and that's what we want to do
@@ -220,7 +223,7 @@ async fn setup_chain_state(
220223
let last_prior_commitment = provider_commitments.last();
221224
if last_prior_commitment.is_some()
222225
&& last_prior_commitment
223-
.unwrap()
226+
.ok_or_else(|| anyhow!("No prior commitment found"))?
224227
.original_commitment_sequence_number
225228
>= provider_info.original_commitment_sequence_number
226229
{

apps/fortuna/src/eth_utils/eth_gas_oracle.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,14 @@ pub fn eip1559_default_estimator(base_fee_per_gas: U256, rewards: Vec<Vec<U256>>
108108
fn estimate_priority_fee(rewards: Vec<Vec<U256>>) -> U256 {
109109
let mut rewards: Vec<U256> = rewards
110110
.iter()
111-
.map(|r| r[0])
111+
.map(|r| r.first().copied().unwrap_or_default())
112112
.filter(|r| *r > U256::zero())
113113
.collect();
114114
if rewards.is_empty() {
115115
return U256::zero();
116116
}
117117
if rewards.len() == 1 {
118-
return rewards[0];
118+
return rewards.first().copied().unwrap_or_default();
119119
}
120120
// Sort the rewards as we will eventually take the median.
121121
rewards.sort();
@@ -129,32 +129,33 @@ fn estimate_priority_fee(rewards: Vec<Vec<U256>>) -> U256 {
129129
.iter()
130130
.zip(rewards_copy.iter())
131131
.map(|(a, b)| {
132-
let a = I256::try_from(*a).expect("priority fee overflow");
133-
let b = I256::try_from(*b).expect("priority fee overflow");
132+
let a = I256::try_from(*a).unwrap_or_default();
133+
let b = I256::try_from(*b).unwrap_or_default();
134134
((b - a) * 100) / a
135135
})
136136
.collect();
137137
percentage_change.pop();
138138

139139
// Fetch the max of the percentage change, and that element's index.
140-
let max_change = percentage_change.iter().max().unwrap();
140+
let zero = I256::zero();
141+
let max_change = percentage_change.iter().max().unwrap_or(&zero);
141142
let max_change_index = percentage_change
142143
.iter()
143144
.position(|&c| c == *max_change)
144-
.unwrap();
145+
.unwrap_or(0);
145146

146147
// If we encountered a big change in fees at a certain position, then consider only
147148
// the values >= it.
148149
let values = if *max_change >= EIP1559_FEE_ESTIMATION_THRESHOLD_MAX_CHANGE.into()
149150
&& (max_change_index >= (rewards.len() / 2))
150151
{
151-
rewards[max_change_index..].to_vec()
152+
rewards.get(max_change_index..).unwrap_or(&rewards).to_vec()
152153
} else {
153154
rewards
154155
};
155156

156157
// Return the median.
157-
values[values.len() / 2]
158+
values.get(values.len() / 2).copied().unwrap_or_default()
158159
}
159160

160161
fn base_fee_surged(base_fee_per_gas: U256) -> U256 {

apps/fortuna/src/eth_utils/traced_client.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ impl TracedClient {
120120
let client = Client::builder()
121121
.timeout(Duration::from_secs(10))
122122
.build()
123-
.expect("Failed to create HTTP client");
123+
.map_err(|e| anyhow::anyhow!("Failed to create HTTP client: {}", e))?;
124124
Ok(Provider::new(TracedClient {
125125
inner: Http::new_with_client(url, client),
126126
chain_id,

apps/fortuna/src/keeper.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,11 @@ pub async fn run_keeper_threads(
145145
chain_eth_config.gas_limit,
146146
// NOTE: unwrap() here so we panic early if someone configures these values below -100.
147147
u64::try_from(100 + chain_eth_config.min_profit_pct)
148-
.expect("min_profit_pct must be >= -100"),
148+
.map_err(|_| anyhow::anyhow!("min_profit_pct must be >= -100"))?,
149149
u64::try_from(100 + chain_eth_config.target_profit_pct)
150-
.expect("target_profit_pct must be >= -100"),
150+
.map_err(|_| anyhow::anyhow!("target_profit_pct must be >= -100"))?,
151151
u64::try_from(100 + chain_eth_config.max_profit_pct)
152-
.expect("max_profit_pct must be >= -100"),
152+
.map_err(|_| anyhow::anyhow!("max_profit_pct must be >= -100"))?,
153153
chain_eth_config.fee,
154154
metrics.clone(),
155155
)

apps/fortuna/src/keeper/track.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,13 @@ pub async fn track_block_timestamp_lag(
5656
Ok(block) => match block {
5757
Some(block) => {
5858
let block_timestamp = block.timestamp;
59-
let server_timestamp = SystemTime::now()
60-
.duration_since(UNIX_EPOCH)
61-
.unwrap()
62-
.as_secs();
59+
let server_timestamp = match SystemTime::now().duration_since(UNIX_EPOCH) {
60+
Ok(duration) => duration.as_secs(),
61+
Err(e) => {
62+
tracing::error!("Failed to get system time: {}", e);
63+
u64::MAX
64+
}
65+
};
6366
let lag: i64 = (server_timestamp as i64) - (block_timestamp.as_u64() as i64);
6467
lag
6568
}

apps/fortuna/src/state.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ impl PebbleHashChain {
9494
let sample_interval: usize = sample_interval.try_into()?;
9595
let hash_chain = spawn_blocking(move || Self::new(secret, chain_length, sample_interval))
9696
.await
97-
.expect("Failed to make hash chain");
97+
.map_err(|e| anyhow::anyhow!("Failed to make hash chain: {}", e))?;
9898

9999
Ok(hash_chain)
100100
}
@@ -106,7 +106,8 @@ impl PebbleHashChain {
106106
// actually at the *front* of the list. Thus, it's easier to compute indexes from the end of the list.
107107
let index_from_end_of_subsampled_list = ((self.len() - 1) - i) / self.sample_interval;
108108
let mut i_index = self.len() - 1 - index_from_end_of_subsampled_list * self.sample_interval;
109-
let mut val = self.hash[self.hash.len() - 1 - index_from_end_of_subsampled_list];
109+
let mut val = *self.hash.get(self.hash.len().saturating_sub(1 + index_from_end_of_subsampled_list))
110+
.ok_or_else(|| anyhow::anyhow!("Index out of bounds in hash chain"))?;
110111

111112
while i_index > i {
112113
val = Keccak256::digest(val).into();
@@ -148,7 +149,11 @@ impl HashChainState {
148149
.ok_or(anyhow::anyhow!(
149150
"Hash chain for the requested sequence number is not available."
150151
))?;
151-
self.hash_chains[chain_index].reveal_ith(sequence_number - self.offsets[chain_index])
152+
let chain = self.hash_chains.get(chain_index)
153+
.ok_or_else(|| anyhow::anyhow!("Chain index out of bounds"))?;
154+
let offset = self.offsets.get(chain_index)
155+
.ok_or_else(|| anyhow::anyhow!("Offset index out of bounds"))?;
156+
chain.reveal_ith(sequence_number - offset)
152157
}
153158
}
154159

0 commit comments

Comments
 (0)