github.com/decred/dcrlnd@v0.7.6/channeldb/migration_01_to_11/route.go (about)

     1  package migration_01_to_11
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"encoding/hex"
     7  	"fmt"
     8  	"io"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    13  	lnwire "github.com/decred/dcrlnd/channeldb/migration/lnwire21"
    14  	"github.com/decred/dcrlnd/record"
    15  	"github.com/decred/dcrlnd/tlv"
    16  	sphinx "github.com/decred/lightning-onion/v4"
    17  )
    18  
    19  // VertexSize is the size of the array to store a vertex.
    20  const VertexSize = 33
    21  
    22  // ErrNoRouteHopsProvided is returned when a caller attempts to construct a new
    23  // sphinx packet, but provides an empty set of hops for each route.
    24  var ErrNoRouteHopsProvided = fmt.Errorf("empty route hops provided")
    25  
    26  // Vertex is a simple alias for the serialization of a compressed Bitcoin
    27  // public key.
    28  type Vertex [VertexSize]byte
    29  
    30  // NewVertex returns a new Vertex given a public key.
    31  func NewVertex(pub *secp256k1.PublicKey) Vertex {
    32  	var v Vertex
    33  	copy(v[:], pub.SerializeCompressed())
    34  	return v
    35  }
    36  
    37  // NewVertexFromBytes returns a new Vertex based on a serialized pubkey in a
    38  // byte slice.
    39  func NewVertexFromBytes(b []byte) (Vertex, error) {
    40  	vertexLen := len(b)
    41  	if vertexLen != VertexSize {
    42  		return Vertex{}, fmt.Errorf("invalid vertex length of %v, "+
    43  			"want %v", vertexLen, VertexSize)
    44  	}
    45  
    46  	var v Vertex
    47  	copy(v[:], b)
    48  	return v, nil
    49  }
    50  
    51  // NewVertexFromStr returns a new Vertex given its hex-encoded string format.
    52  func NewVertexFromStr(v string) (Vertex, error) {
    53  	// Return error if hex string is of incorrect length.
    54  	if len(v) != VertexSize*2 {
    55  		return Vertex{}, fmt.Errorf("invalid vertex string length of "+
    56  			"%v, want %v", len(v), VertexSize*2)
    57  	}
    58  
    59  	vertex, err := hex.DecodeString(v)
    60  	if err != nil {
    61  		return Vertex{}, err
    62  	}
    63  
    64  	return NewVertexFromBytes(vertex)
    65  }
    66  
    67  // String returns a human readable version of the Vertex which is the
    68  // hex-encoding of the serialized compressed public key.
    69  func (v Vertex) String() string {
    70  	return fmt.Sprintf("%x", v[:])
    71  }
    72  
    73  // Hop represents an intermediate or final node of the route. This naming
    74  // is in line with the definition given in BOLT #4: Onion Routing Protocol.
    75  // The struct houses the channel along which this hop can be reached and
    76  // the values necessary to create the HTLC that needs to be sent to the
    77  // next hop. It is also used to encode the per-hop payload included within
    78  // the Sphinx packet.
    79  type Hop struct {
    80  	// PubKeyBytes is the raw bytes of the public key of the target node.
    81  	PubKeyBytes Vertex
    82  
    83  	// ChannelID is the unique channel ID for the channel. The first 3
    84  	// bytes are the block height, the next 3 the index within the block,
    85  	// and the last 2 bytes are the output index for the channel.
    86  	ChannelID uint64
    87  
    88  	// OutgoingTimeLock is the timelock value that should be used when
    89  	// crafting the _outgoing_ HTLC from this hop.
    90  	OutgoingTimeLock uint32
    91  
    92  	// AmtToForward is the amount that this hop will forward to the next
    93  	// hop. This value is less than the value that the incoming HTLC
    94  	// carries as a fee will be subtracted by the hop.
    95  	AmtToForward lnwire.MilliAtom
    96  
    97  	// TLVRecords if non-nil are a set of additional TLV records that
    98  	// should be included in the forwarding instructions for this node.
    99  	TLVRecords []tlv.Record
   100  
   101  	// LegacyPayload if true, then this signals that this node doesn't
   102  	// understand the new TLV payload, so we must instead use the legacy
   103  	// payload.
   104  	LegacyPayload bool
   105  }
   106  
   107  // PackHopPayload writes to the passed io.Writer, the series of byes that can
   108  // be placed directly into the per-hop payload (EOB) for this hop. This will
   109  // include the required routing fields, as well as serializing any of the
   110  // passed optional TLVRecords.  nextChanID is the unique channel ID that
   111  // references the _outgoing_ channel ID that follows this hop. This field
   112  // follows the same semantics as the NextAddress field in the onion: it should
   113  // be set to zero to indicate the terminal hop.
   114  func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64) error {
   115  	// If this is a legacy payload, then we'll exit here as this method
   116  	// shouldn't be called.
   117  	if h.LegacyPayload {
   118  		return fmt.Errorf("cannot pack hop payloads for legacy " +
   119  			"payloads")
   120  	}
   121  
   122  	// Otherwise, we'll need to make a new stream that includes our
   123  	// required routing fields, as well as these optional values.
   124  	var records []tlv.Record
   125  
   126  	// Every hop must have an amount to forward and CLTV expiry.
   127  	amt := uint64(h.AmtToForward)
   128  	records = append(records,
   129  		record.NewAmtToFwdRecord(&amt),
   130  		record.NewLockTimeRecord(&h.OutgoingTimeLock),
   131  	)
   132  
   133  	// BOLT 04 says the next_hop_id should be omitted for the final hop,
   134  	// but present for all others.
   135  	//
   136  	// TODO(conner): test using hop.Exit once available
   137  	if nextChanID != 0 {
   138  		records = append(records,
   139  			record.NewNextHopIDRecord(&nextChanID),
   140  		)
   141  	}
   142  
   143  	// Append any custom types destined for this hop.
   144  	records = append(records, h.TLVRecords...)
   145  
   146  	// To ensure we produce a canonical stream, we'll sort the records
   147  	// before encoding them as a stream in the hop payload.
   148  	tlv.SortRecords(records)
   149  
   150  	tlvStream, err := tlv.NewStream(records...)
   151  	if err != nil {
   152  		return err
   153  	}
   154  
   155  	return tlvStream.Encode(w)
   156  }
   157  
   158  // Route represents a path through the channel graph which runs over one or
   159  // more channels in succession. This struct carries all the information
   160  // required to craft the Sphinx onion packet, and send the payment along the
   161  // first hop in the path. A route is only selected as valid if all the channels
   162  // have sufficient capacity to carry the initial payment amount after fees are
   163  // accounted for.
   164  type Route struct {
   165  	// TotalTimeLock is the cumulative (final) time lock across the entire
   166  	// route. This is the CLTV value that should be extended to the first
   167  	// hop in the route. All other hops will decrement the time-lock as
   168  	// advertised, leaving enough time for all hops to wait for or present
   169  	// the payment preimage to complete the payment.
   170  	TotalTimeLock uint32
   171  
   172  	// TotalAmount is the total amount of funds required to complete a
   173  	// payment over this route. This value includes the cumulative fees at
   174  	// each hop. As a result, the HTLC extended to the first-hop in the
   175  	// route will need to have at least this many satoshis, otherwise the
   176  	// route will fail at an intermediate node due to an insufficient
   177  	// amount of fees.
   178  	TotalAmount lnwire.MilliAtom
   179  
   180  	// SourcePubKey is the pubkey of the node where this route originates
   181  	// from.
   182  	SourcePubKey Vertex
   183  
   184  	// Hops contains details concerning the specific forwarding details at
   185  	// each hop.
   186  	Hops []*Hop
   187  }
   188  
   189  // HopFee returns the fee charged by the route hop indicated by hopIndex.
   190  func (r *Route) HopFee(hopIndex int) lnwire.MilliAtom {
   191  	var incomingAmt lnwire.MilliAtom
   192  	if hopIndex == 0 {
   193  		incomingAmt = r.TotalAmount
   194  	} else {
   195  		incomingAmt = r.Hops[hopIndex-1].AmtToForward
   196  	}
   197  
   198  	// Fee is calculated as difference between incoming and outgoing amount.
   199  	return incomingAmt - r.Hops[hopIndex].AmtToForward
   200  }
   201  
   202  // TotalFees is the sum of the fees paid at each hop within the final route. In
   203  // the case of a one-hop payment, this value will be zero as we don't need to
   204  // pay a fee to ourself.
   205  func (r *Route) TotalFees() lnwire.MilliAtom {
   206  	if len(r.Hops) == 0 {
   207  		return 0
   208  	}
   209  
   210  	return r.TotalAmount - r.Hops[len(r.Hops)-1].AmtToForward
   211  }
   212  
   213  // NewRouteFromHops creates a new Route structure from the minimally required
   214  // information to perform the payment. It infers fee amounts and populates the
   215  // node, chan and prev/next hop maps.
   216  func NewRouteFromHops(amtToSend lnwire.MilliAtom, timeLock uint32,
   217  	sourceVertex Vertex, hops []*Hop) (*Route, error) {
   218  
   219  	if len(hops) == 0 {
   220  		return nil, ErrNoRouteHopsProvided
   221  	}
   222  
   223  	// First, we'll create a route struct and populate it with the fields
   224  	// for which the values are provided as arguments of this function.
   225  	// TotalFees is determined based on the difference between the amount
   226  	// that is send from the source and the final amount that is received
   227  	// by the destination.
   228  	route := &Route{
   229  		SourcePubKey:  sourceVertex,
   230  		Hops:          hops,
   231  		TotalTimeLock: timeLock,
   232  		TotalAmount:   amtToSend,
   233  	}
   234  
   235  	return route, nil
   236  }
   237  
   238  // ToSphinxPath converts a complete route into a sphinx PaymentPath that
   239  // contains the per-hop paylods used to encoding the HTLC routing data for each
   240  // hop in the route. This method also accepts an optional EOB payload for the
   241  // final hop.
   242  func (r *Route) ToSphinxPath() (*sphinx.PaymentPath, error) {
   243  	var path sphinx.PaymentPath
   244  
   245  	// For each hop encoded within the route, we'll convert the hop struct
   246  	// to an OnionHop with matching per-hop payload within the path as used
   247  	// by the sphinx package.
   248  	for i, hop := range r.Hops {
   249  		pub, err := secp256k1.ParsePubKey(
   250  			hop.PubKeyBytes[:],
   251  		)
   252  		if err != nil {
   253  			return nil, err
   254  		}
   255  
   256  		// As a base case, the next hop is set to all zeroes in order
   257  		// to indicate that the "last hop" as no further hops after it.
   258  		nextHop := uint64(0)
   259  
   260  		// If we aren't on the last hop, then we set the "next address"
   261  		// field to be the channel that directly follows it.
   262  		if i != len(r.Hops)-1 {
   263  			nextHop = r.Hops[i+1].ChannelID
   264  		}
   265  
   266  		var payload sphinx.HopPayload
   267  
   268  		// If this is the legacy payload, then we can just include the
   269  		// hop data as normal.
   270  		if hop.LegacyPayload {
   271  			// Before we encode this value, we'll pack the next hop
   272  			// into the NextAddress field of the hop info to ensure
   273  			// we point to the right now.
   274  			hopData := sphinx.HopData{
   275  				ForwardAmount: uint64(hop.AmtToForward),
   276  				OutgoingCltv:  hop.OutgoingTimeLock,
   277  			}
   278  			binary.BigEndian.PutUint64(
   279  				hopData.NextAddress[:], nextHop,
   280  			)
   281  
   282  			payload, err = sphinx.NewHopPayload(&hopData, nil)
   283  			if err != nil {
   284  				return nil, err
   285  			}
   286  		} else {
   287  			// For non-legacy payloads, we'll need to pack the
   288  			// routing information, along with any extra TLV
   289  			// information into the new per-hop payload format.
   290  			// We'll also pass in the chan ID of the hop this
   291  			// channel should be forwarded to so we can construct a
   292  			// valid payload.
   293  			var b bytes.Buffer
   294  			err := hop.PackHopPayload(&b, nextHop)
   295  			if err != nil {
   296  				return nil, err
   297  			}
   298  
   299  			// TODO(roasbeef): make better API for NewHopPayload?
   300  			payload, err = sphinx.NewHopPayload(nil, b.Bytes())
   301  			if err != nil {
   302  				return nil, err
   303  			}
   304  		}
   305  
   306  		path[i] = sphinx.OnionHop{
   307  			NodePub:    *pub,
   308  			HopPayload: payload,
   309  		}
   310  	}
   311  
   312  	return &path, nil
   313  }
   314  
   315  // String returns a human readable representation of the route.
   316  func (r *Route) String() string {
   317  	var b strings.Builder
   318  
   319  	for i, hop := range r.Hops {
   320  		if i > 0 {
   321  			b.WriteString(",")
   322  		}
   323  		b.WriteString(strconv.FormatUint(hop.ChannelID, 10))
   324  	}
   325  
   326  	return fmt.Sprintf("amt=%v, fees=%v, tl=%v, chans=%v",
   327  		r.TotalAmount-r.TotalFees(), r.TotalFees(), r.TotalTimeLock,
   328  		b.String(),
   329  	)
   330  }