XMoveFunds/Main.cpp carried a duplicate of FundsMover/Attempter.cpp's
parse_chan_update that had drifted behind the PR320 deep-review
hardening: it lacked the fee_proportional_millionths > 1e6 reject, its
TLV bounds check (tpos + tlen > cu_size) could wrap on an
attacker-supplied bigsize length, and it accepted inbound-fee TLVs of
any length >= 8 instead of exactly 8.
Extract the parser into a new shared module, Ln/OnionError, carrying
the hardened semantics:
- failcode_name(): BOLT 04 failcode -> grep-friendly name
(previously Track-A-only).
- ChanUpdate: the parsed channel_update policy subset askrene
consumes, plus the bLIP-18 inbound-fee TLV fields, and an
operator== that deliberately compares only the askrene-visible
policy (the repeat-update detection use case).
- parse_chan_update(): extract the embedded BOLT 07 channel_update
from a sendpay 204 raw_message, with the prop clamp doubling as
an overflow guarantee for callers' ceil(amt * prop / 1e6) math.
XMoveFunds switches to the shared module via using-declarations, so
call sites are unchanged; its three 204 log lines now print the
failcode name next to the hex. Also correct a stale comment above
failure_summary() claiming the channel_update-refresh branch is
skipped -- it has existed since the FEE_INSUFFICIENT dissection work;
only the in-call retry is (deliberately) absent.
FundsMover/Attempter.cpp intentionally keeps its own copy: Track A is
being treated as done, and switching it to Ln/OnionError is deferred
to the next time Track A is touched for its own reasons.
New unit test tests/ln/test_onionerror covers the onion wire format
for all carrying failcodes, both type-prefix forms, the prop clamp
boundary (1e6 accepted, 1e6+1 rejected), signed inbound-fee values,
exact-8 TLV length enforcement, and the bigsize-wrap attack that the
old Track B bounds check would have mishandled. 86/86 tests pass.