Summary
The only way a milestone transitions to Missed status is when the oracle calls update_progress after the deadline with a current_value < target_value. There is no admin-callable or permissionless mark_missed(milestone_id) function. If the oracle goes offline (see related issue), stops sending updates, or is throttled, milestones that have clearly expired remain perpetually in Pending or InProgress status with no recourse. This blocks any downstream logic that gates on Missed status.
Fix
Add a permissionless mark_missed function that anyone can call once a milestone is past its deadline:
pub fn mark_missed(env: Env, milestone_id: u64) {
let mut milestone: Milestone = env.storage().persistent()
.get(&DataKey::Milestone(milestone_id)).expect("not found");
if milestone.status == MilestoneStatus::Achieved || milestone.status == MilestoneStatus::Missed {
panic!("already finalized");
}
if env.ledger().timestamp() <= milestone.deadline {
panic!("deadline has not passed");
}
milestone.status = MilestoneStatus::Missed;
env.storage().persistent().set(&DataKey::Milestone(milestone_id), &milestone);
}
Summary
The only way a milestone transitions to
Missedstatus is when the oracle callsupdate_progressafter the deadline with acurrent_value < target_value. There is no admin-callable or permissionlessmark_missed(milestone_id)function. If the oracle goes offline (see related issue), stops sending updates, or is throttled, milestones that have clearly expired remain perpetually inPendingorInProgressstatus with no recourse. This blocks any downstream logic that gates onMissedstatus.Fix
Add a permissionless
mark_missedfunction that anyone can call once a milestone is past its deadline: