github.com/decred/dcrlnd@v0.7.6/htlcswitch/interceptable_switch.go (about)

     1  package htlcswitch
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/decred/dcrlnd/channeldb"
     8  	"github.com/decred/dcrlnd/htlcswitch/hop"
     9  	"github.com/decred/dcrlnd/lntypes"
    10  	"github.com/decred/dcrlnd/lnwire"
    11  	"github.com/go-errors/errors"
    12  )
    13  
    14  var (
    15  	// ErrFwdNotExists is an error returned when the caller tries to resolve
    16  	// a forward that doesn't exist anymore.
    17  	ErrFwdNotExists = errors.New("forward does not exist")
    18  )
    19  
    20  // InterceptableSwitch is an implementation of ForwardingSwitch interface.
    21  // This implementation is used like a proxy that wraps the switch and
    22  // intercepts forward requests. A reference to the Switch is held in order
    23  // to communicate back the interception result where the options are:
    24  // Resume - forwards the original request to the switch as is.
    25  // Settle - routes UpdateFulfillHTLC to the originating link.
    26  // Fail - routes UpdateFailHTLC to the originating link.
    27  type InterceptableSwitch struct {
    28  	sync.RWMutex
    29  
    30  	// htlcSwitch is the underline switch
    31  	htlcSwitch *Switch
    32  
    33  	// fwdInterceptor is the callback that is called for each forward of
    34  	// an incoming htlc. It should return true if it is interested in handling
    35  	// it.
    36  	fwdInterceptor ForwardInterceptor
    37  }
    38  
    39  // NewInterceptableSwitch returns an instance of InterceptableSwitch.
    40  func NewInterceptableSwitch(s *Switch) *InterceptableSwitch {
    41  	return &InterceptableSwitch{htlcSwitch: s}
    42  }
    43  
    44  // SetInterceptor sets the ForwardInterceptor to be used.
    45  func (s *InterceptableSwitch) SetInterceptor(
    46  	interceptor ForwardInterceptor) {
    47  
    48  	s.Lock()
    49  	defer s.Unlock()
    50  	s.fwdInterceptor = interceptor
    51  }
    52  
    53  // ForwardPackets attempts to forward the batch of htlcs through the
    54  // switch, any failed packets will be returned to the provided
    55  // ChannelLink. The link's quit signal should be provided to allow
    56  // cancellation of forwarding during link shutdown.
    57  func (s *InterceptableSwitch) ForwardPackets(linkQuit chan struct{},
    58  	packets ...*htlcPacket) error {
    59  
    60  	var interceptor ForwardInterceptor
    61  	s.Lock()
    62  	interceptor = s.fwdInterceptor
    63  	s.Unlock()
    64  
    65  	// Optimize for the case we don't have an interceptor.
    66  	if interceptor == nil {
    67  		return s.htlcSwitch.ForwardPackets(linkQuit, packets...)
    68  	}
    69  
    70  	var notIntercepted []*htlcPacket
    71  	for _, p := range packets {
    72  		if !s.interceptForward(p, interceptor, linkQuit) {
    73  			notIntercepted = append(notIntercepted, p)
    74  		}
    75  	}
    76  	return s.htlcSwitch.ForwardPackets(linkQuit, notIntercepted...)
    77  }
    78  
    79  // interceptForward checks if there is any external interceptor interested in
    80  // this packet. Currently only htlc type of UpdateAddHTLC that are forwarded
    81  // are being checked for interception. It can be extended in the future given
    82  // the right use case.
    83  func (s *InterceptableSwitch) interceptForward(packet *htlcPacket,
    84  	interceptor ForwardInterceptor, linkQuit chan struct{}) bool {
    85  
    86  	switch htlc := packet.htlc.(type) {
    87  	case *lnwire.UpdateAddHTLC:
    88  		// We are not interested in intercepting initated payments.
    89  		if packet.incomingChanID == hop.Source {
    90  			return false
    91  		}
    92  
    93  		intercepted := &interceptedForward{
    94  			linkQuit:   linkQuit,
    95  			htlc:       htlc,
    96  			packet:     packet,
    97  			htlcSwitch: s.htlcSwitch,
    98  		}
    99  
   100  		// If this htlc was intercepted, don't handle the forward.
   101  		return interceptor(intercepted)
   102  	default:
   103  		return false
   104  	}
   105  }
   106  
   107  // interceptedForward implements the InterceptedForward interface.
   108  // It is passed from the switch to external interceptors that are interested
   109  // in holding forwards and resolve them manually.
   110  type interceptedForward struct {
   111  	linkQuit   chan struct{}
   112  	htlc       *lnwire.UpdateAddHTLC
   113  	packet     *htlcPacket
   114  	htlcSwitch *Switch
   115  }
   116  
   117  // Packet returns the intercepted htlc packet.
   118  func (f *interceptedForward) Packet() InterceptedPacket {
   119  	return InterceptedPacket{
   120  		IncomingCircuit: channeldb.CircuitKey{
   121  			ChanID: f.packet.incomingChanID,
   122  			HtlcID: f.packet.incomingHTLCID,
   123  		},
   124  		OutgoingChanID: f.packet.outgoingChanID,
   125  		Hash:           f.htlc.PaymentHash,
   126  		OutgoingExpiry: f.htlc.Expiry,
   127  		OutgoingAmount: f.htlc.Amount,
   128  		IncomingAmount: f.packet.incomingAmount,
   129  		IncomingExpiry: f.packet.incomingTimeout,
   130  		CustomRecords:  f.packet.customRecords,
   131  		OnionBlob:      f.htlc.OnionBlob,
   132  	}
   133  }
   134  
   135  // Resume resumes the default behavior as if the packet was not intercepted.
   136  func (f *interceptedForward) Resume() error {
   137  	return f.htlcSwitch.ForwardPackets(f.linkQuit, f.packet)
   138  }
   139  
   140  // Fail forward a failed packet to the switch.
   141  func (f *interceptedForward) Fail() error {
   142  	update, err := f.htlcSwitch.cfg.FetchLastChannelUpdate(
   143  		f.packet.incomingChanID,
   144  	)
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	reason, err := f.packet.obfuscator.EncryptFirstHop(
   150  		lnwire.NewTemporaryChannelFailure(update),
   151  	)
   152  	if err != nil {
   153  		return fmt.Errorf("failed to encrypt failure reason %v", err)
   154  	}
   155  	return f.resolve(&lnwire.UpdateFailHTLC{
   156  		Reason: reason,
   157  	})
   158  }
   159  
   160  // Settle forwards a settled packet to the switch.
   161  func (f *interceptedForward) Settle(preimage lntypes.Preimage) error {
   162  	if !preimage.Matches(f.htlc.PaymentHash) {
   163  		return errors.New("preimage does not match hash")
   164  	}
   165  	return f.resolve(&lnwire.UpdateFulfillHTLC{
   166  		PaymentPreimage: preimage,
   167  	})
   168  }
   169  
   170  // resolve is used for both Settle and Fail and forwards the message to the
   171  // switch.
   172  func (f *interceptedForward) resolve(message lnwire.Message) error {
   173  	pkt := &htlcPacket{
   174  		incomingChanID: f.packet.incomingChanID,
   175  		incomingHTLCID: f.packet.incomingHTLCID,
   176  		outgoingChanID: f.packet.outgoingChanID,
   177  		outgoingHTLCID: f.packet.outgoingHTLCID,
   178  		isResolution:   true,
   179  		circuit:        f.packet.circuit,
   180  		htlc:           message,
   181  		obfuscator:     f.packet.obfuscator,
   182  		sourceRef:      f.packet.sourceRef,
   183  	}
   184  	return f.htlcSwitch.mailOrchestrator.Deliver(pkt.incomingChanID, pkt)
   185  }