autopilot+config: support opening private channels with autopilot agent
This commit is contained in:
parent
6989316b11
commit
89325a13b7
@ -75,23 +75,27 @@ func (m *mockHeuristic) Select(self *btcec.PublicKey, graph ChannelGraph,
|
|||||||
var _ AttachmentHeuristic = (*mockHeuristic)(nil)
|
var _ AttachmentHeuristic = (*mockHeuristic)(nil)
|
||||||
|
|
||||||
type openChanIntent struct {
|
type openChanIntent struct {
|
||||||
target *btcec.PublicKey
|
target *btcec.PublicKey
|
||||||
amt btcutil.Amount
|
amt btcutil.Amount
|
||||||
addrs []net.Addr
|
addrs []net.Addr
|
||||||
|
private bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type mockChanController struct {
|
type mockChanController struct {
|
||||||
openChanSignals chan openChanIntent
|
openChanSignals chan openChanIntent
|
||||||
|
private bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockChanController) OpenChannel(target *btcec.PublicKey, amt btcutil.Amount,
|
func (m *mockChanController) OpenChannel(target *btcec.PublicKey, amt btcutil.Amount,
|
||||||
addrs []net.Addr) error {
|
addrs []net.Addr) error {
|
||||||
|
|
||||||
m.openChanSignals <- openChanIntent{
|
m.openChanSignals <- openChanIntent{
|
||||||
target: target,
|
target: target,
|
||||||
amt: amt,
|
amt: amt,
|
||||||
addrs: addrs,
|
addrs: addrs,
|
||||||
|
private: m.private,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -704,6 +708,134 @@ func TestAgentImmediateAttach(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestAgentPrivateChannels ensure that only requests for private channels are
|
||||||
|
// sent if set.
|
||||||
|
func TestAgentPrivateChannels(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// First, we'll create all the dependencies that we'll need in order to
|
||||||
|
// create the autopilot agent.
|
||||||
|
self, err := randKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to generate key: %v", err)
|
||||||
|
}
|
||||||
|
heuristic := &mockHeuristic{
|
||||||
|
moreChansResps: make(chan moreChansResp),
|
||||||
|
directiveResps: make(chan []AttachmentDirective),
|
||||||
|
}
|
||||||
|
// The chanController should be initialized such that all of its open
|
||||||
|
// channel requests are for private channels.
|
||||||
|
chanController := &mockChanController{
|
||||||
|
openChanSignals: make(chan openChanIntent),
|
||||||
|
private: true,
|
||||||
|
}
|
||||||
|
memGraph, _, _ := newMemChanGraph()
|
||||||
|
|
||||||
|
// The wallet will start with 10 BTC available.
|
||||||
|
const walletBalance = btcutil.SatoshiPerBitcoin * 10
|
||||||
|
|
||||||
|
// With the dependencies we created, we can now create the initial
|
||||||
|
// agent itself.
|
||||||
|
cfg := Config{
|
||||||
|
Self: self,
|
||||||
|
Heuristic: heuristic,
|
||||||
|
ChanController: chanController,
|
||||||
|
WalletBalance: func() (btcutil.Amount, error) {
|
||||||
|
return walletBalance, nil
|
||||||
|
},
|
||||||
|
Graph: memGraph,
|
||||||
|
MaxPendingOpens: 10,
|
||||||
|
}
|
||||||
|
agent, err := New(cfg, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create agent: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// With the autopilot agent and all its dependencies we'll star the
|
||||||
|
// primary controller goroutine.
|
||||||
|
if err := agent.Start(); err != nil {
|
||||||
|
t.Fatalf("unable to start agent: %v", err)
|
||||||
|
}
|
||||||
|
defer agent.Stop()
|
||||||
|
|
||||||
|
const numChans = 5
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
// The very first thing the agent should do is query the NeedMoreChans
|
||||||
|
// method on the passed heuristic. So we'll provide it with a response
|
||||||
|
// that will kick off the main loop.
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
// We'll send over a response indicating that it should
|
||||||
|
// establish more channels, and give it a budget of 5 BTC to do
|
||||||
|
// so.
|
||||||
|
resp := moreChansResp{
|
||||||
|
needMore: true,
|
||||||
|
numMore: numChans,
|
||||||
|
amt: 5 * btcutil.SatoshiPerBitcoin,
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case heuristic.moreChansResps <- resp:
|
||||||
|
return
|
||||||
|
case <-time.After(time.Second * 10):
|
||||||
|
t.Fatalf("heuristic wasn't queried in time")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// We'll wait here for the agent to query the heuristic. If it doesn't
|
||||||
|
// do so within 10 seconds, then the test will fail out.
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
// At this point, the agent should now be querying the heuristic to
|
||||||
|
// requests attachment directives. We'll generate 5 mock directives so
|
||||||
|
// it can progress within its loop.
|
||||||
|
directives := make([]AttachmentDirective, numChans)
|
||||||
|
for i := 0; i < numChans; i++ {
|
||||||
|
directives[i] = AttachmentDirective{
|
||||||
|
PeerKey: self,
|
||||||
|
ChanAmt: btcutil.SatoshiPerBitcoin,
|
||||||
|
Addrs: []net.Addr{
|
||||||
|
&net.TCPAddr{
|
||||||
|
IP: bytes.Repeat([]byte("a"), 16),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// With our fake directives created, we'll now send then to the agent
|
||||||
|
// as a return value for the Select function.
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case heuristic.directiveResps <- directives:
|
||||||
|
return
|
||||||
|
case <-time.After(time.Second * 10):
|
||||||
|
t.Fatalf("heuristic wasn't queried in time")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// We'll wait here for either the agent to query the heuristic to be
|
||||||
|
// queried, or for the timeout above to tick.
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
// Finally, we should receive 5 calls to the OpenChannel method, each
|
||||||
|
// specifying that it's for a private channel.
|
||||||
|
for i := 0; i < numChans; i++ {
|
||||||
|
select {
|
||||||
|
case openChan := <-chanController.openChanSignals:
|
||||||
|
if !openChan.private {
|
||||||
|
t.Fatal("expected open channel request to be private")
|
||||||
|
}
|
||||||
|
case <-time.After(10 * time.Second):
|
||||||
|
t.Fatal("channel not opened in time")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestAgentPendingChannelState ensures that the agent properly factors in its
|
// TestAgentPendingChannelState ensures that the agent properly factors in its
|
||||||
// pending channel state when making decisions w.r.t if it needs more channels
|
// pending channel state when making decisions w.r.t if it needs more channels
|
||||||
// or not, and if so, who is eligible to open new channels to.
|
// or not, and if so, who is eligible to open new channels to.
|
||||||
|
@ -145,6 +145,7 @@ type autoPilotConfig struct {
|
|||||||
Allocation float64 `long:"allocation" description:"The percentage of total funds that should be committed to automatic channel establishment"`
|
Allocation float64 `long:"allocation" description:"The percentage of total funds that should be committed to automatic channel establishment"`
|
||||||
MinChannelSize int64 `long:"minchansize" description:"The smallest channel that the autopilot agent should create"`
|
MinChannelSize int64 `long:"minchansize" description:"The smallest channel that the autopilot agent should create"`
|
||||||
MaxChannelSize int64 `long:"maxchansize" description:"The largest channel that the autopilot agent should create"`
|
MaxChannelSize int64 `long:"maxchansize" description:"The largest channel that the autopilot agent should create"`
|
||||||
|
Private bool `long:"private" description:"Whether the channels created by the autopilot agent should be private or not. Private channels won't be announced to the network."`
|
||||||
}
|
}
|
||||||
|
|
||||||
type torConfig struct {
|
type torConfig struct {
|
||||||
|
14
pilot.go
14
pilot.go
@ -16,7 +16,8 @@ import (
|
|||||||
// chanController is an implementation of the autopilot.ChannelController
|
// chanController is an implementation of the autopilot.ChannelController
|
||||||
// interface that's backed by a running lnd instance.
|
// interface that's backed by a running lnd instance.
|
||||||
type chanController struct {
|
type chanController struct {
|
||||||
server *server
|
server *server
|
||||||
|
private bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenChannel opens a channel to a target peer, with a capacity of the
|
// OpenChannel opens a channel to a target peer, with a capacity of the
|
||||||
@ -89,7 +90,7 @@ func (c *chanController) OpenChannel(target *btcec.PublicKey,
|
|||||||
minHtlc := lnwire.NewMSatFromSatoshis(1)
|
minHtlc := lnwire.NewMSatFromSatoshis(1)
|
||||||
|
|
||||||
updateStream, errChan := c.server.OpenChannel(
|
updateStream, errChan := c.server.OpenChannel(
|
||||||
target, amt, 0, minHtlc, feePerKw, false, 0,
|
target, amt, 0, minHtlc, feePerKw, c.private, 0,
|
||||||
)
|
)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
@ -148,9 +149,12 @@ func initAutoPilot(svr *server, cfg *autoPilotConfig) (*autopilot.Agent, error)
|
|||||||
// of the items that the autopilot agent needs to perform its duties.
|
// of the items that the autopilot agent needs to perform its duties.
|
||||||
self := svr.identityPriv.PubKey()
|
self := svr.identityPriv.PubKey()
|
||||||
pilotCfg := autopilot.Config{
|
pilotCfg := autopilot.Config{
|
||||||
Self: self,
|
Self: self,
|
||||||
Heuristic: prefAttachment,
|
Heuristic: prefAttachment,
|
||||||
ChanController: &chanController{svr},
|
ChanController: &chanController{
|
||||||
|
server: svr,
|
||||||
|
private: cfg.Private,
|
||||||
|
},
|
||||||
WalletBalance: func() (btcutil.Amount, error) {
|
WalletBalance: func() (btcutil.Amount, error) {
|
||||||
return svr.cc.wallet.ConfirmedBalance(1)
|
return svr.cc.wallet.ConfirmedBalance(1)
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user