milestones::allocate: issue_id uniqueness is only enforced within a single milestone_id — the same GitHub issue can be paid out from multiple milestones
I maintainer di solito rispondono entro 1 giorno
Valutazione
Questa issue non è ancora stata valutata.
Descrizione
Overview
milestones scopes an issue's allocation status by the pair (milestone_id, issue_id), not by issue_id alone: DataKey::IssueStatus(u64, u64) (contracts/milestones/src/types.rs:35), read/written exclusively as DataKey::IssueStatus(milestone_id, issue_id) throughout allocate and release_issue (contracts/milestones/src/lib.rs:121, 148, 179). allocate's only duplicate-allocation guard, milestone.allocations.contains_key(issue_id) (contracts/milestones/src/lib.rs:109), is likewise scoped to the one Milestone record it's operating on — it has no visibility into, and performs no check against, any other milestone's allocations.
Consequence: the same underlying GitHub issue_id can be allocated, and independently released, from multiple different milestones simultaneously, with the contract providing zero defense-in-depth against it. Concretely: allocate(milestone_id=1, issue_id=555, amount=X) and allocate(milestone_id=2, issue_id=555, amount=Y) both succeed unconditionally — nothing in either call inspects milestone 2's state while processing milestone 1's, or vice versa. Both allocations can subsequently be independently released via release_issue(1, 555, ...) and release_issue(2, 555, ...), each paying out its own full amount, for what is, off-chain, the same piece of merged work being paid for twice (or more) over.
This is a real gap even under the codebase's explicit "contracts are deliberately dumb, backend is the trusted oracle" trust model (README: "GitHub remains the source of truth... these contracts are deliberately dumb about that — they only know what the oracle tells them") — that model justifies the contract trusting the admin's judgment about whether work was done; it does not, by itself, justify providing zero structural defense against the admin being tricked, or making an honest mistake, into allocating the same issue_id twice across different milestones, something a single admin key operating many milestones over time is entirely capable of doing accidentally (e.g. a spreadsheet/database error on the backend side associating one GitHub issue with two different release milestones). Compare with escrow::fund, which enforces issue_id uniqueness globally via a single DataKey::Escrow(issue_id) key with no milestone-equivalent scoping concept at all — milestones' weaker, per-milestone-only scoping is a deliberate consequence of its data model (an issue_id legitimately needs to be nameable within many different milestones' allocations maps in principle), but the complete absence of any global "this issue_id is already claimed somewhere" registry is a gap escrow's design doesn't share.
Requirements
- Add a global (contract-instance-wide) registry — e.g.
DataKey::GlobalIssueClaim(issue_id) -> milestone_id— checked and set byallocate, rejecting a secondallocatecall for anissue_idthat's already claimed by a differentmilestone_id, with a newErrorvariant distinguishing this from the existing per-milestoneIssueAlreadyAllocated. - Decide what should happen when an issue's allocation is later removed (once the companion
deallocatefunction from this batch's other issue lands) — the global claim should presumably be released at that point too, so the registry doesn't itself become a source of permanent false "already claimed" rejections. - Document, in the README's "Contracts" section for
milestones, the intended guarantee explicitly: "anissue_idcan be allocated in at most one milestone at a time."
Acceptance Criteria
- Global issue-claim registry implemented and enforced in
allocate - New
Errorvariant distinguishing "already allocated in this milestone" from "already allocated in a different milestone" - Test:
test_allocate_rejects_issue_already_claimed_by_different_milestone— allocateissue_id=Xin milestone A, attempt to allocate the sameissue_idin milestone B, assert rejection - Test: interaction with
deallocate(if landed) — deallocating frees the global claim, allowing legitimate reallocation elsewhere - README updated with the explicit cross-milestone uniqueness guarantee
-
cargo test --workspacepasses
Additional Notes
- Precise references:
contracts/milestones/src/types.rs:35(IssueStatus(u64, u64)keying, confirming the scoping is milestone-relative, not global);contracts/milestones/src/lib.rs:109(allocate's sole duplicate check, scoped to the singleMilestonerecord already loaded intomilestone— provably has no access to any other milestone's data within that function call). - Test sketch:
test_double_funding_same_issue_across_two_milestones_pre_fixas a reproduction —create_milestone(1, ...),create_milestone(2, ...),allocate(1, 555, amount_a),allocate(2, 555, amount_b)(both currently succeed),release_issue(1, 555, recipients),release_issue(2, 555, recipients)(both currently succeed), assert the recipient(s) received two separate payouts for the sameissue_id— demonstrating the double-payment is real and silent before the fix closes it. - Cross-references: #1 (escrow's issue-id squatting — a different mechanism, front-running by an unrelated party, not double-allocation by the legitimate admin across milestones); the companion cross-contract double-funding issue filed in this same batch (this issue's within-milestones-contract scope is the narrower, more directly fixable half of that broader concern); the "no deallocate/reallocate" issue filed in this batch (interacts directly with how a global claim registry should behave on deallocation).
- Lingua principale
- Rust
- Stelle
- 1
- Fork
- 102
- Merge medio
- 4h 12m
- PR unite (30g)
- 30
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Nessuna guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di MergeFi/contracts
-
allocations map retains released issues — phantom ledger driftForse di nuovo libera @PINYOPATTANAWASANPORN l’ha presa 12 giorni fa e non c’è nessuna pull request aperta. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 25/100
MergeFi/contracts#608 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
escrow::contribute and milestones::contribute accept new funds after deadline + GRACE_PERIOD, when anyone can already refund or cancelForse già presa @urielit1313-creator l’ha presa 29 giorni fa. Apertabug
Difficoltà 3/5 1-2 giorni Idoneità per principianti 25/100
MergeFi/contracts#568 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Makefile deploy targets deploy the unoptimised .wasm — `stellar contract optimize` writes *.optimized.wasmForse già presa @urielit1313-creator l’ha presa 29 giorni fa. Apertabug good first issue
Difficoltà 2/5 1-3 ore Idoneità per principianti 25/100
MergeFi/contracts#567 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
keep_alive never refreshes instance storage, so a contract kept alive only via keep_alive can still archive Admin/Oracle/TreasuryForse già presa @urielit1313-creator l’ha presa 29 giorni fa. Apertabug
Difficoltà 4/5 3-5 giorni Idoneità per principianti 25/100
MergeFi/contracts#566 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
[Regression on closed #254] milestones::deallocate still has no test asserting admin authorizationForse già presa @urielit1313-creator l’ha presa 29 giorni fa. Apertasecurity testing
Difficoltà 2/5 1-3 ore Idoneità per principianti 25/100
MergeFi/contracts#565 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di MergeFi/contracts
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
aws/aws-emulator#30 · 1 commento ·
-
tormach-plasma refusal text with no machine reads "no machine is not declared a plasma machine"Apertaarea:post bug effort:low P2 source:agent
Difficoltà 1/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
I maintainer di solito rispondono entro 1 giorno
-
Chat images never show without ImageMagick: ffprobe fallback uses an option string ffprobe 9 rejectsAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 79/100
eliasstravik/herdr-projects#1011 ·
-
design-data-spec
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
adobe/spectrum-design-data#1744 ·
I maintainer di solito rispondono entro 1 giorno