Before we have sufficient signaling in the network to relay this
signal, set a zero value experimental endorsement value on the sender's
outgoing htlc. Once the network is relaying this signal and a flag day
has been set, we'll be able to set a non-zero value here.
We mine quite a few blocks in this test to trigger a htlc timeout,
so it can be pretty slow. This fix adds assertions for additional
"state steps" that happen in between the failing of the htlc on-chain
and asserting that we're fully cleared out so that slower running
machines won't timeout.
This itest adds a test that we still propagate blinded errors back
properly after a restart with an on-chain resolution. The test also
updates our sendpayment timeout to longer so that there's time to
resolve the on chain claim.
Create our error encrypter with a wrapped type if we have a blinding
point present. Doing this in the iterator allows us to track this
information when we have both pieces of information available to us,
compared to trying to handle this later down the line:
- Downstream link on failure: we know that we've set a blinding point
for out outgoing HTLC, but not whether we're introduction or not
- Upstream link on failure: once the failure packet has been sent
through the switch, we no longer know whether we were the introduction
point (without looking it up / examining our payload again /
propagating this information through the switch).
Introduce two wrapper types for our existing SphinxErrorEncrypter
that are used to represent error encrypters where we're a part of a
blinded route. These encrypters are functionally the same as a sphinx
encrypter, and are just used as "markers" so that we know that we
need to handle our error differently due to our different role.
We need to persist this information to account for restart cases where
we've resovled the outgoing HTLC, then restart and need to handle the
error for the incoming link. Specifically, this is relevant for:
- On chain resolution messages received after restart
- Forwarding packages that are re-forwarded after restart
This is also generally helpful, because we can store this information
in one place (the circuit) rather than trying to reconstruct it in
various places when forwarding the failure back over the switch.
We need to know what role we're playing to be able to handle errors
correctly, but the information that we need for this is held by our
iterator:
- Whether we had a blinding point in update add (blinding kit)
- Whether we had a blinding point in payload
As we're now going to use the route role return value even when our
err!=nil, we rename the error to signal that we're using less
canonical golang here.
An alternative to this approach is to attach a RouteRole to our
ErrInvalidPayload. The downside of that approach is:
- Propagate context through parsing (whether we had updateAddHtlc)
- Clumsy handling for errors that are not of type ErrInvalidPayload
When handling blinded errors, we need to know whether there was a
blinding key in our payload when we successfully parsed our payload
but then found an invalid set of fields. The combination of
parsing and validation in NewPayloadFromReader means that we don't know
whether a blinding point was available to us by the time the error is
returned.
This commit splits parsing and validation into two functions so that
we can take a look at what we actually pulled of the payload in between
parsing and TLV validation.
This commit moves all our validation related to the presence of fields
into ValidateParsedPayloadTypes so that we can handle them in a single
place. We draw the distinction between:
- Validation of the payload (and the context within it's being parsed,
final hop / blinded hop etc)
- Processing and validation of encrypted data, where we perform
additional cryptographic operations and validate that the fields
contained in the blob are valid.
This helps draw the line more clearly between the two validation types,
rather than splitting some payload-releated blinded hop processing
into the encrypted data processing part. The downside of this approach
(vs doing the blinded path payload check _after_ payload validation)
is that we have to pass additional context into payload validation
(ie, whether we got a blinding point in our UpdateAddHtlc - as we
already do for isFinalHop).
This commit adds handling for malformed HTLC errors related to blinded
paths. We expect to receive these errors _within_ a blinded path,
because all non-introduction nodes are instructed to return malformed
errors for failures.
Note that we may actually switch back to a malformed error later on if
we too are a relaying node in the route, but we handle that case the
incoming link.
This commit turns off route blinding for the daemon while we're waiting
on full handling for blinded errors. The feature remains on for itests
so that tests covering blinding can run as usual.
We don't support receiving blinded in this PR - just intercept and
settle instead. The HTLC's arrival on the interceptor indicates that
it was successfully forwarded on a blinded hop.
Reject any HTLCs that use us as an introduction point in a blinded
route if we have disabled route blinding. We have to do this after
we've processed the payload, because we only know we're an introduction
point once we've processed the payload itself.
If we received a payload with a encrypted data point set, our forwarding
information should be set from the information in our encrypted blob.
This behavior is the same for introduction and relying nodes in a
blinded route.
To separate blinded route parsing from payload parsing, we need to
return the parsed types map so that we can properly validate blinded
data payloads against what we saw in the onion.
When we have a HTLC that is part of a blinded route, we need to include
the next ephemeral blinding point in UpdateAddHtlc for the next hop. The
way that we handle the addition of this key is the same for introduction
nodes and relaying nodes within the route.
This commit introduces a blinding kits which abstracts over the
operations required to decrypt, deserialize and reconstruct forwarding
data from an encrypted blob of data included for nodes in blinded
routes.
Add an option to disable route blinding, failing back any HTLC with
a blinding point set when we haven't got the feature enabled.
Note that this commit only handles the case where we're chosen as the
relaying node (where the blinding point is in update_add_htlc), we'll
add handling for the introduction node case once we get to handling of
blinded payloads).
This field is incorrectly suffixed as "msat", when it is actually
interpreted as the proportional fee rate. This is the value that we
should be using because the sender will calculate proportional fees
accordingly. This is a breaking change to the RPC, but on an
experimental and unreleased API.
When we have payments inside of a blinded route, we need to know
the incoming amount to be able to back-calculate the amount that
we need to forward using the forwarding parameters provided in the
blinded route encrypted data. This commit adds the payment amount
to our DecodeHopIteratorRequest so that it can be threaded down to
payment forwarding information creation in later commits.
This commit adds an optional blinding point to payment descriptors and
persists them in our HTLC's extra data. A get/set pattern is used to
populate the ExtraData on our disk representation of the HTLC so that
callers do not need to worry about the underlying storage detail.
Add blinding points to update_add_htlc. This TLV will be set for
nodes that are relaying payments in blinded routes that are _not_
the introduction node.
This commit adds encoding and decoding for blinded route data blobs.
TLV fields such as path_id (which are only used for the final hop)
are omitted to minimize the change size.
We'll need to pack feature vectors for route blinding, so we pull
the encoding/decoding out into separate functions (currently
contained in ChannelType). Though it's more lines of code, we keep
most of the ChannelType assertions so that we strictly enforce
use of the alias.
We do not expect blinding errors from the final node:
1. If the introduction is the recipient, they should use regular errors.
2. Otherwise, nodes have no business sending this error when they are
not part of a blinded route.
Note: this refactor updates the inequality used from >= 2 to > 1 to
align with the rest of this file so that we express this concept
consistently throughout the code.
This commit adds handling for route blinding errors that are reported
by the introduction node in a multi-hop blinded route. As the
introduction node is always responsible for handling blinded errors,
it is not penalized - only the final hop is penalized to discourage the
blinded route without filling up mission control with ephemeral
results.
If this error code is reported by a node that is not an introduction
node, we penalize the node because it is returning an error code that
it should not be using.
This commit adds handling for errors that originate after the
introduction node when making payment to a blinded route. This
indicates that the introduction node is not obeying the spec, so
it is punished for the violation.
Fix our existing test to have a valid intermediate hop that will pass
stricter validation. Previously, we did not specify a next channel for
an intermediate hop (which violates bolt4).
Previously, we'd use the value of nextChanID to infer whether a payload
was for the final hop in a route. This commit updates our packing logic
to explicitly signal to account for blinded routes, which allow zero
value nextChanID in intermediate hops. This is a preparatory commit
that allows us to more thoroughly validate payloads.
Previously, we were using nextChanID to determine whether a hop
payload is for the final recipient. This is no longer suitable in a
route-blinding world where intermediate hops are allowed to have zero
nextChanID TLVs (as this information is provided to forwarding nodes
in their encrypted data). This commit updates payload reading to use
the signal provided by sphinx that we are on the last packet, rather
than implying it from the contents of a hop.
Update test to include the sphinx action to more closely represent
reality. This will be required when we add more validation to the
presence of a nextChanID field. A MoreHops action is chose because
we're testing the case with a payload that contains forwarding info.
Add the missing channel field to the final hop in our clear text
route test case. Note that this is the channel of the hop. With the
addition of stricter validation, we'll need this so that the
penultimate hop has a non-zero next channel ID.
Running test cases in parallel with shared state means that they
can intermingle if they're run at the same time and set up incorrect
values when we assert on the number of channels we have. Moving the
peer setup into the parallel test run fixes this because no single
test case can interfere with the other.
Note: This commit can be dropped before merge, it's mostly added
to make the PR easier to manually test against other
implementations that have bolt 12 invoices implemented already!
This commit updates route construction to backfill the fields
required for payment to blinded paths and set amount to forward
and expiry fields to zero for intermediate hops (as is instructed
in the route blinding specification).
We could attempt to do this in the first pass, but that loop
relies on fields like amount to forward and expiry to calculate
each hop backwards, so we keep it simple (stupid) and post
processes the blinded portion, since it's computationally cheap
and more readable.
Add the option to include a blinded route in a route request (exclusive
to including hop hints, because it's incongruous to include both), and
express the route as a chain of hop hints.
Using a chain of hints over a single hint to represent the whole path
allows us to re-use our route construction to fill in a lot of the
path on our behalf.
This commit introduces a single struct to hold all of the parameters
that are passed to FindRoute. This cleans up an already overloaded
function signature and prepares us for handling requests with blinded
routes where we need to perform some additional processing on our
para (such as extracting the target node from the blinded path).
When we introduce blinded routes, some of our hops are expected
to have zero amounts to forward in their hop payload. This commit
updates our hop fee logic to attribute the full blinded route's
fees to the introduction node. We can't actually know where/how
these fees are distributed, so we collect them all at the
introduction node.
With the addition of blinded routes, we now need to account for the
possibility that intermediate nodes payloads will not have an amount
and expiry set because that information is provided by the recipient
encrypted data blob. This commit updates our payload packing to only
optionally include those fields.
This commit adds the encrypted_data, blinding_point and total_amt_msat
tlvs to the known set of even tlvs for the onion payload. These TLVs
are added in two places (the onion payload and hop struct) because
lnd uses the same set of TLV types for both structs (and they
inherently represent the same thing).
Note: in some places, unit tests intentionally mimic the style
of older tests, so as to be more consistently readable.
This commit adds a representation of blinded payments, which include a
blinded path and aggregate routing parameters to be used in payment to
the path.
For our relatively "static" test invoice, add a simple constructor
using the globals defined, rather than having a global invoice which
risks being mutated.
Test utils currently have two different test invoices - a minimalist
one that is used in a variety of test cases, and a customizable one
that is used specifically for tests concerning invoice expiry. The
latter is renamed and moved closer to the calling code to more clearly
indicate its use.
Details of race:
- A global testInvoice is used in various invoice related tests.
- All tests are running with t.Parallel.
- TestMppPaymentWithOverpayment passes a reference to the testInvoice
to AddInvoice which writes the AddIndex.
- TestMultipleSetHeightExpiry reads the testInvoice to make a copy.