The full-node daemon unconditionally shuffles a transaction's shielded outputs (transaction_builder.cpp ShuffleOutputs, to hide the change position), so the two 0-value HushChat memo outputs land at random note positions — the header is NOT guaranteed to precede its payload. The receive parser paired a header only with the NEXT payload in position order, so a shuffled send failed to pair (the message was silently lost) ~50%+ of the time. groupHushChatMemoOutputs now holds an orphan payload (one seen before its header) and pairs it with the header when it arrives, so header↔payload pair regardless of on-chain order. A chat tx carries exactly one header + one payload (the harvest already skips change outputs), so this is unambiguous. This is the complete fix for the ObsidianDragon ecosystem: full-node and lite both receive through this one C++ parser, so full-node→full-node and full-node→lite now work despite the shuffle, with no daemon fork and no lite-backend change. Context: SilentDragonXLite (which hardcodes payload position==1 and forced the daemon/backend question) is being replaced by ObsidianDragonLite. Lite send never shuffles. Tests: shuffled receive (payload-before-header, and change-interspersed) pairs + decrypts; all existing ordered-input tests unchanged. Verified: Linux + Windows build, ctest 100%, hygiene clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
254 KiB
254 KiB