htlcswitch: add new ProcessContractResolution method
In this commit, we add a new method: ProcessContractResolution. This will be used by entities of the contract court package to notify us whenever they discover that we can resolve an incoming contract off-chain after the outgoing contract was fully resolved on-chain. We’ll take a contractcourt.ResolutionMsg and map it to the proper internal package so we can fully resolve an active circuit.
This commit is contained in:
parent
8807d1d752
commit
9cb3657314
@ -79,7 +79,7 @@ const (
|
|||||||
// wireCourier is a type of courier that handles wire messages.
|
// wireCourier is a type of courier that handles wire messages.
|
||||||
wireCourier courierType = iota
|
wireCourier courierType = iota
|
||||||
|
|
||||||
// pktCourier is a type of courier that handles hltc packets.
|
// pktCourier is a type of courier that handles htlc packets.
|
||||||
pktCourier
|
pktCourier
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -46,4 +46,11 @@ type htlcPacket struct {
|
|||||||
// of a forwarded fail packet are already set and do not need to be looked
|
// of a forwarded fail packet are already set and do not need to be looked
|
||||||
// up in the circuit map.
|
// up in the circuit map.
|
||||||
isRouted bool
|
isRouted bool
|
||||||
|
|
||||||
|
// isResolution is set to true if this packet was actually an incoming
|
||||||
|
// resolution message from an outside sub-system. We'll treat these as
|
||||||
|
// if they emanated directly from the switch. As a result, we'll
|
||||||
|
// encrypt all errors related to this packet as if we were the first
|
||||||
|
// hop.
|
||||||
|
isResolution bool
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/roasbeef/btcd/btcec"
|
"github.com/roasbeef/btcd/btcec"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
|
"github.com/lightningnetwork/lnd/contractcourt"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
@ -161,6 +162,10 @@ type Switch struct {
|
|||||||
// the channel close handler.
|
// the channel close handler.
|
||||||
chanCloseRequests chan *ChanClose
|
chanCloseRequests chan *ChanClose
|
||||||
|
|
||||||
|
// resolutionMsgs is the channel that all external contract resolution
|
||||||
|
// messages will be sent over.
|
||||||
|
resolutionMsgs chan *resolutionMsg
|
||||||
|
|
||||||
// linkControl is a channel used to propagate add/remove/get htlc
|
// linkControl is a channel used to propagate add/remove/get htlc
|
||||||
// switch handler commands.
|
// switch handler commands.
|
||||||
linkControl chan interface{}
|
linkControl chan interface{}
|
||||||
@ -177,11 +182,48 @@ func New(cfg Config) *Switch {
|
|||||||
pendingPayments: make(map[uint64]*pendingPayment),
|
pendingPayments: make(map[uint64]*pendingPayment),
|
||||||
htlcPlex: make(chan *plexPacket),
|
htlcPlex: make(chan *plexPacket),
|
||||||
chanCloseRequests: make(chan *ChanClose),
|
chanCloseRequests: make(chan *ChanClose),
|
||||||
|
resolutionMsgs: make(chan *resolutionMsg),
|
||||||
linkControl: make(chan interface{}),
|
linkControl: make(chan interface{}),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolutionMsg is a struct that wraps an existing ResolutionMsg with a done
|
||||||
|
// channel. We'll use this channel to synchronize delivery of the message with
|
||||||
|
// the caller.
|
||||||
|
type resolutionMsg struct {
|
||||||
|
contractcourt.ResolutionMsg
|
||||||
|
|
||||||
|
doneChan chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessContractResolution is called by active contract resolvers once a
|
||||||
|
// contract they are watching over has been fully resolved. The message carries
|
||||||
|
// an external signal that *would* have been sent if the outgoing channel
|
||||||
|
// didn't need to go to the chain in order to fulfill a contract. We'll process
|
||||||
|
// this message just as if it came from an active outgoing channel.
|
||||||
|
func (s *Switch) ProcessContractResolution(msg contractcourt.ResolutionMsg) error {
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
|
||||||
|
select {
|
||||||
|
case s.resolutionMsgs <- &resolutionMsg{
|
||||||
|
ResolutionMsg: msg,
|
||||||
|
doneChan: done,
|
||||||
|
}:
|
||||||
|
case <-s.quit:
|
||||||
|
return fmt.Errorf("switch shutting down")
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
case <-s.quit:
|
||||||
|
return fmt.Errorf("switch shutting down")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// 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, htlc *lnwire.UpdateAddHTLC,
|
func (s *Switch) SendHTLC(nextNode [33]byte, htlc *lnwire.UpdateAddHTLC,
|
||||||
@ -620,12 +662,34 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
|
|||||||
packet.incomingChanID = circuit.IncomingChanID
|
packet.incomingChanID = circuit.IncomingChanID
|
||||||
packet.incomingHTLCID = circuit.IncomingHTLCID
|
packet.incomingHTLCID = circuit.IncomingHTLCID
|
||||||
|
|
||||||
// Obfuscate the error message for fail updates before sending back
|
// Obfuscate the error message for fail updates before
|
||||||
// through the circuit unless the payment was generated locally.
|
// sending back through the circuit unless the payment
|
||||||
|
// was generated locally.
|
||||||
if circuit.ErrorEncrypter != nil {
|
if circuit.ErrorEncrypter != nil {
|
||||||
if htlc, ok := htlc.(*lnwire.UpdateFailHTLC); ok {
|
if htlc, ok := htlc.(*lnwire.UpdateFailHTLC); ok {
|
||||||
htlc.Reason = circuit.ErrorEncrypter.IntermediateEncrypt(
|
// If this is a resolution message,
|
||||||
htlc.Reason)
|
// then we'll need to encrypt it as
|
||||||
|
// it's actually internally sourced.
|
||||||
|
if packet.isResolution {
|
||||||
|
// TODO(roasbeef): don't need to pass actually?
|
||||||
|
failure := &lnwire.FailPermanentChannelFailure{}
|
||||||
|
htlc.Reason, err = circuit.ErrorEncrypter.EncryptFirstHop(
|
||||||
|
failure,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
err := errors.Errorf("unable to obfuscate "+
|
||||||
|
"error: %v", err)
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Otherwise, it's a forwarded
|
||||||
|
// error, so we'll perform a
|
||||||
|
// wrapper encryption as
|
||||||
|
// normal.
|
||||||
|
htlc.Reason = circuit.ErrorEncrypter.IntermediateEncrypt(
|
||||||
|
htlc.Reason,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -736,6 +800,40 @@ func (s *Switch) htlcForwarder() {
|
|||||||
|
|
||||||
go s.cfg.LocalChannelClose(peerPub[:], req)
|
go s.cfg.LocalChannelClose(peerPub[:], req)
|
||||||
|
|
||||||
|
case resolutionMsg := <-s.resolutionMsgs:
|
||||||
|
pkt := &htlcPacket{
|
||||||
|
outgoingChanID: resolutionMsg.SourceChan,
|
||||||
|
outgoingHTLCID: resolutionMsg.HtlcIndex,
|
||||||
|
isResolution: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolution messages will either be cancelling
|
||||||
|
// backwards an existing HTLC, or settling a previously
|
||||||
|
// outgoing HTLC. Based on this, we'll map the message
|
||||||
|
// to the proper htlcPacket.
|
||||||
|
if resolutionMsg.Failure != nil {
|
||||||
|
pkt.htlc = &lnwire.UpdateFailHTLC{}
|
||||||
|
} else {
|
||||||
|
pkt.htlc = &lnwire.UpdateFufillHTLC{
|
||||||
|
PaymentPreimage: *resolutionMsg.PreImage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Received outside contract resolution, "+
|
||||||
|
"mapping to: %v", spew.Sdump(pkt))
|
||||||
|
|
||||||
|
// We don't check the error, as the only failure we can
|
||||||
|
// encounter is due to the circuit already being
|
||||||
|
// closed. This is fine, as processing this message is
|
||||||
|
// meant to be idempotent.
|
||||||
|
err := s.handlePacketForward(pkt)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Unable to forward resolution msg: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// With the message processed, we'll now close out
|
||||||
|
close(resolutionMsg.doneChan)
|
||||||
|
|
||||||
// A new packet has arrived for forwarding, we'll interpret the
|
// A new packet has arrived for forwarding, we'll interpret the
|
||||||
// packet concretely, then either forward it along, or
|
// packet concretely, then either forward it along, or
|
||||||
// interpret a return packet to a locally initialized one.
|
// interpret a return packet to a locally initialized one.
|
||||||
|
Loading…
Reference in New Issue
Block a user