autopilot: define AgentConstraints
To decouple the autopilot heuristic from the constraints, we start by abstracting them behind an interface to make them easier to mock. We also rename them HeuristicConstraints->AgentConstraints to make it clear that they are now constraints the agent must adhere to.
This commit is contained in:
parent
21460c9e67
commit
1d82e12fcf
@ -57,7 +57,7 @@ type Config struct {
|
|||||||
|
|
||||||
// Constraints is the set of constraints the autopilot must adhere to
|
// Constraints is the set of constraints the autopilot must adhere to
|
||||||
// when opening channels.
|
// when opening channels.
|
||||||
Constraints *HeuristicConstraints
|
Constraints AgentConstraints
|
||||||
|
|
||||||
// TODO(roasbeef): add additional signals from fee rates and revenue of
|
// TODO(roasbeef): add additional signals from fee rates and revenue of
|
||||||
// currently opened channels
|
// currently opened channels
|
||||||
@ -573,11 +573,11 @@ func (a *Agent) openChans(availableFunds btcutil.Amount, numChans uint32,
|
|||||||
// available to future heuristic selections.
|
// available to future heuristic selections.
|
||||||
a.pendingMtx.Lock()
|
a.pendingMtx.Lock()
|
||||||
defer a.pendingMtx.Unlock()
|
defer a.pendingMtx.Unlock()
|
||||||
if uint16(len(a.pendingOpens)) >= a.cfg.Constraints.MaxPendingOpens {
|
if uint16(len(a.pendingOpens)) >= a.cfg.Constraints.MaxPendingOpens() {
|
||||||
log.Debugf("Reached cap of %v pending "+
|
log.Debugf("Reached cap of %v pending "+
|
||||||
"channel opens, will retry "+
|
"channel opens, will retry "+
|
||||||
"after success/failure",
|
"after success/failure",
|
||||||
a.cfg.Constraints.MaxPendingOpens)
|
a.cfg.Constraints.MaxPendingOpens())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -642,7 +642,7 @@ func (a *Agent) executeDirective(directive AttachmentDirective) {
|
|||||||
// first.
|
// first.
|
||||||
a.pendingMtx.Lock()
|
a.pendingMtx.Lock()
|
||||||
if uint16(len(a.pendingOpens)) >=
|
if uint16(len(a.pendingOpens)) >=
|
||||||
a.cfg.Constraints.MaxPendingOpens {
|
a.cfg.Constraints.MaxPendingOpens() {
|
||||||
// Since we've reached our max number of pending opens, we'll
|
// Since we've reached our max number of pending opens, we'll
|
||||||
// disconnect this peer and exit. However, if we were
|
// disconnect this peer and exit. However, if we were
|
||||||
// previously connected to them, then we'll make sure to
|
// previously connected to them, then we'll make sure to
|
||||||
|
151
autopilot/agent_constraints.go
Normal file
151
autopilot/agent_constraints.go
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
package autopilot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/btcsuite/btcutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AgentConstraints is an interface the agent will query to determine what
|
||||||
|
// limits it will need to stay inside when opening channels.
|
||||||
|
type AgentConstraints interface {
|
||||||
|
// ChannelBudget should, given the passed parameters, return whether
|
||||||
|
// more channels can be be opened while still staying withing the set
|
||||||
|
// constraints. If the constraints allow us to open more channels, then
|
||||||
|
// the first return value will represent the amount of additional funds
|
||||||
|
// available towards creating channels. The second return value is the
|
||||||
|
// exact *number* of additional channels available.
|
||||||
|
ChannelBudget(chans []Channel, balance btcutil.Amount) (
|
||||||
|
btcutil.Amount, uint32)
|
||||||
|
|
||||||
|
// MaxPendingOpens returns the maximum number of pending channel
|
||||||
|
// establishment goroutines that can be lingering. We cap this value in
|
||||||
|
// order to control the level of parallelism caused by the autopilot
|
||||||
|
// agent.
|
||||||
|
MaxPendingOpens() uint16
|
||||||
|
|
||||||
|
// MinChanSize returns the smallest channel that the autopilot agent
|
||||||
|
// should create.
|
||||||
|
MinChanSize() btcutil.Amount
|
||||||
|
|
||||||
|
// MaxChanSize returns largest channel that the autopilot agent should
|
||||||
|
// create.
|
||||||
|
MaxChanSize() btcutil.Amount
|
||||||
|
}
|
||||||
|
|
||||||
|
// agenConstraints is an implementation of the AgentConstraints interface that
|
||||||
|
// indicate the constraints the autopilot agent must adhere to when opening
|
||||||
|
// channels.
|
||||||
|
type agentConstraints struct {
|
||||||
|
// minChanSize is the smallest channel that the autopilot agent should
|
||||||
|
// create.
|
||||||
|
minChanSize btcutil.Amount
|
||||||
|
|
||||||
|
// maxChanSize the largest channel that the autopilot agent should
|
||||||
|
// create.
|
||||||
|
maxChanSize btcutil.Amount
|
||||||
|
|
||||||
|
// chanLimit the maximum number of channels that should be created.
|
||||||
|
chanLimit uint16
|
||||||
|
|
||||||
|
// allocation the percentage of total funds that should be committed to
|
||||||
|
// automatic channel establishment.
|
||||||
|
allocation float64
|
||||||
|
|
||||||
|
// maxPendingOpens is the maximum number of pending channel
|
||||||
|
// establishment goroutines that can be lingering. We cap this value in
|
||||||
|
// order to control the level of parallelism caused by the autopilot
|
||||||
|
// agent.
|
||||||
|
maxPendingOpens uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// A compile time assertion to ensure agentConstraints satisfies the
|
||||||
|
// AgentConstraints interface.
|
||||||
|
var _ AgentConstraints = (*agentConstraints)(nil)
|
||||||
|
|
||||||
|
// NewConstraints returns a new AgentConstraints with the given limits.
|
||||||
|
func NewConstraints(minChanSize, maxChanSize btcutil.Amount, chanLimit,
|
||||||
|
maxPendingOpens uint16, allocation float64) AgentConstraints {
|
||||||
|
|
||||||
|
return &agentConstraints{
|
||||||
|
minChanSize: minChanSize,
|
||||||
|
maxChanSize: maxChanSize,
|
||||||
|
chanLimit: chanLimit,
|
||||||
|
allocation: allocation,
|
||||||
|
maxPendingOpens: maxPendingOpens,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChannelBudget should, given the passed parameters, return whether more
|
||||||
|
// channels can be be opened while still staying withing the set constraints.
|
||||||
|
// If the constraints allow us to open more channels, then the first return
|
||||||
|
// value will represent the amount of additional funds available towards
|
||||||
|
// creating channels. The second return value is the exact *number* of
|
||||||
|
// additional channels available.
|
||||||
|
//
|
||||||
|
// Note: part of the AgentConstraints interface.
|
||||||
|
func (h *agentConstraints) ChannelBudget(channels []Channel,
|
||||||
|
funds btcutil.Amount) (btcutil.Amount, uint32) {
|
||||||
|
|
||||||
|
// If we're already over our maximum allowed number of channels, then
|
||||||
|
// we'll instruct the controller not to create any more channels.
|
||||||
|
if len(channels) >= int(h.chanLimit) {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// The number of additional channels that should be opened is the
|
||||||
|
// difference between the channel limit, and the number of channels we
|
||||||
|
// already have open.
|
||||||
|
numAdditionalChans := uint32(h.chanLimit) - uint32(len(channels))
|
||||||
|
|
||||||
|
// First, we'll tally up the total amount of funds that are currently
|
||||||
|
// present within the set of active channels.
|
||||||
|
var totalChanAllocation btcutil.Amount
|
||||||
|
for _, channel := range channels {
|
||||||
|
totalChanAllocation += channel.Capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
// With this value known, we'll now compute the total amount of fund
|
||||||
|
// allocated across regular utxo's and channel utxo's.
|
||||||
|
totalFunds := funds + totalChanAllocation
|
||||||
|
|
||||||
|
// Once the total amount has been computed, we then calculate the
|
||||||
|
// fraction of funds currently allocated to channels.
|
||||||
|
fundsFraction := float64(totalChanAllocation) / float64(totalFunds)
|
||||||
|
|
||||||
|
// If this fraction is below our threshold, then we'll return true, to
|
||||||
|
// indicate the controller should call Select to obtain a candidate set
|
||||||
|
// of channels to attempt to open.
|
||||||
|
needMore := fundsFraction < h.allocation
|
||||||
|
if !needMore {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we know we need more funds, we'll compute the amount of
|
||||||
|
// additional funds we should allocate towards channels.
|
||||||
|
targetAllocation := btcutil.Amount(float64(totalFunds) * h.allocation)
|
||||||
|
fundsAvailable := targetAllocation - totalChanAllocation
|
||||||
|
return fundsAvailable, numAdditionalChans
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPendingOpens returns the maximum number of pending channel establishment
|
||||||
|
// goroutines that can be lingering. We cap this value in order to control the
|
||||||
|
// level of parallelism caused by the autopilot agent.
|
||||||
|
//
|
||||||
|
// Note: part of the AgentConstraints interface.
|
||||||
|
func (h *agentConstraints) MaxPendingOpens() uint16 {
|
||||||
|
return h.maxPendingOpens
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinChanSize returns the smallest channel that the autopilot agent should
|
||||||
|
// create.
|
||||||
|
//
|
||||||
|
// Note: part of the AgentConstraints interface.
|
||||||
|
func (h *agentConstraints) MinChanSize() btcutil.Amount {
|
||||||
|
return h.minChanSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxChanSize returns largest channel that the autopilot agent should create.
|
||||||
|
//
|
||||||
|
// Note: part of the AgentConstraints interface.
|
||||||
|
func (h *agentConstraints) MaxChanSize() btcutil.Amount {
|
||||||
|
return h.maxChanSize
|
||||||
|
}
|
@ -25,6 +25,27 @@ type moreChanArg struct {
|
|||||||
balance btcutil.Amount
|
balance btcutil.Amount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mockConstraints struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockConstraints) ChannelBudget(chans []Channel,
|
||||||
|
balance btcutil.Amount) (btcutil.Amount, uint32) {
|
||||||
|
return 1e8, 10
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockConstraints) MaxPendingOpens() uint16 {
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockConstraints) MinChanSize() btcutil.Amount {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
func (m *mockConstraints) MaxChanSize() btcutil.Amount {
|
||||||
|
return 1e8
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ AgentConstraints = (*mockConstraints)(nil)
|
||||||
|
|
||||||
type mockHeuristic struct {
|
type mockHeuristic struct {
|
||||||
moreChansResps chan moreChansResp
|
moreChansResps chan moreChansResp
|
||||||
moreChanArgs chan moreChanArg
|
moreChanArgs chan moreChanArg
|
||||||
@ -150,6 +171,8 @@ func TestAgentChannelOpenSignal(t *testing.T) {
|
|||||||
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
||||||
quit: quit,
|
quit: quit,
|
||||||
}
|
}
|
||||||
|
constraints := &mockConstraints{}
|
||||||
|
|
||||||
chanController := &mockChanController{
|
chanController := &mockChanController{
|
||||||
openChanSignals: make(chan openChanIntent, 10),
|
openChanSignals: make(chan openChanIntent, 10),
|
||||||
}
|
}
|
||||||
@ -170,10 +193,8 @@ func TestAgentChannelOpenSignal(t *testing.T) {
|
|||||||
DisconnectPeer: func(*btcec.PublicKey) error {
|
DisconnectPeer: func(*btcec.PublicKey) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
Graph: memGraph,
|
Graph: memGraph,
|
||||||
Constraints: &HeuristicConstraints{
|
Constraints: constraints,
|
||||||
MaxPendingOpens: 10,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
initialChans := []Channel{}
|
initialChans := []Channel{}
|
||||||
agent, err := New(testCfg, initialChans)
|
agent, err := New(testCfg, initialChans)
|
||||||
@ -283,6 +304,8 @@ func TestAgentChannelFailureSignal(t *testing.T) {
|
|||||||
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
||||||
quit: quit,
|
quit: quit,
|
||||||
}
|
}
|
||||||
|
constraints := &mockConstraints{}
|
||||||
|
|
||||||
chanController := &mockFailingChanController{}
|
chanController := &mockFailingChanController{}
|
||||||
memGraph, _, _ := newMemChanGraph()
|
memGraph, _, _ := newMemChanGraph()
|
||||||
|
|
||||||
@ -301,10 +324,8 @@ func TestAgentChannelFailureSignal(t *testing.T) {
|
|||||||
DisconnectPeer: func(*btcec.PublicKey) error {
|
DisconnectPeer: func(*btcec.PublicKey) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
Graph: memGraph,
|
Graph: memGraph,
|
||||||
Constraints: &HeuristicConstraints{
|
Constraints: constraints,
|
||||||
MaxPendingOpens: 10,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initialChans := []Channel{}
|
initialChans := []Channel{}
|
||||||
@ -394,6 +415,8 @@ func TestAgentChannelCloseSignal(t *testing.T) {
|
|||||||
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
||||||
quit: quit,
|
quit: quit,
|
||||||
}
|
}
|
||||||
|
constraints := &mockConstraints{}
|
||||||
|
|
||||||
chanController := &mockChanController{
|
chanController := &mockChanController{
|
||||||
openChanSignals: make(chan openChanIntent),
|
openChanSignals: make(chan openChanIntent),
|
||||||
}
|
}
|
||||||
@ -414,10 +437,8 @@ func TestAgentChannelCloseSignal(t *testing.T) {
|
|||||||
DisconnectPeer: func(*btcec.PublicKey) error {
|
DisconnectPeer: func(*btcec.PublicKey) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
Graph: memGraph,
|
Graph: memGraph,
|
||||||
Constraints: &HeuristicConstraints{
|
Constraints: constraints,
|
||||||
MaxPendingOpens: 10,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll start the agent with two channels already being active.
|
// We'll start the agent with two channels already being active.
|
||||||
@ -512,6 +533,8 @@ func TestAgentBalanceUpdate(t *testing.T) {
|
|||||||
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
||||||
quit: quit,
|
quit: quit,
|
||||||
}
|
}
|
||||||
|
constraints := &mockConstraints{}
|
||||||
|
|
||||||
chanController := &mockChanController{
|
chanController := &mockChanController{
|
||||||
openChanSignals: make(chan openChanIntent),
|
openChanSignals: make(chan openChanIntent),
|
||||||
}
|
}
|
||||||
@ -538,10 +561,8 @@ func TestAgentBalanceUpdate(t *testing.T) {
|
|||||||
DisconnectPeer: func(*btcec.PublicKey) error {
|
DisconnectPeer: func(*btcec.PublicKey) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
Graph: memGraph,
|
Graph: memGraph,
|
||||||
Constraints: &HeuristicConstraints{
|
Constraints: constraints,
|
||||||
MaxPendingOpens: 10,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
initialChans := []Channel{}
|
initialChans := []Channel{}
|
||||||
agent, err := New(testCfg, initialChans)
|
agent, err := New(testCfg, initialChans)
|
||||||
@ -630,6 +651,8 @@ func TestAgentImmediateAttach(t *testing.T) {
|
|||||||
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
||||||
quit: quit,
|
quit: quit,
|
||||||
}
|
}
|
||||||
|
constraints := &mockConstraints{}
|
||||||
|
|
||||||
chanController := &mockChanController{
|
chanController := &mockChanController{
|
||||||
openChanSignals: make(chan openChanIntent),
|
openChanSignals: make(chan openChanIntent),
|
||||||
}
|
}
|
||||||
@ -653,10 +676,8 @@ func TestAgentImmediateAttach(t *testing.T) {
|
|||||||
DisconnectPeer: func(*btcec.PublicKey) error {
|
DisconnectPeer: func(*btcec.PublicKey) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
Graph: memGraph,
|
Graph: memGraph,
|
||||||
Constraints: &HeuristicConstraints{
|
Constraints: constraints,
|
||||||
MaxPendingOpens: 10,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
initialChans := []Channel{}
|
initialChans := []Channel{}
|
||||||
agent, err := New(testCfg, initialChans)
|
agent, err := New(testCfg, initialChans)
|
||||||
@ -773,6 +794,8 @@ func TestAgentPrivateChannels(t *testing.T) {
|
|||||||
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
||||||
quit: quit,
|
quit: quit,
|
||||||
}
|
}
|
||||||
|
constraints := &mockConstraints{}
|
||||||
|
|
||||||
// The chanController should be initialized such that all of its open
|
// The chanController should be initialized such that all of its open
|
||||||
// channel requests are for private channels.
|
// channel requests are for private channels.
|
||||||
chanController := &mockChanController{
|
chanController := &mockChanController{
|
||||||
@ -799,10 +822,8 @@ func TestAgentPrivateChannels(t *testing.T) {
|
|||||||
DisconnectPeer: func(*btcec.PublicKey) error {
|
DisconnectPeer: func(*btcec.PublicKey) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
Graph: memGraph,
|
Graph: memGraph,
|
||||||
Constraints: &HeuristicConstraints{
|
Constraints: constraints,
|
||||||
MaxPendingOpens: 10,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
agent, err := New(cfg, nil)
|
agent, err := New(cfg, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -905,6 +926,8 @@ func TestAgentPendingChannelState(t *testing.T) {
|
|||||||
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
||||||
quit: quit,
|
quit: quit,
|
||||||
}
|
}
|
||||||
|
constraints := &mockConstraints{}
|
||||||
|
|
||||||
chanController := &mockChanController{
|
chanController := &mockChanController{
|
||||||
openChanSignals: make(chan openChanIntent),
|
openChanSignals: make(chan openChanIntent),
|
||||||
}
|
}
|
||||||
@ -932,10 +955,8 @@ func TestAgentPendingChannelState(t *testing.T) {
|
|||||||
DisconnectPeer: func(*btcec.PublicKey) error {
|
DisconnectPeer: func(*btcec.PublicKey) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
Graph: memGraph,
|
Graph: memGraph,
|
||||||
Constraints: &HeuristicConstraints{
|
Constraints: constraints,
|
||||||
MaxPendingOpens: 10,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
initialChans := []Channel{}
|
initialChans := []Channel{}
|
||||||
agent, err := New(testCfg, initialChans)
|
agent, err := New(testCfg, initialChans)
|
||||||
@ -1097,6 +1118,8 @@ func TestAgentPendingOpenChannel(t *testing.T) {
|
|||||||
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
||||||
quit: quit,
|
quit: quit,
|
||||||
}
|
}
|
||||||
|
constraints := &mockConstraints{}
|
||||||
|
|
||||||
chanController := &mockChanController{
|
chanController := &mockChanController{
|
||||||
openChanSignals: make(chan openChanIntent),
|
openChanSignals: make(chan openChanIntent),
|
||||||
}
|
}
|
||||||
@ -1114,10 +1137,8 @@ func TestAgentPendingOpenChannel(t *testing.T) {
|
|||||||
WalletBalance: func() (btcutil.Amount, error) {
|
WalletBalance: func() (btcutil.Amount, error) {
|
||||||
return walletBalance, nil
|
return walletBalance, nil
|
||||||
},
|
},
|
||||||
Graph: memGraph,
|
Graph: memGraph,
|
||||||
Constraints: &HeuristicConstraints{
|
Constraints: constraints,
|
||||||
MaxPendingOpens: 10,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
agent, err := New(cfg, nil)
|
agent, err := New(cfg, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1190,6 +1211,8 @@ func TestAgentOnNodeUpdates(t *testing.T) {
|
|||||||
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
||||||
quit: quit,
|
quit: quit,
|
||||||
}
|
}
|
||||||
|
constraints := &mockConstraints{}
|
||||||
|
|
||||||
chanController := &mockChanController{
|
chanController := &mockChanController{
|
||||||
openChanSignals: make(chan openChanIntent),
|
openChanSignals: make(chan openChanIntent),
|
||||||
}
|
}
|
||||||
@ -1207,10 +1230,8 @@ func TestAgentOnNodeUpdates(t *testing.T) {
|
|||||||
WalletBalance: func() (btcutil.Amount, error) {
|
WalletBalance: func() (btcutil.Amount, error) {
|
||||||
return walletBalance, nil
|
return walletBalance, nil
|
||||||
},
|
},
|
||||||
Graph: memGraph,
|
Graph: memGraph,
|
||||||
Constraints: &HeuristicConstraints{
|
Constraints: constraints,
|
||||||
MaxPendingOpens: 10,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
agent, err := New(cfg, nil)
|
agent, err := New(cfg, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1303,6 +1324,8 @@ func TestAgentSkipPendingConns(t *testing.T) {
|
|||||||
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
nodeScoresResps: make(chan map[NodeID]*AttachmentDirective),
|
||||||
quit: quit,
|
quit: quit,
|
||||||
}
|
}
|
||||||
|
constraints := &mockConstraints{}
|
||||||
|
|
||||||
chanController := &mockChanController{
|
chanController := &mockChanController{
|
||||||
openChanSignals: make(chan openChanIntent),
|
openChanSignals: make(chan openChanIntent),
|
||||||
}
|
}
|
||||||
@ -1341,10 +1364,8 @@ func TestAgentSkipPendingConns(t *testing.T) {
|
|||||||
DisconnectPeer: func(*btcec.PublicKey) error {
|
DisconnectPeer: func(*btcec.PublicKey) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
Graph: memGraph,
|
Graph: memGraph,
|
||||||
Constraints: &HeuristicConstraints{
|
Constraints: constraints,
|
||||||
MaxPendingOpens: 10,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
initialChans := []Channel{}
|
initialChans := []Channel{}
|
||||||
agent, err := New(testCfg, initialChans)
|
agent, err := New(testCfg, initialChans)
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
package autopilot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/btcsuite/btcutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HeuristicConstraints is a struct that indicate the constraints an autopilot
|
|
||||||
// heuristic must adhere to when opening channels.
|
|
||||||
type HeuristicConstraints struct {
|
|
||||||
// MinChanSize is the smallest channel that the autopilot agent should
|
|
||||||
// create.
|
|
||||||
MinChanSize btcutil.Amount
|
|
||||||
|
|
||||||
// MaxChanSize the largest channel that the autopilot agent should
|
|
||||||
// create.
|
|
||||||
MaxChanSize btcutil.Amount
|
|
||||||
|
|
||||||
// ChanLimit the maximum number of channels that should be created.
|
|
||||||
ChanLimit uint16
|
|
||||||
|
|
||||||
// Allocation the percentage of total funds that should be committed to
|
|
||||||
// automatic channel establishment.
|
|
||||||
Allocation float64
|
|
||||||
|
|
||||||
// MaxPendingOpens is the maximum number of pending channel
|
|
||||||
// establishment goroutines that can be lingering. We cap this value in
|
|
||||||
// order to control the level of parallelism caused by the autopilot
|
|
||||||
// agent.
|
|
||||||
MaxPendingOpens uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
// availableChans returns the funds and number of channels slots the autopilot
|
|
||||||
// has available towards new channels, and still be within the set constraints.
|
|
||||||
func (h *HeuristicConstraints) availableChans(channels []Channel,
|
|
||||||
funds btcutil.Amount) (btcutil.Amount, uint32) {
|
|
||||||
|
|
||||||
// If we're already over our maximum allowed number of channels, then
|
|
||||||
// we'll instruct the controller not to create any more channels.
|
|
||||||
if len(channels) >= int(h.ChanLimit) {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// The number of additional channels that should be opened is the
|
|
||||||
// difference between the channel limit, and the number of channels we
|
|
||||||
// already have open.
|
|
||||||
numAdditionalChans := uint32(h.ChanLimit) - uint32(len(channels))
|
|
||||||
|
|
||||||
// First, we'll tally up the total amount of funds that are currently
|
|
||||||
// present within the set of active channels.
|
|
||||||
var totalChanAllocation btcutil.Amount
|
|
||||||
for _, channel := range channels {
|
|
||||||
totalChanAllocation += channel.Capacity
|
|
||||||
}
|
|
||||||
|
|
||||||
// With this value known, we'll now compute the total amount of fund
|
|
||||||
// allocated across regular utxo's and channel utxo's.
|
|
||||||
totalFunds := funds + totalChanAllocation
|
|
||||||
|
|
||||||
// Once the total amount has been computed, we then calculate the
|
|
||||||
// fraction of funds currently allocated to channels.
|
|
||||||
fundsFraction := float64(totalChanAllocation) / float64(totalFunds)
|
|
||||||
|
|
||||||
// If this fraction is below our threshold, then we'll return true, to
|
|
||||||
// indicate the controller should call Select to obtain a candidate set
|
|
||||||
// of channels to attempt to open.
|
|
||||||
needMore := fundsFraction < h.Allocation
|
|
||||||
if !needMore {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now that we know we need more funds, we'll compute the amount of
|
|
||||||
// additional funds we should allocate towards channels.
|
|
||||||
targetAllocation := btcutil.Amount(float64(totalFunds) * h.Allocation)
|
|
||||||
fundsAvailable := targetAllocation - totalChanAllocation
|
|
||||||
return fundsAvailable, numAdditionalChans
|
|
||||||
}
|
|
@ -21,7 +21,7 @@ import (
|
|||||||
//
|
//
|
||||||
// TODO(roasbeef): BA, with k=-3
|
// TODO(roasbeef): BA, with k=-3
|
||||||
type ConstrainedPrefAttachment struct {
|
type ConstrainedPrefAttachment struct {
|
||||||
constraints *HeuristicConstraints
|
constraints AgentConstraints
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConstrainedPrefAttachment creates a new instance of a
|
// NewConstrainedPrefAttachment creates a new instance of a
|
||||||
@ -29,7 +29,7 @@ type ConstrainedPrefAttachment struct {
|
|||||||
// and an allocation amount which is interpreted as a percentage of funds that
|
// and an allocation amount which is interpreted as a percentage of funds that
|
||||||
// is to be committed to channels at all times.
|
// is to be committed to channels at all times.
|
||||||
func NewConstrainedPrefAttachment(
|
func NewConstrainedPrefAttachment(
|
||||||
cfg *HeuristicConstraints) *ConstrainedPrefAttachment {
|
cfg AgentConstraints) *ConstrainedPrefAttachment {
|
||||||
|
|
||||||
prand.Seed(time.Now().Unix())
|
prand.Seed(time.Now().Unix())
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ func (p *ConstrainedPrefAttachment) NeedMoreChans(channels []Channel,
|
|||||||
funds btcutil.Amount) (btcutil.Amount, uint32, bool) {
|
funds btcutil.Amount) (btcutil.Amount, uint32, bool) {
|
||||||
|
|
||||||
// We'll try to open more channels as long as the constraints allow it.
|
// We'll try to open more channels as long as the constraints allow it.
|
||||||
availableFunds, availableChans := p.constraints.availableChans(
|
availableFunds, availableChans := p.constraints.ChannelBudget(
|
||||||
channels, funds,
|
channels, funds,
|
||||||
)
|
)
|
||||||
return availableFunds, availableChans, availableChans > 0
|
return availableFunds, availableChans, availableChans > 0
|
||||||
@ -142,7 +142,7 @@ func (p *ConstrainedPrefAttachment) NodeScores(g ChannelGraph, chans []Channel,
|
|||||||
candidates := make(map[NodeID]*AttachmentDirective)
|
candidates := make(map[NodeID]*AttachmentDirective)
|
||||||
for nID, nodeChans := range nodeChanNum {
|
for nID, nodeChans := range nodeChanNum {
|
||||||
// As channel size we'll use the maximum channel size available.
|
// As channel size we'll use the maximum channel size available.
|
||||||
chanSize := p.constraints.MaxChanSize
|
chanSize := p.constraints.MaxChanSize()
|
||||||
if fundsAvailable-chanSize < 0 {
|
if fundsAvailable-chanSize < 0 {
|
||||||
chanSize = fundsAvailable
|
chanSize = fundsAvailable
|
||||||
}
|
}
|
||||||
@ -159,7 +159,7 @@ func (p *ConstrainedPrefAttachment) NodeScores(g ChannelGraph, chans []Channel,
|
|||||||
|
|
||||||
// If the amount is too small, we don't want to attempt opening
|
// If the amount is too small, we don't want to attempt opening
|
||||||
// another channel.
|
// another channel.
|
||||||
case chanSize == 0 || chanSize < p.constraints.MinChanSize:
|
case chanSize == 0 || chanSize < p.constraints.MinChanSize():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
// If the node has no addresses, we cannot connect to it, so we
|
// If the node has no addresses, we cannot connect to it, so we
|
||||||
|
@ -29,12 +29,13 @@ func TestConstrainedPrefAttachmentNeedMoreChan(t *testing.T) {
|
|||||||
threshold = 0.5
|
threshold = 0.5
|
||||||
)
|
)
|
||||||
|
|
||||||
constraints := &HeuristicConstraints{
|
constraints := NewConstraints(
|
||||||
MinChanSize: minChanSize,
|
minChanSize,
|
||||||
MaxChanSize: maxChanSize,
|
maxChanSize,
|
||||||
ChanLimit: chanLimit,
|
chanLimit,
|
||||||
Allocation: threshold,
|
0,
|
||||||
}
|
threshold,
|
||||||
|
)
|
||||||
|
|
||||||
randChanID := func() lnwire.ShortChannelID {
|
randChanID := func() lnwire.ShortChannelID {
|
||||||
return lnwire.NewShortChanIDFromInt(uint64(prand.Int63()))
|
return lnwire.NewShortChanIDFromInt(uint64(prand.Int63()))
|
||||||
@ -242,12 +243,13 @@ func TestConstrainedPrefAttachmentSelectEmptyGraph(t *testing.T) {
|
|||||||
threshold = 0.5
|
threshold = 0.5
|
||||||
)
|
)
|
||||||
|
|
||||||
constraints := &HeuristicConstraints{
|
constraints := NewConstraints(
|
||||||
MinChanSize: minChanSize,
|
minChanSize,
|
||||||
MaxChanSize: maxChanSize,
|
maxChanSize,
|
||||||
ChanLimit: chanLimit,
|
chanLimit,
|
||||||
Allocation: threshold,
|
0,
|
||||||
}
|
threshold,
|
||||||
|
)
|
||||||
|
|
||||||
prefAttach := NewConstrainedPrefAttachment(constraints)
|
prefAttach := NewConstrainedPrefAttachment(constraints)
|
||||||
|
|
||||||
@ -350,12 +352,14 @@ func TestConstrainedPrefAttachmentSelectTwoVertexes(t *testing.T) {
|
|||||||
threshold = 0.5
|
threshold = 0.5
|
||||||
)
|
)
|
||||||
|
|
||||||
constraints := &HeuristicConstraints{
|
constraints := NewConstraints(
|
||||||
MinChanSize: minChanSize,
|
minChanSize,
|
||||||
MaxChanSize: maxChanSize,
|
maxChanSize,
|
||||||
ChanLimit: chanLimit,
|
chanLimit,
|
||||||
Allocation: threshold,
|
0,
|
||||||
}
|
threshold,
|
||||||
|
)
|
||||||
|
|
||||||
for _, graph := range chanGraphs {
|
for _, graph := range chanGraphs {
|
||||||
success := t.Run(graph.name, func(t1 *testing.T) {
|
success := t.Run(graph.name, func(t1 *testing.T) {
|
||||||
graph, cleanup, err := graph.genFunc()
|
graph, cleanup, err := graph.genFunc()
|
||||||
@ -474,12 +478,13 @@ func TestConstrainedPrefAttachmentSelectInsufficientFunds(t *testing.T) {
|
|||||||
threshold = 0.5
|
threshold = 0.5
|
||||||
)
|
)
|
||||||
|
|
||||||
constraints := &HeuristicConstraints{
|
constraints := NewConstraints(
|
||||||
MinChanSize: minChanSize,
|
minChanSize,
|
||||||
MaxChanSize: maxChanSize,
|
maxChanSize,
|
||||||
ChanLimit: chanLimit,
|
chanLimit,
|
||||||
Allocation: threshold,
|
0,
|
||||||
}
|
threshold,
|
||||||
|
)
|
||||||
|
|
||||||
for _, graph := range chanGraphs {
|
for _, graph := range chanGraphs {
|
||||||
success := t.Run(graph.name, func(t1 *testing.T) {
|
success := t.Run(graph.name, func(t1 *testing.T) {
|
||||||
@ -544,12 +549,13 @@ func TestConstrainedPrefAttachmentSelectGreedyAllocation(t *testing.T) {
|
|||||||
threshold = 0.5
|
threshold = 0.5
|
||||||
)
|
)
|
||||||
|
|
||||||
constraints := &HeuristicConstraints{
|
constraints := NewConstraints(
|
||||||
MinChanSize: minChanSize,
|
minChanSize,
|
||||||
MaxChanSize: maxChanSize,
|
maxChanSize,
|
||||||
ChanLimit: chanLimit,
|
chanLimit,
|
||||||
Allocation: threshold,
|
0,
|
||||||
}
|
threshold,
|
||||||
|
)
|
||||||
|
|
||||||
for _, graph := range chanGraphs {
|
for _, graph := range chanGraphs {
|
||||||
success := t.Run(graph.name, func(t1 *testing.T) {
|
success := t.Run(graph.name, func(t1 *testing.T) {
|
||||||
@ -711,12 +717,13 @@ func TestConstrainedPrefAttachmentSelectSkipNodes(t *testing.T) {
|
|||||||
threshold = 0.5
|
threshold = 0.5
|
||||||
)
|
)
|
||||||
|
|
||||||
constraints := &HeuristicConstraints{
|
constraints := NewConstraints(
|
||||||
MinChanSize: minChanSize,
|
minChanSize,
|
||||||
MaxChanSize: maxChanSize,
|
maxChanSize,
|
||||||
ChanLimit: chanLimit,
|
chanLimit,
|
||||||
Allocation: threshold,
|
0,
|
||||||
}
|
threshold,
|
||||||
|
)
|
||||||
|
|
||||||
for _, graph := range chanGraphs {
|
for _, graph := range chanGraphs {
|
||||||
success := t.Run(graph.name, func(t1 *testing.T) {
|
success := t.Run(graph.name, func(t1 *testing.T) {
|
||||||
|
14
pilot.go
14
pilot.go
@ -87,13 +87,13 @@ func initAutoPilot(svr *server, cfg *autoPilotConfig) *autopilot.ManagerCfg {
|
|||||||
atplLog.Infof("Instantiating autopilot with cfg: %v", spew.Sdump(cfg))
|
atplLog.Infof("Instantiating autopilot with cfg: %v", spew.Sdump(cfg))
|
||||||
|
|
||||||
// Set up the constraints the autopilot heuristics must adhere to.
|
// Set up the constraints the autopilot heuristics must adhere to.
|
||||||
atplConstraints := &autopilot.HeuristicConstraints{
|
atplConstraints := autopilot.NewConstraints(
|
||||||
MinChanSize: btcutil.Amount(cfg.MinChannelSize),
|
btcutil.Amount(cfg.MinChannelSize),
|
||||||
MaxChanSize: btcutil.Amount(cfg.MaxChannelSize),
|
btcutil.Amount(cfg.MaxChannelSize),
|
||||||
ChanLimit: uint16(cfg.MaxChannels),
|
uint16(cfg.MaxChannels),
|
||||||
Allocation: cfg.Allocation,
|
10,
|
||||||
MaxPendingOpens: 10,
|
cfg.Allocation,
|
||||||
}
|
)
|
||||||
|
|
||||||
// First, we'll create the preferential attachment heuristic,
|
// First, we'll create the preferential attachment heuristic,
|
||||||
// initialized with the passed auto pilot configuration parameters.
|
// initialized with the passed auto pilot configuration parameters.
|
||||||
|
Loading…
Reference in New Issue
Block a user