sameSendRequest function
Whether two requests are THE SAME request to the send flow — the identity a
final "no transaction" attaches to (stage S8 deadline, R05): after the
entry's grace revoked a request, a host re-navigation onto the send path
with the same request is refused, and one with a different request is a
new host act that pays.
The rule, in the host's terms: when BOTH requests carry a tag
(WalletSendRequest.correlationId) the host has said what identifies them,
so the tags decide — the same tag is the same request, a different tag is a
different one, whatever the fields say. Otherwise the payment fields decide:
untagged on both sides, which is what makes two const literals with the
same fields — one object in Dart — the same request without a special
case; and tagged on ONE side only, because a tag the other side never
carried is not a host's statement that two payments differ (the diff
review, ADR-0558: an untagged request the grace revoked, re-navigated with
the same fields plus a retry tag, paid — the paying direction on a money
predicate; the fields are the safe side, and a fresh push is always a new
grant). Pure + total.
Implementation
bool sameSendRequest(WalletSendRequest a, WalletSendRequest b) {
if (identical(a, b)) return true;
final tagA = a.correlationId;
final tagB = b.correlationId;
if (tagA != null && tagB != null) return tagA == tagB;
final memoA = a.machineMemo;
final memoB = b.machineMemo;
final sameMachineMemo = memoA == null || memoB == null
? memoA == memoB
: memoA.purpose == memoB.purpose && listEquals(memoA.bytes, memoB.bytes);
return a.address == b.address &&
a.amountZat == b.amountZat &&
a.memo == b.memo &&
a.label == b.label &&
a.lockRecipient == b.lockRecipient &&
sameMachineMemo;
}