router: convert Route.ToHopPayloads() to Route.ToSphinxPath()

In this commit, we update the process that we use to generate a sphinx
packet to send our onion routed HTLC. Due to recent changes in the
`sphinx` package we use, we now need to use a new PaymentPath struct. As
a result, it no longer makes sense to split up the nodes in a route and
their per hop paylods as they're now in the same struct. All tests have
been updated accordingly.
This commit is contained in:
Olaoluwa Osuntokun 2019-01-10 20:03:07 -08:00
parent cba803b856
commit bab957382f
No known key found for this signature in database
GPG Key ID: CE58F7F8E20FD9A2
3 changed files with 70 additions and 60 deletions

@ -807,19 +807,22 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc
// Next, we'll assert that the "next hop" field in each route payload
// properly points to the channel ID that the HTLC should be forwarded
// along.
hopPayloads := route.ToHopPayloads()
if len(hopPayloads) != expectedHopCount {
sphinxPath, err := route.ToSphinxPath()
if err != nil {
t.Fatalf("unable to make sphinx path: %v", err)
}
if sphinxPath.TrueRouteLength() != expectedHopCount {
t.Fatalf("incorrect number of hop payloads: expected %v, got %v",
expectedHopCount, len(hopPayloads))
expectedHopCount, sphinxPath.TrueRouteLength())
}
// Hops should point to the next hop
for i := 0; i < len(expectedHops)-1; i++ {
var expectedHop [8]byte
binary.BigEndian.PutUint64(expectedHop[:], route.Hops[i+1].ChannelID)
if !bytes.Equal(hopPayloads[i].NextAddress[:], expectedHop[:]) {
if !bytes.Equal(sphinxPath[i].HopData.NextAddress[:], expectedHop[:]) {
t.Fatalf("first hop has incorrect next hop: expected %x, got %x",
expectedHop[:], hopPayloads[i].NextAddress)
expectedHop[:], sphinxPath[i].HopData.NextAddress)
}
}
@ -827,9 +830,9 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc
// to indicate it's the exit hop.
var exitHop [8]byte
lastHopIndex := len(expectedHops) - 1
if !bytes.Equal(hopPayloads[lastHopIndex].NextAddress[:], exitHop[:]) {
if !bytes.Equal(sphinxPath[lastHopIndex].HopData.NextAddress[:], exitHop[:]) {
t.Fatalf("first hop has incorrect next hop: expected %x, got %x",
exitHop[:], hopPayloads[lastHopIndex].NextAddress)
exitHop[:], sphinxPath[lastHopIndex].HopData.NextAddress)
}
var expectedTotalFee lnwire.MilliSatoshi

@ -104,40 +104,6 @@ func (r *Route) HopFee(hopIndex int) lnwire.MilliSatoshi {
return incomingAmt - r.Hops[hopIndex].AmtToForward
}
// ToHopPayloads converts a complete route into the series of per-hop payloads
// that is to be encoded within each HTLC using an opaque Sphinx packet.
func (r *Route) ToHopPayloads() []sphinx.HopData {
hopPayloads := make([]sphinx.HopData, len(r.Hops))
// For each hop encoded within the route, we'll convert the hop struct
// to the matching per-hop payload struct as used by the sphinx
// package.
for i, hop := range r.Hops {
hopPayloads[i] = sphinx.HopData{
// TODO(roasbeef): properly set realm, make sphinx type
// an enum actually?
Realm: 0,
ForwardAmount: uint64(hop.AmtToForward),
OutgoingCltv: hop.OutgoingTimeLock,
}
// As a base case, the next hop is set to all zeroes in order
// to indicate that the "last hop" as no further hops after it.
nextHop := uint64(0)
// If we aren't on the last hop, then we set the "next address"
// field to be the channel that directly follows it.
if i != len(r.Hops)-1 {
nextHop = r.Hops[i+1].ChannelID
}
binary.BigEndian.PutUint64(hopPayloads[i].NextAddress[:],
nextHop)
}
return hopPayloads
}
// NewRouteFromHops creates a new Route structure from the minimally required
// information to perform the payment. It infers fee amounts and populates the
// node, chan and prev/next hop maps.
@ -163,3 +129,49 @@ func NewRouteFromHops(amtToSend lnwire.MilliSatoshi, timeLock uint32,
return route, nil
}
// ToSphinxPath converts a complete route into a sphinx PaymentPath that
// contains the per-hop paylods used to encoding the HTLC routing data for each
// hop in the route.
func (r *Route) ToSphinxPath() (*sphinx.PaymentPath, error) {
var path sphinx.PaymentPath
// For each hop encoded within the route, we'll convert the hop struct
// to an OnionHop with matching per-hop payload within the path as used
// by the sphinx package.
for i, hop := range r.Hops {
pub, err := btcec.ParsePubKey(
hop.PubKeyBytes[:], btcec.S256(),
)
if err != nil {
return nil, err
}
path[i] = sphinx.OnionHop{
NodePub: *pub,
HopData: sphinx.HopData{
// TODO(roasbeef): properly set realm, make
// sphinx type an enum actually?
Realm: [1]byte{0},
ForwardAmount: uint64(hop.AmtToForward),
OutgoingCltv: hop.OutgoingTimeLock,
},
}
// As a base case, the next hop is set to all zeroes in order
// to indicate that the "last hop" as no further hops after it.
nextHop := uint64(0)
// If we aren't on the last hop, then we set the "next address"
// field to be the channel that directly follows it.
if i != len(r.Hops)-1 {
nextHop = r.Hops[i+1].ChannelID
}
binary.BigEndian.PutUint64(
path[i].HopData.NextAddress[:], nextHop,
)
}
return &path, nil
}

@ -1467,30 +1467,25 @@ func generateSphinxPacket(rt *route.Route, paymentHash []byte) ([]byte,
return nil, nil, route.ErrNoRouteHopsProvided
}
// First obtain all the public keys along the route which are contained
// in each hop.
nodes := make([]*btcec.PublicKey, len(rt.Hops))
for i, hop := range rt.Hops {
pub, err := btcec.ParsePubKey(hop.PubKeyBytes[:],
btcec.S256())
if err != nil {
return nil, nil, err
}
nodes[i] = pub
// Now that we know we have an actual route, we'll map the route into a
// sphinx payument path which includes per-hop paylods for each hop
// that give each node within the route the necessary information
// (fees, CLTV value, etc) to properly forward the payment.
sphinxPath, err := rt.ToSphinxPath()
if err != nil {
return nil, nil, err
}
// Next we generate the per-hop payload which gives each node within
// the route the necessary information (fees, CLTV value, etc) to
// properly forward the payment.
hopPayloads := rt.ToHopPayloads()
log.Tracef("Constructed per-hop payloads for payment_hash=%x: %v",
paymentHash[:], newLogClosure(func() string {
return spew.Sdump(hopPayloads)
return spew.Sdump(sphinxPath[:sphinxPath.TrueRouteLength()])
}),
)
// Generate a new random session key to ensure that we don't trigger
// any replay.
//
// TODO(roasbeef): add more sources of randomness?
sessionKey, err := btcec.NewPrivateKey(btcec.S256())
if err != nil {
return nil, nil, err
@ -1499,7 +1494,7 @@ func generateSphinxPacket(rt *route.Route, paymentHash []byte) ([]byte,
// Next generate the onion routing packet which allows us to perform
// privacy preserving source routing across the network.
sphinxPacket, err := sphinx.NewOnionPacket(
nodes, sessionKey, hopPayloads, paymentHash,
sphinxPath, sessionKey, paymentHash,
)
if err != nil {
return nil, nil, err
@ -1523,7 +1518,7 @@ func generateSphinxPacket(rt *route.Route, paymentHash []byte) ([]byte,
return onionBlob.Bytes(), &sphinx.Circuit{
SessionKey: sessionKey,
PaymentPath: nodes,
PaymentPath: sphinxPath.NodeKeys(),
}, nil
}