Skip to content

fix: reject negative escrow amount; clean up dead multi-same-out check#1031

Open
ohager wants to merge 3 commits into
developfrom
security/value-validation-hardening
Open

fix: reject negative escrow amount; clean up dead multi-same-out check#1031
ohager wants to merge 3 commits into
developfrom
security/value-validation-hardening

Conversation

@ohager

@ohager ohager commented Jun 25, 2026

Copy link
Copy Markdown
Member

Summary

Two value-validation hardening changes found during a focused security review of integer-overflow / negative-value / value-minting risks. Both are in TransactionType.java and are consistent with the negative-value guards added in #1028.

1. Reject negative escrow amount (defense-in-depth)

ESCROW_CREATION.validateAttachment only checked the aggregate amount + fee + signers·ONE_SIGNA against [0, MAX_BALANCE], so a small negative amountNqt (read raw via buffer.getLong()) could pass. It is not a mint — the negative nets to zero when the escrow is settled at its mandatory deadline action — but it is an illegal value that should be rejected at validation on every ingress path (API, raw broadcast, peer relay, block acceptance). Now guarded explicitly, mirroring the commitment / distribute-to-holders guards from #1028.

2. Remove dead/misleading MULTI_SAME_OUT check

The existing if (size < 2 && amount % size == 0) check is dead (recipient count is already bounded to 2..MAX at parse time) and misleadingly looks like a divisibility check. Enforcing divisibility was rejected as unsafe: validateAttachment runs during block acceptance, and non-divisible multi-same-out transactions have been accepted on mainnet historically, so rejecting them would break resync (chain split) with no security benefit (the per-recipient share is floor(amount/n), so the remainder is burned, never minted). The dead check is removed and the intentional behavior is documented.

Tests

  • TransactionTypeEscrowTest — negative amount rejected, non-negative passes (written test-first, watched fail before the fix).
  • TransactionTypeMultiSameOutTest — characterization test locking in that a non-divisible amount stays valid, to prevent a future accidental consensus break.

Full unit + chain.* integration suite passes locally.

Reviewer note (consensus)

Change #1 tightens a consensus validation. As with the #1028 guards, it assumes no historical mainnet escrow ever carried a negative amountNqt. Escrow is an old feature, so a quick chain scan to confirm this is worthwhile before merge.

🤖 Generated with Claude Code

Add an explicit non-negative guard for the escrow creation amount in
ESCROW_CREATION.validateAttachment, consistent with the negative-value
guards added for commitment and distribute-to-holders in #1028. A negative
escrow amount nets to zero at payout (not a mint) but is an illegal value
that should be rejected at validation on every ingress path, including raw
byte broadcast.

Also remove the dead/misleading recipient-count check in
MULTI_SAME_OUT.validateAttachment (recipient bounds are already enforced
when the attachment is parsed) and document why divisibility is
intentionally NOT enforced: the per-recipient share is floor(amount/n) so
any remainder is burned (never minted), and tightening this consensus rule
would reject historically-accepted transactions on resync.

Tests:
- TransactionTypeEscrowTest: negative amount rejected, non-negative passes.
- TransactionTypeMultiSameOutTest: characterizes that a non-divisible
  amount stays valid, preventing a future accidental consensus break.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ohager
ohager requested a review from jjos2372 as a code owner June 25, 2026 22:53
@github-actions

Copy link
Copy Markdown

PR Size Check: Passed

Files: 3/10, Lines added: 151/1000

Protocol-Frozen Files Modified

The following consensus-critical files were changed. These require explicit maintainer approval and are only permitted for verified security fixes:

  • src/brs/TransactionType.java

@github-actions

Copy link
Copy Markdown

PR Size Check: Passed

Files: 3/10, Lines added: 151/1000

Protocol-Frozen Files Modified

The following consensus-critical files were changed. These require explicit maintainer approval and are only permitted for verified security fixes:

  • src/brs/TransactionType.java

@ohager ohager added the claude-review 🕵️ This PR is being reviewed by Claude label Jun 26, 2026
@github-actions

Copy link
Copy Markdown

PR Size Check: Passed

Files: 4/10, Lines added: 156/1000

Protocol-Frozen Files Modified

The following consensus-critical files were changed. These require explicit maintainer approval and are only permitted for verified security fixes:

  • src/brs/TransactionType.java

@ohager ohager added claude-review 🕵️ This PR is being reviewed by Claude and removed claude-review 🕵️ This PR is being reviewed by Claude labels Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

claude-review 🕵️ This PR is being reviewed by Claude

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant