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

     1  package hop
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"sync"
     8  
     9  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    10  	"github.com/decred/dcrlnd/lnwire"
    11  	sphinx "github.com/decred/lightning-onion/v4"
    12  )
    13  
    14  // Iterator is an interface that abstracts away the routing information
    15  // included in HTLC's which includes the entirety of the payment path of an
    16  // HTLC. This interface provides two basic method which carry out: how to
    17  // interpret the forwarding information encoded within the HTLC packet, and hop
    18  // to encode the forwarding information for the _next_ hop.
    19  type Iterator interface {
    20  	// HopPayload returns the set of fields that detail exactly _how_ this
    21  	// hop should forward the HTLC to the next hop.  Additionally, the
    22  	// information encoded within the returned ForwardingInfo is to be used
    23  	// by each hop to authenticate the information given to it by the prior
    24  	// hop. The payload will also contain any additional TLV fields provided
    25  	// by the sender.
    26  	HopPayload() (*Payload, error)
    27  
    28  	// EncodeNextHop encodes the onion packet destined for the next hop
    29  	// into the passed io.Writer.
    30  	EncodeNextHop(w io.Writer) error
    31  
    32  	// ExtractErrorEncrypter returns the ErrorEncrypter needed for this hop,
    33  	// along with a failure code to signal if the decoding was successful.
    34  	ExtractErrorEncrypter(ErrorEncrypterExtracter) (ErrorEncrypter,
    35  		lnwire.FailCode)
    36  }
    37  
    38  // sphinxHopIterator is the Sphinx implementation of hop iterator which uses
    39  // onion routing to encode the payment route  in such a way so that node might
    40  // see only the next hop in the route..
    41  type sphinxHopIterator struct {
    42  	// ogPacket is the original packet from which the processed packet is
    43  	// derived.
    44  	ogPacket *sphinx.OnionPacket
    45  
    46  	// processedPacket is the outcome of processing an onion packet. It
    47  	// includes the information required to properly forward the packet to
    48  	// the next hop.
    49  	processedPacket *sphinx.ProcessedPacket
    50  }
    51  
    52  // makeSphinxHopIterator converts a processed packet returned from a sphinx
    53  // router and converts it into an hop iterator for usage in the link.
    54  func makeSphinxHopIterator(ogPacket *sphinx.OnionPacket,
    55  	packet *sphinx.ProcessedPacket) *sphinxHopIterator {
    56  
    57  	return &sphinxHopIterator{
    58  		ogPacket:        ogPacket,
    59  		processedPacket: packet,
    60  	}
    61  }
    62  
    63  // A compile time check to ensure sphinxHopIterator implements the HopIterator
    64  // interface.
    65  var _ Iterator = (*sphinxHopIterator)(nil)
    66  
    67  // Encode encodes iterator and writes it to the writer.
    68  //
    69  // NOTE: Part of the HopIterator interface.
    70  func (r *sphinxHopIterator) EncodeNextHop(w io.Writer) error {
    71  	return r.processedPacket.NextPacket.Encode(w)
    72  }
    73  
    74  // HopPayload returns the set of fields that detail exactly _how_ this hop
    75  // should forward the HTLC to the next hop.  Additionally, the information
    76  // encoded within the returned ForwardingInfo is to be used by each hop to
    77  // authenticate the information given to it by the prior hop. The payload will
    78  // also contain any additional TLV fields provided by the sender.
    79  //
    80  // NOTE: Part of the HopIterator interface.
    81  func (r *sphinxHopIterator) HopPayload() (*Payload, error) {
    82  	switch r.processedPacket.Payload.Type {
    83  
    84  	// If this is the legacy payload, then we'll extract the information
    85  	// directly from the pre-populated ForwardingInstructions field.
    86  	case sphinx.PayloadLegacy:
    87  		fwdInst := r.processedPacket.ForwardingInstructions
    88  		return NewLegacyPayload(fwdInst), nil
    89  
    90  	// Otherwise, if this is the TLV payload, then we'll make a new stream
    91  	// to decode only what we need to make routing decisions.
    92  	case sphinx.PayloadTLV:
    93  		return NewPayloadFromReader(bytes.NewReader(
    94  			r.processedPacket.Payload.Payload,
    95  		))
    96  
    97  	default:
    98  		return nil, fmt.Errorf("unknown sphinx payload type: %v",
    99  			r.processedPacket.Payload.Type)
   100  	}
   101  }
   102  
   103  // ExtractErrorEncrypter decodes and returns the ErrorEncrypter for this hop,
   104  // along with a failure code to signal if the decoding was successful. The
   105  // ErrorEncrypter is used to encrypt errors back to the sender in the event that
   106  // a payment fails.
   107  //
   108  // NOTE: Part of the HopIterator interface.
   109  func (r *sphinxHopIterator) ExtractErrorEncrypter(
   110  	extracter ErrorEncrypterExtracter) (ErrorEncrypter, lnwire.FailCode) {
   111  
   112  	return extracter(r.ogPacket.EphemeralKey)
   113  }
   114  
   115  // OnionProcessor is responsible for keeping all sphinx dependent parts inside
   116  // and expose only decoding function. With such approach we give freedom for
   117  // subsystems which wants to decode sphinx path to not be dependable from
   118  // sphinx at all.
   119  //
   120  // NOTE: The reason for keeping decoder separated from hop iterator is too
   121  // maintain the hop iterator abstraction. Without it the structures which using
   122  // the hop iterator should contain sphinx router which makes their creations in
   123  // tests dependent from the sphinx internal parts.
   124  type OnionProcessor struct {
   125  	router *sphinx.Router
   126  }
   127  
   128  // NewOnionProcessor creates new instance of decoder.
   129  func NewOnionProcessor(router *sphinx.Router) *OnionProcessor {
   130  	return &OnionProcessor{router}
   131  }
   132  
   133  // Start spins up the onion processor's sphinx router.
   134  func (p *OnionProcessor) Start() error {
   135  	return p.router.Start()
   136  }
   137  
   138  // Stop shutsdown the onion processor's sphinx router.
   139  func (p *OnionProcessor) Stop() error {
   140  
   141  	log.Info("Onion processor shutting down")
   142  
   143  	p.router.Stop()
   144  	return nil
   145  }
   146  
   147  // DecodeHopIterator attempts to decode a valid sphinx packet from the passed io.Reader
   148  // instance using the rHash as the associated data when checking the relevant
   149  // MACs during the decoding process.
   150  func (p *OnionProcessor) DecodeHopIterator(r io.Reader, rHash []byte,
   151  	incomingCltv uint32) (Iterator, lnwire.FailCode) {
   152  
   153  	onionPkt := &sphinx.OnionPacket{}
   154  	if err := onionPkt.Decode(r); err != nil {
   155  		switch err {
   156  		case sphinx.ErrInvalidOnionVersion:
   157  			return nil, lnwire.CodeInvalidOnionVersion
   158  		case sphinx.ErrInvalidOnionKey:
   159  			return nil, lnwire.CodeInvalidOnionKey
   160  		default:
   161  			log.Errorf("unable to decode onion packet: %v", err)
   162  			return nil, lnwire.CodeInvalidOnionKey
   163  		}
   164  	}
   165  
   166  	// Attempt to process the Sphinx packet. We include the payment hash of
   167  	// the HTLC as it's authenticated within the Sphinx packet itself as
   168  	// associated data in order to thwart attempts a replay attacks. In the
   169  	// case of a replay, an attacker is *forced* to use the same payment
   170  	// hash twice, thereby losing their money entirely.
   171  	sphinxPacket, err := p.router.ProcessOnionPacket(
   172  		onionPkt, rHash, incomingCltv,
   173  	)
   174  	if err != nil {
   175  		switch err {
   176  		case sphinx.ErrInvalidOnionVersion:
   177  			return nil, lnwire.CodeInvalidOnionVersion
   178  		case sphinx.ErrInvalidOnionHMAC:
   179  			return nil, lnwire.CodeInvalidOnionHmac
   180  		case sphinx.ErrInvalidOnionKey:
   181  			return nil, lnwire.CodeInvalidOnionKey
   182  		default:
   183  			log.Errorf("unable to process onion packet: %v", err)
   184  			return nil, lnwire.CodeInvalidOnionKey
   185  		}
   186  	}
   187  
   188  	return makeSphinxHopIterator(onionPkt, sphinxPacket), lnwire.CodeNone
   189  }
   190  
   191  // ReconstructHopIterator attempts to decode a valid sphinx packet from the passed io.Reader
   192  // instance using the rHash as the associated data when checking the relevant
   193  // MACs during the decoding process.
   194  func (p *OnionProcessor) ReconstructHopIterator(r io.Reader, rHash []byte) (
   195  	Iterator, error) {
   196  
   197  	onionPkt := &sphinx.OnionPacket{}
   198  	if err := onionPkt.Decode(r); err != nil {
   199  		return nil, err
   200  	}
   201  
   202  	// Attempt to process the Sphinx packet. We include the payment hash of
   203  	// the HTLC as it's authenticated within the Sphinx packet itself as
   204  	// associated data in order to thwart attempts a replay attacks. In the
   205  	// case of a replay, an attacker is *forced* to use the same payment
   206  	// hash twice, thereby losing their money entirely.
   207  	sphinxPacket, err := p.router.ReconstructOnionPacket(onionPkt, rHash)
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  
   212  	return makeSphinxHopIterator(onionPkt, sphinxPacket), nil
   213  }
   214  
   215  // DecodeHopIteratorRequest encapsulates all date necessary to process an onion
   216  // packet, perform sphinx replay detection, and schedule the entry for garbage
   217  // collection.
   218  type DecodeHopIteratorRequest struct {
   219  	OnionReader  io.Reader
   220  	RHash        []byte
   221  	IncomingCltv uint32
   222  }
   223  
   224  // DecodeHopIteratorResponse encapsulates the outcome of a batched sphinx onion
   225  // processing.
   226  type DecodeHopIteratorResponse struct {
   227  	HopIterator Iterator
   228  	FailCode    lnwire.FailCode
   229  }
   230  
   231  // Result returns the (HopIterator, lnwire.FailCode) tuple, which should
   232  // correspond to the index of a particular DecodeHopIteratorRequest.
   233  //
   234  // NOTE: The HopIterator should be considered invalid if the fail code is
   235  // anything but lnwire.CodeNone.
   236  func (r *DecodeHopIteratorResponse) Result() (Iterator, lnwire.FailCode) {
   237  	return r.HopIterator, r.FailCode
   238  }
   239  
   240  // DecodeHopIterators performs batched decoding and validation of incoming
   241  // sphinx packets. For the same `id`, this method will return the same iterators
   242  // and failcodes upon subsequent invocations.
   243  //
   244  // NOTE: In order for the responses to be valid, the caller must guarantee that
   245  // the presented readers and rhashes *NEVER* deviate across invocations for the
   246  // same id.
   247  func (p *OnionProcessor) DecodeHopIterators(id []byte,
   248  	reqs []DecodeHopIteratorRequest) ([]DecodeHopIteratorResponse, error) {
   249  
   250  	var (
   251  		batchSize = len(reqs)
   252  		onionPkts = make([]sphinx.OnionPacket, batchSize)
   253  		resps     = make([]DecodeHopIteratorResponse, batchSize)
   254  	)
   255  
   256  	tx := p.router.BeginTxn(id, batchSize)
   257  
   258  	decode := func(seqNum uint16, onionPkt *sphinx.OnionPacket,
   259  		req DecodeHopIteratorRequest) lnwire.FailCode {
   260  
   261  		err := onionPkt.Decode(req.OnionReader)
   262  		switch err {
   263  		case nil:
   264  			// success
   265  
   266  		case sphinx.ErrInvalidOnionVersion:
   267  			return lnwire.CodeInvalidOnionVersion
   268  
   269  		case sphinx.ErrInvalidOnionKey:
   270  			return lnwire.CodeInvalidOnionKey
   271  
   272  		default:
   273  			log.Errorf("unable to decode onion packet: %v", err)
   274  			return lnwire.CodeInvalidOnionKey
   275  		}
   276  
   277  		err = tx.ProcessOnionPacket(
   278  			seqNum, onionPkt, req.RHash, req.IncomingCltv,
   279  		)
   280  		switch err {
   281  		case nil:
   282  			// success
   283  			return lnwire.CodeNone
   284  
   285  		case sphinx.ErrInvalidOnionVersion:
   286  			return lnwire.CodeInvalidOnionVersion
   287  
   288  		case sphinx.ErrInvalidOnionHMAC:
   289  			return lnwire.CodeInvalidOnionHmac
   290  
   291  		case sphinx.ErrInvalidOnionKey:
   292  			return lnwire.CodeInvalidOnionKey
   293  
   294  		default:
   295  			log.Errorf("unable to process onion packet: %v", err)
   296  			return lnwire.CodeInvalidOnionKey
   297  		}
   298  	}
   299  
   300  	// Execute cpu-heavy onion decoding in parallel.
   301  	var wg sync.WaitGroup
   302  	for i := range reqs {
   303  		wg.Add(1)
   304  		go func(seqNum uint16) {
   305  			defer wg.Done()
   306  
   307  			onionPkt := &onionPkts[seqNum]
   308  
   309  			resps[seqNum].FailCode = decode(
   310  				seqNum, onionPkt, reqs[seqNum],
   311  			)
   312  		}(uint16(i))
   313  	}
   314  	wg.Wait()
   315  
   316  	// With that batch created, we will now attempt to write the shared
   317  	// secrets to disk. This operation will returns the set of indices that
   318  	// were detected as replays, and the computed sphinx packets for all
   319  	// indices that did not fail the above loop. Only indices that are not
   320  	// in the replay set should be considered valid, as they are
   321  	// opportunistically computed.
   322  	packets, replays, err := tx.Commit()
   323  	if err != nil {
   324  		log.Errorf("unable to process onion packet batch %x: %v",
   325  			id, err)
   326  
   327  		// If we failed to commit the batch to the secret share log, we
   328  		// will mark all not-yet-failed channels with a temporary
   329  		// channel failure and exit since we cannot proceed.
   330  		for i := range resps {
   331  			resp := &resps[i]
   332  
   333  			// Skip any indexes that already failed onion decoding.
   334  			if resp.FailCode != lnwire.CodeNone {
   335  				continue
   336  			}
   337  
   338  			log.Errorf("unable to process onion packet %x-%v",
   339  				id, i)
   340  			resp.FailCode = lnwire.CodeTemporaryChannelFailure
   341  		}
   342  
   343  		// TODO(conner): return real errors to caller so link can fail?
   344  		return resps, err
   345  	}
   346  
   347  	// Otherwise, the commit was successful. Now we will post process any
   348  	// remaining packets, additionally failing any that were included in the
   349  	// replay set.
   350  	for i := range resps {
   351  		resp := &resps[i]
   352  
   353  		// Skip any indexes that already failed onion decoding.
   354  		if resp.FailCode != lnwire.CodeNone {
   355  			continue
   356  		}
   357  
   358  		// If this index is contained in the replay set, mark it with a
   359  		// temporary channel failure error code. We infer that the
   360  		// offending error was due to a replayed packet because this
   361  		// index was found in the replay set.
   362  		if replays.Contains(uint16(i)) {
   363  			log.Errorf("unable to process onion packet: %v",
   364  				sphinx.ErrReplayedPacket)
   365  			resp.FailCode = lnwire.CodeTemporaryChannelFailure
   366  			continue
   367  		}
   368  
   369  		// Finally, construct a hop iterator from our processed sphinx
   370  		// packet, simultaneously caching the original onion packet.
   371  		resp.HopIterator = makeSphinxHopIterator(&onionPkts[i], &packets[i])
   372  	}
   373  
   374  	return resps, nil
   375  }
   376  
   377  // ExtractErrorEncrypter takes an io.Reader which should contain the onion
   378  // packet as original received by a forwarding node and creates an
   379  // ErrorEncrypter instance using the derived shared secret. In the case that en
   380  // error occurs, a lnwire failure code detailing the parsing failure will be
   381  // returned.
   382  func (p *OnionProcessor) ExtractErrorEncrypter(ephemeralKey *secp256k1.PublicKey) (
   383  	ErrorEncrypter, lnwire.FailCode) {
   384  
   385  	onionObfuscator, err := sphinx.NewOnionErrorEncrypter(
   386  		p.router, ephemeralKey,
   387  	)
   388  	if err != nil {
   389  		switch err {
   390  		case sphinx.ErrInvalidOnionVersion:
   391  			return nil, lnwire.CodeInvalidOnionVersion
   392  		case sphinx.ErrInvalidOnionHMAC:
   393  			return nil, lnwire.CodeInvalidOnionHmac
   394  		case sphinx.ErrInvalidOnionKey:
   395  			return nil, lnwire.CodeInvalidOnionKey
   396  		default:
   397  			log.Errorf("unable to process onion packet: %v", err)
   398  			return nil, lnwire.CodeInvalidOnionKey
   399  		}
   400  	}
   401  
   402  	return &SphinxErrorEncrypter{
   403  		OnionErrorEncrypter: onionObfuscator,
   404  		EphemeralKey:        ephemeralKey,
   405  	}, lnwire.CodeNone
   406  }