routing: update path finding and notifications to use mSAT

This commit is contained in:
Olaoluwa Osuntokun 2017-08-21 23:43:20 -07:00
parent 862af6f2d4
commit 6467fdd829
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
6 changed files with 74 additions and 72 deletions

@ -9,6 +9,7 @@ import (
"github.com/davecgh/go-spew/spew"
"github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/wire"
"github.com/roasbeef/btcutil"
@ -254,15 +255,15 @@ type ChannelEdgeUpdate struct {
Capacity btcutil.Amount
// MinHTLC is the minimum HTLC amount that this channel will forward.
MinHTLC btcutil.Amount
MinHTLC lnwire.MilliSatoshi
// BaseFee is the base fee that will charged for all HTLC's forwarded
// across the this channel direction.
BaseFee btcutil.Amount
BaseFee lnwire.MilliSatoshi
// FeeRate is the fee rate that will be shared for all HTLC's forwarded
// across this channel direction.
FeeRate btcutil.Amount
FeeRate lnwire.MilliSatoshi
// TimeLockDelta is the time-lock expressed in blocks that will be
// added to outgoing HTLC's from incoming HTLC's. This value is the

@ -71,22 +71,22 @@ func randEdgePolicy(chanID *lnwire.ShortChannelID,
ChannelID: chanID.ToUint64(),
LastUpdate: time.Unix(int64(prand.Int31()), 0),
TimeLockDelta: uint16(prand.Int63()),
MinHTLC: btcutil.Amount(prand.Int31()),
FeeBaseMSat: btcutil.Amount(prand.Int31()),
FeeProportionalMillionths: btcutil.Amount(prand.Int31()),
MinHTLC: lnwire.MilliSatoshi(prand.Int31()),
FeeBaseMSat: lnwire.MilliSatoshi(prand.Int31()),
FeeProportionalMillionths: lnwire.MilliSatoshi(prand.Int31()),
Node: node,
}
}
func createChannelEdge(ctx *testCtx, bitcoinKey1, bitcoinKey2 []byte,
chanValue int64, fundingHeight uint32) (*wire.MsgTx, *wire.OutPoint,
chanValue btcutil.Amount, fundingHeight uint32) (*wire.MsgTx, *wire.OutPoint,
*lnwire.ShortChannelID, error) {
fundingTx := wire.NewMsgTx(2)
_, tx, err := lnwallet.GenFundingPkScript(
bitcoinKey1,
bitcoinKey2,
chanValue,
int64(chanValue),
)
if err != nil {
return nil, nil, nil, err
@ -365,17 +365,17 @@ func TestEdgeUpdateNotification(t *testing.T) {
t.Fatalf("capacity of edge doesn't match: "+
"expected %v, got %v", chanValue, edgeUpdate.Capacity)
}
if edgeUpdate.MinHTLC != btcutil.Amount(edgeAnn.MinHTLC) {
if edgeUpdate.MinHTLC != edgeAnn.MinHTLC {
t.Fatalf("min HTLC of edge doesn't match: "+
"expected %v, got %v", btcutil.Amount(edgeAnn.MinHTLC),
"expected %v, got %v", edgeAnn.MinHTLC,
edgeUpdate.MinHTLC)
}
if edgeUpdate.BaseFee != btcutil.Amount(edgeAnn.FeeBaseMSat) {
if edgeUpdate.BaseFee != edgeAnn.FeeBaseMSat {
t.Fatalf("base fee of edge doesn't match: "+
"expected %v, got %v", edgeAnn.FeeBaseMSat,
edgeUpdate.BaseFee)
}
if edgeUpdate.FeeRate != btcutil.Amount(edgeAnn.FeeProportionalMillionths) {
if edgeUpdate.FeeRate != edgeAnn.FeeProportionalMillionths {
t.Fatalf("fee rate of edge doesn't match: "+
"expected %v, got %v", edgeAnn.FeeProportionalMillionths,
edgeUpdate.FeeRate)

@ -2,6 +2,7 @@ package routing
import (
"encoding/binary"
"fmt"
"math"
"container/heap"
@ -9,6 +10,7 @@ import (
"github.com/boltdb/bolt"
"github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcutil"
@ -60,18 +62,18 @@ type Hop struct {
// AmtToForward is the amount that this hop will forward to the next
// hop. This value is less than the value that the incoming HTLC
// carries as a fee will be subtracted by the hop.
AmtToForward btcutil.Amount
AmtToForward lnwire.MilliSatoshi
// Fee is the total fee that this hop will subtract from the incoming
// payment, this difference nets the hop fees for forwarding the
// payment.
Fee btcutil.Amount
Fee lnwire.MilliSatoshi
}
// computeFee computes the fee to forward an HTLC of `amt` satoshis over the
// passed active payment channel. This value is currently computed as specified
// in BOLT07, but will likely change in the near future.
func computeFee(amt btcutil.Amount, edge *ChannelHop) btcutil.Amount {
// computeFee computes the fee to forward an HTLC of `amt` milli-satoshis over
// the passed active payment channel. This value is currently computed as
// specified in BOLT07, but will likely change in the near future.
func computeFee(amt lnwire.MilliSatoshi, edge *ChannelHop) lnwire.MilliSatoshi {
return edge.FeeBaseMSat + (amt*edge.FeeProportionalMillionths)/1000000
}
@ -108,7 +110,7 @@ type Route struct {
// TotalFees is the sum of the fees paid at each hop within the final
// route. In the case of a one-hop payment, this value will be zero as
// we don't need to pay a fee it ourself.
TotalFees btcutil.Amount
TotalFees lnwire.MilliSatoshi
// TotalAmount is the total amount of funds required to complete a
// payment over this route. This value includes the cumulative fees at
@ -116,7 +118,7 @@ type Route struct {
// route will need to have at least this many satoshis, otherwise the
// route will fail at an intermediate node due to an insufficient
// amount of fees.
TotalAmount btcutil.Amount
TotalAmount lnwire.MilliSatoshi
// Hops contains details concerning the specific forwarding details at
// each hop.
@ -199,7 +201,7 @@ func (s sortableRoutes) Swap(i, j int) {
//
// NOTE: The passed slice of ChannelHops MUST be sorted in forward order: from
// the source to the target node of the path finding attempt.
func newRoute(amtToSend btcutil.Amount, pathEdges []*ChannelHop,
func newRoute(amtToSend lnwire.MilliSatoshi, pathEdges []*ChannelHop,
currentHeight uint32) (*Route, error) {
// First, we'll create a new empty route with enough hops to match the
@ -236,9 +238,13 @@ func newRoute(amtToSend btcutil.Amount, pathEdges []*ChannelHop,
// As a sanity check, we ensure that the selected channel has
// enough capacity to forward the required amount which
// includes the fee dictated at each hop.
if nextHop.AmtToForward > nextHop.Channel.Capacity {
return nil, newErrf(ErrInsufficientCapacity, "channel graph has "+
"insufficient capacity for the payment")
if nextHop.AmtToForward.ToSatoshis() > nextHop.Channel.Capacity {
err := fmt.Sprintf("channel graph has insufficient "+
"capacity for the payment: need %v, have %v",
nextHop.AmtToForward.ToSatoshis(),
nextHop.Channel.Capacity)
return nil, newErrf(ErrInsufficientCapacity, err)
}
// We don't pay any fees to ourselves on the first-hop channel,
@ -329,7 +335,7 @@ func edgeWeight(e *channeldb.ChannelEdgePolicy) float64 {
// from the target to the source.
func findPath(graph *channeldb.ChannelGraph, sourceNode *channeldb.LightningNode,
target *btcec.PublicKey, ignoredNodes map[vertex]struct{},
ignoredEdges map[uint64]struct{}, amt btcutil.Amount) ([]*ChannelHop, error) {
ignoredEdges map[uint64]struct{}, amt lnwire.MilliSatoshi) ([]*ChannelHop, error) {
// First we'll initialize an empty heap which'll help us to quickly
// locate the next edge we should visit next during our graph
@ -414,7 +420,7 @@ func findPath(graph *channeldb.ChannelGraph, sourceNode *channeldb.LightningNode
// off irrelevant edges by adding the sufficient
// capacity of an edge to our relaxation condition.
if tempDist < distance[v].dist &&
edgeInfo.Capacity >= amt {
edgeInfo.Capacity >= amt.ToSatoshis() {
// TODO(roasbeef): need to also account
// for min HTLC
@ -497,7 +503,7 @@ func findPath(graph *channeldb.ChannelGraph, sourceNode *channeldb.LightningNode
// algorithm, rather than attempting to use an unmodified path finding
// algorithm in a block box manner.
func findPaths(graph *channeldb.ChannelGraph, source *channeldb.LightningNode,
target *btcec.PublicKey, amt btcutil.Amount) ([][]*ChannelHop, error) {
target *btcec.PublicKey, amt lnwire.MilliSatoshi) ([][]*ChannelHop, error) {
ignoredEdges := make(map[uint64]struct{})
ignoredVertexes := make(map[vertex]struct{})

@ -15,6 +15,7 @@ import (
"time"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/wire"
@ -265,9 +266,9 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err
ChannelID: edge.ChannelID,
LastUpdate: time.Now(),
TimeLockDelta: edge.Expiry,
MinHTLC: btcutil.Amount(edge.MinHTLC),
FeeBaseMSat: btcutil.Amount(edge.FeeBaseMsat),
FeeProportionalMillionths: btcutil.Amount(edge.FeeRate),
MinHTLC: lnwire.MilliSatoshi(edge.MinHTLC),
FeeBaseMSat: lnwire.MilliSatoshi(edge.FeeBaseMsat),
FeeProportionalMillionths: lnwire.MilliSatoshi(edge.FeeRate),
}
// As the graph itself is directed, we need to insert two edges
@ -312,7 +313,7 @@ func TestBasicGraphPathFinding(t *testing.T) {
// finding.
const startingHeight = 100
const paymentAmt = btcutil.Amount(100)
paymentAmt := lnwire.NewMSatFromSatoshis(100)
target := aliases["sophon"]
path, err := findPath(graph, sourceNode, target, ignoredVertexes,
ignoredEdges, paymentAmt)
@ -465,7 +466,7 @@ func TestKShortestPathFinding(t *testing.T) {
// ji. Our algorithm should properly find both paths, and also rank
// them in order of their total "distance".
const paymentAmt = btcutil.Amount(100)
paymentAmt := lnwire.NewMSatFromSatoshis(100)
target := aliases["luoji"]
paths, err := findPaths(graph, sourceNode, target, paymentAmt)
if err != nil {
@ -523,7 +524,7 @@ func TestNewRoutePathTooLong(t *testing.T) {
ignoredEdges := make(map[uint64]struct{})
ignoredVertexes := make(map[vertex]struct{})
const paymentAmt = btcutil.Amount(100)
paymentAmt := lnwire.NewMSatFromSatoshis(100)
// We start by confirminig that routing a payment 20 hops away is possible.
// Alice should be able to find a valid route to ursula.

@ -71,19 +71,16 @@ type ChannelGraphSource interface {
// FeeSchema is the set fee configuration for a Lighting Node on the network.
// Using the coefficients described within he schema, the required fee to
// forward outgoing payments can be derived.
//
// TODO(roasbeef): should be in switch instead?
type FeeSchema struct {
// TODO(rosbeef): all these should be in msat instead
// BaseFee is the base amount that will be chained for ANY payment
// forwarded.
BaseFee btcutil.Amount
// BaseFee is the base amount of milli-satoshis that will be chained
// for ANY payment forwarded.
BaseFee lnwire.MilliSatoshi
// FeeRate is the rate that will be charged for forwarding payments.
// The fee rate has a granularity of 1/1000 th of a mili-satoshi, or a
// millionth of a satoshi.
FeeRate btcutil.Amount
// This value should be interpreted as the numerator for a fraction
// whose denominator is 1 million. As a result the effective fee rate
// charged per mSAT will be: (amount * FeeRate/1,000,000)
FeeRate uint32
}
// Config defines the configuration for the ChannelRouter. ALL elements within
@ -105,11 +102,6 @@ type Config struct {
// we need in order to properly maintain the channel graph.
ChainView chainview.FilteredChainView
// FeeSchema is the set fee schema that will be announced on to the
// network.
// TODO(roasbeef): should either be in discovery or switch
FeeSchema *FeeSchema
// SendToSwitch is a function that directs a link-layer switch to
// forward a fully encoded payment to the first hop in the route
// denoted by its public key. A non-nil error is to be returned if the
@ -123,12 +115,12 @@ type Config struct {
// amount. We required the target amount as that will influence the available
// set of paths for a payment.
type routeTuple struct {
amt btcutil.Amount
amt lnwire.MilliSatoshi
dest [33]byte
}
// newRouteTuple creates a new route tuple from the target and amount.
func newRouteTuple(amt btcutil.Amount, dest *btcec.PublicKey) routeTuple {
func newRouteTuple(amt lnwire.MilliSatoshi, dest *btcec.PublicKey) routeTuple {
r := routeTuple{
amt: amt,
}
@ -388,6 +380,8 @@ func (r *ChannelRouter) syncGraphWithChain() error {
func (r *ChannelRouter) networkHandler() {
defer r.wg.Done()
// TODO(roasbeef): ticker to check if should prune in two weeks or not
for {
select {
// A new fully validated network update has just arrived. As a
@ -812,9 +806,10 @@ type routingMsg struct {
// required fee and time lock values running backwards along the route. The
// route that will be ranked the highest is the one with the lowest cumulative
// fee along the route.
func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey, amt btcutil.Amount) ([]*Route, error) {
dest := target.SerializeCompressed()
func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey,
amt lnwire.MilliSatoshi) ([]*Route, error) {
dest := target.SerializeCompressed()
log.Debugf("Searching for path to %x, sending %v", dest, amt)
// We can short circuit the routing by opportunistically checking to
@ -953,9 +948,8 @@ type LightningPayment struct {
Target *btcec.PublicKey
// Amount is the value of the payment to send through the network in
// satoshis.
// TODO(roasbeef): this should be milli satoshis
Amount btcutil.Amount
// milli-satoshis.
Amount lnwire.MilliSatoshi
// PaymentHash is the r-hash value to use within the HTLC extended to
// the first hop.

@ -15,7 +15,6 @@ import (
"github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcutil"
)
type testCtx struct {
@ -126,7 +125,7 @@ func TestFindRoutesFeeSorting(t *testing.T) {
// selection.
// Execute a query for all possible routes between roasbeef and luo ji.
const paymentAmt = btcutil.Amount(100)
paymentAmt := lnwire.NewMSatFromSatoshis(100)
target := ctx.aliases["luoji"]
routes, err := ctx.router.FindRoutes(target, paymentAmt)
if err != nil {
@ -166,7 +165,7 @@ func TestSendPaymentRouteFailureFallback(t *testing.T) {
var payHash [32]byte
payment := LightningPayment{
Target: ctx.aliases["luoji"],
Amount: btcutil.Amount(1000),
Amount: lnwire.NewMSatFromSatoshis(1000),
PaymentHash: payHash,
}
@ -341,7 +340,8 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
// that the nodes are found after the fact.
fundingTx, _, chanID, err := createChannelEdge(ctx,
bitcoinKey1.SerializeCompressed(),
bitcoinKey2.SerializeCompressed(), 10000, 500)
bitcoinKey2.SerializeCompressed(),
10000, 500)
if err != nil {
t.Fatalf("unable to create channel edge: %v", err)
}
@ -370,9 +370,9 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
ChannelID: edge.ChannelID,
LastUpdate: time.Now(),
TimeLockDelta: 10,
MinHTLC: btcutil.Amount(1),
FeeBaseMSat: btcutil.Amount(10),
FeeProportionalMillionths: btcutil.Amount(10000),
MinHTLC: 1,
FeeBaseMSat: 10,
FeeProportionalMillionths: 10000,
}
edgePolicy.Flags = 0
@ -386,9 +386,9 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
ChannelID: edge.ChannelID,
LastUpdate: time.Now(),
TimeLockDelta: 10,
MinHTLC: btcutil.Amount(1),
FeeBaseMSat: btcutil.Amount(10),
FeeProportionalMillionths: btcutil.Amount(10000),
MinHTLC: 1,
FeeBaseMSat: 10,
FeeProportionalMillionths: 10000,
}
edgePolicy.Flags = 1
@ -466,9 +466,9 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
ChannelID: edge.ChannelID,
LastUpdate: time.Now(),
TimeLockDelta: 10,
MinHTLC: btcutil.Amount(1),
FeeBaseMSat: btcutil.Amount(10),
FeeProportionalMillionths: btcutil.Amount(10000),
MinHTLC: 1,
FeeBaseMSat: 10,
FeeProportionalMillionths: 10000,
}
edgePolicy.Flags = 0
@ -481,9 +481,9 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
ChannelID: edge.ChannelID,
LastUpdate: time.Now(),
TimeLockDelta: 10,
MinHTLC: btcutil.Amount(1),
FeeBaseMSat: btcutil.Amount(10),
FeeProportionalMillionths: btcutil.Amount(10000),
MinHTLC: 1,
FeeBaseMSat: 10,
FeeProportionalMillionths: 10000,
}
edgePolicy.Flags = 1
@ -492,7 +492,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
}
// We should now be able to find one route to node 2.
const paymentAmt = btcutil.Amount(100)
paymentAmt := lnwire.NewMSatFromSatoshis(100)
targetNode := priv2.PubKey()
routes, err := ctx.router.FindRoutes(targetNode, paymentAmt)
if err != nil {