Merge pull request #3468 from joostjager/move-onion-processor

htlcswitch: move hop iterator into htlcswitch/hop package
This commit is contained in:
Olaoluwa Osuntokun 2019-09-09 03:29:41 -07:00 committed by GitHub
commit 1f92b7587c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 301 additions and 259 deletions

@ -5,6 +5,7 @@ import (
"io" "io"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
) )
@ -51,7 +52,7 @@ type PaymentCircuit struct {
// ErrorEncrypter is used to re-encrypt the onion failure before // ErrorEncrypter is used to re-encrypt the onion failure before
// sending it back to the originator of the payment. // 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 // LoadedFromDisk is set true for any circuits loaded after the circuit
// map is reloaded from disk. // map is reloaded from disk.
@ -136,7 +137,7 @@ func (c *PaymentCircuit) Encode(w io.Writer) error {
} }
// Defaults to EncrypterTypeNone. // Defaults to EncrypterTypeNone.
var encrypterType EncrypterType var encrypterType hop.EncrypterType
if c.ErrorEncrypter != nil { if c.ErrorEncrypter != nil {
encrypterType = c.ErrorEncrypter.Type() 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. // Skip encoding of error encrypter if this half add does not have one.
if encrypterType == EncrypterTypeNone { if encrypterType == hop.EncrypterTypeNone {
return nil return nil
} }
@ -183,23 +184,23 @@ func (c *PaymentCircuit) Decode(r io.Reader) error {
binary.BigEndian.Uint64(scratch[:])) binary.BigEndian.Uint64(scratch[:]))
// Read the encrypter type used for this circuit. // Read the encrypter type used for this circuit.
var encrypterType EncrypterType var encrypterType hop.EncrypterType
err := binary.Read(r, binary.BigEndian, &encrypterType) err := binary.Read(r, binary.BigEndian, &encrypterType)
if err != nil { if err != nil {
return err return err
} }
switch encrypterType { switch encrypterType {
case EncrypterTypeNone: case hop.EncrypterTypeNone:
// No encrypter was provided, such as when the payment is // No encrypter was provided, such as when the payment is
// locally initiated. // locally initiated.
return nil return nil
case EncrypterTypeSphinx: case hop.EncrypterTypeSphinx:
// Sphinx encrypter was used as this is a forwarded HTLC. // Sphinx encrypter was used as this is a forwarded HTLC.
c.ErrorEncrypter = NewSphinxErrorEncrypter() c.ErrorEncrypter = hop.NewSphinxErrorEncrypter()
case EncrypterTypeMock: case hop.EncrypterTypeMock:
// Test encrypter. // Test encrypter.
c.ErrorEncrypter = NewMockObfuscator() c.ErrorEncrypter = NewMockObfuscator()

@ -179,7 +179,7 @@ type CircuitMapConfig struct {
// ExtractErrorEncrypter derives the shared secret used to encrypt // ExtractErrorEncrypter derives the shared secret used to encrypt
// errors from the obfuscator's ephemeral public key. // errors from the obfuscator's ephemeral public key.
ExtractErrorEncrypter ErrorEncrypterExtracter ExtractErrorEncrypter hop.ErrorEncrypterExtracter
} }
// NewCircuitMap creates a new instance of the circuitMap. // NewCircuitMap creates a new instance of the circuitMap.

@ -9,9 +9,10 @@ import (
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
bitcoinCfg "github.com/btcsuite/btcd/chaincfg" bitcoinCfg "github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lightning-onion" sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
) )
@ -30,7 +31,7 @@ var (
// testExtracter is a precomputed extraction of testEphemeralKey, using // testExtracter is a precomputed extraction of testEphemeralKey, using
// the sphinxPrivKey. // the sphinxPrivKey.
testExtracter *htlcswitch.SphinxErrorEncrypter testExtracter *hop.SphinxErrorEncrypter
) )
func init() { func init() {
@ -67,7 +68,7 @@ func initTestExtracter() {
testEphemeralKey, testEphemeralKey,
) )
sphinxExtracter, ok := obfuscator.(*htlcswitch.SphinxErrorEncrypter) sphinxExtracter, ok := obfuscator.(*hop.SphinxErrorEncrypter)
if !ok { if !ok {
panic("did not extract sphinx error encrypter") panic("did not extract sphinx error encrypter")
} }
@ -81,7 +82,7 @@ func initTestExtracter() {
// newOnionProcessor creates starts a new htlcswitch.OnionProcessor using a temp // newOnionProcessor creates starts a new htlcswitch.OnionProcessor using a temp
// db and no garbage collection. // db and no garbage collection.
func newOnionProcessor(t *testing.T) *htlcswitch.OnionProcessor { func newOnionProcessor(t *testing.T) *hop.OnionProcessor {
sphinxRouter := sphinx.NewRouter( sphinxRouter := sphinx.NewRouter(
sphinxPrivKey, &bitcoinCfg.SimNetParams, sphinx.NewMemoryReplayLog(), 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) 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 // newCircuitMap creates a new htlcswitch.CircuitMap using a temp db and a
@ -128,7 +129,7 @@ var halfCircuitTests = []struct {
outValue btcutil.Amount outValue btcutil.Amount
chanID lnwire.ShortChannelID chanID lnwire.ShortChannelID
htlcID uint64 htlcID uint64
encrypter htlcswitch.ErrorEncrypter encrypter hop.ErrorEncrypter
}{ }{
{ {
hash: hash1, hash: hash1,
@ -1142,7 +1143,7 @@ func TestCircuitMapCloseOpenCircuits(t *testing.T) {
ChanID: chan1, ChanID: chan1,
HtlcID: 3, HtlcID: 3,
}, },
ErrorEncrypter: &htlcswitch.SphinxErrorEncrypter{ ErrorEncrypter: &hop.SphinxErrorEncrypter{
EphemeralKey: testEphemeralKey, EphemeralKey: testEphemeralKey,
}, },
} }

@ -3,10 +3,9 @@ package htlcswitch
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"github.com/btcsuite/btcd/btcec"
sphinx "github.com/lightningnetwork/lightning-onion" sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
) )
@ -47,203 +46,15 @@ type ErrorDecrypter interface {
DecryptError(lnwire.OpaqueReason) (*ForwardingError, error) 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 // UnknownEncrypterType is an error message used to signal that an unexpected
// EncrypterType was encountered during decoding. // EncrypterType was encountered during decoding.
type UnknownEncrypterType EncrypterType type UnknownEncrypterType hop.EncrypterType
// Error returns a formatted error indicating the invalid EncrypterType. // Error returns a formatted error indicating the invalid EncrypterType.
func (e UnknownEncrypterType) Error() string { func (e UnknownEncrypterType) Error() string {
return fmt.Sprintf("unknown error encrypter type: %d", e) 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 // OnionErrorDecrypter is the interface that provides onion level error
// decryption. // decryption.
type OnionErrorDecrypter interface { type OnionErrorDecrypter interface {

@ -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)

@ -1,4 +1,4 @@
package htlcswitch package hop
import ( import (
"bytes" "bytes"
@ -6,23 +6,22 @@ import (
"io" "io"
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
"github.com/lightningnetwork/lightning-onion" sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/lnwire" "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 // 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 // HTLC. This interface provides two basic method which carry out: how to
// interpret the forwarding information encoded within the HTLC packet, and hop // interpret the forwarding information encoded within the HTLC packet, and hop
// to encode the forwarding information for the _next_ 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 // ForwardingInstructions returns the set of fields that detail exactly
// _how_ this hop should forward the HTLC to the next hop. // _how_ this hop should forward the HTLC to the next hop.
// Additionally, the information encoded within the returned // Additionally, the information encoded within the returned
// ForwardingInfo is to be used by each hop to authenticate the // ForwardingInfo is to be used by each hop to authenticate the
// information given to it by the prior hop. // information given to it by the prior hop.
ForwardingInstructions() (hop.ForwardingInfo, error) ForwardingInstructions() (ForwardingInfo, error)
// ExtraOnionBlob returns the additional EOB data (if available). // ExtraOnionBlob returns the additional EOB data (if available).
ExtraOnionBlob() []byte ExtraOnionBlob() []byte
@ -64,7 +63,7 @@ func makeSphinxHopIterator(ogPacket *sphinx.OnionPacket,
// A compile time check to ensure sphinxHopIterator implements the HopIterator // A compile time check to ensure sphinxHopIterator implements the HopIterator
// interface. // interface.
var _ HopIterator = (*sphinxHopIterator)(nil) var _ Iterator = (*sphinxHopIterator)(nil)
// Encode encodes iterator and writes it to the writer. // 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. // hop to authenticate the information given to it by the prior hop.
// //
// NOTE: Part of the HopIterator interface. // NOTE: Part of the HopIterator interface.
func (r *sphinxHopIterator) ForwardingInstructions() ( func (r *sphinxHopIterator) ForwardingInstructions() (ForwardingInfo, error) {
hop.ForwardingInfo, error) {
switch r.processedPacket.Payload.Type { switch r.processedPacket.Payload.Type {
// If this is the legacy payload, then we'll extract the information // If this is the legacy payload, then we'll extract the information
// directly from the pre-populated ForwardingInstructions field. // directly from the pre-populated ForwardingInstructions field.
case sphinx.PayloadLegacy: case sphinx.PayloadLegacy:
fwdInst := r.processedPacket.ForwardingInstructions fwdInst := r.processedPacket.ForwardingInstructions
p := hop.NewLegacyPayload(fwdInst) p := NewLegacyPayload(fwdInst)
return p.ForwardingInfo(), nil return p.ForwardingInfo(), nil
// Otherwise, if this is the TLV payload, then we'll make a new stream // Otherwise, if this is the TLV payload, then we'll make a new stream
// to decode only what we need to make routing decisions. // to decode only what we need to make routing decisions.
case sphinx.PayloadTLV: case sphinx.PayloadTLV:
p, err := hop.NewPayloadFromReader(bytes.NewReader( p, err := NewPayloadFromReader(bytes.NewReader(
r.processedPacket.Payload.Payload, r.processedPacket.Payload.Payload,
)) ))
if err != nil { if err != nil {
return hop.ForwardingInfo{}, err return ForwardingInfo{}, err
} }
return p.ForwardingInfo(), nil return p.ForwardingInfo(), nil
default: default:
return hop.ForwardingInfo{}, fmt.Errorf("unknown "+ return ForwardingInfo{}, fmt.Errorf("unknown "+
"sphinx payload type: %v", "sphinx payload type: %v",
r.processedPacket.Payload.Type) 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 // instance using the rHash as the associated data when checking the relevant
// MACs during the decoding process. // MACs during the decoding process.
func (p *OnionProcessor) DecodeHopIterator(r io.Reader, rHash []byte, func (p *OnionProcessor) DecodeHopIterator(r io.Reader, rHash []byte,
incomingCltv uint32) (HopIterator, lnwire.FailCode) { incomingCltv uint32) (Iterator, lnwire.FailCode) {
onionPkt := &sphinx.OnionPacket{} onionPkt := &sphinx.OnionPacket{}
if err := onionPkt.Decode(r); err != nil { if err := onionPkt.Decode(r); err != nil {
@ -216,7 +213,7 @@ type DecodeHopIteratorRequest struct {
// DecodeHopIteratorResponse encapsulates the outcome of a batched sphinx onion // DecodeHopIteratorResponse encapsulates the outcome of a batched sphinx onion
// processing. // processing.
type DecodeHopIteratorResponse struct { type DecodeHopIteratorResponse struct {
HopIterator HopIterator HopIterator Iterator
FailCode lnwire.FailCode FailCode lnwire.FailCode
} }
@ -225,7 +222,7 @@ type DecodeHopIteratorResponse struct {
// //
// NOTE: The HopIterator should be considered invalid if the fail code is // NOTE: The HopIterator should be considered invalid if the fail code is
// anything but lnwire.CodeNone. // anything but lnwire.CodeNone.
func (r *DecodeHopIteratorResponse) Result() (HopIterator, lnwire.FailCode) { func (r *DecodeHopIteratorResponse) Result() (Iterator, lnwire.FailCode) {
return r.HopIterator, r.FailCode return r.HopIterator, r.FailCode
} }

@ -1,4 +1,4 @@
package htlcswitch package hop
import ( import (
"bytes" "bytes"
@ -7,7 +7,6 @@ import (
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
sphinx "github.com/lightningnetwork/lightning-onion" sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/record" "github.com/lightningnetwork/lnd/record"
"github.com/lightningnetwork/lnd/tlv" "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 // Next, we'll make the hop forwarding information that we should
// extract each type, no matter the payload type. // extract each type, no matter the payload type.
nextAddrInt := binary.BigEndian.Uint64(hopData.NextAddress[:]) nextAddrInt := binary.BigEndian.Uint64(hopData.NextAddress[:])
expectedFwdInfo := hop.ForwardingInfo{ expectedFwdInfo := ForwardingInfo{
NextHop: lnwire.NewShortChanIDFromInt(nextAddrInt), NextHop: lnwire.NewShortChanIDFromInt(nextAddrInt),
AmountToForward: lnwire.MilliSatoshi(hopData.ForwardAmount), AmountToForward: lnwire.MilliSatoshi(hopData.ForwardAmount),
OutgoingCTLV: hopData.OutgoingCltv, OutgoingCTLV: hopData.OutgoingCltv,
@ -54,7 +53,7 @@ func TestSphinxHopIteratorForwardingInstructions(t *testing.T) {
var testCases = []struct { var testCases = []struct {
sphinxPacket *sphinx.ProcessedPacket sphinxPacket *sphinx.ProcessedPacket
expectedFwdInfo hop.ForwardingInfo expectedFwdInfo ForwardingInfo
}{ }{
// A regular legacy payload that signals more hops. // A regular legacy payload that signals more hops.
{ {

16
htlcswitch/hop/log.go Normal file

@ -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
}

@ -144,12 +144,12 @@ type ChannelLinkConfig struct {
// //
// NOTE: This function assumes the same set of readers and preimages // NOTE: This function assumes the same set of readers and preimages
// are always presented for the same identifier. // are always presented for the same identifier.
DecodeHopIterators func([]byte, []DecodeHopIteratorRequest) ( DecodeHopIterators func([]byte, []hop.DecodeHopIteratorRequest) (
[]DecodeHopIteratorResponse, error) []hop.DecodeHopIteratorResponse, error)
// ExtractErrorEncrypter function is responsible for decoding HTLC // ExtractErrorEncrypter function is responsible for decoding HTLC
// Sphinx onion blob, and creating onion failure obfuscator. // Sphinx onion blob, and creating onion failure obfuscator.
ExtractErrorEncrypter ErrorEncrypterExtracter ExtractErrorEncrypter hop.ErrorEncrypterExtracter
// FetchLastChannelUpdate retrieves the latest routing policy for a // FetchLastChannelUpdate retrieves the latest routing policy for a
// target channel. This channel will typically be the outgoing channel // 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. // hodlHtlc contains htlc data that is required for resolution.
type hodlHtlc struct { type hodlHtlc struct {
pd *lnwallet.PaymentDescriptor pd *lnwallet.PaymentDescriptor
obfuscator ErrorEncrypter obfuscator hop.ErrorEncrypter
} }
// NewChannelLink creates a new instance of a ChannelLink given a configuration // 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", l.tracef("processing %d remote adds for height %d",
len(lockedInHtlcs), fwdPkg.Height) len(lockedInHtlcs), fwdPkg.Height)
decodeReqs := make([]DecodeHopIteratorRequest, 0, len(lockedInHtlcs)) decodeReqs := make(
[]hop.DecodeHopIteratorRequest, 0, len(lockedInHtlcs),
)
for _, pd := range lockedInHtlcs { for _, pd := range lockedInHtlcs {
switch pd.EntryType { switch pd.EntryType {
@ -2565,7 +2567,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg,
// which process the Sphinx packet. // which process the Sphinx packet.
onionReader := bytes.NewReader(pd.OnionBlob) onionReader := bytes.NewReader(pd.OnionBlob)
req := DecodeHopIteratorRequest{ req := hop.DecodeHopIteratorRequest{
OnionReader: onionReader, OnionReader: onionReader,
RHash: pd.RHash[:], RHash: pd.RHash[:],
IncomingCltv: pd.Timeout, 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 // processExitHop handles an htlc for which this link is the exit hop. It
// returns a boolean indicating whether the commitment tx needs an update. // returns a boolean indicating whether the commitment tx needs an update.
func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor, func (l *channelLink) processExitHop(pd *lnwallet.PaymentDescriptor,
obfuscator ErrorEncrypter, fwdInfo hop.ForwardingInfo, obfuscator hop.ErrorEncrypter, fwdInfo hop.ForwardingInfo,
heightNow uint32, eob []byte) (bool, error) { heightNow uint32, eob []byte) (bool, error) {
// If hodl.ExitSettle is requested, we will not validate the final hop's // 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 // sendHTLCError functions cancels HTLC and send cancel message back to the
// peer from which HTLC was received. // peer from which HTLC was received.
func (l *channelLink) sendHTLCError(htlcIndex uint64, failure lnwire.FailureMessage, 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) reason, err := e.EncryptFirstHop(failure)
if err != nil { if err != nil {

@ -1292,7 +1292,7 @@ func TestChannelLinkMultiHopDecodeError(t *testing.T) {
// Replace decode function with another which throws an error. // Replace decode function with another which throws an error.
n.carolChannelLink.cfg.ExtractErrorEncrypter = func( n.carolChannelLink.cfg.ExtractErrorEncrypter = func(
*btcec.PublicKey) (ErrorEncrypter, lnwire.FailCode) { *btcec.PublicKey) (hop.ErrorEncrypter, lnwire.FailCode) {
return nil, lnwire.CodeInvalidOnionVersion return nil, lnwire.CodeInvalidOnionVersion
} }
@ -1666,7 +1666,7 @@ func newSingleLinkTestHarness(chanAmt, chanReserve btcutil.Amount) (
ForwardPackets: aliceSwitch.ForwardPackets, ForwardPackets: aliceSwitch.ForwardPackets,
DecodeHopIterators: decoder.DecodeHopIterators, DecodeHopIterators: decoder.DecodeHopIterators,
ExtractErrorEncrypter: func(*btcec.PublicKey) ( ExtractErrorEncrypter: func(*btcec.PublicKey) (
ErrorEncrypter, lnwire.FailCode) { hop.ErrorEncrypter, lnwire.FailCode) {
return obfuscator, lnwire.CodeNone return obfuscator, lnwire.CodeNone
}, },
FetchLastChannelUpdate: mockGetChanUpdateMessage, FetchLastChannelUpdate: mockGetChanUpdateMessage,
@ -4230,7 +4230,7 @@ func (h *persistentLinkHarness) restartLink(
ForwardPackets: aliceSwitch.ForwardPackets, ForwardPackets: aliceSwitch.ForwardPackets,
DecodeHopIterators: decoder.DecodeHopIterators, DecodeHopIterators: decoder.DecodeHopIterators,
ExtractErrorEncrypter: func(*btcec.PublicKey) ( ExtractErrorEncrypter: func(*btcec.PublicKey) (
ErrorEncrypter, lnwire.FailCode) { hop.ErrorEncrypter, lnwire.FailCode) {
return obfuscator, lnwire.CodeNone return obfuscator, lnwire.CodeNone
}, },
FetchLastChannelUpdate: mockGetChanUpdateMessage, FetchLastChannelUpdate: mockGetChanUpdateMessage,

@ -3,6 +3,7 @@ package htlcswitch
import ( import (
"github.com/btcsuite/btclog" "github.com/btcsuite/btclog"
"github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/build"
"github.com/lightningnetwork/lnd/htlcswitch/hop"
) )
// log is a logger that is initialized with no output filters. This // 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. // The default amount of logging is none.
func init() { 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 // DisableLog disables all library log output. Logging output is disabled

@ -272,7 +272,7 @@ type mockHopIterator struct {
hops []hop.ForwardingInfo hops []hop.ForwardingInfo
} }
func newMockHopIterator(hops ...hop.ForwardingInfo) HopIterator { func newMockHopIterator(hops ...hop.ForwardingInfo) hop.Iterator {
return &mockHopIterator{hops: hops} return &mockHopIterator{hops: hops}
} }
@ -289,7 +289,8 @@ func (r *mockHopIterator) ExtraOnionBlob() []byte {
} }
func (r *mockHopIterator) ExtractErrorEncrypter( func (r *mockHopIterator) ExtractErrorEncrypter(
extracter ErrorEncrypterExtracter) (ErrorEncrypter, lnwire.FailCode) { extracter hop.ErrorEncrypterExtracter) (hop.ErrorEncrypter,
lnwire.FailCode) {
return extracter(nil) return extracter(nil)
} }
@ -331,7 +332,7 @@ func encodeFwdInfo(w io.Writer, f *hop.ForwardingInfo) error {
return nil return nil
} }
var _ HopIterator = (*mockHopIterator)(nil) var _ hop.Iterator = (*mockHopIterator)(nil)
// mockObfuscator mock implementation of the failure obfuscator which only // mockObfuscator mock implementation of the failure obfuscator which only
// encodes the failure and do not makes any onion obfuscation. // 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. // NewMockObfuscator initializes a dummy mockObfuscator used for testing.
func NewMockObfuscator() ErrorEncrypter { func NewMockObfuscator() hop.ErrorEncrypter {
return &mockObfuscator{} return &mockObfuscator{}
} }
@ -348,8 +349,8 @@ func (o *mockObfuscator) OnionPacket() *sphinx.OnionPacket {
return o.ogPacket return o.ogPacket
} }
func (o *mockObfuscator) Type() EncrypterType { func (o *mockObfuscator) Type() hop.EncrypterType {
return EncrypterTypeMock return hop.EncrypterTypeMock
} }
func (o *mockObfuscator) Encode(w io.Writer) error { func (o *mockObfuscator) Encode(w io.Writer) error {
@ -360,7 +361,9 @@ func (o *mockObfuscator) Decode(r io.Reader) error {
return nil return nil
} }
func (o *mockObfuscator) Reextract(extracter ErrorEncrypterExtracter) error { func (o *mockObfuscator) Reextract(
extracter hop.ErrorEncrypterExtracter) error {
return nil return nil
} }
@ -411,19 +414,19 @@ var _ ErrorDecrypter = (*mockDeobfuscator)(nil)
type mockIteratorDecoder struct { type mockIteratorDecoder struct {
mu sync.RWMutex mu sync.RWMutex
responses map[[32]byte][]DecodeHopIteratorResponse responses map[[32]byte][]hop.DecodeHopIteratorResponse
decodeFail bool decodeFail bool
} }
func newMockIteratorDecoder() *mockIteratorDecoder { func newMockIteratorDecoder() *mockIteratorDecoder {
return &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, func (p *mockIteratorDecoder) DecodeHopIterator(r io.Reader, rHash []byte,
cltv uint32) (HopIterator, lnwire.FailCode) { cltv uint32) (hop.Iterator, lnwire.FailCode) {
var b [4]byte var b [4]byte
_, err := r.Read(b[:]) _, err := r.Read(b[:])
@ -446,7 +449,8 @@ func (p *mockIteratorDecoder) DecodeHopIterator(r io.Reader, rHash []byte,
} }
func (p *mockIteratorDecoder) DecodeHopIterators(id []byte, func (p *mockIteratorDecoder) DecodeHopIterators(id []byte,
reqs []DecodeHopIteratorRequest) ([]DecodeHopIteratorResponse, error) { reqs []hop.DecodeHopIteratorRequest) (
[]hop.DecodeHopIteratorResponse, error) {
idHash := sha256.Sum256(id) idHash := sha256.Sum256(id)
@ -459,7 +463,7 @@ func (p *mockIteratorDecoder) DecodeHopIterators(id []byte,
batchSize := len(reqs) batchSize := len(reqs)
resps := make([]DecodeHopIteratorResponse, 0, batchSize) resps := make([]hop.DecodeHopIteratorResponse, 0, batchSize)
for _, req := range reqs { for _, req := range reqs {
iterator, failcode := p.DecodeHopIterator( iterator, failcode := p.DecodeHopIterator(
req.OnionReader, req.RHash, req.IncomingCltv, req.OnionReader, req.RHash, req.IncomingCltv,
@ -469,7 +473,7 @@ func (p *mockIteratorDecoder) DecodeHopIterators(id []byte,
failcode = lnwire.CodeTemporaryChannelFailure failcode = lnwire.CodeTemporaryChannelFailure
} }
resp := DecodeHopIteratorResponse{ resp := hop.DecodeHopIteratorResponse{
HopIterator: iterator, HopIterator: iterator,
FailCode: failcode, FailCode: failcode,
} }

@ -2,6 +2,7 @@ package htlcswitch
import ( import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
) )
@ -54,7 +55,7 @@ type htlcPacket struct {
// obfuscator contains the necessary state to allow the switch to wrap // obfuscator contains the necessary state to allow the switch to wrap
// any forwarded errors in an additional layer of encryption. // 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 // 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 // the first hop. In this case, the failure reason is simply encoded, not

@ -143,7 +143,7 @@ type Config struct {
// ExtractErrorEncrypter is an interface allowing switch to reextract // ExtractErrorEncrypter is an interface allowing switch to reextract
// error encrypters stored in the circuit map on restarts, since they // error encrypters stored in the circuit map on restarts, since they
// are not stored directly within the database. // are not stored directly within the database.
ExtractErrorEncrypter ErrorEncrypterExtracter ExtractErrorEncrypter hop.ErrorEncrypterExtracter
// FetchLastChannelUpdate retrieves the latest routing policy for a // FetchLastChannelUpdate retrieves the latest routing policy for a
// target channel. This channel will typically be the outgoing channel // target channel. This channel will typically be the outgoing channel

@ -1054,7 +1054,7 @@ func createTwoClusterChannels(aliceToBob, bobToCarol btcutil.Amount) (
type hopNetwork struct { type hopNetwork struct {
feeEstimator *mockFeeEstimator feeEstimator *mockFeeEstimator
globalPolicy ForwardingPolicy globalPolicy ForwardingPolicy
obfuscator ErrorEncrypter obfuscator hop.ErrorEncrypter
defaultDelta uint32 defaultDelta uint32
} }
@ -1101,7 +1101,7 @@ func (h *hopNetwork) createChannelLink(server, peer *mockServer,
ForwardPackets: server.htlcSwitch.ForwardPackets, ForwardPackets: server.htlcSwitch.ForwardPackets,
DecodeHopIterators: decoder.DecodeHopIterators, DecodeHopIterators: decoder.DecodeHopIterators,
ExtractErrorEncrypter: func(*btcec.PublicKey) ( ExtractErrorEncrypter: func(*btcec.PublicKey) (
ErrorEncrypter, lnwire.FailCode) { hop.ErrorEncrypter, lnwire.FailCode) {
return h.obfuscator, lnwire.CodeNone return h.obfuscator, lnwire.CodeNone
}, },
FetchLastChannelUpdate: mockGetChanUpdateMessage, FetchLastChannelUpdate: mockGetChanUpdateMessage,

@ -34,6 +34,7 @@ import (
"github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/contractcourt"
"github.com/lightningnetwork/lnd/discovery" "github.com/lightningnetwork/lnd/discovery"
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/htlcswitch/hop"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/invoices"
"github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lncfg"
@ -208,7 +209,7 @@ type server struct {
chainArb *contractcourt.ChainArbitrator chainArb *contractcourt.ChainArbitrator
sphinx *htlcswitch.OnionProcessor sphinx *hop.OnionProcessor
towerClient wtclient.Client towerClient wtclient.Client
@ -367,7 +368,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
// TODO(roasbeef): derive proper onion key based on rotation // TODO(roasbeef): derive proper onion key based on rotation
// schedule // schedule
sphinx: htlcswitch.NewOnionProcessor(sphinxRouter), sphinx: hop.NewOnionProcessor(sphinxRouter),
persistentPeers: make(map[string]struct{}), persistentPeers: make(map[string]struct{}),
persistentPeersBackoff: make(map[string]time.Duration), persistentPeersBackoff: make(map[string]time.Duration),