[ANCHOR-1186] Fix MEMO_ID validation to support full Stellar uint64 range#1918
[ANCHOR-1186] Fix MEMO_ID validation to support full Stellar uint64 range#1918
MEMO_ID validation to support full Stellar uint64 range#1918Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates memo parsing/serialization to correctly support Stellar MEMO_ID values across the full uint64 space by moving off Java signed long parsing/formatting and centralizing memo-id creation in MemoHelper.
Changes:
- Introduces
MemoHelper.makeMemoId()usingBigIntegerand updates memo parsing to use it. - Updates XDR memo-id string conversion to avoid
longValue()truncation for values aboveLong.MAX_VALUE. - Updates muxed-account memo handling and adds unit tests for uint64 memo-id round-trips and boundary values.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| platform/src/main/java/org/stellar/anchor/platform/observer/stellar/DefaultPaymentListener.java | Uses muxed-id memo creation without narrowing to signed long. |
| core/src/main/java/org/stellar/anchor/util/MemoHelper.java | Adds BigInteger-based memo-id parsing and fixes XDR memo-id string conversion. |
| core/src/main/java/org/stellar/anchor/sep10/Sep10Service.java | Reuses shared memo-id parsing for SEP-10 memo validation. |
| core/src/test/kotlin/org/stellar/anchor/util/MemoHelperTest.kt | Adds tests for uint64 memo-id boundaries and XDR round-trip behavior. |
Comments suppressed due to low confidence (1)
core/src/main/java/org/stellar/anchor/sep10/Sep10Service.java:213
- validateChallengeRequestMemo() catches NumberFormatException for both non-numeric input and out-of-range/invalid numeric values (e.g., <=0 or > uint64 max), but the log/error message says "invalid memo format". Consider changing the message to "Invalid memo value" (or including the allowed range) so callers get an accurate error for numeric-but-invalid memos.
public Memo validateChallengeRequestMemo(ChallengeRequest request) throws SepException {
// Validate memo. It should be a positive uint64 integer if not null.
try {
if (request.getMemo() != null) {
return makeMemoId(request.getMemo());
} else {
return null;
}
} catch (NumberFormatException e) {
infoF("invalid memo format: {}. Only MEMO_ID is supported", request.getMemo());
throw new SepValidationException(format("Invalid memo format: %s", request.getMemo()));
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public static MemoId makeMemoId(String memo) { | ||
| try { | ||
| BigInteger memoId = new BigInteger(memo); | ||
| if (memoId.compareTo(BigInteger.ZERO) <= 0) { |
There was a problem hiding this comment.
We should probably also catch the upper bound, otherwise new MemoId(memoId) can throw an illegal argument exception. One option would be :
if (memoId.compareTo(XdrUnsignedHyperInteger.MIN_VALUE) <= 0
|| memoId.compareTo(XdrUnsignedHyperInteger.MAX_VALUE) > 0) {
throw new NumberFormatException("MEMO_ID must be between 0 and 2^64-1");
}There was a problem hiding this comment.
Good point on the upper bound!
I looked into this and the SDK's MemoId(BigInteger) constructor already validates the exact same range (0 to 2^64-1) using XdrUnsignedHyperInteger.MIN_VALUE and MAX_VALUE. So I decided to delegate to the constructor and just wrap the IllegalArgumentException into NumberFormatException (since the caller catches NumberFormatException). This way if the SDK ever adjusts its validation, we inherit the change automatically.
MEMO_ID validation to support full Stellar uint64 rangeMEMO_ID validation to support full Stellar uint64 range
Description
Context
Partner reported that SEP-24 requests with refund_memo 11872666534918305457 were rejected with "Invalid Memo" due to this refund_memo value above Java's
Long.MAX_VALUE(9,223,372,036,854,775,807)Stellar protocol defines
MEMO_IDas uint64, but the platform was using Java's signed long for parsing, which only supports half the range.The same issue existed in SEP-10 memo validation,
xdrMemoToString(used by the payment observer for memo matching), and muxed account memo handling inDefaultPaymentListenerTesting
Documentation
N/A
Known limitations
N/A