htlcswitch: move hop iterator into htlcswitch/hop package
Prepares for onion blob decoding outside of htlcswitch.
This commit is contained in:
parent
fb565bcd5d
commit
e54b24289f
@ -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 {
|
||||||
|
205
htlcswitch/hop/error_encryptor.go
Normal file
205
htlcswitch/hop/error_encryptor.go
Normal file
@ -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
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),
|
||||||
|
Loading…
Reference in New Issue
Block a user