mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-05-04 00:40:34 +02:00
htlcswitch+router: add onion error obfuscation
Within the network, it's important that when an HTLC forwarding failure occurs, the recipient is notified in a timely manner in order to ensure that errors are graceful and not unknown. For that reason with accordance to BOLT №4 onion failure obfuscation have been added.
This commit is contained in:
parent
ef73062c14
commit
2d378b3280
@ -38,17 +38,23 @@ type paymentCircuit struct {
|
|||||||
// request back.
|
// request back.
|
||||||
Dest lnwire.ShortChannelID
|
Dest lnwire.ShortChannelID
|
||||||
|
|
||||||
|
// Obfuscator is used to obfuscate the onion failure before sending it
|
||||||
|
// back to the originator of the payment.
|
||||||
|
Obfuscator Obfuscator
|
||||||
|
|
||||||
// RefCount is used to count the circuits with the same circuit key.
|
// RefCount is used to count the circuits with the same circuit key.
|
||||||
RefCount int
|
RefCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
// newPaymentCircuit creates new payment circuit instance.
|
// newPaymentCircuit creates new payment circuit instance.
|
||||||
func newPaymentCircuit(src, dest lnwire.ShortChannelID, key circuitKey) *paymentCircuit {
|
func newPaymentCircuit(src, dest lnwire.ShortChannelID, key circuitKey,
|
||||||
|
obfuscator Obfuscator) *paymentCircuit {
|
||||||
return &paymentCircuit{
|
return &paymentCircuit{
|
||||||
Src: src,
|
Src: src,
|
||||||
Dest: dest,
|
Dest: dest,
|
||||||
PaymentHash: key,
|
PaymentHash: key,
|
||||||
RefCount: 1,
|
RefCount: 1,
|
||||||
|
Obfuscator: obfuscator,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
87
htlcswitch/failure.go
Normal file
87
htlcswitch/failure.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package htlcswitch
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lightning-onion"
|
||||||
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Deobfuscator entity which is used to de-obfuscate the onion opaque reason and
|
||||||
|
// extract failure.
|
||||||
|
type Deobfuscator interface {
|
||||||
|
// Deobfuscate function decodes the onion error failure.
|
||||||
|
Deobfuscate(lnwire.OpaqueReason) (lnwire.FailureMessage, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obfuscator entity which is used to do the initial and backward onion
|
||||||
|
// failure obfuscation.
|
||||||
|
type Obfuscator interface {
|
||||||
|
// InitialObfuscate is used to convert the failure into opaque
|
||||||
|
// reason.
|
||||||
|
InitialObfuscate(lnwire.FailureMessage) (lnwire.OpaqueReason, error)
|
||||||
|
|
||||||
|
// BackwardObfuscate is used to make the processing over onion error
|
||||||
|
// when it moves backward to the htlc sender.
|
||||||
|
BackwardObfuscate(lnwire.OpaqueReason) lnwire.OpaqueReason
|
||||||
|
}
|
||||||
|
|
||||||
|
// FailureObfuscator is used to obfuscate the onion failure.
|
||||||
|
type FailureObfuscator struct {
|
||||||
|
*sphinx.OnionObfuscator
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitialObfuscate is used by the failure sender to decode the failure and
|
||||||
|
// make the initial failure obfuscation with addition of the failure data hmac.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the Obfuscator interface.
|
||||||
|
func (o *FailureObfuscator) InitialObfuscate(failure lnwire.FailureMessage) (
|
||||||
|
lnwire.OpaqueReason, error) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
if err := lnwire.EncodeFailure(&b, failure, 0); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the initial obfuscation with appending hmac.
|
||||||
|
return o.OnionObfuscator.Obfuscate(true, b.Bytes()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackwardObfuscate is used by the forwarding nodes in order to obfuscate the
|
||||||
|
// already obfuscated onion failure blob with the stream which have been
|
||||||
|
// generated with our shared secret. The reason we re-encrypt the message on the
|
||||||
|
// backwards path is to ensure that the error is computationally
|
||||||
|
// indistinguishable from any other error seen.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the Obfuscator interface.
|
||||||
|
func (o *FailureObfuscator) BackwardObfuscate(
|
||||||
|
reason lnwire.OpaqueReason) lnwire.OpaqueReason {
|
||||||
|
return o.OnionObfuscator.Obfuscate(false, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A compile time check to ensure FailureObfuscator implements the
|
||||||
|
// Obfuscator interface.
|
||||||
|
var _ Obfuscator = (*FailureObfuscator)(nil)
|
||||||
|
|
||||||
|
// FailureDeobfuscator wraps the sphinx data obfuscator and adds awareness of
|
||||||
|
// the lnwire onion failure messages to it.
|
||||||
|
type FailureDeobfuscator struct {
|
||||||
|
*sphinx.OnionDeobfuscator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deobfuscate decodes the obfuscated onion failure.
|
||||||
|
//
|
||||||
|
// NOTE: Part of the Obfuscator interface.
|
||||||
|
func (o *FailureDeobfuscator) Deobfuscate(reason lnwire.OpaqueReason) (lnwire.FailureMessage,
|
||||||
|
error) {
|
||||||
|
_, failureData, err := o.OnionDeobfuscator.Deobfuscate(reason)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r := bytes.NewReader(failureData)
|
||||||
|
return lnwire.DecodeFailure(r, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A compile time check to ensure FailureDeobfuscator implements the
|
||||||
|
// Deobfuscator interface.
|
||||||
|
var _ Deobfuscator = (*FailureDeobfuscator)(nil)
|
@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
|
||||||
"github.com/lightningnetwork/lightning-onion"
|
"github.com/lightningnetwork/lightning-onion"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/roasbeef/btcutil"
|
"github.com/roasbeef/btcutil"
|
||||||
@ -139,7 +138,7 @@ func (r *sphinxHopIterator) ForwardingInstructions() ForwardingInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SphinxDecoder is responsible for keeping all sphinx dependent parts inside
|
// OnionProcessor is responsible for keeping all sphinx dependent parts inside
|
||||||
// and expose only decoding function. With such approach we give freedom for
|
// and expose only decoding function. With such approach we give freedom for
|
||||||
// subsystems which wants to decode sphinx path to not be dependable from
|
// subsystems which wants to decode sphinx path to not be dependable from
|
||||||
// sphinx at all.
|
// sphinx at all.
|
||||||
@ -148,25 +147,23 @@ func (r *sphinxHopIterator) ForwardingInstructions() ForwardingInfo {
|
|||||||
// maintain the hop iterator abstraction. Without it the structures which using
|
// maintain the hop iterator abstraction. Without it the structures which using
|
||||||
// the hop iterator should contain sphinx router which makes their creations in
|
// the hop iterator should contain sphinx router which makes their creations in
|
||||||
// tests dependent from the sphinx internal parts.
|
// tests dependent from the sphinx internal parts.
|
||||||
type SphinxDecoder struct {
|
type OnionProcessor struct {
|
||||||
router *sphinx.Router
|
router *sphinx.Router
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSphinxDecoder creates new instance of decoder.
|
// NewOnionProcessor creates new instance of decoder.
|
||||||
func NewSphinxDecoder(router *sphinx.Router) *SphinxDecoder {
|
func NewOnionProcessor(router *sphinx.Router) *OnionProcessor {
|
||||||
return &SphinxDecoder{router}
|
return &OnionProcessor{router}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode attempts to decode a valid sphinx packet from the passed io.Reader
|
// DecodeHopIterator attempts to decode a valid sphinx packet from the passed io.Reader
|
||||||
// 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 *SphinxDecoder) Decode(r io.Reader, rHash []byte) (HopIterator, error) {
|
func (p *OnionProcessor) DecodeHopIterator(r io.Reader, rHash []byte) (HopIterator,
|
||||||
// Before adding the new HTLC to the state machine, parse the onion
|
lnwire.FailCode) {
|
||||||
// object in order to obtain the routing information.
|
|
||||||
onionPkt := &sphinx.OnionPacket{}
|
onionPkt := &sphinx.OnionPacket{}
|
||||||
if err := onionPkt.Decode(r); err != nil {
|
if err := onionPkt.Decode(r); err != nil {
|
||||||
return nil, errors.Errorf("unable to decode onion pkt: %v",
|
return nil, lnwire.CodeTemporaryChannelFailure
|
||||||
err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to process the Sphinx packet. We include the payment hash of
|
// Attempt to process the Sphinx packet. We include the payment hash of
|
||||||
@ -176,12 +173,49 @@ func (p *SphinxDecoder) Decode(r io.Reader, rHash []byte) (HopIterator, error) {
|
|||||||
// hash twice, thereby losing their money entirely.
|
// hash twice, thereby losing their money entirely.
|
||||||
sphinxPacket, err := p.router.ProcessOnionPacket(onionPkt, rHash)
|
sphinxPacket, err := p.router.ProcessOnionPacket(onionPkt, rHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Errorf("unable to process onion pkt: "+
|
switch err {
|
||||||
"%v", err)
|
case sphinx.ErrInvalidOnionVersion:
|
||||||
|
return nil, lnwire.CodeInvalidOnionVersion
|
||||||
|
case sphinx.ErrInvalidOnionHMAC:
|
||||||
|
return nil, lnwire.CodeInvalidOnionHmac
|
||||||
|
case sphinx.ErrInvalidOnionKey:
|
||||||
|
return nil, lnwire.CodeInvalidOnionKey
|
||||||
|
default:
|
||||||
|
return nil, lnwire.CodeTemporaryChannelFailure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &sphinxHopIterator{
|
return &sphinxHopIterator{
|
||||||
nextPacket: sphinxPacket.NextPacket,
|
nextPacket: sphinxPacket.NextPacket,
|
||||||
processedPacket: sphinxPacket,
|
processedPacket: sphinxPacket,
|
||||||
}, nil
|
}, lnwire.CodeNone
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeOnionObfuscator takes the onion blob as input extract the shared secret
|
||||||
|
// and return the entity which is able to obfuscate failure data.
|
||||||
|
func (p *OnionProcessor) DecodeOnionObfuscator(r io.Reader) (Obfuscator,
|
||||||
|
lnwire.FailCode) {
|
||||||
|
onionPkt := &sphinx.OnionPacket{}
|
||||||
|
if err := onionPkt.Decode(r); err != nil {
|
||||||
|
return nil, lnwire.CodeTemporaryChannelFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
onionObfuscator, err := sphinx.NewOnionObfuscator(p.router,
|
||||||
|
onionPkt.EphemeralKey)
|
||||||
|
if err != nil {
|
||||||
|
switch err {
|
||||||
|
case sphinx.ErrInvalidOnionVersion:
|
||||||
|
return nil, lnwire.CodeInvalidOnionVersion
|
||||||
|
case sphinx.ErrInvalidOnionHMAC:
|
||||||
|
return nil, lnwire.CodeInvalidOnionHmac
|
||||||
|
case sphinx.ErrInvalidOnionKey:
|
||||||
|
return nil, lnwire.CodeInvalidOnionKey
|
||||||
|
default:
|
||||||
|
return nil, lnwire.CodeTemporaryChannelFailure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &FailureObfuscator{
|
||||||
|
OnionObfuscator: onionObfuscator,
|
||||||
|
}, lnwire.CodeNone
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,9 @@ import (
|
|||||||
|
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"crypto/sha256"
|
||||||
|
|
||||||
|
"github.com/go-errors/errors"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/roasbeef/btcd/btcec"
|
"github.com/roasbeef/btcd/btcec"
|
||||||
@ -80,10 +83,18 @@ type ChannelLinkConfig struct {
|
|||||||
// contained in the forwarding blob within each HTLC.
|
// contained in the forwarding blob within each HTLC.
|
||||||
Switch *Switch
|
Switch *Switch
|
||||||
|
|
||||||
// DecodeOnion function responsible for decoding htlc Sphinx onion
|
// DecodeHopIterator function is responsible for decoding htlc Sphinx onion
|
||||||
// blob, and creating hop iterator which will give us next destination
|
// blob, and creating hop iterator which will give us next destination
|
||||||
// of htlc.
|
// of htlc.
|
||||||
DecodeOnion func(r io.Reader, meta []byte) (HopIterator, error)
|
DecodeHopIterator func(r io.Reader, rHash []byte) (HopIterator, lnwire.FailCode)
|
||||||
|
|
||||||
|
// DecodeOnionObfuscator function is responsible for decoding htlc Sphinx
|
||||||
|
// onion blob, and creating onion failure obfuscator.
|
||||||
|
DecodeOnionObfuscator func(r io.Reader) (Obfuscator, lnwire.FailCode)
|
||||||
|
|
||||||
|
// GetLastChannelUpdate retrieve the topology info about the channel in
|
||||||
|
// order to create the channel update.
|
||||||
|
GetLastChannelUpdate func() (*lnwire.ChannelUpdate, error)
|
||||||
|
|
||||||
// Peer is a lightning network node with which we have the channel link
|
// Peer is a lightning network node with which we have the channel link
|
||||||
// opened.
|
// opened.
|
||||||
@ -406,32 +417,57 @@ func (l *channelLink) handleDownStreamPkt(pkt *htlcPacket) {
|
|||||||
// commitment chains.
|
// commitment chains.
|
||||||
htlc.ChanID = l.ChanID()
|
htlc.ChanID = l.ChanID()
|
||||||
index, err := l.channel.AddHTLC(htlc)
|
index, err := l.channel.AddHTLC(htlc)
|
||||||
if err == lnwallet.ErrMaxHTLCNumber {
|
if err != nil {
|
||||||
log.Infof("Downstream htlc add update with "+
|
switch err {
|
||||||
"payment hash(%x) have been added to "+
|
case lnwallet.ErrMaxHTLCNumber:
|
||||||
"reprocessing queue, batch: %v",
|
log.Infof("Downstream htlc add update with "+
|
||||||
htlc.PaymentHash[:],
|
"payment hash(%x) have been added to "+
|
||||||
l.batchCounter)
|
"reprocessing queue, batch: %v",
|
||||||
l.overflowQueue.consume(pkt)
|
htlc.PaymentHash[:],
|
||||||
return
|
l.batchCounter)
|
||||||
} else if err != nil {
|
l.overflowQueue.consume(pkt)
|
||||||
failPacket := newFailPacket(
|
return
|
||||||
l.ShortChanID(),
|
|
||||||
&lnwire.UpdateFailHTLC{
|
|
||||||
Reason: []byte{byte(0)},
|
|
||||||
},
|
|
||||||
htlc.PaymentHash,
|
|
||||||
htlc.Amount,
|
|
||||||
)
|
|
||||||
|
|
||||||
// The HTLC was unable to be added to the state
|
default:
|
||||||
// machine, as a result, we'll signal the switch to
|
// The HTLC was unable to be added to the state
|
||||||
// cancel the pending payment.
|
// machine, as a result, we'll signal the switch to
|
||||||
go l.cfg.Switch.forward(failPacket)
|
// cancel the pending payment.
|
||||||
|
var (
|
||||||
|
isObfuscated bool
|
||||||
|
reason lnwire.OpaqueReason
|
||||||
|
)
|
||||||
|
|
||||||
log.Errorf("unable to handle downstream add HTLC: %v",
|
failure := lnwire.NewTemporaryChannelFailure(nil)
|
||||||
err)
|
onionReader := bytes.NewReader(htlc.OnionBlob[:])
|
||||||
return
|
obfuscator, failCode := l.cfg.DecodeOnionObfuscator(onionReader)
|
||||||
|
if failCode != lnwire.CodeNone {
|
||||||
|
var b bytes.Buffer
|
||||||
|
err := lnwire.EncodeFailure(&b, failure, 0)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("unable to encode failure: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
reason = lnwire.OpaqueReason(b.Bytes())
|
||||||
|
isObfuscated = false
|
||||||
|
} else {
|
||||||
|
reason, err = obfuscator.InitialObfuscate(failure)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("unable to obfuscate error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isObfuscated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
go l.cfg.Switch.forward(newFailPacket(
|
||||||
|
l.ShortChanID(),
|
||||||
|
&lnwire.UpdateFailHTLC{
|
||||||
|
Reason: reason,
|
||||||
|
}, htlc.PaymentHash, htlc.Amount, isObfuscated,
|
||||||
|
))
|
||||||
|
|
||||||
|
log.Infof("Unable to handle downstream add HTLC: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Tracef("Received downstream htlc: payment_hash=%x, "+
|
log.Tracef("Received downstream htlc: payment_hash=%x, "+
|
||||||
@ -439,7 +475,6 @@ func (l *channelLink) handleDownStreamPkt(pkt *htlcPacket) {
|
|||||||
htlc.PaymentHash[:], index, l.batchCounter+1)
|
htlc.PaymentHash[:], index, l.batchCounter+1)
|
||||||
|
|
||||||
htlc.ID = index
|
htlc.ID = index
|
||||||
|
|
||||||
l.cfg.Peer.SendMessage(htlc)
|
l.cfg.Peer.SendMessage(htlc)
|
||||||
|
|
||||||
case *lnwire.UpdateFufillHTLC:
|
case *lnwire.UpdateFufillHTLC:
|
||||||
@ -516,9 +551,6 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
|
|||||||
log.Tracef("Receive upstream htlc with payment hash(%x), "+
|
log.Tracef("Receive upstream htlc with payment hash(%x), "+
|
||||||
"assigning index: %v", msg.PaymentHash[:], index)
|
"assigning index: %v", msg.PaymentHash[:], index)
|
||||||
|
|
||||||
// TODO(roasbeef): perform sanity checks on per-hop payload
|
|
||||||
// * time-lock is sane, fee, chain, etc
|
|
||||||
|
|
||||||
// Store the onion blob which encapsulate the htlc route and
|
// Store the onion blob which encapsulate the htlc route and
|
||||||
// use in on stage of HTLC inclusion to retrieve the next hop
|
// use in on stage of HTLC inclusion to retrieve the next hop
|
||||||
// and propagate the HTLC along the remaining route.
|
// and propagate the HTLC along the remaining route.
|
||||||
@ -535,6 +567,46 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
|
|||||||
|
|
||||||
// TODO(roasbeef): add preimage to DB in order to swipe
|
// TODO(roasbeef): add preimage to DB in order to swipe
|
||||||
// repeated r-values
|
// repeated r-values
|
||||||
|
|
||||||
|
// If remote side have been unable to parse the onion blob we have sent
|
||||||
|
// to it, than we should transform the malformed notification to the the
|
||||||
|
// usual htlc fail message.
|
||||||
|
case *lnwire.UpdateFailMalformedHTLC:
|
||||||
|
idx := msg.ID
|
||||||
|
if err := l.channel.ReceiveFailHTLC(idx); err != nil {
|
||||||
|
l.fail("unable to handle upstream fail HTLC: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var failure lnwire.FailureMessage
|
||||||
|
switch msg.FailureCode {
|
||||||
|
case lnwire.CodeInvalidOnionVersion:
|
||||||
|
failure = &lnwire.FailInvalidOnionVersion{
|
||||||
|
OnionSHA256: msg.ShaOnionBlob,
|
||||||
|
}
|
||||||
|
case lnwire.CodeInvalidOnionHmac:
|
||||||
|
failure = &lnwire.FailInvalidOnionHmac{
|
||||||
|
OnionSHA256: msg.ShaOnionBlob,
|
||||||
|
}
|
||||||
|
|
||||||
|
case lnwire.CodeInvalidOnionKey:
|
||||||
|
failure = &lnwire.FailInvalidOnionKey{
|
||||||
|
OnionSHA256: msg.ShaOnionBlob,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Errorf("unable to understand code of received " +
|
||||||
|
"malformed error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
if err := lnwire.EncodeFailure(&b, failure, 0); err != nil {
|
||||||
|
log.Errorf("unable to encode malformed error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l.cancelReasons[idx] = lnwire.OpaqueReason(b.Bytes())
|
||||||
|
|
||||||
case *lnwire.UpdateFailHTLC:
|
case *lnwire.UpdateFailHTLC:
|
||||||
idx := msg.ID
|
idx := msg.ID
|
||||||
if err := l.channel.ReceiveFailHTLC(idx); err != nil {
|
if err := l.channel.ReceiveFailHTLC(idx); err != nil {
|
||||||
@ -847,7 +919,7 @@ func (l *channelLink) processLockedInHtlcs(
|
|||||||
packetsToForward = append(packetsToForward, settlePacket)
|
packetsToForward = append(packetsToForward, settlePacket)
|
||||||
l.overflowQueue.release()
|
l.overflowQueue.release()
|
||||||
|
|
||||||
// A failure message for a previously forwarded HTLC has been
|
// A failureCode message for a previously forwarded HTLC has been
|
||||||
// received. As a result a new slot will be freed up in our
|
// received. As a result a new slot will be freed up in our
|
||||||
// commitment state, so we'll forward this to the switch so the
|
// commitment state, so we'll forward this to the switch so the
|
||||||
// backwards undo can continue.
|
// backwards undo can continue.
|
||||||
@ -861,7 +933,7 @@ func (l *channelLink) processLockedInHtlcs(
|
|||||||
ChanID: l.ChanID(),
|
ChanID: l.ChanID(),
|
||||||
}
|
}
|
||||||
failPacket := newFailPacket(l.ShortChanID(), failUpdate,
|
failPacket := newFailPacket(l.ShortChanID(), failUpdate,
|
||||||
pd.RHash, pd.Amount)
|
pd.RHash, pd.Amount, false)
|
||||||
|
|
||||||
// Add the packet to the batch to be forwarded, and
|
// Add the packet to the batch to be forwarded, and
|
||||||
// notify the overflow queue that a spare spot has been
|
// notify the overflow queue that a spare spot has been
|
||||||
@ -880,11 +952,22 @@ func (l *channelLink) processLockedInHtlcs(
|
|||||||
onionBlob := l.clearedOnionBlobs[pd.Index]
|
onionBlob := l.clearedOnionBlobs[pd.Index]
|
||||||
delete(l.clearedOnionBlobs, pd.Index)
|
delete(l.clearedOnionBlobs, pd.Index)
|
||||||
|
|
||||||
|
// Retrieve onion obfuscator from onion blob in order to produce
|
||||||
|
// initial obfuscation of the onion failureCode.
|
||||||
onionReader := bytes.NewReader(onionBlob[:])
|
onionReader := bytes.NewReader(onionBlob[:])
|
||||||
|
obfuscator, failureCode := l.cfg.DecodeOnionObfuscator(onionReader)
|
||||||
|
if failureCode != lnwire.CodeNone {
|
||||||
|
log.Error("unable to decode onion obfuscator")
|
||||||
|
// If we unable to process the onion blob than we should send
|
||||||
|
// the malformed htlc error to payment sender.
|
||||||
|
l.sendMalformedHTLCError(pd.RHash, failureCode, onionBlob[:])
|
||||||
|
needUpdate = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Before adding the new htlc to the state machine,
|
// Before adding the new htlc to the state machine,
|
||||||
// parse the onion object in order to obtain the
|
// parse the onion object in order to obtain the
|
||||||
// routing information with DecodeOnion function which
|
// routing information with DecodeHopIterator function which
|
||||||
// process the Sphinx packet.
|
// process the Sphinx packet.
|
||||||
//
|
//
|
||||||
// We include the payment hash of the htlc as it's
|
// We include the payment hash of the htlc as it's
|
||||||
@ -893,20 +976,18 @@ func (l *channelLink) processLockedInHtlcs(
|
|||||||
// attacks. In the case of a replay, an attacker is
|
// attacks. In the case of a replay, an attacker is
|
||||||
// *forced* to use the same payment hash twice, thereby
|
// *forced* to use the same payment hash twice, thereby
|
||||||
// losing their money entirely.
|
// losing their money entirely.
|
||||||
chanIterator, err := l.cfg.DecodeOnion(onionReader,
|
onionReader = bytes.NewReader(onionBlob[:])
|
||||||
pd.RHash[:])
|
chanIterator, failureCode := l.cfg.DecodeHopIterator(onionReader, pd.RHash[:])
|
||||||
if err != nil {
|
if failureCode != lnwire.CodeNone {
|
||||||
// If we're unable to parse the Sphinx packet,
|
log.Error("unable to decode onion hop iterator")
|
||||||
// then we'll cancel the htlc.
|
// If we unable to process the onion blob than we should send
|
||||||
log.Errorf("unable to get the next hop: %v", err)
|
// the malformed htlc error to payment sender.
|
||||||
reason := []byte{byte(lnwire.SphinxParseError)}
|
l.sendMalformedHTLCError(pd.RHash, failureCode, onionBlob[:])
|
||||||
l.sendHTLCError(pd.RHash, reason)
|
|
||||||
needUpdate = true
|
needUpdate = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
fwdInfo := chanIterator.ForwardingInstructions()
|
fwdInfo := chanIterator.ForwardingInstructions()
|
||||||
|
|
||||||
switch fwdInfo.NextHop {
|
switch fwdInfo.NextHop {
|
||||||
case exitHop:
|
case exitHop:
|
||||||
// We're the designated payment destination.
|
// We're the designated payment destination.
|
||||||
@ -918,8 +999,8 @@ func (l *channelLink) processLockedInHtlcs(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("unable to query to locate:"+
|
log.Errorf("unable to query to locate:"+
|
||||||
" %v", err)
|
" %v", err)
|
||||||
reason := []byte{byte(lnwire.UnknownPaymentHash)}
|
failure := lnwire.FailUnknownPaymentHash{}
|
||||||
l.sendHTLCError(pd.RHash, reason)
|
l.sendHTLCError(pd.RHash, failure, obfuscator)
|
||||||
needUpdate = true
|
needUpdate = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -940,8 +1021,8 @@ func (l *channelLink) processLockedInHtlcs(
|
|||||||
invoice.Terms.Value,
|
invoice.Terms.Value,
|
||||||
fwdInfo.AmountToForward)
|
fwdInfo.AmountToForward)
|
||||||
|
|
||||||
reason := []byte{byte(lnwire.IncorrectValue)}
|
failure := lnwire.FailIncorrectPaymentAmount{}
|
||||||
l.sendHTLCError(pd.RHash, reason)
|
l.sendHTLCError(pd.RHash, failure, obfuscator)
|
||||||
needUpdate = true
|
needUpdate = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -954,8 +1035,8 @@ func (l *channelLink) processLockedInHtlcs(
|
|||||||
pd.RHash[:], l.cfg.FwrdingPolicy.TimeLockDelta,
|
pd.RHash[:], l.cfg.FwrdingPolicy.TimeLockDelta,
|
||||||
fwdInfo.OutgoingCTLV)
|
fwdInfo.OutgoingCTLV)
|
||||||
|
|
||||||
reason := []byte{byte(lnwire.UpstreamTimeout)}
|
failure := lnwire.NewFinalIncorrectCltvExpiry(fwdInfo.OutgoingCTLV)
|
||||||
l.sendHTLCError(pd.RHash, reason)
|
l.sendHTLCError(pd.RHash, failure, obfuscator)
|
||||||
needUpdate = true
|
needUpdate = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -970,9 +1051,8 @@ func (l *channelLink) processLockedInHtlcs(
|
|||||||
log.Errorf("rejecting htlc due to incorrect "+
|
log.Errorf("rejecting htlc due to incorrect "+
|
||||||
"amount: expected %v, received %v",
|
"amount: expected %v, received %v",
|
||||||
invoice.Terms.Value, pd.Amount)
|
invoice.Terms.Value, pd.Amount)
|
||||||
|
failure := lnwire.FailIncorrectPaymentAmount{}
|
||||||
reason := []byte{byte(lnwire.IncorrectValue)}
|
l.sendHTLCError(pd.RHash, failure, obfuscator)
|
||||||
l.sendHTLCError(pd.RHash, reason)
|
|
||||||
needUpdate = true
|
needUpdate = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -1016,8 +1096,16 @@ func (l *channelLink) processLockedInHtlcs(
|
|||||||
pd.RHash[:], l.cfg.FwrdingPolicy.MinHTLC,
|
pd.RHash[:], l.cfg.FwrdingPolicy.MinHTLC,
|
||||||
pd.Amount)
|
pd.Amount)
|
||||||
|
|
||||||
reason := []byte{byte(lnwire.IncorrectValue)}
|
var failure lnwire.FailureMessage
|
||||||
l.sendHTLCError(pd.RHash, reason)
|
update, err := l.cfg.GetLastChannelUpdate()
|
||||||
|
if err != nil {
|
||||||
|
failure = lnwire.NewTemporaryChannelFailure(nil)
|
||||||
|
} else {
|
||||||
|
failure = lnwire.NewAmountBelowMinimum(
|
||||||
|
pd.Amount, *update)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.sendHTLCError(pd.RHash, failure, obfuscator)
|
||||||
needUpdate = true
|
needUpdate = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -1046,9 +1134,16 @@ func (l *channelLink) processLockedInHtlcs(
|
|||||||
btcutil.Amount(pd.Amount-fwdInfo.AmountToForward),
|
btcutil.Amount(pd.Amount-fwdInfo.AmountToForward),
|
||||||
btcutil.Amount(expectedFee))
|
btcutil.Amount(expectedFee))
|
||||||
|
|
||||||
// TODO(andrew.shvv): send proper back error
|
var failure lnwire.FailureMessage
|
||||||
reason := []byte{byte(lnwire.IncorrectValue)}
|
update, err := l.cfg.GetLastChannelUpdate()
|
||||||
l.sendHTLCError(pd.RHash, reason)
|
if err != nil {
|
||||||
|
failure = lnwire.NewTemporaryChannelFailure(nil)
|
||||||
|
} else {
|
||||||
|
failure = lnwire.NewFeeInsufficient(pd.Amount,
|
||||||
|
*update)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.sendHTLCError(pd.RHash, failure, obfuscator)
|
||||||
needUpdate = true
|
needUpdate = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -1062,16 +1157,22 @@ func (l *channelLink) processLockedInHtlcs(
|
|||||||
// with the HTLC.
|
// with the HTLC.
|
||||||
timeDelta := l.cfg.FwrdingPolicy.TimeLockDelta
|
timeDelta := l.cfg.FwrdingPolicy.TimeLockDelta
|
||||||
if pd.Timeout-timeDelta != fwdInfo.OutgoingCTLV {
|
if pd.Timeout-timeDelta != fwdInfo.OutgoingCTLV {
|
||||||
// TODO(andrew.shvv): send proper back error
|
|
||||||
log.Errorf("Incoming htlc(%x) has "+
|
log.Errorf("Incoming htlc(%x) has "+
|
||||||
"incorrect time-lock value: expected "+
|
"incorrect time-lock value: expected "+
|
||||||
"%v blocks, got %v blocks",
|
"%v blocks, got %v blocks",
|
||||||
pd.RHash[:], pd.Timeout-timeDelta,
|
pd.RHash[:], pd.Timeout-timeDelta,
|
||||||
fwdInfo.OutgoingCTLV)
|
fwdInfo.OutgoingCTLV)
|
||||||
|
|
||||||
// TODO(andrew.shvv): send proper back error
|
update, err := l.cfg.GetLastChannelUpdate()
|
||||||
reason := []byte{byte(lnwire.UpstreamTimeout)}
|
if err != nil {
|
||||||
l.sendHTLCError(pd.RHash, reason)
|
l.fail("unable to create channel update "+
|
||||||
|
"while handling the error: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
failure := lnwire.NewIncorrectCltvExpiry(
|
||||||
|
pd.Timeout, *update)
|
||||||
|
l.sendHTLCError(pd.RHash, failure, obfuscator)
|
||||||
needUpdate = true
|
needUpdate = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -1094,14 +1195,15 @@ func (l *channelLink) processLockedInHtlcs(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("unable to encode the "+
|
log.Errorf("unable to encode the "+
|
||||||
"remaining route %v", err)
|
"remaining route %v", err)
|
||||||
reason := []byte{byte(lnwire.UnknownError)}
|
|
||||||
l.sendHTLCError(pd.RHash, reason)
|
failure := lnwire.NewTemporaryChannelFailure(nil)
|
||||||
|
l.sendHTLCError(pd.RHash, failure, obfuscator)
|
||||||
needUpdate = true
|
needUpdate = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePacket := newAddPacket(l.ShortChanID(),
|
updatePacket := newAddPacket(l.ShortChanID(),
|
||||||
fwdInfo.NextHop, addMsg)
|
fwdInfo.NextHop, addMsg, obfuscator)
|
||||||
packetsToForward = append(packetsToForward, updatePacket)
|
packetsToForward = append(packetsToForward, updatePacket)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1122,8 +1224,13 @@ func (l *channelLink) processLockedInHtlcs(
|
|||||||
|
|
||||||
// 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(rHash [32]byte,
|
func (l *channelLink) sendHTLCError(rHash [32]byte, failure lnwire.FailureMessage,
|
||||||
reason lnwire.OpaqueReason) {
|
obfuscator Obfuscator) {
|
||||||
|
reason, err := obfuscator.InitialObfuscate(failure)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("unable to obfuscate error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
index, err := l.channel.FailHTLC(rHash)
|
index, err := l.channel.FailHTLC(rHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1138,10 +1245,28 @@ func (l *channelLink) sendHTLCError(rHash [32]byte,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sendMalformedHTLCError helper function which sends the malformed htlc update
|
||||||
|
// to the payment sender.
|
||||||
|
func (l *channelLink) sendMalformedHTLCError(rHash [32]byte, code lnwire.FailCode,
|
||||||
|
onionBlob []byte) {
|
||||||
|
index, err := l.channel.FailHTLC(rHash)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("unable cancel htlc: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l.cfg.Peer.SendMessage(&lnwire.UpdateFailMalformedHTLC{
|
||||||
|
ChanID: l.ChanID(),
|
||||||
|
ID: index,
|
||||||
|
ShaOnionBlob: sha256.Sum256(onionBlob),
|
||||||
|
FailureCode: code,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// fail helper function which is used to encapsulate the action neccessary
|
// fail helper function which is used to encapsulate the action neccessary
|
||||||
// for proper disconnect.
|
// for proper disconnect.
|
||||||
func (l *channelLink) fail(format string, a ...interface{}) {
|
func (l *channelLink) fail(format string, a ...interface{}) {
|
||||||
reason := errors.Errorf(format, a...)
|
reason := errors.Errorf(format, a...)
|
||||||
log.Error(reason)
|
log.Error(reason)
|
||||||
l.cfg.Peer.Disconnect(reason)
|
l.cfg.Peer.Disconnect(reason)
|
||||||
}
|
}
|
||||||
|
@ -376,10 +376,10 @@ func TestExitNodeTimelockPayloadMismatch(t *testing.T) {
|
|||||||
n.bobServer.PubKey(), hops, amount, htlcAmt, htlcExpiry)
|
n.bobServer.PubKey(), hops, amount, htlcAmt, htlcExpiry)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("payment should have failed but didn't")
|
t.Fatalf("payment should have failed but didn't")
|
||||||
} else if err.Error() != lnwire.UpstreamTimeout.String() {
|
} else if err.Error() != lnwire.CodeFinalIncorrectCltvExpiry.String() {
|
||||||
// TODO(roasbeef): use proper error after error propagation is
|
// TODO(roasbeef): use proper error after error propagation is
|
||||||
// in
|
// in
|
||||||
t.Fatalf("incorrect error, expected insufficient value, "+
|
t.Fatalf("incorrect error, expected incorrect cltv expiry, "+
|
||||||
"instead have: %v", err)
|
"instead have: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -413,7 +413,7 @@ func TestExitNodeAmountPayloadMismatch(t *testing.T) {
|
|||||||
n.bobServer.PubKey(), hops, amount, htlcAmt, htlcExpiry)
|
n.bobServer.PubKey(), hops, amount, htlcAmt, htlcExpiry)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("payment should have failed but didn't")
|
t.Fatalf("payment should have failed but didn't")
|
||||||
} else if err.Error() != lnwire.IncorrectValue.String() {
|
} else if err.Error() != lnwire.CodeIncorrectPaymentAmount.String() {
|
||||||
// TODO(roasbeef): use proper error after error propagation is
|
// TODO(roasbeef): use proper error after error propagation is
|
||||||
// in
|
// in
|
||||||
t.Fatalf("incorrect error, expected insufficient value, "+
|
t.Fatalf("incorrect error, expected insufficient value, "+
|
||||||
@ -455,10 +455,8 @@ func TestLinkForwardTimelockPolicyMismatch(t *testing.T) {
|
|||||||
// should be rejected due to a policy violation.
|
// should be rejected due to a policy violation.
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("payment should have failed but didn't")
|
t.Fatalf("payment should have failed but didn't")
|
||||||
} else if err.Error() != lnwire.UpstreamTimeout.String() {
|
} else if err.Error() != lnwire.CodeIncorrectCltvExpiry.String() {
|
||||||
// TODO(roasbeef): use proper error after error propagation is
|
t.Fatalf("incorrect error, expected incorrect cltv expiry, "+
|
||||||
// in
|
|
||||||
t.Fatalf("incorrect error, expected insufficient value, "+
|
|
||||||
"instead have: %v", err)
|
"instead have: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -498,10 +496,10 @@ func TestLinkForwardFeePolicyMismatch(t *testing.T) {
|
|||||||
// should be rejected due to a policy violation.
|
// should be rejected due to a policy violation.
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("payment should have failed but didn't")
|
t.Fatalf("payment should have failed but didn't")
|
||||||
} else if err.Error() != lnwire.IncorrectValue.String() {
|
} else if err.Error() != lnwire.CodeFeeInsufficient.String() {
|
||||||
// TODO(roasbeef): use proper error after error propagation is
|
// TODO(roasbeef): use proper error after error propagation is
|
||||||
// in
|
// in
|
||||||
t.Fatalf("incorrect error, expected insufficient value, "+
|
t.Fatalf("incorrect error, expected fee insufficient, "+
|
||||||
"instead have: %v", err)
|
"instead have: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -541,10 +539,10 @@ func TestLinkForwardMinHTLCPolicyMismatch(t *testing.T) {
|
|||||||
// should be rejected due to a policy violation (below min HTLC).
|
// should be rejected due to a policy violation (below min HTLC).
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("payment should have failed but didn't")
|
t.Fatalf("payment should have failed but didn't")
|
||||||
} else if err.Error() != lnwire.IncorrectValue.String() {
|
} else if err.Error() != lnwire.CodeAmountBelowMinimum.String() {
|
||||||
// TODO(roasbeef): use proper error after error propagation is
|
// TODO(roasbeef): use proper error after error propagation is
|
||||||
// in
|
// in
|
||||||
t.Fatalf("incorrect error, expected insufficient value, "+
|
t.Fatalf("incorrect error, expected amount below minimum, "+
|
||||||
"instead have: %v", err)
|
"instead have: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -633,7 +631,7 @@ func TestChannelLinkMultiHopInsufficientPayment(t *testing.T) {
|
|||||||
btcutil.SatoshiPerBitcoin*5,
|
btcutil.SatoshiPerBitcoin*5,
|
||||||
)
|
)
|
||||||
if err := n.start(); err != nil {
|
if err := n.start(); err != nil {
|
||||||
t.Fatalf("can't start three hop network: %v", err)
|
t.Fatalf("unable to start three hop network: %v", err)
|
||||||
}
|
}
|
||||||
defer n.stop()
|
defer n.stop()
|
||||||
|
|
||||||
@ -656,7 +654,7 @@ func TestChannelLinkMultiHopInsufficientPayment(t *testing.T) {
|
|||||||
n.bobServer.PubKey(), hops, amount, htlcAmt, totalTimelock)
|
n.bobServer.PubKey(), hops, amount, htlcAmt, totalTimelock)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("error haven't been received")
|
t.Fatal("error haven't been received")
|
||||||
} else if err.Error() != errors.New(lnwire.InsufficientCapacity).Error() {
|
} else if err.Error() != errors.New(lnwire.CodeTemporaryChannelFailure).Error() {
|
||||||
t.Fatalf("wrong error have been received: %v", err)
|
t.Fatalf("wrong error have been received: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -702,7 +700,7 @@ func TestChannelLinkMultiHopUnknownPaymentHash(t *testing.T) {
|
|||||||
btcutil.SatoshiPerBitcoin*5,
|
btcutil.SatoshiPerBitcoin*5,
|
||||||
)
|
)
|
||||||
if err := n.start(); err != nil {
|
if err := n.start(); err != nil {
|
||||||
t.Fatalf("can't start three hop network: %v", err)
|
t.Fatalf("unable to start three hop network: %v", err)
|
||||||
}
|
}
|
||||||
defer n.stop()
|
defer n.stop()
|
||||||
|
|
||||||
@ -733,13 +731,14 @@ func TestChannelLinkMultiHopUnknownPaymentHash(t *testing.T) {
|
|||||||
|
|
||||||
// Check who is last in the route and add invoice to server registry.
|
// Check who is last in the route and add invoice to server registry.
|
||||||
if err := n.carolServer.registry.AddInvoice(invoice); err != nil {
|
if err := n.carolServer.registry.AddInvoice(invoice); err != nil {
|
||||||
t.Fatalf("can't add invoice in carol registry: %v", err)
|
t.Fatalf("unable to add invoice in carol registry: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send payment and expose err channel.
|
// Send payment and expose err channel.
|
||||||
_, err = n.aliceServer.htlcSwitch.SendHTLC(n.bobServer.PubKey(), htlc)
|
_, err = n.aliceServer.htlcSwitch.SendHTLC(n.bobServer.PubKey(), htlc,
|
||||||
if err == nil {
|
newMockDeobfuscator())
|
||||||
t.Fatal("error wasn't received")
|
if err.Error() != lnwire.CodeUnknownPaymentHash.String() {
|
||||||
|
t.Fatal("error haven't been received")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for Alice to receive the revocation.
|
// Wait for Alice to receive the revocation.
|
||||||
@ -802,7 +801,7 @@ func TestChannelLinkMultiHopUnknownNextHop(t *testing.T) {
|
|||||||
amount, htlcAmt, totalTimelock)
|
amount, htlcAmt, totalTimelock)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("error haven't been received")
|
t.Fatal("error haven't been received")
|
||||||
} else if err.Error() != errors.New(lnwire.UnknownDestination).Error() {
|
} else if err.Error() != lnwire.CodeUnknownNextPeer.String() {
|
||||||
t.Fatalf("wrong error have been received: %v", err)
|
t.Fatalf("wrong error have been received: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -848,14 +847,14 @@ func TestChannelLinkMultiHopDecodeError(t *testing.T) {
|
|||||||
btcutil.SatoshiPerBitcoin*5,
|
btcutil.SatoshiPerBitcoin*5,
|
||||||
)
|
)
|
||||||
if err := n.start(); err != nil {
|
if err := n.start(); err != nil {
|
||||||
t.Fatalf("can't start three hop network: %v", err)
|
t.Fatalf("unable to start three hop network: %v", err)
|
||||||
}
|
}
|
||||||
defer n.stop()
|
defer n.stop()
|
||||||
|
|
||||||
// Replace decode function with another which throws an error.
|
// Replace decode function with another which throws an error.
|
||||||
n.carolChannelLink.cfg.DecodeOnion = func(r io.Reader, meta []byte) (
|
n.carolChannelLink.cfg.DecodeOnionObfuscator = func(
|
||||||
HopIterator, error) {
|
r io.Reader) (Obfuscator, lnwire.FailCode) {
|
||||||
return nil, errors.New("some sphinx decode error")
|
return nil, lnwire.CodeInvalidOnionVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
carolBandwidthBefore := n.carolChannelLink.Bandwidth()
|
carolBandwidthBefore := n.carolChannelLink.Bandwidth()
|
||||||
@ -871,7 +870,7 @@ func TestChannelLinkMultiHopDecodeError(t *testing.T) {
|
|||||||
n.bobServer.PubKey(), hops, amount, htlcAmt, totalTimelock)
|
n.bobServer.PubKey(), hops, amount, htlcAmt, totalTimelock)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("error haven't been received")
|
t.Fatal("error haven't been received")
|
||||||
} else if err.Error() != errors.New(lnwire.SphinxParseError).Error() {
|
} else if err.Error() != lnwire.CodeInvalidOnionVersion.String() {
|
||||||
t.Fatalf("wrong error have been received: %v", err)
|
t.Fatalf("wrong error have been received: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -994,7 +993,7 @@ func TestChannelLinkSingleHopMessageOrdering(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err := n.start(); err != nil {
|
if err := n.start(); err != nil {
|
||||||
t.Fatalf("can't start three hop network: %v", err)
|
t.Fatalf("unable to start three hop network: %v", err)
|
||||||
}
|
}
|
||||||
defer n.stop()
|
defer n.stop()
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
"bytes"
|
||||||
|
|
||||||
"github.com/btcsuite/fastsha256"
|
"github.com/btcsuite/fastsha256"
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
@ -147,15 +149,60 @@ func (f *ForwardingInfo) encode(w io.Writer) error {
|
|||||||
|
|
||||||
var _ HopIterator = (*mockHopIterator)(nil)
|
var _ HopIterator = (*mockHopIterator)(nil)
|
||||||
|
|
||||||
|
// mockObfuscator mock implementation of the failure obfuscator which only
|
||||||
|
// encodes the failure and do not makes any onion obfuscation.
|
||||||
|
type mockObfuscator struct{}
|
||||||
|
|
||||||
|
func newMockObfuscator() Obfuscator {
|
||||||
|
return &mockObfuscator{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *mockObfuscator) InitialObfuscate(failure lnwire.FailureMessage) (
|
||||||
|
lnwire.OpaqueReason, error) {
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
if err := lnwire.EncodeFailure(&b, failure, 0); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *mockObfuscator) BackwardObfuscate(reason lnwire.OpaqueReason) lnwire.OpaqueReason {
|
||||||
|
return reason
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// mockDeobfuscator mock implementation of the failure deobfuscator which
|
||||||
|
// only decodes the failure do not makes any onion obfuscation.
|
||||||
|
type mockDeobfuscator struct{}
|
||||||
|
|
||||||
|
func newMockDeobfuscator() Deobfuscator {
|
||||||
|
return &mockDeobfuscator{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *mockDeobfuscator) Deobfuscate(reason lnwire.OpaqueReason) (lnwire.FailureMessage,
|
||||||
|
error) {
|
||||||
|
r := bytes.NewReader(reason)
|
||||||
|
failure, err := lnwire.DecodeFailure(r, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return failure, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Deobfuscator = (*mockDeobfuscator)(nil)
|
||||||
|
|
||||||
// mockIteratorDecoder test version of hop iterator decoder which decodes the
|
// mockIteratorDecoder test version of hop iterator decoder which decodes the
|
||||||
// encoded array of hops.
|
// encoded array of hops.
|
||||||
type mockIteratorDecoder struct{}
|
type mockIteratorDecoder struct{}
|
||||||
|
|
||||||
func (p *mockIteratorDecoder) Decode(r io.Reader, meta []byte) (HopIterator, error) {
|
func (p *mockIteratorDecoder) DecodeHopIterator(r io.Reader, meta []byte) (
|
||||||
|
HopIterator, lnwire.FailCode) {
|
||||||
|
|
||||||
var b [4]byte
|
var b [4]byte
|
||||||
_, err := r.Read(b[:])
|
_, err := r.Read(b[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, lnwire.CodeTemporaryChannelFailure
|
||||||
}
|
}
|
||||||
hopLength := binary.BigEndian.Uint32(b[:])
|
hopLength := binary.BigEndian.Uint32(b[:])
|
||||||
|
|
||||||
@ -163,13 +210,13 @@ func (p *mockIteratorDecoder) Decode(r io.Reader, meta []byte) (HopIterator, err
|
|||||||
for i := uint32(0); i < hopLength; i++ {
|
for i := uint32(0); i < hopLength; i++ {
|
||||||
f := &ForwardingInfo{}
|
f := &ForwardingInfo{}
|
||||||
if err := f.decode(r); err != nil {
|
if err := f.decode(r); err != nil {
|
||||||
return nil, err
|
return nil, lnwire.CodeTemporaryChannelFailure
|
||||||
}
|
}
|
||||||
|
|
||||||
hops[i] = *f
|
hops[i] = *f
|
||||||
}
|
}
|
||||||
|
|
||||||
return newMockHopIterator(hops...), nil
|
return newMockHopIterator(hops...), lnwire.CodeNone
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *ForwardingInfo) decode(r io.Reader) error {
|
func (f *ForwardingInfo) decode(r io.Reader) error {
|
||||||
@ -223,6 +270,8 @@ func (s *mockServer) readHandler(message lnwire.Message) error {
|
|||||||
targetChan = msg.ChanID
|
targetChan = msg.ChanID
|
||||||
case *lnwire.UpdateFailHTLC:
|
case *lnwire.UpdateFailHTLC:
|
||||||
targetChan = msg.ChanID
|
targetChan = msg.ChanID
|
||||||
|
case *lnwire.UpdateFailMalformedHTLC:
|
||||||
|
targetChan = msg.ChanID
|
||||||
case *lnwire.RevokeAndAck:
|
case *lnwire.RevokeAndAck:
|
||||||
targetChan = msg.ChanID
|
targetChan = msg.ChanID
|
||||||
case *lnwire.CommitSig:
|
case *lnwire.CommitSig:
|
||||||
|
@ -29,12 +29,27 @@ type htlcPacket struct {
|
|||||||
src lnwire.ShortChannelID
|
src lnwire.ShortChannelID
|
||||||
|
|
||||||
// amount is the value of the HTLC that is being created or modified.
|
// amount is the value of the HTLC that is being created or modified.
|
||||||
//
|
|
||||||
// TODO(andrew.shvv) should be removed after introducing sphinx payment.
|
// TODO(andrew.shvv) should be removed after introducing sphinx payment.
|
||||||
amount btcutil.Amount
|
amount btcutil.Amount
|
||||||
|
|
||||||
// htlc lnwire message type of which depends on switch request type.
|
// htlc lnwire message type of which depends on switch request type.
|
||||||
htlc lnwire.Message
|
htlc lnwire.Message
|
||||||
|
|
||||||
|
// obfuscator is entity which is needed to make the obfuscation of the
|
||||||
|
// onion failure, it is carried inside the packet from channel
|
||||||
|
// link to the switch because we have to create onion error inside the
|
||||||
|
// switch to, but we unable to restore obfuscator from the onion, because
|
||||||
|
// on stage of forwarding onion inside payment belongs to the remote node.
|
||||||
|
// TODO(andrew.shvv) revisit after refactoring the way of returning errors
|
||||||
|
// inside the htlcswitch packet.
|
||||||
|
obfuscator Obfuscator
|
||||||
|
|
||||||
|
// isObfuscated is used in case if switch sent the packet to the link,
|
||||||
|
// but error have occurred locally, in this case we shouldn't obfuscate
|
||||||
|
// it again.
|
||||||
|
// TODO(andrew.shvv) revisit after refactoring the way of returning errors
|
||||||
|
// inside the htlcswitch packet.
|
||||||
|
isObfuscated bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// newInitPacket creates htlc switch add packet which encapsulates the add htlc
|
// newInitPacket creates htlc switch add packet which encapsulates the add htlc
|
||||||
@ -49,12 +64,13 @@ func newInitPacket(destNode [33]byte, htlc *lnwire.UpdateAddHTLC) *htlcPacket {
|
|||||||
// newAddPacket creates htlc switch add packet which encapsulates the add htlc
|
// newAddPacket creates htlc switch add packet which encapsulates the add htlc
|
||||||
// request and additional information for proper forwarding over htlc switch.
|
// request and additional information for proper forwarding over htlc switch.
|
||||||
func newAddPacket(src, dest lnwire.ShortChannelID,
|
func newAddPacket(src, dest lnwire.ShortChannelID,
|
||||||
htlc *lnwire.UpdateAddHTLC) *htlcPacket {
|
htlc *lnwire.UpdateAddHTLC, obfuscator Obfuscator) *htlcPacket {
|
||||||
|
|
||||||
return &htlcPacket{
|
return &htlcPacket{
|
||||||
dest: dest,
|
dest: dest,
|
||||||
src: src,
|
src: src,
|
||||||
htlc: htlc,
|
htlc: htlc,
|
||||||
|
obfuscator: obfuscator,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,11 +93,12 @@ func newSettlePacket(src lnwire.ShortChannelID, htlc *lnwire.UpdateFufillHTLC,
|
|||||||
// add request if something wrong happened on the path to the final
|
// add request if something wrong happened on the path to the final
|
||||||
// destination.
|
// destination.
|
||||||
func newFailPacket(src lnwire.ShortChannelID, htlc *lnwire.UpdateFailHTLC,
|
func newFailPacket(src lnwire.ShortChannelID, htlc *lnwire.UpdateFailHTLC,
|
||||||
payHash [sha256.Size]byte, amount btcutil.Amount) *htlcPacket {
|
payHash [sha256.Size]byte, amount btcutil.Amount, isObfuscated bool) *htlcPacket {
|
||||||
return &htlcPacket{
|
return &htlcPacket{
|
||||||
src: src,
|
src: src,
|
||||||
payHash: payHash,
|
payHash: payHash,
|
||||||
htlc: htlc,
|
htlc: htlc,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
|
isObfuscated: isObfuscated,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
@ -34,6 +35,11 @@ type pendingPayment struct {
|
|||||||
|
|
||||||
preimage chan [sha256.Size]byte
|
preimage chan [sha256.Size]byte
|
||||||
err chan error
|
err chan error
|
||||||
|
|
||||||
|
// deobfuscator is an serializable entity which is used if we received
|
||||||
|
// an error, it deobfuscates the onion failure blob, and extracts the
|
||||||
|
// exact error from it.
|
||||||
|
deobfuscator Deobfuscator
|
||||||
}
|
}
|
||||||
|
|
||||||
// plexPacket encapsulates switch packet and adds error channel to receive
|
// plexPacket encapsulates switch packet and adds error channel to receive
|
||||||
@ -162,18 +168,17 @@ func New(cfg Config) *Switch {
|
|||||||
|
|
||||||
// SendHTLC is used by other subsystems which aren't belong to htlc switch
|
// SendHTLC is used by other subsystems which aren't belong to htlc switch
|
||||||
// package in order to send the htlc update.
|
// package in order to send the htlc update.
|
||||||
func (s *Switch) SendHTLC(nextNode [33]byte, update lnwire.Message) (
|
func (s *Switch) SendHTLC(nextNode [33]byte, htlc *lnwire.UpdateAddHTLC,
|
||||||
[sha256.Size]byte, error) {
|
deobfuscator Deobfuscator) ([sha256.Size]byte, error) {
|
||||||
|
|
||||||
htlc := update.(*lnwire.UpdateAddHTLC)
|
|
||||||
|
|
||||||
// Create payment and add to the map of payment in order later to be
|
// Create payment and add to the map of payment in order later to be
|
||||||
// able to retrieve it and return response to the user.
|
// able to retrieve it and return response to the user.
|
||||||
payment := &pendingPayment{
|
payment := &pendingPayment{
|
||||||
err: make(chan error, 1),
|
err: make(chan error, 1),
|
||||||
preimage: make(chan [sha256.Size]byte, 1),
|
preimage: make(chan [sha256.Size]byte, 1),
|
||||||
paymentHash: htlc.PaymentHash,
|
paymentHash: htlc.PaymentHash,
|
||||||
amount: htlc.Amount,
|
amount: htlc.Amount,
|
||||||
|
deobfuscator: deobfuscator,
|
||||||
}
|
}
|
||||||
|
|
||||||
s.pendingMutex.Lock()
|
s.pendingMutex.Lock()
|
||||||
@ -259,7 +264,7 @@ func (s *Switch) handleLocalDispatch(payment *pendingPayment, packet *htlcPacket
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("unable to find links by "+
|
log.Errorf("unable to find links by "+
|
||||||
"destination %v", err)
|
"destination %v", err)
|
||||||
return errors.New(lnwire.UnknownDestination)
|
return errors.New(lnwire.CodeUnknownNextPeer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to find destination channel link with appropriate
|
// Try to find destination channel link with appropriate
|
||||||
@ -278,7 +283,7 @@ func (s *Switch) handleLocalDispatch(payment *pendingPayment, packet *htlcPacket
|
|||||||
if destination == nil {
|
if destination == nil {
|
||||||
log.Errorf("unable to find appropriate channel link "+
|
log.Errorf("unable to find appropriate channel link "+
|
||||||
"insufficient capacity, need %v", htlc.Amount)
|
"insufficient capacity, need %v", htlc.Amount)
|
||||||
return errors.New(lnwire.InsufficientCapacity)
|
return errors.New(lnwire.CodeTemporaryChannelFailure)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the packet to the destination channel link which
|
// Send the packet to the destination channel link which
|
||||||
@ -300,11 +305,15 @@ func (s *Switch) handleLocalDispatch(payment *pendingPayment, packet *htlcPacket
|
|||||||
case *lnwire.UpdateFailHTLC:
|
case *lnwire.UpdateFailHTLC:
|
||||||
// Retrieving the fail code from byte representation of error.
|
// Retrieving the fail code from byte representation of error.
|
||||||
var userErr error
|
var userErr error
|
||||||
if code, err := htlc.Reason.ToFailCode(); err != nil {
|
|
||||||
userErr = errors.Errorf("can't decode fail code id"+
|
failure, err := payment.deobfuscator.Deobfuscate(htlc.Reason)
|
||||||
"(%v): %v", htlc.ID, err)
|
if err != nil {
|
||||||
|
userErr = errors.Errorf("unable to de-obfuscate "+
|
||||||
|
"onion failure, htlc with hash(%v): %v", payment.paymentHash[:],
|
||||||
|
err)
|
||||||
|
log.Error(userErr)
|
||||||
} else {
|
} else {
|
||||||
userErr = errors.New(code)
|
userErr = errors.New(failure.Code())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify user that his payment was discarded.
|
// Notify user that his payment was discarded.
|
||||||
@ -342,15 +351,23 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
|
|||||||
// If packet was forwarded from another channel link
|
// If packet was forwarded from another channel link
|
||||||
// than we should notify this link that some error
|
// than we should notify this link that some error
|
||||||
// occurred.
|
// occurred.
|
||||||
reason := []byte{byte(lnwire.UnknownDestination)}
|
failure := lnwire.FailUnknownNextPeer{}
|
||||||
|
reason, err := packet.obfuscator.InitialObfuscate(failure)
|
||||||
|
if err != nil {
|
||||||
|
err := errors.Errorf("unable to obfuscate "+
|
||||||
|
"error: %v", err)
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
go source.HandleSwitchPacket(newFailPacket(
|
go source.HandleSwitchPacket(newFailPacket(
|
||||||
packet.src,
|
packet.src,
|
||||||
&lnwire.UpdateFailHTLC{
|
&lnwire.UpdateFailHTLC{
|
||||||
Reason: reason,
|
Reason: reason,
|
||||||
},
|
},
|
||||||
htlc.PaymentHash, 0,
|
htlc.PaymentHash, 0, true,
|
||||||
))
|
))
|
||||||
err := errors.Errorf("unable to find link with "+
|
err = errors.Errorf("unable to find link with "+
|
||||||
"destination %v", packet.dest)
|
"destination %v", packet.dest)
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return err
|
return err
|
||||||
@ -371,20 +388,28 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
|
|||||||
// over has insufficient capacity, then we'll cancel the htlc
|
// over has insufficient capacity, then we'll cancel the htlc
|
||||||
// as the payment cannot succeed.
|
// as the payment cannot succeed.
|
||||||
if destination == nil {
|
if destination == nil {
|
||||||
// If packet was forwarded from another channel link
|
// If packet was forwarded from another
|
||||||
// than we should notify this link that some error
|
// channel link than we should notify this
|
||||||
// occurred.
|
// link that some error occurred.
|
||||||
reason := []byte{byte(lnwire.InsufficientCapacity)}
|
failure := lnwire.NewTemporaryChannelFailure(nil)
|
||||||
|
reason, err := packet.obfuscator.InitialObfuscate(failure)
|
||||||
|
if err != nil {
|
||||||
|
err := errors.Errorf("unable to obfuscate "+
|
||||||
|
"error: %v", err)
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
go source.HandleSwitchPacket(newFailPacket(
|
go source.HandleSwitchPacket(newFailPacket(
|
||||||
packet.src,
|
packet.src,
|
||||||
&lnwire.UpdateFailHTLC{
|
&lnwire.UpdateFailHTLC{
|
||||||
Reason: reason,
|
Reason: reason,
|
||||||
},
|
},
|
||||||
htlc.PaymentHash,
|
htlc.PaymentHash,
|
||||||
0,
|
0, true,
|
||||||
))
|
))
|
||||||
|
|
||||||
err := errors.Errorf("unable to find appropriate "+
|
err = errors.Errorf("unable to find appropriate "+
|
||||||
"channel link insufficient capacity, need "+
|
"channel link insufficient capacity, need "+
|
||||||
"%v", htlc.Amount)
|
"%v", htlc.Amount)
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
@ -398,17 +423,25 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
|
|||||||
source.ShortChanID(),
|
source.ShortChanID(),
|
||||||
destination.ShortChanID(),
|
destination.ShortChanID(),
|
||||||
htlc.PaymentHash,
|
htlc.PaymentHash,
|
||||||
|
packet.obfuscator,
|
||||||
)); err != nil {
|
)); err != nil {
|
||||||
reason := []byte{byte(lnwire.UnknownError)}
|
failure := lnwire.NewTemporaryChannelFailure(nil)
|
||||||
|
reason, err := packet.obfuscator.InitialObfuscate(failure)
|
||||||
|
if err != nil {
|
||||||
|
err := errors.Errorf("unable to obfuscate "+
|
||||||
|
"error: %v", err)
|
||||||
|
log.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
go source.HandleSwitchPacket(newFailPacket(
|
go source.HandleSwitchPacket(newFailPacket(
|
||||||
packet.src,
|
packet.src,
|
||||||
&lnwire.UpdateFailHTLC{
|
&lnwire.UpdateFailHTLC{
|
||||||
Reason: reason,
|
Reason: reason,
|
||||||
},
|
},
|
||||||
htlc.PaymentHash,
|
htlc.PaymentHash, 0, true,
|
||||||
0,
|
|
||||||
))
|
))
|
||||||
err := errors.Errorf("unable to add circuit: "+
|
err = errors.Errorf("unable to add circuit: "+
|
||||||
"%v", err)
|
"%v", err)
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return err
|
return err
|
||||||
@ -433,6 +466,11 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is failure than we need to obfuscate the error.
|
||||||
|
if htlc, ok := htlc.(*lnwire.UpdateFailHTLC); ok && !packet.isObfuscated {
|
||||||
|
htlc.Reason = circuit.Obfuscator.BackwardObfuscate(htlc.Reason)
|
||||||
|
}
|
||||||
|
|
||||||
// Propagating settle/fail htlc back to src of add htlc packet.
|
// Propagating settle/fail htlc back to src of add htlc packet.
|
||||||
source, err := s.getLinkByShortID(circuit.Src)
|
source, err := s.getLinkByShortID(circuit.Src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -59,7 +59,7 @@ func TestSwitchForward(t *testing.T) {
|
|||||||
&lnwire.UpdateAddHTLC{
|
&lnwire.UpdateAddHTLC{
|
||||||
PaymentHash: rhash,
|
PaymentHash: rhash,
|
||||||
Amount: 1,
|
Amount: 1,
|
||||||
},
|
}, newMockObfuscator(),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handle the request and checks that bob channel link received it.
|
// Handle the request and checks that bob channel link received it.
|
||||||
@ -137,7 +137,7 @@ func TestSwitchCancel(t *testing.T) {
|
|||||||
&lnwire.UpdateAddHTLC{
|
&lnwire.UpdateAddHTLC{
|
||||||
PaymentHash: rhash,
|
PaymentHash: rhash,
|
||||||
Amount: 1,
|
Amount: 1,
|
||||||
},
|
}, newMockObfuscator(),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handle the request and checks that bob channel link received it.
|
// Handle the request and checks that bob channel link received it.
|
||||||
@ -162,7 +162,7 @@ func TestSwitchCancel(t *testing.T) {
|
|||||||
request = newFailPacket(
|
request = newFailPacket(
|
||||||
bobChannelLink.ShortChanID(),
|
bobChannelLink.ShortChanID(),
|
||||||
&lnwire.UpdateFailHTLC{},
|
&lnwire.UpdateFailHTLC{},
|
||||||
rhash, 1)
|
rhash, 1, true)
|
||||||
|
|
||||||
// Handle the request and checks that payment circuit works properly.
|
// Handle the request and checks that payment circuit works properly.
|
||||||
if err := s.forward(request); err != nil {
|
if err := s.forward(request); err != nil {
|
||||||
@ -213,7 +213,7 @@ func TestSwitchAddSamePayment(t *testing.T) {
|
|||||||
&lnwire.UpdateAddHTLC{
|
&lnwire.UpdateAddHTLC{
|
||||||
PaymentHash: rhash,
|
PaymentHash: rhash,
|
||||||
Amount: 1,
|
Amount: 1,
|
||||||
},
|
}, newMockObfuscator(),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handle the request and checks that bob channel link received it.
|
// Handle the request and checks that bob channel link received it.
|
||||||
@ -247,7 +247,7 @@ func TestSwitchAddSamePayment(t *testing.T) {
|
|||||||
request = newFailPacket(
|
request = newFailPacket(
|
||||||
bobChannelLink.ShortChanID(),
|
bobChannelLink.ShortChanID(),
|
||||||
&lnwire.UpdateFailHTLC{},
|
&lnwire.UpdateFailHTLC{},
|
||||||
rhash, 1)
|
rhash, 1, true)
|
||||||
|
|
||||||
// Handle the request and checks that payment circuit works properly.
|
// Handle the request and checks that payment circuit works properly.
|
||||||
if err := s.forward(request); err != nil {
|
if err := s.forward(request); err != nil {
|
||||||
@ -308,14 +308,16 @@ func TestSwitchSendPayment(t *testing.T) {
|
|||||||
// Handle the request and checks that bob channel link received it.
|
// Handle the request and checks that bob channel link received it.
|
||||||
errChan := make(chan error)
|
errChan := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
_, err := s.SendHTLC(aliceChannelLink.Peer().PubKey(), update)
|
_, err := s.SendHTLC(aliceChannelLink.Peer().PubKey(), update,
|
||||||
|
newMockDeobfuscator())
|
||||||
errChan <- err
|
errChan <- err
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
// Send the payment with the same payment hash and same
|
// Send the payment with the same payment hash and same
|
||||||
// amount and check that it will be propagated successfully
|
// amount and check that it will be propagated successfully
|
||||||
_, err := s.SendHTLC(aliceChannelLink.Peer().PubKey(), update)
|
_, err := s.SendHTLC(aliceChannelLink.Peer().PubKey(), update,
|
||||||
|
newMockDeobfuscator())
|
||||||
errChan <- err
|
errChan <- err
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -345,18 +347,22 @@ func TestSwitchSendPayment(t *testing.T) {
|
|||||||
t.Fatal("wrong amount of circuits")
|
t.Fatal("wrong amount of circuits")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create fail request pretending that bob channel link handled the add
|
// Create fail request pretending that bob channel link handled
|
||||||
// htlc request with error and sent the htlc fail request back. This
|
// the add htlc request with error and sent the htlc fail request
|
||||||
// request should be forwarder back to alice channel link.
|
// back. This request should be forwarded back to alice channel link.
|
||||||
packet := newFailPacket(
|
obfuscator := newMockObfuscator()
|
||||||
aliceChannelLink.ShortChanID(),
|
failure := lnwire.FailIncorrectPaymentAmount{}
|
||||||
|
reason, err := obfuscator.InitialObfuscate(failure)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable obfuscate failure: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
packet := newFailPacket(aliceChannelLink.ShortChanID(),
|
||||||
&lnwire.UpdateFailHTLC{
|
&lnwire.UpdateFailHTLC{
|
||||||
Reason: []byte{byte(lnwire.IncorrectValue)},
|
Reason: reason,
|
||||||
ID: 1,
|
ID: 1,
|
||||||
},
|
},
|
||||||
rhash,
|
rhash, 1, true)
|
||||||
1,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := s.forward(packet); err != nil {
|
if err := s.forward(packet); err != nil {
|
||||||
t.Fatalf("can't forward htlc packet: %v", err)
|
t.Fatalf("can't forward htlc packet: %v", err)
|
||||||
@ -364,7 +370,7 @@ func TestSwitchSendPayment(t *testing.T) {
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
case err := <-errChan:
|
case err := <-errChan:
|
||||||
if err.Error() != errors.New(lnwire.IncorrectValue).Error() {
|
if err.Error() != errors.New(lnwire.CodeIncorrectPaymentAmount).Error() {
|
||||||
t.Fatal("err wasn't received")
|
t.Fatal("err wasn't received")
|
||||||
}
|
}
|
||||||
case <-time.After(time.Second):
|
case <-time.After(time.Second):
|
||||||
@ -379,7 +385,7 @@ func TestSwitchSendPayment(t *testing.T) {
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
case err := <-errChan:
|
case err := <-errChan:
|
||||||
if err.Error() != errors.New(lnwire.IncorrectValue).Error() {
|
if err.Error() != errors.New(lnwire.CodeIncorrectPaymentAmount).Error() {
|
||||||
t.Fatal("err wasn't received")
|
t.Fatal("err wasn't received")
|
||||||
}
|
}
|
||||||
case <-time.After(time.Second):
|
case <-time.After(time.Second):
|
||||||
|
@ -10,6 +10,10 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/btcsuite/fastsha256"
|
"github.com/btcsuite/fastsha256"
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
@ -26,8 +30,34 @@ var (
|
|||||||
alicePrivKey = []byte("alice priv key")
|
alicePrivKey = []byte("alice priv key")
|
||||||
bobPrivKey = []byte("bob priv key")
|
bobPrivKey = []byte("bob priv key")
|
||||||
carolPrivKey = []byte("carol priv key")
|
carolPrivKey = []byte("carol priv key")
|
||||||
|
|
||||||
|
testPrivKey = []byte{
|
||||||
|
0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda,
|
||||||
|
0x63, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17,
|
||||||
|
0xd, 0xe7, 0x95, 0xe4, 0xb7, 0x25, 0xb8, 0x4d,
|
||||||
|
0x1e, 0xb, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, testPubKey = btcec.PrivKeyFromBytes(btcec.S256(), testPrivKey)
|
||||||
|
testSig = &btcec.Signature{
|
||||||
|
R: new(big.Int),
|
||||||
|
S: new(big.Int),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = testSig.R.SetString("6372440660162918006277497454296753625158993"+
|
||||||
|
"5445068131219452686511677818569431", 10)
|
||||||
|
_, _ = testSig.S.SetString("1880105606924982582529128710493133386286603"+
|
||||||
|
"3135609736119018462340006816851118", 10)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// mockGetChanUpdateMessage helper function which returns topology update
|
||||||
|
// of the channel
|
||||||
|
func mockGetChanUpdateMessage() (*lnwire.ChannelUpdate, error) {
|
||||||
|
return &lnwire.ChannelUpdate{
|
||||||
|
Signature: testSig,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// generateRandomBytes returns securely generated random bytes.
|
// generateRandomBytes returns securely generated random bytes.
|
||||||
// It will return an error if the system's secure random
|
// It will return an error if the system's secure random
|
||||||
// number generator fails to function correctly, in which
|
// number generator fails to function correctly, in which
|
||||||
@ -423,7 +453,8 @@ func (n *threeHopNetwork) makePayment(sendingPeer, receivingPeer Peer,
|
|||||||
// Send payment and expose err channel.
|
// Send payment and expose err channel.
|
||||||
errChan := make(chan error)
|
errChan := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
_, err := sender.htlcSwitch.SendHTLC(firstHopPub, htlc)
|
_, err := sender.htlcSwitch.SendHTLC(firstHopPub, htlc,
|
||||||
|
newMockDeobfuscator())
|
||||||
errChan <- err
|
errChan <- err
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -523,14 +554,19 @@ func newThreeHopNetwork(t *testing.T, aliceToBob,
|
|||||||
BaseFee: btcutil.Amount(1),
|
BaseFee: btcutil.Amount(1),
|
||||||
TimeLockDelta: 1,
|
TimeLockDelta: 1,
|
||||||
}
|
}
|
||||||
|
obfuscator := newMockObfuscator()
|
||||||
aliceChannelLink := NewChannelLink(
|
aliceChannelLink := NewChannelLink(
|
||||||
ChannelLinkConfig{
|
ChannelLinkConfig{
|
||||||
FwrdingPolicy: globalPolicy,
|
FwrdingPolicy: globalPolicy,
|
||||||
Peer: bobServer,
|
Peer: bobServer,
|
||||||
Switch: aliceServer.htlcSwitch,
|
Switch: aliceServer.htlcSwitch,
|
||||||
DecodeOnion: decoder.Decode,
|
DecodeHopIterator: decoder.DecodeHopIterator,
|
||||||
Registry: aliceServer.registry,
|
DecodeOnionObfuscator: func(io.Reader) (Obfuscator,
|
||||||
|
lnwire.FailCode) {
|
||||||
|
return obfuscator, lnwire.CodeNone
|
||||||
|
},
|
||||||
|
GetLastChannelUpdate: mockGetChanUpdateMessage,
|
||||||
|
Registry: aliceServer.registry,
|
||||||
},
|
},
|
||||||
aliceChannel,
|
aliceChannel,
|
||||||
)
|
)
|
||||||
@ -540,11 +576,16 @@ func newThreeHopNetwork(t *testing.T, aliceToBob,
|
|||||||
|
|
||||||
firstBobChannelLink := NewChannelLink(
|
firstBobChannelLink := NewChannelLink(
|
||||||
ChannelLinkConfig{
|
ChannelLinkConfig{
|
||||||
FwrdingPolicy: globalPolicy,
|
FwrdingPolicy: globalPolicy,
|
||||||
Peer: aliceServer,
|
Peer: aliceServer,
|
||||||
Switch: bobServer.htlcSwitch,
|
Switch: bobServer.htlcSwitch,
|
||||||
DecodeOnion: decoder.Decode,
|
DecodeHopIterator: decoder.DecodeHopIterator,
|
||||||
Registry: bobServer.registry,
|
DecodeOnionObfuscator: func(io.Reader) (Obfuscator,
|
||||||
|
lnwire.FailCode) {
|
||||||
|
return obfuscator, lnwire.CodeNone
|
||||||
|
},
|
||||||
|
GetLastChannelUpdate: mockGetChanUpdateMessage,
|
||||||
|
Registry: bobServer.registry,
|
||||||
},
|
},
|
||||||
firstBobChannel,
|
firstBobChannel,
|
||||||
)
|
)
|
||||||
@ -554,11 +595,16 @@ func newThreeHopNetwork(t *testing.T, aliceToBob,
|
|||||||
|
|
||||||
secondBobChannelLink := NewChannelLink(
|
secondBobChannelLink := NewChannelLink(
|
||||||
ChannelLinkConfig{
|
ChannelLinkConfig{
|
||||||
FwrdingPolicy: globalPolicy,
|
FwrdingPolicy: globalPolicy,
|
||||||
Peer: carolServer,
|
Peer: carolServer,
|
||||||
Switch: bobServer.htlcSwitch,
|
Switch: bobServer.htlcSwitch,
|
||||||
DecodeOnion: decoder.Decode,
|
DecodeHopIterator: decoder.DecodeHopIterator,
|
||||||
Registry: bobServer.registry,
|
DecodeOnionObfuscator: func(io.Reader) (Obfuscator,
|
||||||
|
lnwire.FailCode) {
|
||||||
|
return obfuscator, lnwire.CodeNone
|
||||||
|
},
|
||||||
|
GetLastChannelUpdate: mockGetChanUpdateMessage,
|
||||||
|
Registry: bobServer.registry,
|
||||||
},
|
},
|
||||||
secondBobChannel,
|
secondBobChannel,
|
||||||
)
|
)
|
||||||
@ -568,11 +614,16 @@ func newThreeHopNetwork(t *testing.T, aliceToBob,
|
|||||||
|
|
||||||
carolChannelLink := NewChannelLink(
|
carolChannelLink := NewChannelLink(
|
||||||
ChannelLinkConfig{
|
ChannelLinkConfig{
|
||||||
FwrdingPolicy: globalPolicy,
|
FwrdingPolicy: globalPolicy,
|
||||||
Peer: bobServer,
|
Peer: bobServer,
|
||||||
Switch: carolServer.htlcSwitch,
|
Switch: carolServer.htlcSwitch,
|
||||||
DecodeOnion: decoder.Decode,
|
DecodeHopIterator: decoder.DecodeHopIterator,
|
||||||
Registry: carolServer.registry,
|
DecodeOnionObfuscator: func(io.Reader) (Obfuscator,
|
||||||
|
lnwire.FailCode) {
|
||||||
|
return obfuscator, lnwire.CodeNone
|
||||||
|
},
|
||||||
|
GetLastChannelUpdate: mockGetChanUpdateMessage,
|
||||||
|
Registry: carolServer.registry,
|
||||||
},
|
},
|
||||||
carolChannel,
|
carolChannel,
|
||||||
)
|
)
|
||||||
|
30
lnd_test.go
30
lnd_test.go
@ -2030,10 +2030,11 @@ out:
|
|||||||
} else if resp.PaymentError == "" {
|
} else if resp.PaymentError == "" {
|
||||||
t.Fatalf("payment should have been rejected due to invalid " +
|
t.Fatalf("payment should have been rejected due to invalid " +
|
||||||
"payment hash")
|
"payment hash")
|
||||||
} else if !strings.Contains(resp.PaymentError, "preimage") {
|
} else if !strings.Contains(resp.PaymentError,
|
||||||
|
lnwire.CodeUnknownPaymentHash.String()) {
|
||||||
// TODO(roasbeef): make into proper gRPC error code
|
// TODO(roasbeef): make into proper gRPC error code
|
||||||
t.Fatalf("payment should have failed due to unknown preimage, "+
|
t.Fatalf("payment should have failed due to unknown payment hash, "+
|
||||||
"instead failed due to : %v", err)
|
"instead failed due to: %v", resp.PaymentError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The balances of all parties should be the same as initially since
|
// The balances of all parties should be the same as initially since
|
||||||
@ -2066,9 +2067,10 @@ out:
|
|||||||
} else if resp.PaymentError == "" {
|
} else if resp.PaymentError == "" {
|
||||||
t.Fatalf("payment should have been rejected due to wrong " +
|
t.Fatalf("payment should have been rejected due to wrong " +
|
||||||
"HTLC amount")
|
"HTLC amount")
|
||||||
} else if !strings.Contains(resp.PaymentError, "htlc value") {
|
} else if !strings.Contains(resp.PaymentError,
|
||||||
|
lnwire.CodeIncorrectPaymentAmount.String()) {
|
||||||
t.Fatalf("payment should have failed due to wrong amount, "+
|
t.Fatalf("payment should have failed due to wrong amount, "+
|
||||||
"instead failed due to: %v", err)
|
"instead failed due to: %v", resp.PaymentError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The balances of all parties should be the same as initially since
|
// The balances of all parties should be the same as initially since
|
||||||
@ -2129,9 +2131,10 @@ out:
|
|||||||
} else if resp.PaymentError == "" {
|
} else if resp.PaymentError == "" {
|
||||||
t.Fatalf("payment should fail due to insufficient "+
|
t.Fatalf("payment should fail due to insufficient "+
|
||||||
"capacity: %v", err)
|
"capacity: %v", err)
|
||||||
} else if !strings.Contains(resp.PaymentError, "capacity") {
|
} else if !strings.Contains(resp.PaymentError,
|
||||||
|
lnwire.CodeTemporaryChannelFailure.String()) {
|
||||||
t.Fatalf("payment should fail due to insufficient capacity, "+
|
t.Fatalf("payment should fail due to insufficient capacity, "+
|
||||||
"instead: %v", err)
|
"instead: %v", resp.PaymentError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// For our final test, we'll ensure that if a target link isn't
|
// For our final test, we'll ensure that if a target link isn't
|
||||||
@ -2158,9 +2161,10 @@ out:
|
|||||||
t.Fatalf("payment stream has been closed: %v", err)
|
t.Fatalf("payment stream has been closed: %v", err)
|
||||||
} else if resp.PaymentError == "" {
|
} else if resp.PaymentError == "" {
|
||||||
t.Fatalf("payment should have failed")
|
t.Fatalf("payment should have failed")
|
||||||
} else if !strings.Contains(resp.PaymentError, "hop unknown") {
|
} else if !strings.Contains(resp.PaymentError,
|
||||||
|
lnwire.CodeUnknownNextPeer.String()) {
|
||||||
t.Fatalf("payment should fail due to unknown hop, instead: %v",
|
t.Fatalf("payment should fail due to unknown hop, instead: %v",
|
||||||
err)
|
resp.PaymentError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, immediately close the channel. This function will also
|
// Finally, immediately close the channel. This function will also
|
||||||
@ -2575,17 +2579,19 @@ func testAsyncPayments(net *networkHarness, t *harnessTest) {
|
|||||||
|
|
||||||
if err := alicePayStream.Send(sendReq); err != nil {
|
if err := alicePayStream.Send(sendReq); err != nil {
|
||||||
t.Fatalf("unable to send payment: "+
|
t.Fatalf("unable to send payment: "+
|
||||||
"%v", err)
|
"stream has been closed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We should receive one insufficient capacity error, because we are
|
||||||
|
// sending on one invoice bigger.
|
||||||
errorReceived := false
|
errorReceived := false
|
||||||
for i := 0; i < numInvoices; i++ {
|
for i := 0; i < numInvoices; i++ {
|
||||||
if resp, err := alicePayStream.Recv(); err != nil {
|
if resp, err := alicePayStream.Recv(); err != nil {
|
||||||
t.Fatalf("payment stream have been closed: %v", err)
|
t.Fatalf("payment stream have been closed: %v", err)
|
||||||
} else if resp.PaymentError != "" {
|
} else if resp.PaymentError != "" {
|
||||||
if strings.Contains(resp.PaymentError,
|
if strings.Contains(resp.PaymentError,
|
||||||
"Insufficient") {
|
lnwire.CodeTemporaryChannelFailure.String()) {
|
||||||
if errorReceived {
|
if errorReceived {
|
||||||
t.Fatalf("redundant payment "+
|
t.Fatalf("redundant payment "+
|
||||||
"error: %v", resp.PaymentError)
|
"error: %v", resp.PaymentError)
|
||||||
@ -2595,7 +2601,7 @@ func testAsyncPayments(net *networkHarness, t *harnessTest) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Fatalf("unable to send payment: %v", err)
|
t.Fatalf("unable to send payment: %v", resp.PaymentError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@ import (
|
|||||||
"github.com/roasbeef/btcd/wire"
|
"github.com/roasbeef/btcd/wire"
|
||||||
"github.com/roasbeef/btcutil"
|
"github.com/roasbeef/btcutil"
|
||||||
|
|
||||||
|
"crypto/sha256"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/lightningnetwork/lightning-onion"
|
"github.com/lightningnetwork/lightning-onion"
|
||||||
)
|
)
|
||||||
@ -111,8 +113,8 @@ type Config struct {
|
|||||||
// forward a fully encoded payment to the first hop in the route
|
// forward a fully encoded payment to the first hop in the route
|
||||||
// denoted by its public key. A non-nil error is to be returned if the
|
// denoted by its public key. A non-nil error is to be returned if the
|
||||||
// payment was unsuccessful.
|
// payment was unsuccessful.
|
||||||
SendToSwitch func(firstHop *btcec.PublicKey,
|
SendToSwitch func(firstHop *btcec.PublicKey, htlcAdd *lnwire.UpdateAddHTLC,
|
||||||
htlcAdd *lnwire.UpdateAddHTLC) ([32]byte, error)
|
circuit *sphinx.Circuit) ([sha256.Size]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// routeTuple is an entry within the ChannelRouter's route cache. We cache
|
// routeTuple is an entry within the ChannelRouter's route cache. We cache
|
||||||
@ -851,7 +853,8 @@ func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey, amt btcutil.Amount)
|
|||||||
// the onion route specified by the passed layer 3 route. The blob returned
|
// the onion route specified by the passed layer 3 route. The blob returned
|
||||||
// from this function can immediately be included within an HTLC add packet to
|
// from this function can immediately be included within an HTLC add packet to
|
||||||
// be sent to the first hop within the route.
|
// be sent to the first hop within the route.
|
||||||
func generateSphinxPacket(route *Route, paymentHash []byte) ([]byte, error) {
|
func generateSphinxPacket(route *Route, paymentHash []byte) ([]byte,
|
||||||
|
*sphinx.Circuit, error) {
|
||||||
// First obtain all the public keys along the route which are contained
|
// First obtain all the public keys along the route which are contained
|
||||||
// in each hop.
|
// in each hop.
|
||||||
nodes := make([]*btcec.PublicKey, len(route.Hops))
|
nodes := make([]*btcec.PublicKey, len(route.Hops))
|
||||||
@ -877,7 +880,7 @@ func generateSphinxPacket(route *Route, paymentHash []byte) ([]byte, error) {
|
|||||||
|
|
||||||
sessionKey, err := btcec.NewPrivateKey(btcec.S256())
|
sessionKey, err := btcec.NewPrivateKey(btcec.S256())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next generate the onion routing packet which allows us to perform
|
// Next generate the onion routing packet which allows us to perform
|
||||||
@ -885,14 +888,14 @@ func generateSphinxPacket(route *Route, paymentHash []byte) ([]byte, error) {
|
|||||||
sphinxPacket, err := sphinx.NewOnionPacket(nodes, sessionKey,
|
sphinxPacket, err := sphinx.NewOnionPacket(nodes, sessionKey,
|
||||||
hopPayloads, paymentHash)
|
hopPayloads, paymentHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, encode Sphinx packet using it's wire representation to be
|
// Finally, encode Sphinx packet using it's wire representation to be
|
||||||
// included within the HTLC add packet.
|
// included within the HTLC add packet.
|
||||||
var onionBlob bytes.Buffer
|
var onionBlob bytes.Buffer
|
||||||
if err := sphinxPacket.Encode(&onionBlob); err != nil {
|
if err := sphinxPacket.Encode(&onionBlob); err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Tracef("Generated sphinx packet: %v",
|
log.Tracef("Generated sphinx packet: %v",
|
||||||
@ -904,7 +907,10 @@ func generateSphinxPacket(route *Route, paymentHash []byte) ([]byte, error) {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
return onionBlob.Bytes(), nil
|
return onionBlob.Bytes(), &sphinx.Circuit{
|
||||||
|
SessionKey: sessionKey,
|
||||||
|
PaymentPath: nodes,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LightningPayment describes a payment to be sent through the network to the
|
// LightningPayment describes a payment to be sent through the network to the
|
||||||
@ -989,7 +995,8 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route
|
|||||||
// Generate the raw encoded sphinx packet to be included along
|
// Generate the raw encoded sphinx packet to be included along
|
||||||
// with the htlcAdd message that we send directly to the
|
// with the htlcAdd message that we send directly to the
|
||||||
// switch.
|
// switch.
|
||||||
sphinxPacket, err := generateSphinxPacket(route, payment.PaymentHash[:])
|
onionBlob, circuit, err := generateSphinxPacket(route,
|
||||||
|
payment.PaymentHash[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return preImage, nil, err
|
return preImage, nil, err
|
||||||
}
|
}
|
||||||
@ -1002,13 +1009,14 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route
|
|||||||
Expiry: route.TotalTimeLock,
|
Expiry: route.TotalTimeLock,
|
||||||
PaymentHash: payment.PaymentHash,
|
PaymentHash: payment.PaymentHash,
|
||||||
}
|
}
|
||||||
copy(htlcAdd.OnionBlob[:], sphinxPacket)
|
copy(htlcAdd.OnionBlob[:], onionBlob)
|
||||||
|
|
||||||
// Attempt to send this payment through the network to complete
|
// Attempt to send this payment through the network to complete
|
||||||
// the payment. If this attempt fails, then we'll continue on
|
// the payment. If this attempt fails, then we'll continue on
|
||||||
// to the next available route.
|
// to the next available route.
|
||||||
firstHop := route.Hops[0].Channel.Node.PubKey
|
firstHop := route.Hops[0].Channel.Node.PubKey
|
||||||
preImage, sendError = r.cfg.SendToSwitch(firstHop, htlcAdd)
|
preImage, sendError = r.cfg.SendToSwitch(firstHop, htlcAdd,
|
||||||
|
circuit)
|
||||||
if sendError != nil {
|
if sendError != nil {
|
||||||
log.Errorf("Attempt to send payment %x failed: %v",
|
log.Errorf("Attempt to send payment %x failed: %v",
|
||||||
payment.PaymentHash, sendError)
|
payment.PaymentHash, sendError)
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/roasbeef/btcd/wire"
|
"github.com/roasbeef/btcd/wire"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/lightningnetwork/lightning-onion"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/roasbeef/btcd/btcec"
|
"github.com/roasbeef/btcd/btcec"
|
||||||
"github.com/roasbeef/btcutil"
|
"github.com/roasbeef/btcutil"
|
||||||
@ -80,7 +81,7 @@ func createTestCtx(startingHeight uint32, testGraph ...string) (*testCtx, func()
|
|||||||
Chain: chain,
|
Chain: chain,
|
||||||
ChainView: chainView,
|
ChainView: chainView,
|
||||||
SendToSwitch: func(_ *btcec.PublicKey,
|
SendToSwitch: func(_ *btcec.PublicKey,
|
||||||
_ *lnwire.UpdateAddHTLC) ([32]byte, error) {
|
_ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) {
|
||||||
return [32]byte{}, nil
|
return [32]byte{}, nil
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -175,7 +176,7 @@ func TestSendPaymentRouteFailureFallback(t *testing.T) {
|
|||||||
// first hop. This should force the router to instead take the
|
// first hop. This should force the router to instead take the
|
||||||
// available two hop path (through satoshi).
|
// available two hop path (through satoshi).
|
||||||
ctx.router.cfg.SendToSwitch = func(n *btcec.PublicKey,
|
ctx.router.cfg.SendToSwitch = func(n *btcec.PublicKey,
|
||||||
_ *lnwire.UpdateAddHTLC) ([32]byte, error) {
|
_ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) {
|
||||||
|
|
||||||
if ctx.aliases["luoji"].IsEqual(n) {
|
if ctx.aliases["luoji"].IsEqual(n) {
|
||||||
return [32]byte{}, errors.New("send error")
|
return [32]byte{}, errors.New("send error")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user