You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
230 lines
6.5 KiB
230 lines
6.5 KiB
package htlcswitch |
|
|
|
import ( |
|
"encoding/binary" |
|
"io" |
|
|
|
"github.com/lightningnetwork/lnd/channeldb" |
|
"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 = channeldb.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() |
|
|
|
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 |
|
}
|
|
|