mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
9f038c6191
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.
238 lines
6.7 KiB
Go
238 lines
6.7 KiB
Go
package htlcswitch
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"io"
|
|
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
|
"github.com/lightningnetwork/lnd/channeldb/models"
|
|
"github.com/lightningnetwork/lnd/htlcswitch/hop"
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
)
|
|
|
|
// EmptyCircuitKey is a default value for an outgoing circuit key returned when
|
|
// a circuit's keystone has not been set. Note that this value is invalid for
|
|
// use as a keystone, since the outgoing channel id can never be equal to
|
|
// sourceHop.
|
|
var EmptyCircuitKey CircuitKey
|
|
|
|
// CircuitKey is a tuple of channel ID and HTLC ID, used to uniquely identify
|
|
// HTLCs in a circuit. Circuits are identified primarily by the circuit key of
|
|
// the incoming HTLC. However, a circuit may also be referenced by its outgoing
|
|
// circuit key after the HTLC has been forwarded via the outgoing link.
|
|
type CircuitKey = models.CircuitKey
|
|
|
|
// PaymentCircuit is used by the switch as placeholder between when the
|
|
// switch makes a forwarding decision and the outgoing link determines the
|
|
// proper HTLC ID for the local log. After the outgoing HTLC ID has been
|
|
// determined, the half circuit will be converted into a full PaymentCircuit.
|
|
type PaymentCircuit struct {
|
|
// AddRef is the forward reference of the Add update in the incoming
|
|
// link's forwarding package. This value is set on the htlcPacket of the
|
|
// returned settle/fail so that it can be removed from disk.
|
|
AddRef channeldb.AddRef
|
|
|
|
// Incoming is the circuit key identifying the incoming channel and htlc
|
|
// index from which this ADD originates.
|
|
Incoming CircuitKey
|
|
|
|
// Outgoing is the circuit key identifying the outgoing channel, and the
|
|
// HTLC index that was used to forward the ADD. It will be nil if this
|
|
// circuit's keystone has not been set.
|
|
Outgoing *CircuitKey
|
|
|
|
// PaymentHash used as unique identifier of payment.
|
|
PaymentHash [32]byte
|
|
|
|
// IncomingAmount is the value of the HTLC from the incoming link.
|
|
IncomingAmount lnwire.MilliSatoshi
|
|
|
|
// OutgoingAmount specifies the value of the HTLC leaving the switch,
|
|
// either as a payment or forwarded amount.
|
|
OutgoingAmount lnwire.MilliSatoshi
|
|
|
|
// ErrorEncrypter is used to re-encrypt the onion failure before
|
|
// sending it back to the originator of the payment.
|
|
ErrorEncrypter hop.ErrorEncrypter
|
|
|
|
// LoadedFromDisk is set true for any circuits loaded after the circuit
|
|
// map is reloaded from disk.
|
|
//
|
|
// NOTE: This value is determined implicitly during a restart. It is not
|
|
// persisted, and should never be set outside the circuit map.
|
|
LoadedFromDisk bool
|
|
}
|
|
|
|
// HasKeystone returns true if an outgoing link has assigned this circuit's
|
|
// outgoing circuit key.
|
|
func (c *PaymentCircuit) HasKeystone() bool {
|
|
return c.Outgoing != nil
|
|
}
|
|
|
|
// newPaymentCircuit initializes a payment circuit on the heap using the payment
|
|
// hash and an in-memory htlc packet.
|
|
func newPaymentCircuit(hash *[32]byte, pkt *htlcPacket) *PaymentCircuit {
|
|
var addRef channeldb.AddRef
|
|
if pkt.sourceRef != nil {
|
|
addRef = *pkt.sourceRef
|
|
}
|
|
|
|
return &PaymentCircuit{
|
|
AddRef: addRef,
|
|
Incoming: CircuitKey{
|
|
ChanID: pkt.incomingChanID,
|
|
HtlcID: pkt.incomingHTLCID,
|
|
},
|
|
PaymentHash: *hash,
|
|
IncomingAmount: pkt.incomingAmount,
|
|
OutgoingAmount: pkt.amount,
|
|
ErrorEncrypter: pkt.obfuscator,
|
|
}
|
|
}
|
|
|
|
// makePaymentCircuit initializes a payment circuit on the stack using the
|
|
// payment hash and an in-memory htlc packet.
|
|
func makePaymentCircuit(hash *[32]byte, pkt *htlcPacket) PaymentCircuit {
|
|
var addRef channeldb.AddRef
|
|
if pkt.sourceRef != nil {
|
|
addRef = *pkt.sourceRef
|
|
}
|
|
|
|
return PaymentCircuit{
|
|
AddRef: addRef,
|
|
Incoming: CircuitKey{
|
|
ChanID: pkt.incomingChanID,
|
|
HtlcID: pkt.incomingHTLCID,
|
|
},
|
|
PaymentHash: *hash,
|
|
IncomingAmount: pkt.incomingAmount,
|
|
OutgoingAmount: pkt.amount,
|
|
ErrorEncrypter: pkt.obfuscator,
|
|
}
|
|
}
|
|
|
|
// Encode writes a PaymentCircuit to the provided io.Writer.
|
|
func (c *PaymentCircuit) Encode(w io.Writer) error {
|
|
if err := c.AddRef.Encode(w); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := c.Incoming.Encode(w); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := w.Write(c.PaymentHash[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
var scratch [8]byte
|
|
|
|
binary.BigEndian.PutUint64(scratch[:], uint64(c.IncomingAmount))
|
|
if _, err := w.Write(scratch[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
binary.BigEndian.PutUint64(scratch[:], uint64(c.OutgoingAmount))
|
|
if _, err := w.Write(scratch[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Defaults to EncrypterTypeNone.
|
|
var encrypterType hop.EncrypterType
|
|
if c.ErrorEncrypter != nil {
|
|
encrypterType = c.ErrorEncrypter.Type()
|
|
}
|
|
|
|
err := binary.Write(w, binary.BigEndian, encrypterType)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Skip encoding of error encrypter if this half add does not have one.
|
|
if encrypterType == hop.EncrypterTypeNone {
|
|
return nil
|
|
}
|
|
|
|
return c.ErrorEncrypter.Encode(w)
|
|
}
|
|
|
|
// Decode reads a PaymentCircuit from the provided io.Reader.
|
|
func (c *PaymentCircuit) Decode(r io.Reader) error {
|
|
if err := c.AddRef.Decode(r); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := c.Incoming.Decode(r); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := io.ReadFull(r, c.PaymentHash[:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
var scratch [8]byte
|
|
|
|
if _, err := io.ReadFull(r, scratch[:]); err != nil {
|
|
return err
|
|
}
|
|
c.IncomingAmount = lnwire.MilliSatoshi(
|
|
binary.BigEndian.Uint64(scratch[:]))
|
|
|
|
if _, err := io.ReadFull(r, scratch[:]); err != nil {
|
|
return err
|
|
}
|
|
c.OutgoingAmount = lnwire.MilliSatoshi(
|
|
binary.BigEndian.Uint64(scratch[:]))
|
|
|
|
// Read the encrypter type used for this circuit.
|
|
var encrypterType hop.EncrypterType
|
|
err := binary.Read(r, binary.BigEndian, &encrypterType)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch encrypterType {
|
|
case hop.EncrypterTypeNone:
|
|
// No encrypter was provided, such as when the payment is
|
|
// locally initiated.
|
|
return nil
|
|
|
|
case hop.EncrypterTypeSphinx:
|
|
// Sphinx encrypter was used as this is a forwarded HTLC.
|
|
c.ErrorEncrypter = hop.NewSphinxErrorEncrypter()
|
|
|
|
case hop.EncrypterTypeMock:
|
|
// Test encrypter.
|
|
c.ErrorEncrypter = NewMockObfuscator()
|
|
|
|
case hop.EncrypterTypeIntroduction:
|
|
c.ErrorEncrypter = hop.NewIntroductionErrorEncrypter()
|
|
|
|
case hop.EncrypterTypeRelaying:
|
|
c.ErrorEncrypter = hop.NewRelayingErrorEncrypter()
|
|
|
|
default:
|
|
return UnknownEncrypterType(encrypterType)
|
|
}
|
|
|
|
return c.ErrorEncrypter.Decode(r)
|
|
}
|
|
|
|
// InKey returns the primary identifier for the circuit corresponding to the
|
|
// incoming HTLC.
|
|
func (c *PaymentCircuit) InKey() CircuitKey {
|
|
return c.Incoming
|
|
}
|
|
|
|
// OutKey returns the keystone identifying the outgoing link and HTLC ID. If the
|
|
// circuit hasn't been completed, this method returns an EmptyKeystone, which is
|
|
// an invalid outgoing circuit key. Only call this method if HasKeystone returns
|
|
// true.
|
|
func (c *PaymentCircuit) OutKey() CircuitKey {
|
|
if c.Outgoing != nil {
|
|
return *c.Outgoing
|
|
}
|
|
|
|
return EmptyCircuitKey
|
|
}
|