routing: add routing plugin inteface and skeleton

pull/439/head
Andras Banki-Horvath 2 years ago
parent 0f002733f6
commit 04ebc8d568
No known key found for this signature in database
GPG Key ID: 80E5375C094198D8

@ -0,0 +1,116 @@
package loop
import (
"context"
"fmt"
"sync"
"github.com/btcsuite/btcutil"
"github.com/lightninglabs/lndclient"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/zpay32"
)
var (
// ErrRoutingPluginNotApplicable means that the selected routing plugin
// is not able to enhance routing given the current conditions and
// therefore shouldn't be used.
ErrRoutingPluginNotApplicable = fmt.Errorf("routing plugin not " +
"applicable")
// ErrRoutingPluginNoMoreRetries means that the routing plugin can't
// effectively help the payment with more retries.
ErrRoutingPluginNoMoreRetries = fmt.Errorf("routing plugin can't " +
"retry more")
)
var (
routingPluginMx sync.Mutex
routingPluginInstance RoutingPlugin
)
// RoutingPlugin is a generic interface for off-chain payment helpers.
type RoutingPlugin interface {
// Init initializes the routing plugin.
Init(ctx context.Context, target route.Vertex,
routeHints [][]zpay32.HopHint, amt btcutil.Amount) error
// Done deinitializes the routing plugin (restoring any state the
// plugin might have changed).
Done(ctx context.Context) error
// BeforePayment is called before each payment. Attempt counter is
// passed, counting attempts from 1.
BeforePayment(ctx context.Context, attempt int, maxAttempts int) error
}
// makeRoutingPlugin is a helper to instantiate routing plugins.
func makeRoutingPlugin(plugin RoutingPluginType,
_ lndclient.LndServices) RoutingPlugin {
return nil
}
// AcquireRoutingPlugin will return a RoutingPlugin instance (or nil). As the
// LND instance used is a shared resource, currently only one requestor will be
// able to acquire a RoutingPlugin instance. If someone is already holding the
// instance a nil is returned.
func AcquireRoutingPlugin(ctx context.Context, pluginType RoutingPluginType,
lnd lndclient.LndServices, target route.Vertex,
routeHints [][]zpay32.HopHint, amt btcutil.Amount) (
RoutingPlugin, error) {
routingPluginMx.Lock()
defer routingPluginMx.Unlock()
// Another swap is already using the routing plugin.
if routingPluginInstance != nil {
return nil, nil
}
routingPluginInstance = makeRoutingPlugin(pluginType, lnd)
if routingPluginInstance == nil {
return nil, nil
}
// Initialize the plugin with the passed parameters.
err := routingPluginInstance.Init(ctx, target, routeHints, amt)
if err != nil {
if err == ErrRoutingPluginNotApplicable {
// Since the routing plugin is not applicable for this
// payment, we can immediately destruct it.
if err := routingPluginInstance.Done(ctx); err != nil {
log.Errorf("Error while releasing routing "+
"plugin: %v", err)
}
// ErrRoutingPluginNotApplicable is non critical, so
// we're masking this error as we can continue the swap
// flow without the routing plugin.
err = nil
}
routingPluginInstance = nil
return nil, err
}
return routingPluginInstance, nil
}
// ReleaseRoutingPlugin will release the RoutingPlugin, allowing other
// requestors to acquire the instance.
func ReleaseRoutingPlugin(ctx context.Context) {
routingPluginMx.Lock()
defer routingPluginMx.Unlock()
if routingPluginInstance == nil {
return
}
if err := routingPluginInstance.Done(ctx); err != nil {
log.Errorf("Error while releasing routing plugin: %v",
err)
}
routingPluginInstance = nil
}

@ -0,0 +1,119 @@
package loop
import (
"context"
"testing"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcutil"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/stretchr/testify/require"
)
var (
alice = route.Vertex{1}
bob = route.Vertex{2}
charlie = route.Vertex{3}
dave = route.Vertex{4}
eugene = route.Vertex{5}
loopNode = route.Vertex{99}
privFrank, _ = btcec.NewPrivateKey(btcec.S256())
frankPubKey = privFrank.PubKey()
frank = route.NewVertex(frankPubKey)
privGeorge, _ = btcec.NewPrivateKey(btcec.S256())
georgePubKey = privGeorge.PubKey()
george = route.NewVertex(georgePubKey)
)
// testChan holds simplified test data for channels.
type testChan struct {
nodeID1 route.Vertex
nodeID2 route.Vertex
chanID uint64
capacity int64
feeBase1 int64
feeRate1 int64
feeBase2 int64
feeRate2 int64
}
// makeTestNetwork is a helper creating mocked network data from test inputs.
func makeTestNetwork(channels []testChan) ([]lndclient.ChannelInfo,
map[uint64]*lndclient.ChannelEdge) {
chanInfos := make([]lndclient.ChannelInfo, len(channels))
edges := make(map[uint64]*lndclient.ChannelEdge, len(channels))
for i, ch := range channels {
chanInfos[i] = lndclient.ChannelInfo{
ChannelID: ch.chanID,
}
edges[ch.chanID] = &lndclient.ChannelEdge{
ChannelID: ch.chanID,
Capacity: btcutil.Amount(ch.capacity),
Node1: ch.nodeID1,
Node2: ch.nodeID2,
Node1Policy: &lndclient.RoutingPolicy{
FeeBaseMsat: ch.feeBase1,
FeeRateMilliMsat: ch.feeRate1,
},
Node2Policy: &lndclient.RoutingPolicy{
FeeBaseMsat: ch.feeBase2,
FeeRateMilliMsat: ch.feeRate2,
},
}
}
return chanInfos, edges
}
func TestRoutingPluginAcquireRelease(t *testing.T) {
mockLnd := test.NewMockLnd()
// _____Bob_____
// / \
// Alice Dave---Loop
// \___ ___/
// Charlie
//
channels := []testChan{
{alice, bob, 1, 1000, 1000, 1, 1000, 1},
{alice, charlie, 2, 1000, 1000, 1, 1000, 1},
{bob, dave, 3, 1000, 1000, 1, 1000, 1},
{charlie, dave, 4, 1000, 1000, 100, 1000, 1},
{dave, loopNode, 5, 1000, 1000, 1, 1000, 1},
}
mockLnd.Channels, mockLnd.ChannelEdges = makeTestNetwork(channels)
lnd := lndclient.LndServices{
Client: mockLnd.Client,
Router: mockLnd.Router,
}
target := loopNode
amt := btcutil.Amount(50)
ctx := context.TODO()
// RoutingPluginNone returns nil.
plugin, err := AcquireRoutingPlugin(
ctx, RoutingPluginNone, lnd, target, nil, amt,
)
require.Nil(t, plugin)
require.NoError(t, err)
// Attempting to acquire RoutingPluginNone again still returns nil.
plugin, err = AcquireRoutingPlugin(
ctx, RoutingPluginNone, lnd, target, nil, amt,
)
require.Nil(t, plugin)
require.NoError(t, err)
// Call ReleaseRoutingPlugin twice to ensure we can call it even when no
// plugin is acquired.
ReleaseRoutingPlugin(ctx)
ReleaseRoutingPlugin(ctx)
}

@ -243,14 +243,14 @@ func (s *serverMock) Probe(ctx context.Context, amt btcutil.Amount,
return nil
}
func (s *serverMock) RecommendRoutingPlugin(_ context.Context, _ lntypes.Hash) (
RoutingPluginType, error) {
func (s *serverMock) RecommendRoutingPlugin(_ context.Context, _ lntypes.Hash,
_ [32]byte) (RoutingPluginType, error) {
return RoutingPluginNone, nil
}
func (s *serverMock) ReportRoutingResult(_ context.Context, _ lntypes.Hash,
_ RoutingPluginType, _ bool, _ int32, _ int64) error {
_ [32]byte, _ RoutingPluginType, _ bool, _ int32, _ int64) error {
return nil
}

Loading…
Cancel
Save