Skip to content

Commit 7c4eeaa

Browse files
committed
fix: avoid race conditions by checking and storing messages in a single operation
1 parent 8d8eaa9 commit 7c4eeaa

File tree

3 files changed

+39
-22
lines changed

3 files changed

+39
-22
lines changed

apps/hermes/server/src/state/aggregate.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -314,18 +314,22 @@ where
314314
let slot = accumulator_messages.slot;
315315
tracing::info!(slot = slot, "Storing Accumulator Messages.");
316316

317-
// Check if we already have accumulator messages for this slot
318-
if (self.fetch_accumulator_messages(slot).await?).is_some() {
317+
// Store the accumulator messages and check if they were already stored in a single operation
318+
// This avoids the race condition where multiple threads could check and find nothing
319+
// but then both store the same messages
320+
let is_new = self
321+
.store_accumulator_messages(accumulator_messages)
322+
.await?;
323+
324+
// If the messages were already stored, return early
325+
if !is_new {
319326
tracing::info!(
320327
slot = slot,
321328
"Accumulator Messages already stored, skipping."
322329
);
323330
return Ok(());
324331
}
325332

326-
self.store_accumulator_messages(accumulator_messages)
327-
.await?;
328-
329333
self.into()
330334
.data
331335
.write()

apps/hermes/server/src/state/aggregate/wormhole_merkle.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,12 @@ pub async fn store_wormhole_merkle_verified_message<S>(
5959
where
6060
S: Cache,
6161
{
62-
// Check if we already have a state for this slot
63-
if (state.fetch_wormhole_merkle_state(root.slot).await?).is_some() {
64-
// State already exists, return early
65-
return Ok(false);
66-
}
67-
68-
// State doesn't exist, store it
62+
// Store the state and check if it was already stored in a single operation
63+
// This avoids the race condition where multiple threads could check and find nothing
64+
// but then both store the same state
6965
state
7066
.store_wormhole_merkle_state(WormholeMerkleState { root, vaa })
71-
.await?;
72-
Ok(true)
67+
.await
7368
}
7469

7570
pub fn construct_message_states_proofs(

apps/hermes/server/src/state/cache.rs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,12 @@ pub trait Cache {
122122
async fn store_accumulator_messages(
123123
&self,
124124
accumulator_messages: AccumulatorMessages,
125-
) -> Result<()>;
125+
) -> Result<bool>;
126126
async fn fetch_accumulator_messages(&self, slot: Slot) -> Result<Option<AccumulatorMessages>>;
127127
async fn store_wormhole_merkle_state(
128128
&self,
129129
wormhole_merkle_state: WormholeMerkleState,
130-
) -> Result<()>;
130+
) -> Result<bool>;
131131
async fn fetch_wormhole_merkle_state(&self, slot: Slot) -> Result<Option<WormholeMerkleState>>;
132132
async fn message_state_keys(&self) -> Vec<MessageStateKey>;
133133
async fn fetch_message_states(
@@ -226,13 +226,22 @@ where
226226
async fn store_accumulator_messages(
227227
&self,
228228
accumulator_messages: AccumulatorMessages,
229-
) -> Result<()> {
229+
) -> Result<bool> {
230230
let mut cache = self.into().accumulator_messages_cache.write().await;
231-
cache.insert(accumulator_messages.slot, accumulator_messages);
231+
let slot = accumulator_messages.slot;
232+
233+
// Check if we already have messages for this slot while holding the lock
234+
if cache.contains_key(&slot) {
235+
// Messages already exist, return false to indicate no insertion happened
236+
return Ok(false);
237+
}
238+
239+
// Messages don't exist, store them
240+
cache.insert(slot, accumulator_messages);
232241
while cache.len() > self.into().cache_size as usize {
233242
cache.pop_first();
234243
}
235-
Ok(())
244+
Ok(true)
236245
}
237246

238247
async fn fetch_accumulator_messages(&self, slot: Slot) -> Result<Option<AccumulatorMessages>> {
@@ -243,13 +252,22 @@ where
243252
async fn store_wormhole_merkle_state(
244253
&self,
245254
wormhole_merkle_state: WormholeMerkleState,
246-
) -> Result<()> {
255+
) -> Result<bool> {
247256
let mut cache = self.into().wormhole_merkle_state_cache.write().await;
248-
cache.insert(wormhole_merkle_state.root.slot, wormhole_merkle_state);
257+
let slot = wormhole_merkle_state.root.slot;
258+
259+
// Check if we already have a state for this slot while holding the lock
260+
if cache.contains_key(&slot) {
261+
// State already exists, return false to indicate no insertion happened
262+
return Ok(false);
263+
}
264+
265+
// State doesn't exist, store it
266+
cache.insert(slot, wormhole_merkle_state);
249267
while cache.len() > self.into().cache_size as usize {
250268
cache.pop_first();
251269
}
252-
Ok(())
270+
Ok(true)
253271
}
254272

255273
async fn fetch_wormhole_merkle_state(&self, slot: Slot) -> Result<Option<WormholeMerkleState>> {

0 commit comments

Comments
 (0)