This commit expands the definition of the dust limit to take into
account commitment fees as well as dust HTLCs. The dust limit is now
known as a fee exposure threshold. Dust HTLCs are fees anyways so it
makes sense to account for commitment fees as well. The link has
been modified slightly to calculate dust. In the future, the switch
dust calculations can be removed.
For the final hop in a blinded route, the SCID and RelayInfo fields will
_not_ be set. So these fields need to be converted to optional records.
The existing BlindedRouteData constructor is also renamed to
`NewNonFinalBlindedRouteData` in preparation for a
`NewFinalBlindedRouteData` constructor which will be used to construct
the blinded data for the final hop which will contain a much smaller set
of data. The SCID and RelayInfo parameters of the constructor are left
as non-pointers in order to force the caller to set them in the case
that the constructor is called for non-final nodes. The other option
would be to create a single constructor where all parameters are
optional but I think this makes it easier for the caller to make a
mistake.
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 moves `DetermineFeePerKw` into the `Estimate` method on
`FeePreference`. A few callsites previously calling `DetermineFeePerKw`
without the max fee rate is now also temporarily fixed by forcing them
to use `Estimate` with the default sweeper max fee rate.
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).
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.
Here we notice that the only use of the Peer call on the link is
to find out what the peer's pubkey is. To avoid leaking handles to
IO actions outside the interface we reduce the surface area to
just return the peer's public key.
In this commit, we add the ability to start a link in shutdown mode.
This means that we immediately disable any new HTLC adds in the outgoing
direction and that we queue up a Shutdown message after the next
CommitSig message is sent (or immediately if no CommitSig message is
owed).
In this commit, the `ChannelUpdateHandler`'s `EnableAdds` and
`DisableAdds` methods are adjusted to return booleans instead of errors.
This is done becuase currently, any error returned by these methods is
treated by just logging the error since today all it means is that the
proposed update has already been done. And so all we do today is log the
error. But in future, if these methods are updated to return actual
errors that need to be handled, then we might forget to handle them
correctly at the various call sights. So we instead change the signature
of the function to just return a boolean. In future, if we do need to
return any error, we will have to go inspect every call sight in any
case to fix compliation & then we can be sure we are handling the errors
correctly.
When the link is flushing in the incoming direction, it means
adds are invalid. The best chance we have at dealing with this
is to drop the connection. This should roll back the channel
state to the last CommitSig. If the remote has already sent a
CommitSig we haven't received yet, channel state will be
re-synchronized with a ChannelReestablish message upon
reconnection and the protocol state that caused us to flush
the link will be rolled back. In the event that there was some
non-deterministic behavior in the remote that caused them to
violate the protocol, we have a decent shot at correcting it
this way, since reconnecting will put us in the cleanest
possible state to try again.