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

     1  package htlcswitch
     2  
     3  import (
     4  	"encoding/binary"
     5  	"io"
     6  
     7  	"github.com/decred/dcrlnd/channeldb"
     8  	"github.com/decred/dcrlnd/htlcswitch/hop"
     9  	"github.com/decred/dcrlnd/lnwire"
    10  )
    11  
    12  // EmptyCircuitKey is a default value for an outgoing circuit key returned when
    13  // a circuit's keystone has not been set. Note that this value is invalid for
    14  // use as a keystone, since the outgoing channel id can never be equal to
    15  // sourceHop.
    16  var EmptyCircuitKey CircuitKey
    17  
    18  // CircuitKey is a tuple of channel ID and HTLC ID, used to uniquely identify
    19  // HTLCs in a circuit. Circuits are identified primarily by the circuit key of
    20  // the incoming HTLC. However, a circuit may also be referenced by its outgoing
    21  // circuit key after the HTLC has been forwarded via the outgoing link.
    22  type CircuitKey = channeldb.CircuitKey
    23  
    24  // PaymentCircuit is used by the switch as placeholder between when the
    25  // switch makes a forwarding decision and the outgoing link determines the
    26  // proper HTLC ID for the local log. After the outgoing HTLC ID has been
    27  // determined, the half circuit will be converted into a full PaymentCircuit.
    28  type PaymentCircuit struct {
    29  	// AddRef is the forward reference of the Add update in the incoming
    30  	// link's forwarding package. This value is set on the htlcPacket of the
    31  	// returned settle/fail so that it can be removed from disk.
    32  	AddRef channeldb.AddRef
    33  
    34  	// Incoming is the circuit key identifying the incoming channel and htlc
    35  	// index from which this ADD originates.
    36  	Incoming CircuitKey
    37  
    38  	// Outgoing is the circuit key identifying the outgoing channel, and the
    39  	// HTLC index that was used to forward the ADD. It will be nil if this
    40  	// circuit's keystone has not been set.
    41  	Outgoing *CircuitKey
    42  
    43  	// PaymentHash used as unique identifier of payment.
    44  	PaymentHash [32]byte
    45  
    46  	// IncomingAmount is the value of the HTLC from the incoming link.
    47  	IncomingAmount lnwire.MilliAtom
    48  
    49  	// OutgoingAmount specifies the value of the HTLC leaving the switch,
    50  	// either as a payment or forwarded amount.
    51  	OutgoingAmount lnwire.MilliAtom
    52  
    53  	// ErrorEncrypter is used to re-encrypt the onion failure before
    54  	// sending it back to the originator of the payment.
    55  	ErrorEncrypter hop.ErrorEncrypter
    56  
    57  	// LoadedFromDisk is set true for any circuits loaded after the circuit
    58  	// map is reloaded from disk.
    59  	//
    60  	// NOTE: This value is determined implicitly during a restart. It is not
    61  	// persisted, and should never be set outside the circuit map.
    62  	LoadedFromDisk bool
    63  }
    64  
    65  // HasKeystone returns true if an outgoing link has assigned this circuit's
    66  // outgoing circuit key.
    67  func (c *PaymentCircuit) HasKeystone() bool {
    68  	return c.Outgoing != nil
    69  }
    70  
    71  // newPaymentCircuit initializes a payment circuit on the heap using the payment
    72  // hash and an in-memory htlc packet.
    73  func newPaymentCircuit(hash *[32]byte, pkt *htlcPacket) *PaymentCircuit {
    74  	var addRef channeldb.AddRef
    75  	if pkt.sourceRef != nil {
    76  		addRef = *pkt.sourceRef
    77  	}
    78  
    79  	return &PaymentCircuit{
    80  		AddRef: addRef,
    81  		Incoming: CircuitKey{
    82  			ChanID: pkt.incomingChanID,
    83  			HtlcID: pkt.incomingHTLCID,
    84  		},
    85  		PaymentHash:    *hash,
    86  		IncomingAmount: pkt.incomingAmount,
    87  		OutgoingAmount: pkt.amount,
    88  		ErrorEncrypter: pkt.obfuscator,
    89  	}
    90  }
    91  
    92  // makePaymentCircuit initializes a payment circuit on the stack using the
    93  // payment hash and an in-memory htlc packet.
    94  func makePaymentCircuit(hash *[32]byte, pkt *htlcPacket) PaymentCircuit {
    95  	var addRef channeldb.AddRef
    96  	if pkt.sourceRef != nil {
    97  		addRef = *pkt.sourceRef
    98  	}
    99  
   100  	return PaymentCircuit{
   101  		AddRef: addRef,
   102  		Incoming: CircuitKey{
   103  			ChanID: pkt.incomingChanID,
   104  			HtlcID: pkt.incomingHTLCID,
   105  		},
   106  		PaymentHash:    *hash,
   107  		IncomingAmount: pkt.incomingAmount,
   108  		OutgoingAmount: pkt.amount,
   109  		ErrorEncrypter: pkt.obfuscator,
   110  	}
   111  }
   112  
   113  // Encode writes a PaymentCircuit to the provided io.Writer.
   114  func (c *PaymentCircuit) Encode(w io.Writer) error {
   115  	if err := c.AddRef.Encode(w); err != nil {
   116  		return err
   117  	}
   118  
   119  	if err := c.Incoming.Encode(w); err != nil {
   120  		return err
   121  	}
   122  
   123  	if _, err := w.Write(c.PaymentHash[:]); err != nil {
   124  		return err
   125  	}
   126  
   127  	var scratch [8]byte
   128  
   129  	binary.BigEndian.PutUint64(scratch[:], uint64(c.IncomingAmount))
   130  	if _, err := w.Write(scratch[:]); err != nil {
   131  		return err
   132  	}
   133  
   134  	binary.BigEndian.PutUint64(scratch[:], uint64(c.OutgoingAmount))
   135  	if _, err := w.Write(scratch[:]); err != nil {
   136  		return err
   137  	}
   138  
   139  	// Defaults to EncrypterTypeNone.
   140  	var encrypterType hop.EncrypterType
   141  	if c.ErrorEncrypter != nil {
   142  		encrypterType = c.ErrorEncrypter.Type()
   143  	}
   144  
   145  	err := binary.Write(w, binary.BigEndian, encrypterType)
   146  	if err != nil {
   147  		return err
   148  	}
   149  
   150  	// Skip encoding of error encrypter if this half add does not have one.
   151  	if encrypterType == hop.EncrypterTypeNone {
   152  		return nil
   153  	}
   154  
   155  	return c.ErrorEncrypter.Encode(w)
   156  }
   157  
   158  // Decode reads a PaymentCircuit from the provided io.Reader.
   159  func (c *PaymentCircuit) Decode(r io.Reader) error {
   160  	if err := c.AddRef.Decode(r); err != nil {
   161  		return err
   162  	}
   163  
   164  	if err := c.Incoming.Decode(r); err != nil {
   165  		return err
   166  	}
   167  
   168  	if _, err := io.ReadFull(r, c.PaymentHash[:]); err != nil {
   169  		return err
   170  	}
   171  
   172  	var scratch [8]byte
   173  
   174  	if _, err := io.ReadFull(r, scratch[:]); err != nil {
   175  		return err
   176  	}
   177  	c.IncomingAmount = lnwire.MilliAtom(
   178  		binary.BigEndian.Uint64(scratch[:]))
   179  
   180  	if _, err := io.ReadFull(r, scratch[:]); err != nil {
   181  		return err
   182  	}
   183  	c.OutgoingAmount = lnwire.MilliAtom(
   184  		binary.BigEndian.Uint64(scratch[:]))
   185  
   186  	// Read the encrypter type used for this circuit.
   187  	var encrypterType hop.EncrypterType
   188  	err := binary.Read(r, binary.BigEndian, &encrypterType)
   189  	if err != nil {
   190  		return err
   191  	}
   192  
   193  	switch encrypterType {
   194  	case hop.EncrypterTypeNone:
   195  		// No encrypter was provided, such as when the payment is
   196  		// locally initiated.
   197  		return nil
   198  
   199  	case hop.EncrypterTypeSphinx:
   200  		// Sphinx encrypter was used as this is a forwarded HTLC.
   201  		c.ErrorEncrypter = hop.NewSphinxErrorEncrypter()
   202  
   203  	case hop.EncrypterTypeMock:
   204  		// Test encrypter.
   205  		c.ErrorEncrypter = NewMockObfuscator()
   206  
   207  	default:
   208  		return UnknownEncrypterType(encrypterType)
   209  	}
   210  
   211  	return c.ErrorEncrypter.Decode(r)
   212  }
   213  
   214  // InKey returns the primary identifier for the circuit corresponding to the
   215  // incoming HTLC.
   216  func (c *PaymentCircuit) InKey() CircuitKey {
   217  	return c.Incoming
   218  }
   219  
   220  // OutKey returns the keystone identifying the outgoing link and HTLC ID. If the
   221  // circuit hasn't been completed, this method returns an EmptyKeystone, which is
   222  // an invalid outgoing circuit key. Only call this method if HasKeystone returns
   223  // true.
   224  func (c *PaymentCircuit) OutKey() CircuitKey {
   225  	if c.Outgoing != nil {
   226  		return *c.Outgoing
   227  	}
   228  
   229  	return EmptyCircuitKey
   230  }