Skip to content

Commit 3d7f510

Browse files
committed
fix: backward-compatible claim_milestone for pre-upgrade events
1 parent ce7bb72 commit 3d7f510

2 files changed

Lines changed: 67 additions & 10 deletions

File tree

contracts/events/src/grant.rs

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,8 @@ pub fn claim_milestone(
5656

5757
let is_crowdfunding = matches!(event.pillar, Pillar::Crowdfunding);
5858

59-
let anchor_idx =
60-
storage::get_grant_recipient_idx(env, event_id, &recipient).ok_or(Error::NoSubmissions)?;
61-
let anchor = storage::winner_at(env, event_id, anchor_idx).ok_or(Error::NoSubmissions)?;
62-
if anchor.recipient != recipient || anchor.milestone.is_some() {
63-
return Err(Error::NoSubmissions);
64-
}
65-
let position = anchor.position;
66-
let reputation_bump = anchor.reputation_bump.unwrap_or(0);
67-
let already_claimed_for_recipient =
68-
storage::get_grant_recipient_claim_count(env, event_id, &recipient);
59+
let (position, reputation_bump, already_claimed_for_recipient) =
60+
resolve_recipient(env, event_id, &recipient)?;
6961
let already_paid_to_recipient = if is_crowdfunding {
7062
0
7163
} else {
@@ -167,3 +159,62 @@ pub fn claim_milestone(
167159
idempotency::mark_seen(env, &op_id);
168160
Ok(())
169161
}
162+
163+
/// Resolve recipient anchor (position + reputation_bump) and milestone claim count.
164+
///
165+
/// Tries the O(1) index path first. If the indexes don't exist (pre-upgrade events),
166+
/// falls back to a linear scan over winner rows and persists the indexes so future
167+
/// calls use the fast path.
168+
fn resolve_recipient(
169+
env: &Env,
170+
event_id: u64,
171+
recipient: &Address,
172+
) -> Result<(u32, u32, u32), Error> {
173+
// Fast path: indexes already exist (post-upgrade events).
174+
if let Some(anchor_idx) = storage::get_grant_recipient_idx(env, event_id, recipient) {
175+
let anchor = storage::winner_at(env, event_id, anchor_idx).ok_or(Error::NoSubmissions)?;
176+
if anchor.recipient != *recipient || anchor.milestone.is_some() {
177+
return Err(Error::NoSubmissions);
178+
}
179+
let claim_count = storage::get_grant_recipient_claim_count(env, event_id, recipient);
180+
return Ok((
181+
anchor.position,
182+
anchor.reputation_bump.unwrap_or(0),
183+
claim_count,
184+
));
185+
}
186+
187+
// Legacy fallback: scan winner rows, which works before the indexes existed.
188+
let count = storage::winner_count(env, event_id);
189+
let mut anchor_idx = None;
190+
let mut position = None;
191+
let mut reputation_bump = 0;
192+
let mut claim_count = 0u32;
193+
for idx in 0..count {
194+
let w = match storage::winner_at(env, event_id, idx) {
195+
Some(w) => w,
196+
None => continue,
197+
};
198+
if w.recipient != *recipient {
199+
continue;
200+
}
201+
match w.milestone {
202+
None => {
203+
anchor_idx = Some(idx);
204+
position = Some(w.position);
205+
reputation_bump = w.reputation_bump.unwrap_or(0);
206+
}
207+
Some(_) => {
208+
claim_count = claim_count.saturating_add(1);
209+
}
210+
}
211+
}
212+
let idx = anchor_idx.ok_or(Error::NoSubmissions)?;
213+
let pos = position.ok_or(Error::NoSubmissions)?;
214+
215+
// Persist indexes for future O(1) lookups.
216+
storage::set_grant_recipient_idx(env, event_id, recipient, idx);
217+
storage::set_grant_recipient_claim_count(env, event_id, recipient, claim_count);
218+
219+
Ok((pos, reputation_bump, claim_count))
220+
}

contracts/events/src/storage.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,12 @@ pub fn get_grant_recipient_claim_count(env: &Env, id: u64, recipient: &Address)
490490
count.unwrap_or(0)
491491
}
492492

493+
pub fn set_grant_recipient_claim_count(env: &Env, id: u64, recipient: &Address, count: u32) {
494+
let key = DataKey::GrantRecipientClaimCount(id, recipient.clone());
495+
env.storage().persistent().set(&key, &count);
496+
touch_event_persistent(env, &key);
497+
}
498+
493499
pub fn increment_grant_recipient_claim_count(env: &Env, id: u64, recipient: &Address) {
494500
let key = DataKey::GrantRecipientClaimCount(id, recipient.clone());
495501
let count: u32 = env.storage().persistent().get(&key).unwrap_or(0);

0 commit comments

Comments
 (0)