fix(payments): reject splits whose ratios do not sum to 1.0 - #831
Open
woahwhattheheck wants to merge 3 commits into
Open
fix(payments): reject splits whose ratios do not sum to 1.0#831woahwhattheheck wants to merge 3 commits into
woahwhattheheck wants to merge 3 commits into
Conversation
Recipient percentages were never validated, so a rounding error or a typo produced a split that silently over- or underpaid the total. splitExecutor now checks the sum before any network work, within SPLIT_RATIO_TOLERANCE, and throws SplitRatioSumError carrying the actual sum. Amount-only splits declare no ratios and are unaffected.
0.1+0.2+0.7 sums to exactly 1; 0.7+0.2+0.1 does not. The tolerance case needs the ordering that genuinely falls short.
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #778.
splitExecutorran its capacity pre-flight and returned success without everchecking that the recipients' shares add up. A rounding error or a typo in the
percentages silently produced a split that over- or underpaid the total.
Changes
src/payments/splitExecutor.ts:SPLIT_RATIO_TOLERANCE— exported,1e-9.SplitRatioSumError— thrown when the sum is out of tolerance, carryingactualSumandtolerance.assertSplitRatiosSumToOne(recipients)— exported so callers can validate asplit without executing it.
SplitRecipient.ratio?: number— a recipient's share as a fraction in[0, 1].splitExecutorcalls the check first, before any network work, so amis-allocated split never reaches the capacity checks.
Backward compatibility
SplitRecipienthad no ratio field, so the check has to decide what anexisting amount-only caller means. A split that declares no ratios at all
is treated as amount-only and passes untouched — every current caller is
unaffected. Once any recipient declares a ratio, the whole split is validated,
and a recipient that omits one contributes
0, which surfaces as a failed sumrather than being silently skipped.
Two details
depending on order:
0.1 + 0.2 + 0.7 === 1, but0.7 + 0.2 + 0.1 === 0.9999999999999999. An exact comparison would reject the second ordering,which is why
1e-9exists and why the test uses that ordering.NaNratio fails. The comparison is written as a negated<=so aNaNsum throws instead of slipping through, since every comparison againstNaNis false.Validation
npx vitest run test/splitExecutor.ratio.test.ts— 13/13 passing. Coversamount-only splits, empty lists, exact sums, the floating-point ordering
above, under- and over-allocation, the reported
actualSum, an omittedratio,
NaN, and rejection throughsplitExecutoritself.splitExecutorisexercised with
skipCapacityCheck: true, so no Horizon call is made.npx tsc --noEmitreports 210 errors on this branch and 210 on unmodifiedmain— identical, with none insrc/payments/splitExecutor.ts. Thatbaseline is pre-existing and unrelated to this change.