Skip to content

fix(payment-requests): validate memo length against Stellar 28-byte limit (#49) - #81

Open
Simultech369 wants to merge 1 commit into
StellarSend:mainfrom
Simultech369:fix/issue-49-payment-request-memo-length
Open

Simultech369 wants to merge 1 commit into
StellarSend:mainfrom
Simultech369:fix/issue-49-payment-request-memo-length

Conversation

@Simultech369

Copy link
Copy Markdown

Summary

Resolves #49 by enforcing Stellar's on-chain 28-byte MEMO_TEXT limit (stellar_xdr::curr::Memo::Text / StringM<28>) on payment_requests.memo at both the application service layer and database layer.

Changes

  1. Service Validation (src/services/payment_request.rs):
    • Added canonical constant pub const MAX_MEMO_TEXT_BYTES: usize = 28;.
    • Added PaymentRequestService::validate_memo(memo: Option<&str>) -> AppResult<()>.
    • Enforced validation in PaymentRequestService::create before preparing or executing the database query.
    • Measures UTF-8 byte length via memo.as_bytes().len(), guarding against multi-byte character overflow where character count <= 28 but byte length > 28.
  2. Database Migration (migrations/012_payment_requests_memo_length_check.sql):
    • Added defense-in-depth constraint:
      ALTER TABLE payment_requests
      ADD CONSTRAINT check_payment_requests_memo_length
      CHECK (memo IS NULL OR octet_length(memo) <= 28);
    • Uses octet_length for byte-exact enforcement in PostgreSQL while preserving nullability for optional memos.
  3. Unit Tests (src/services/payment_request.rs):
    • memo_at_exact_28_ascii_bytes_is_accepted (28 bytes accepted).
    • memo_exceeding_28_ascii_bytes_is_rejected (29 bytes rejected with AppError::Validation).
    • memo_multi_byte_utf8_exceeding_28_bytes_is_rejected (10 3-byte unicode symbols = 30 bytes, 10 chars, rejected with AppError::Validation).
    • memo_multi_byte_utf8_within_28_bytes_is_accepted (9 3-byte unicode symbols + 1 ASCII byte = 28 bytes total, accepted).
    • memo_none_and_empty_is_accepted (None and "" accepted).

Closes #49


Payout Address (Stellar USDC): GCK7WCZCOFNYXTH74KADMAIREIJKHIAX4ISJSGXZ4IF2MIRGAFLT4BTH
EVM (Base USDC): 0xEa3A353Fb3fc88DB8F0FA1E6Ea541FEdc9F5F0E7

…imit (StellarSend#49)

Stellar's on-chain MEMO_TEXT field is hard-capped at 28 bytes by the protocol (stellar_xdr::curr::Memo::Text).

- Enforce byte-length validation in PaymentRequestService::create via MAX_MEMO_TEXT_BYTES and validate_memo
- Add migration 012_payment_requests_memo_length_check.sql with CHECK (memo IS NULL OR octet_length(memo) <= 28)
- Add unit tests covering 28-byte ASCII limit, 29-byte rejection, 30-byte multi-byte UTF-8 rejection, 28-byte multi-byte acceptance, and None/empty cases

Closes StellarSend#49
@Simultech369

Copy link
Copy Markdown
Author

Hi @abayomicornelius — PR #81 is fully tested and ready for review. It resolves #49 by enforcing Stellar's 28-byte UTF-8 MEMO_TEXT limit at both the service layer (validate_memo) and database schema level via migration 012 (CHECK (memo IS NULL OR octet_length(memo) <= 28)), with all 5 boundary unit tests passing cleanly in cargo test.

Mergeable and ready for review whenever convenient!

  • Stellar Payout: GCK7WCZCOFNYXTH74KADMAIREIJKHIAX4ISJSGXZ4IF2MIRGAFLT4BTH
  • Base USDC: 0xEa3A353Fb3fc88DB8F0FA1E6Ea541FEdc9F5F0E7

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

payment_requests.memo has no length validation against Stellar's 28-byte on-chain MEMO_TEXT limit

1 participant