Description
Administrative functions may allow split ratios to be modified after invoice creation but before the funding target is reached, enabling a malicious or compromised admin to redirect funds mid-campaign. Once any contributor payment is recorded, the split ratios must be frozen and any subsequent mutation attempt must be rejected with an explicit error.
Technical Context
contracts/split/src/lib.rs — update_split_ratio() or equivalent admin entry point that currently writes to DataKey::Recipients; contracts/split/src/storage.rs — add a read of DataKey::TotalContributed to determine whether any payment has landed before allowing writes; contracts/split/src/error.rs — add Error::SplitRatioLocked; contracts/split/src/events.rs — emit event::split_ratio_locked(invoice_id, locked_at_ledger) when the first contribution is recorded, signalling the freeze.
Acceptance Criteria
Description
Administrative functions may allow split ratios to be modified after invoice creation but before the funding target is reached, enabling a malicious or compromised admin to redirect funds mid-campaign. Once any contributor payment is recorded, the split ratios must be frozen and any subsequent mutation attempt must be rejected with an explicit error.
Technical Context
contracts/split/src/lib.rs—update_split_ratio()or equivalent admin entry point that currently writes toDataKey::Recipients;contracts/split/src/storage.rs— add a read ofDataKey::TotalContributedto determine whether any payment has landed before allowing writes;contracts/split/src/error.rs— addError::SplitRatioLocked;contracts/split/src/events.rs— emitevent::split_ratio_locked(invoice_id, locked_at_ledger)when the first contribution is recorded, signalling the freeze.Acceptance Criteria
update_split_ratio()whenTotalContributed > 0returnsError::SplitRatioLockedwithout modifying any storage entryupdate_split_ratio()whenTotalContributed == 0succeeds and writes the new ratios as beforeTotalContributedread and the ratio-write guard are performed within the same contract invocation with no observable TOCTOU windowsplit_ratio_lockedevent is emitted exactly once — on the firstpay()call — and includes the ledger sequence numbercontracts/split/src/tests/ratio_lock_tests.rscover: ratio update before first payment (allowed), after first payment (denied), and after full funding (denied)