diff --git a/htlcswitch/circuit.go b/htlcswitch/circuit.go index d42a5530..16b48c29 100644 --- a/htlcswitch/circuit.go +++ b/htlcswitch/circuit.go @@ -5,6 +5,7 @@ import ( "io" "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/lnwire" ) @@ -51,7 +52,7 @@ type PaymentCircuit struct { // ErrorEncrypter is used to re-encrypt the onion failure before // sending it back to the originator of the payment. - ErrorEncrypter ErrorEncrypter + ErrorEncrypter hop.ErrorEncrypter // LoadedFromDisk is set true for any circuits loaded after the circuit // map is reloaded from disk. @@ -136,7 +137,7 @@ func (c *PaymentCircuit) Encode(w io.Writer) error { } // Defaults to EncrypterTypeNone. - var encrypterType EncrypterType + var encrypterType hop.EncrypterType if c.ErrorEncrypter != nil { encrypterType = c.ErrorEncrypter.Type() } @@ -147,7 +148,7 @@ func (c *PaymentCircuit) Encode(w io.Writer) error { } // Skip encoding of error encrypter if this half add does not have one. - if encrypterType == EncrypterTypeNone { + if encrypterType == hop.EncrypterTypeNone { return nil } @@ -183,23 +184,23 @@ func (c *PaymentCircuit) Decode(r io.Reader) error { binary.BigEndian.Uint64(scratch[:])) // Read the encrypter type used for this circuit. - var encrypterType EncrypterType + var encrypterType hop.EncrypterType err := binary.Read(r, binary.BigEndian, &encrypterType) if err != nil { return err } switch encrypterType { - case EncrypterTypeNone: + case hop.EncrypterTypeNone: // No encrypter was provided, such as when the payment is // locally initiated. return nil - case EncrypterTypeSphinx: + case hop.EncrypterTypeSphinx: // Sphinx encrypter was used as this is a forwarded HTLC. - c.ErrorEncrypter = NewSphinxErrorEncrypter() + c.ErrorEncrypter = hop.NewSphinxErrorEncrypter() - case EncrypterTypeMock: + case hop.EncrypterTypeMock: // Test encrypter. c.ErrorEncrypter = NewMockObfuscator() diff --git a/htlcswitch/circuit_map.go b/htlcswitch/circuit_map.go index 017e2b78..fa91bfcd 100644 --- a/htlcswitch/circuit_map.go +++ b/htlcswitch/circuit_map.go @@ -179,7 +179,7 @@ type CircuitMapConfig struct { // ExtractErrorEncrypter derives the shared secret used to encrypt // errors from the obfuscator's ephemeral public key. - ExtractErrorEncrypter ErrorEncrypterExtracter + ExtractErrorEncrypter hop.ErrorEncrypterExtracter } // NewCircuitMap creates a new instance of the circuitMap. diff --git a/htlcswitch/circuit_test.go b/htlcswitch/circuit_test.go index 7743768d..8640fd29 100644 --- a/htlcswitch/circuit_test.go +++ b/htlcswitch/circuit_test.go @@ -9,9 +9,10 @@ import ( "github.com/btcsuite/btcd/btcec" bitcoinCfg "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcutil" - "github.com/lightningnetwork/lightning-onion" + sphinx "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/htlcswitch" + "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/lnwire" ) @@ -30,7 +31,7 @@ var ( // testExtracter is a precomputed extraction of testEphemeralKey, using // the sphinxPrivKey. - testExtracter *htlcswitch.SphinxErrorEncrypter + testExtracter *hop.SphinxErrorEncrypter ) func init() { @@ -67,7 +68,7 @@ func initTestExtracter() { testEphemeralKey, ) - sphinxExtracter, ok := obfuscator.(*htlcswitch.SphinxErrorEncrypter) + sphinxExtracter, ok := obfuscator.(*hop.SphinxErrorEncrypter) if !ok { panic("did not extract sphinx error encrypter") } @@ -81,7 +82,7 @@ func initTestExtracter() { // newOnionProcessor creates starts a new htlcswitch.OnionProcessor using a temp // db and no garbage collection. -func newOnionProcessor(t *testing.T) *htlcswitch.OnionProcessor { +func newOnionProcessor(t *testing.T) *hop.OnionProcessor { sphinxRouter := sphinx.NewRouter( sphinxPrivKey, &bitcoinCfg.SimNetParams, sphinx.NewMemoryReplayLog(), ) @@ -90,7 +91,7 @@ func newOnionProcessor(t *testing.T) *htlcswitch.OnionProcessor { t.Fatalf("unable to start sphinx router: %v", err) } - return htlcswitch.NewOnionProcessor(sphinxRouter) + return hop.NewOnionProcessor(sphinxRouter) } // newCircuitMap creates a new htlcswitch.CircuitMap using a temp db and a @@ -128,7 +129,7 @@ var halfCircuitTests = []struct { outValue btcutil.Amount chanID lnwire.ShortChannelID htlcID uint64 - encrypter htlcswitch.ErrorEncrypter + encrypter hop.ErrorEncrypter }{ { hash: hash1, @@ -1142,7 +1143,7 @@ func TestCircuitMapCloseOpenCircuits(t *testing.T) { ChanID: chan1, HtlcID: 3, }, - ErrorEncrypter: &htlcswitch.SphinxErrorEncrypter{ + ErrorEncrypter: &hop.SphinxErrorEncrypter{ EphemeralKey: testEphemeralKey, }, } diff --git a/htlcswitch/failure.go b/htlcswitch/failure.go index 8309f477..4526e356 100644 --- a/htlcswitch/failure.go +++ b/htlcswitch/failure.go @@ -3,10 +3,9 @@ package htlcswitch import ( "bytes" "fmt" - "io" - "github.com/btcsuite/btcd/btcec" sphinx "github.com/lightningnetwork/lightning-onion" + "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/lnwire" ) @@ -47,203 +46,15 @@ type ErrorDecrypter interface { DecryptError(lnwire.OpaqueReason) (*ForwardingError, error) } -// EncrypterType establishes an enum used in serialization to indicate how to -// decode a concrete instance of the ErrorEncrypter interface. -type EncrypterType byte - -const ( - // EncrypterTypeNone signals that no error encyrpter is present, this - // can happen if the htlc is originates in the switch. - EncrypterTypeNone EncrypterType = 0 - - // EncrypterTypeSphinx is used to identify a sphinx onion error - // encrypter instance. - EncrypterTypeSphinx = 1 - - // EncrypterTypeMock is used to identify a mock obfuscator instance. - EncrypterTypeMock = 2 -) - // UnknownEncrypterType is an error message used to signal that an unexpected // EncrypterType was encountered during decoding. -type UnknownEncrypterType EncrypterType +type UnknownEncrypterType hop.EncrypterType // Error returns a formatted error indicating the invalid EncrypterType. func (e UnknownEncrypterType) Error() string { return fmt.Sprintf("unknown error encrypter type: %d", e) } -// ErrorEncrypterExtracter defines a function signature that extracts an -// ErrorEncrypter from an sphinx OnionPacket. -type ErrorEncrypterExtracter func(*btcec.PublicKey) (ErrorEncrypter, - lnwire.FailCode) - -// ErrorEncrypter is an interface that is used to encrypt HTLC related errors -// at the source of the error, and also at each intermediate hop all the way -// back to the source of the payment. -type ErrorEncrypter interface { - // EncryptFirstHop transforms a concrete failure message into an - // encrypted opaque failure reason. This method will be used at the - // source that the error occurs. It differs from IntermediateEncrypt - // slightly, in that it computes a proper MAC over the error. - EncryptFirstHop(lnwire.FailureMessage) (lnwire.OpaqueReason, error) - - // EncryptMalformedError is similar to EncryptFirstHop (it adds the - // MAC), but it accepts an opaque failure reason rather than a failure - // message. This method is used when we receive an - // UpdateFailMalformedHTLC from the remote peer and then need to - // convert that into a proper error from only the raw bytes. - EncryptMalformedError(lnwire.OpaqueReason) lnwire.OpaqueReason - - // IntermediateEncrypt wraps an already encrypted opaque reason error - // in an additional layer of onion encryption. This process repeats - // until the error arrives at the source of the payment. - IntermediateEncrypt(lnwire.OpaqueReason) lnwire.OpaqueReason - - // Type returns an enum indicating the underlying concrete instance - // backing this interface. - Type() EncrypterType - - // Encode serializes the encrypter's ephemeral public key to the given - // io.Writer. - Encode(io.Writer) error - - // Decode deserializes the encrypter' ephemeral public key from the - // given io.Reader. - Decode(io.Reader) error - - // Reextract rederives the encrypter using the extracter, performing an - // ECDH with the sphinx router's key and the ephemeral public key. - // - // NOTE: This should be called shortly after Decode to properly - // reinitialize the error encrypter. - Reextract(ErrorEncrypterExtracter) error -} - -// SphinxErrorEncrypter is a concrete implementation of both the ErrorEncrypter -// interface backed by an implementation of the Sphinx packet format. As a -// result, all errors handled are themselves wrapped in layers of onion -// encryption and must be treated as such accordingly. -type SphinxErrorEncrypter struct { - *sphinx.OnionErrorEncrypter - - EphemeralKey *btcec.PublicKey -} - -// NewSphinxErrorEncrypter initializes a blank sphinx error encrypter, that -// should be used to deserialize an encoded SphinxErrorEncrypter. Since the -// actual encrypter is not stored in plaintext while at rest, reconstructing the -// error encrypter requires: -// 1) Decode: to deserialize the ephemeral public key. -// 2) Reextract: to "unlock" the actual error encrypter using an active -// OnionProcessor. -func NewSphinxErrorEncrypter() *SphinxErrorEncrypter { - return &SphinxErrorEncrypter{ - OnionErrorEncrypter: nil, - EphemeralKey: &btcec.PublicKey{}, - } -} - -// EncryptFirstHop transforms a concrete failure message into an encrypted -// opaque failure reason. This method will be used at the source that the error -// occurs. It differs from BackwardObfuscate slightly, in that it computes a -// proper MAC over the error. -// -// NOTE: Part of the ErrorEncrypter interface. -func (s *SphinxErrorEncrypter) EncryptFirstHop(failure lnwire.FailureMessage) (lnwire.OpaqueReason, error) { - var b bytes.Buffer - if err := lnwire.EncodeFailure(&b, failure, 0); err != nil { - return nil, err - } - - // We pass a true as the first parameter to indicate that a MAC should - // be added. - return s.EncryptError(true, b.Bytes()), nil -} - -// EncryptMalformedError is similar to EncryptFirstHop (it adds the MAC), but -// it accepts an opaque failure reason rather than a failure message. This -// method is used when we receive an UpdateFailMalformedHTLC from the remote -// peer and then need to convert that into an proper error from only the raw -// bytes. -// -// NOTE: Part of the ErrorEncrypter interface. -func (s *SphinxErrorEncrypter) EncryptMalformedError(reason lnwire.OpaqueReason) lnwire.OpaqueReason { - return s.EncryptError(true, reason) -} - -// IntermediateEncrypt wraps an already encrypted opaque reason error in an -// additional layer of onion encryption. This process repeats until the error -// arrives at the source of the payment. We re-encrypt the message on the -// backwards path to ensure that the error is indistinguishable from any other -// error seen. -// -// NOTE: Part of the ErrorEncrypter interface. -func (s *SphinxErrorEncrypter) IntermediateEncrypt(reason lnwire.OpaqueReason) lnwire.OpaqueReason { - return s.EncryptError(false, reason) -} - -// Type returns the identifier for a sphinx error encrypter. -func (s *SphinxErrorEncrypter) Type() EncrypterType { - return EncrypterTypeSphinx -} - -// Encode serializes the error encrypter' ephemeral public key to the provided -// io.Writer. -func (s *SphinxErrorEncrypter) Encode(w io.Writer) error { - ephemeral := s.EphemeralKey.SerializeCompressed() - _, err := w.Write(ephemeral) - return err -} - -// Decode reconstructs the error encrypter's ephemeral public key from the -// provided io.Reader. -func (s *SphinxErrorEncrypter) Decode(r io.Reader) error { - var ephemeral [33]byte - if _, err := io.ReadFull(r, ephemeral[:]); err != nil { - return err - } - - var err error - s.EphemeralKey, err = btcec.ParsePubKey(ephemeral[:], btcec.S256()) - if err != nil { - return err - } - - return nil -} - -// Reextract rederives the error encrypter from the currently held EphemeralKey. -// This intended to be used shortly after Decode, to fully initialize a -// SphinxErrorEncrypter. -func (s *SphinxErrorEncrypter) Reextract( - extract ErrorEncrypterExtracter) error { - - obfuscator, failcode := extract(s.EphemeralKey) - if failcode != lnwire.CodeNone { - // This should never happen, since we already validated that - // this obfuscator can be extracted when it was received in the - // link. - return fmt.Errorf("unable to reconstruct onion "+ - "obfuscator, got failcode: %d", failcode) - } - - sphinxEncrypter, ok := obfuscator.(*SphinxErrorEncrypter) - if !ok { - return fmt.Errorf("incorrect onion error extracter") - } - - // Copy the freshly extracted encrypter. - s.OnionErrorEncrypter = sphinxEncrypter.OnionErrorEncrypter - - return nil - -} - -// A compile time check to ensure SphinxErrorEncrypter implements the -// ErrorEncrypter interface. -var _ ErrorEncrypter = (*SphinxErrorEncrypter)(nil) - // OnionErrorDecrypter is the interface that provides onion level error // decryption. type OnionErrorDecrypter interface { diff --git a/htlcswitch/hop/error_encryptor.go b/htlcswitch/hop/error_encryptor.go new file mode 100644 index 00000000..4c25333c --- /dev/null +++ b/htlcswitch/hop/error_encryptor.go @@ -0,0 +1,205 @@ +package hop + +import ( + "bytes" + "fmt" + "io" + + "github.com/btcsuite/btcd/btcec" + sphinx "github.com/lightningnetwork/lightning-onion" + "github.com/lightningnetwork/lnd/lnwire" +) + +// EncrypterType establishes an enum used in serialization to indicate how to +// decode a concrete instance of the ErrorEncrypter interface. +type EncrypterType byte + +const ( + // EncrypterTypeNone signals that no error encyrpter is present, this + // can happen if the htlc is originates in the switch. + EncrypterTypeNone EncrypterType = 0 + + // EncrypterTypeSphinx is used to identify a sphinx onion error + // encrypter instance. + EncrypterTypeSphinx = 1 + + // EncrypterTypeMock is used to identify a mock obfuscator instance. + EncrypterTypeMock = 2 +) + +// ErrorEncrypterExtracter defines a function signature that extracts an +// ErrorEncrypter from an sphinx OnionPacket. +type ErrorEncrypterExtracter func(*btcec.PublicKey) (ErrorEncrypter, + lnwire.FailCode) + +// ErrorEncrypter is an interface that is used to encrypt HTLC related errors +// at the source of the error, and also at each intermediate hop all the way +// back to the source of the payment. +type ErrorEncrypter interface { + // EncryptFirstHop transforms a concrete failure message into an + // encrypted opaque failure reason. This method will be used at the + // source that the error occurs. It differs from IntermediateEncrypt + // slightly, in that it computes a proper MAC over the error. + EncryptFirstHop(lnwire.FailureMessage) (lnwire.OpaqueReason, error) + + // EncryptMalformedError is similar to EncryptFirstHop (it adds the + // MAC), but it accepts an opaque failure reason rather than a failure + // message. This method is used when we receive an + // UpdateFailMalformedHTLC from the remote peer and then need to + // convert that into a proper error from only the raw bytes. + EncryptMalformedError(lnwire.OpaqueReason) lnwire.OpaqueReason + + // IntermediateEncrypt wraps an already encrypted opaque reason error + // in an additional layer of onion encryption. This process repeats + // until the error arrives at the source of the payment. + IntermediateEncrypt(lnwire.OpaqueReason) lnwire.OpaqueReason + + // Type returns an enum indicating the underlying concrete instance + // backing this interface. + Type() EncrypterType + + // Encode serializes the encrypter's ephemeral public key to the given + // io.Writer. + Encode(io.Writer) error + + // Decode deserializes the encrypter' ephemeral public key from the + // given io.Reader. + Decode(io.Reader) error + + // Reextract rederives the encrypter using the extracter, performing an + // ECDH with the sphinx router's key and the ephemeral public key. + // + // NOTE: This should be called shortly after Decode to properly + // reinitialize the error encrypter. + Reextract(ErrorEncrypterExtracter) error +} + +// SphinxErrorEncrypter is a concrete implementation of both the ErrorEncrypter +// interface backed by an implementation of the Sphinx packet format. As a +// result, all errors handled are themselves wrapped in layers of onion +// encryption and must be treated as such accordingly. +type SphinxErrorEncrypter struct { + *sphinx.OnionErrorEncrypter + + EphemeralKey *btcec.PublicKey +} + +// NewSphinxErrorEncrypter initializes a blank sphinx error encrypter, that +// should be used to deserialize an encoded SphinxErrorEncrypter. Since the +// actual encrypter is not stored in plaintext while at rest, reconstructing the +// error encrypter requires: +// 1) Decode: to deserialize the ephemeral public key. +// 2) Reextract: to "unlock" the actual error encrypter using an active +// OnionProcessor. +func NewSphinxErrorEncrypter() *SphinxErrorEncrypter { + return &SphinxErrorEncrypter{ + OnionErrorEncrypter: nil, + EphemeralKey: &btcec.PublicKey{}, + } +} + +// EncryptFirstHop transforms a concrete failure message into an encrypted +// opaque failure reason. This method will be used at the source that the error +// occurs. It differs from BackwardObfuscate slightly, in that it computes a +// proper MAC over the error. +// +// NOTE: Part of the ErrorEncrypter interface. +func (s *SphinxErrorEncrypter) EncryptFirstHop( + failure lnwire.FailureMessage) (lnwire.OpaqueReason, error) { + + var b bytes.Buffer + if err := lnwire.EncodeFailure(&b, failure, 0); err != nil { + return nil, err + } + + // We pass a true as the first parameter to indicate that a MAC should + // be added. + return s.EncryptError(true, b.Bytes()), nil +} + +// EncryptMalformedError is similar to EncryptFirstHop (it adds the MAC), but +// it accepts an opaque failure reason rather than a failure message. This +// method is used when we receive an UpdateFailMalformedHTLC from the remote +// peer and then need to convert that into an proper error from only the raw +// bytes. +// +// NOTE: Part of the ErrorEncrypter interface. +func (s *SphinxErrorEncrypter) EncryptMalformedError( + reason lnwire.OpaqueReason) lnwire.OpaqueReason { + + return s.EncryptError(true, reason) +} + +// IntermediateEncrypt wraps an already encrypted opaque reason error in an +// additional layer of onion encryption. This process repeats until the error +// arrives at the source of the payment. We re-encrypt the message on the +// backwards path to ensure that the error is indistinguishable from any other +// error seen. +// +// NOTE: Part of the ErrorEncrypter interface. +func (s *SphinxErrorEncrypter) IntermediateEncrypt( + reason lnwire.OpaqueReason) lnwire.OpaqueReason { + + return s.EncryptError(false, reason) +} + +// Type returns the identifier for a sphinx error encrypter. +func (s *SphinxErrorEncrypter) Type() EncrypterType { + return EncrypterTypeSphinx +} + +// Encode serializes the error encrypter' ephemeral public key to the provided +// io.Writer. +func (s *SphinxErrorEncrypter) Encode(w io.Writer) error { + ephemeral := s.EphemeralKey.SerializeCompressed() + _, err := w.Write(ephemeral) + return err +} + +// Decode reconstructs the error encrypter's ephemeral public key from the +// provided io.Reader. +func (s *SphinxErrorEncrypter) Decode(r io.Reader) error { + var ephemeral [33]byte + if _, err := io.ReadFull(r, ephemeral[:]); err != nil { + return err + } + + var err error + s.EphemeralKey, err = btcec.ParsePubKey(ephemeral[:], btcec.S256()) + if err != nil { + return err + } + + return nil +} + +// Reextract rederives the error encrypter from the currently held EphemeralKey. +// This intended to be used shortly after Decode, to fully initialize a +// SphinxErrorEncrypter. +func (s *SphinxErrorEncrypter) Reextract( + extract ErrorEncrypterExtracter) error { + + obfuscator, failcode := extract(s.EphemeralKey) + if failcode != lnwire.CodeNone { + // This should never happen, since we already validated that + // this obfuscator can be extracted when it was received in the + // link. + return fmt.Errorf("unable to reconstruct onion "+ + "obfuscator, got failcode: %d", failcode) + } + + sphinxEncrypter, ok := obfuscator.(*SphinxErrorEncrypter) + if !ok { + return fmt.Errorf("incorrect onion error extracter") + } + + // Copy the freshly extracted encrypter. + s.OnionErrorEncrypter = sphinxEncrypter.OnionErrorEncrypter + + return nil + +} + +// A compile time check to ensure SphinxErrorEncrypter implements the +// ErrorEncrypter interface. +var _ ErrorEncrypter = (*SphinxErrorEncrypter)(nil) diff --git a/htlcswitch/iterator.go b/htlcswitch/hop/iterator.go similarity index 94% rename from htlcswitch/iterator.go rename to htlcswitch/hop/iterator.go index 9dfe3a1a..062ca879 100644 --- a/htlcswitch/iterator.go +++ b/htlcswitch/hop/iterator.go @@ -1,4 +1,4 @@ -package htlcswitch +package hop import ( "bytes" @@ -6,23 +6,22 @@ import ( "io" "github.com/btcsuite/btcd/btcec" - "github.com/lightningnetwork/lightning-onion" - "github.com/lightningnetwork/lnd/htlcswitch/hop" + sphinx "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/lnwire" ) -// HopIterator is an interface that abstracts away the routing information +// Iterator is an interface that abstracts away the routing information // included in HTLC's which includes the entirety of the payment path of an // HTLC. This interface provides two basic method which carry out: how to // interpret the forwarding information encoded within the HTLC packet, and hop // to encode the forwarding information for the _next_ hop. -type HopIterator interface { +type Iterator interface { // ForwardingInstructions returns the set of fields that detail exactly // _how_ this hop should forward the HTLC to the next hop. // Additionally, the information encoded within the returned // ForwardingInfo is to be used by each hop to authenticate the // information given to it by the prior hop. - ForwardingInstructions() (hop.ForwardingInfo, error) + ForwardingInstructions() (ForwardingInfo, error) // ExtraOnionBlob returns the additional EOB data (if available). ExtraOnionBlob() []byte @@ -64,7 +63,7 @@ func makeSphinxHopIterator(ogPacket *sphinx.OnionPacket, // A compile time check to ensure sphinxHopIterator implements the HopIterator // interface. -var _ HopIterator = (*sphinxHopIterator)(nil) +var _ Iterator = (*sphinxHopIterator)(nil) // Encode encodes iterator and writes it to the writer. // @@ -79,32 +78,30 @@ func (r *sphinxHopIterator) EncodeNextHop(w io.Writer) error { // hop to authenticate the information given to it by the prior hop. // // NOTE: Part of the HopIterator interface. -func (r *sphinxHopIterator) ForwardingInstructions() ( - hop.ForwardingInfo, error) { - +func (r *sphinxHopIterator) ForwardingInstructions() (ForwardingInfo, error) { switch r.processedPacket.Payload.Type { // If this is the legacy payload, then we'll extract the information // directly from the pre-populated ForwardingInstructions field. case sphinx.PayloadLegacy: fwdInst := r.processedPacket.ForwardingInstructions - p := hop.NewLegacyPayload(fwdInst) + p := NewLegacyPayload(fwdInst) return p.ForwardingInfo(), nil // Otherwise, if this is the TLV payload, then we'll make a new stream // to decode only what we need to make routing decisions. case sphinx.PayloadTLV: - p, err := hop.NewPayloadFromReader(bytes.NewReader( + p, err := NewPayloadFromReader(bytes.NewReader( r.processedPacket.Payload.Payload, )) if err != nil { - return hop.ForwardingInfo{}, err + return ForwardingInfo{}, err } return p.ForwardingInfo(), nil default: - return hop.ForwardingInfo{}, fmt.Errorf("unknown "+ + return ForwardingInfo{}, fmt.Errorf("unknown "+ "sphinx payload type: %v", r.processedPacket.Payload.Type) } @@ -164,7 +161,7 @@ func (p *OnionProcessor) Stop() error { // instance using the rHash as the associated data when checking the relevant // MACs during the decoding process. func (p *OnionProcessor) DecodeHopIterator(r io.Reader, rHash []byte, - incomingCltv uint32) (HopIterator, lnwire.FailCode) { + incomingCltv uint32) (Iterator, lnwire.FailCode) { onionPkt := &sphinx.OnionPacket{} if err := onionPkt.Decode(r); err != nil { @@ -216,7 +213,7 @@ type DecodeHopIteratorRequest struct { // DecodeHopIteratorResponse encapsulates the outcome of a batched sphinx onion // processing. type DecodeHopIteratorResponse struct { - HopIterator HopIterator + HopIterator Iterator FailCode lnwire.FailCode } @@ -225,7 +222,7 @@ type DecodeHopIteratorResponse struct { // // NOTE: The HopIterator should be considered invalid if the fail code is // anything but lnwire.CodeNone. -func (r *DecodeHopIteratorResponse) Result() (HopIterator, lnwire.FailCode) { +func (r *DecodeHopIteratorResponse) Result() (Iterator, lnwire.FailCode) { return r.HopIterator, r.FailCode } diff --git a/htlcswitch/iterator_test.go b/htlcswitch/hop/iterator_test.go similarity index 95% rename from htlcswitch/iterator_test.go rename to htlcswitch/hop/iterator_test.go index 08d02439..822e8794 100644 --- a/htlcswitch/iterator_test.go +++ b/htlcswitch/hop/iterator_test.go @@ -1,4 +1,4 @@ -package htlcswitch +package hop import ( "bytes" @@ -7,7 +7,6 @@ import ( "github.com/davecgh/go-spew/spew" sphinx "github.com/lightningnetwork/lightning-onion" - "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/tlv" @@ -30,7 +29,7 @@ func TestSphinxHopIteratorForwardingInstructions(t *testing.T) { // Next, we'll make the hop forwarding information that we should // extract each type, no matter the payload type. nextAddrInt := binary.BigEndian.Uint64(hopData.NextAddress[:]) - expectedFwdInfo := hop.ForwardingInfo{ + expectedFwdInfo := ForwardingInfo{ NextHop: lnwire.NewShortChanIDFromInt(nextAddrInt), AmountToForward: lnwire.MilliSatoshi(hopData.ForwardAmount), OutgoingCTLV: hopData.OutgoingCltv, @@ -54,7 +53,7 @@ func TestSphinxHopIteratorForwardingInstructions(t *testing.T) { var testCases = []struct { sphinxPacket *sphinx.ProcessedPacket - expectedFwdInfo hop.ForwardingInfo + expectedFwdInfo ForwardingInfo }{ // A regular legacy payload that signals more hops. { diff --git a/htlcswitch/hop/log.go b/htlcswitch/hop/log.go new file mode 100644 index 00000000..4134d166 --- /dev/null +++ b/htlcswitch/hop/log.go @@ -0,0 +1,16 @@ +package hop + +import ( + "github.com/btcsuite/btclog" +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log btclog.Logger + +// UseLogger uses a specified Logger to output package logging info. This +// function is called from the parent package htlcswitch logger initialization. +func UseLogger(logger btclog.Logger) { + log = logger +} diff --git a/htlcswitch/link.go b/htlcswitch/link.go index f1e1cf24..6f1de368 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -144,12 +144,12 @@ type ChannelLinkConfig struct { // // NOTE: This function assumes the same set of readers and preimages // are always presented for the same identifier. - DecodeHopIterators func([]byte, []DecodeHopIteratorRequest) ( - []DecodeHopIteratorResponse, error) + DecodeHopIterators func([]byte, []hop.DecodeHopIteratorRequest) ( + []hop.DecodeHopIteratorResponse, error) // ExtractErrorEncrypter function is responsible for decoding HTLC // Sphinx onion blob, and creating onion failure obfuscator. - ExtractErrorEncrypter ErrorEncrypterExtracter + ExtractErrorEncrypter hop.ErrorEncrypterExtracter // FetchLastChannelUpdate retrieves the latest routing policy for a // target channel. This channel will typically be the outgoing channel @@ -379,7 +379,7 @@ type channelLink struct { // hodlHtlc contains htlc data that is required for resolution. type hodlHtlc struct { pd *lnwallet.PaymentDescriptor - obfuscator ErrorEncrypter + obfuscator hop.ErrorEncrypter } // NewChannelLink creates a new instance of a ChannelLink given a configuration @@ -2553,7 +2553,9 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, l.tracef("processing %d remote adds for height %d", len(lockedInHtlcs), fwdPkg.Height) - decodeReqs := make([]DecodeHopIteratorRequest, 0, len(lockedInHtlcs)) + decodeReqs := make( + []hop.DecodeHopIteratorRequest, 0, len(lockedInHtlcs), + ) for _, pd := range lockedInHtlcs { switch pd.EntryType { @@ -2565,7 +2567,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // which process the Sphinx packet. onionReader := bytes.NewReader(pd.OnionBlob) - req := DecodeHopIteratorRequest{ + req := hop.DecodeHopIteratorRequest{ OnionReader: onionReader, RHash: pd.RHash[:], IncomingCltv: pd.Timeout, @@ -2850,7 +2852,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // processExitHop handles an htlc for which this link is the exit hop. It // returns a boolean indicating whether the commitment tx needs an update. func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor, - obfuscator ErrorEncrypter, fwdInfo hop.ForwardingInfo, + obfuscator hop.ErrorEncrypter, fwdInfo hop.ForwardingInfo, heightNow uint32, eob []byte) (bool, error) { // If hodl.ExitSettle is requested, we will not validate the final hop's @@ -3021,7 +3023,7 @@ func (l *channelLink) handleBatchFwdErrs(errChan chan error) { // sendHTLCError functions cancels HTLC and send cancel message back to the // peer from which HTLC was received. func (l *channelLink) sendHTLCError(htlcIndex uint64, failure lnwire.FailureMessage, - e ErrorEncrypter, sourceRef *channeldb.AddRef) { + e hop.ErrorEncrypter, sourceRef *channeldb.AddRef) { reason, err := e.EncryptFirstHop(failure) if err != nil { diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index 99889a5f..a3c7ed07 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -1292,7 +1292,7 @@ func TestChannelLinkMultiHopDecodeError(t *testing.T) { // Replace decode function with another which throws an error. n.carolChannelLink.cfg.ExtractErrorEncrypter = func( - *btcec.PublicKey) (ErrorEncrypter, lnwire.FailCode) { + *btcec.PublicKey) (hop.ErrorEncrypter, lnwire.FailCode) { return nil, lnwire.CodeInvalidOnionVersion } @@ -1666,7 +1666,7 @@ func newSingleLinkTestHarness(chanAmt, chanReserve btcutil.Amount) ( ForwardPackets: aliceSwitch.ForwardPackets, DecodeHopIterators: decoder.DecodeHopIterators, ExtractErrorEncrypter: func(*btcec.PublicKey) ( - ErrorEncrypter, lnwire.FailCode) { + hop.ErrorEncrypter, lnwire.FailCode) { return obfuscator, lnwire.CodeNone }, FetchLastChannelUpdate: mockGetChanUpdateMessage, @@ -4230,7 +4230,7 @@ func (h *persistentLinkHarness) restartLink( ForwardPackets: aliceSwitch.ForwardPackets, DecodeHopIterators: decoder.DecodeHopIterators, ExtractErrorEncrypter: func(*btcec.PublicKey) ( - ErrorEncrypter, lnwire.FailCode) { + hop.ErrorEncrypter, lnwire.FailCode) { return obfuscator, lnwire.CodeNone }, FetchLastChannelUpdate: mockGetChanUpdateMessage, diff --git a/htlcswitch/log.go b/htlcswitch/log.go index 63fe63a1..c76a8f72 100644 --- a/htlcswitch/log.go +++ b/htlcswitch/log.go @@ -3,6 +3,7 @@ package htlcswitch import ( "github.com/btcsuite/btclog" "github.com/lightningnetwork/lnd/build" + "github.com/lightningnetwork/lnd/htlcswitch/hop" ) // log is a logger that is initialized with no output filters. This @@ -12,7 +13,10 @@ var log btclog.Logger // The default amount of logging is none. func init() { - UseLogger(build.NewSubLogger("HSWC", nil)) + logger := build.NewSubLogger("HSWC", nil) + + UseLogger(logger) + hop.UseLogger(logger) } // DisableLog disables all library log output. Logging output is disabled diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index b76ff51f..0e739068 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -272,7 +272,7 @@ type mockHopIterator struct { hops []hop.ForwardingInfo } -func newMockHopIterator(hops ...hop.ForwardingInfo) HopIterator { +func newMockHopIterator(hops ...hop.ForwardingInfo) hop.Iterator { return &mockHopIterator{hops: hops} } @@ -289,7 +289,8 @@ func (r *mockHopIterator) ExtraOnionBlob() []byte { } func (r *mockHopIterator) ExtractErrorEncrypter( - extracter ErrorEncrypterExtracter) (ErrorEncrypter, lnwire.FailCode) { + extracter hop.ErrorEncrypterExtracter) (hop.ErrorEncrypter, + lnwire.FailCode) { return extracter(nil) } @@ -331,7 +332,7 @@ func encodeFwdInfo(w io.Writer, f *hop.ForwardingInfo) error { return nil } -var _ HopIterator = (*mockHopIterator)(nil) +var _ hop.Iterator = (*mockHopIterator)(nil) // mockObfuscator mock implementation of the failure obfuscator which only // encodes the failure and do not makes any onion obfuscation. @@ -340,7 +341,7 @@ type mockObfuscator struct { } // NewMockObfuscator initializes a dummy mockObfuscator used for testing. -func NewMockObfuscator() ErrorEncrypter { +func NewMockObfuscator() hop.ErrorEncrypter { return &mockObfuscator{} } @@ -348,8 +349,8 @@ func (o *mockObfuscator) OnionPacket() *sphinx.OnionPacket { return o.ogPacket } -func (o *mockObfuscator) Type() EncrypterType { - return EncrypterTypeMock +func (o *mockObfuscator) Type() hop.EncrypterType { + return hop.EncrypterTypeMock } func (o *mockObfuscator) Encode(w io.Writer) error { @@ -360,7 +361,9 @@ func (o *mockObfuscator) Decode(r io.Reader) error { return nil } -func (o *mockObfuscator) Reextract(extracter ErrorEncrypterExtracter) error { +func (o *mockObfuscator) Reextract( + extracter hop.ErrorEncrypterExtracter) error { + return nil } @@ -411,19 +414,19 @@ var _ ErrorDecrypter = (*mockDeobfuscator)(nil) type mockIteratorDecoder struct { mu sync.RWMutex - responses map[[32]byte][]DecodeHopIteratorResponse + responses map[[32]byte][]hop.DecodeHopIteratorResponse decodeFail bool } func newMockIteratorDecoder() *mockIteratorDecoder { return &mockIteratorDecoder{ - responses: make(map[[32]byte][]DecodeHopIteratorResponse), + responses: make(map[[32]byte][]hop.DecodeHopIteratorResponse), } } func (p *mockIteratorDecoder) DecodeHopIterator(r io.Reader, rHash []byte, - cltv uint32) (HopIterator, lnwire.FailCode) { + cltv uint32) (hop.Iterator, lnwire.FailCode) { var b [4]byte _, err := r.Read(b[:]) @@ -446,7 +449,8 @@ func (p *mockIteratorDecoder) DecodeHopIterator(r io.Reader, rHash []byte, } func (p *mockIteratorDecoder) DecodeHopIterators(id []byte, - reqs []DecodeHopIteratorRequest) ([]DecodeHopIteratorResponse, error) { + reqs []hop.DecodeHopIteratorRequest) ( + []hop.DecodeHopIteratorResponse, error) { idHash := sha256.Sum256(id) @@ -459,7 +463,7 @@ func (p *mockIteratorDecoder) DecodeHopIterators(id []byte, batchSize := len(reqs) - resps := make([]DecodeHopIteratorResponse, 0, batchSize) + resps := make([]hop.DecodeHopIteratorResponse, 0, batchSize) for _, req := range reqs { iterator, failcode := p.DecodeHopIterator( req.OnionReader, req.RHash, req.IncomingCltv, @@ -469,7 +473,7 @@ func (p *mockIteratorDecoder) DecodeHopIterators(id []byte, failcode = lnwire.CodeTemporaryChannelFailure } - resp := DecodeHopIteratorResponse{ + resp := hop.DecodeHopIteratorResponse{ HopIterator: iterator, FailCode: failcode, } diff --git a/htlcswitch/packet.go b/htlcswitch/packet.go index 363a71dd..7ddc3050 100644 --- a/htlcswitch/packet.go +++ b/htlcswitch/packet.go @@ -2,6 +2,7 @@ package htlcswitch import ( "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/lnwire" ) @@ -54,7 +55,7 @@ type htlcPacket struct { // obfuscator contains the necessary state to allow the switch to wrap // any forwarded errors in an additional layer of encryption. - obfuscator ErrorEncrypter + obfuscator hop.ErrorEncrypter // localFailure is set to true if an HTLC fails for a local payment before // the first hop. In this case, the failure reason is simply encoded, not diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index 286dc72d..e22f3962 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -143,7 +143,7 @@ type Config struct { // ExtractErrorEncrypter is an interface allowing switch to reextract // error encrypters stored in the circuit map on restarts, since they // are not stored directly within the database. - ExtractErrorEncrypter ErrorEncrypterExtracter + ExtractErrorEncrypter hop.ErrorEncrypterExtracter // FetchLastChannelUpdate retrieves the latest routing policy for a // target channel. This channel will typically be the outgoing channel diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index 65017002..f69fb8c0 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -1054,7 +1054,7 @@ func createTwoClusterChannels(aliceToBob, bobToCarol btcutil.Amount) ( type hopNetwork struct { feeEstimator *mockFeeEstimator globalPolicy ForwardingPolicy - obfuscator ErrorEncrypter + obfuscator hop.ErrorEncrypter defaultDelta uint32 } @@ -1101,7 +1101,7 @@ func (h *hopNetwork) createChannelLink(server, peer *mockServer, ForwardPackets: server.htlcSwitch.ForwardPackets, DecodeHopIterators: decoder.DecodeHopIterators, ExtractErrorEncrypter: func(*btcec.PublicKey) ( - ErrorEncrypter, lnwire.FailCode) { + hop.ErrorEncrypter, lnwire.FailCode) { return h.obfuscator, lnwire.CodeNone }, FetchLastChannelUpdate: mockGetChanUpdateMessage, diff --git a/server.go b/server.go index 963cb034..c47be832 100644 --- a/server.go +++ b/server.go @@ -34,6 +34,7 @@ import ( "github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/discovery" "github.com/lightningnetwork/lnd/htlcswitch" + "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/lncfg" @@ -208,7 +209,7 @@ type server struct { chainArb *contractcourt.ChainArbitrator - sphinx *htlcswitch.OnionProcessor + sphinx *hop.OnionProcessor towerClient wtclient.Client @@ -367,7 +368,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, // TODO(roasbeef): derive proper onion key based on rotation // schedule - sphinx: htlcswitch.NewOnionProcessor(sphinxRouter), + sphinx: hop.NewOnionProcessor(sphinxRouter), persistentPeers: make(map[string]struct{}), persistentPeersBackoff: make(map[string]time.Duration),