2019-01-24 16:28:25 +03:00
|
|
|
package lnd
|
2017-08-11 07:39:08 +03:00
|
|
|
|
|
|
|
import (
|
2018-08-07 04:58:36 +03:00
|
|
|
"errors"
|
2017-08-11 07:39:08 +03:00
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
|
2018-06-05 04:34:16 +03:00
|
|
|
"github.com/btcsuite/btcd/btcec"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
|
|
"github.com/btcsuite/btcutil"
|
2017-08-11 07:39:08 +03:00
|
|
|
"github.com/lightningnetwork/lnd/autopilot"
|
2020-05-13 16:57:30 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lncfg"
|
2017-08-11 07:39:08 +03:00
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
2018-05-02 09:33:17 +03:00
|
|
|
"github.com/lightningnetwork/lnd/tor"
|
2017-08-11 07:39:08 +03:00
|
|
|
)
|
|
|
|
|
2018-12-19 20:56:43 +03:00
|
|
|
// validateAtplConfig is a helper method that makes sure the passed
|
|
|
|
// configuration is sane. Currently it checks that the heuristic configuration
|
|
|
|
// makes sense. In case the config is valid, it will return a list of
|
|
|
|
// WeightedHeuristics that can be combined for use with the autopilot agent.
|
2020-05-13 16:57:30 +03:00
|
|
|
func validateAtplCfg(cfg *lncfg.AutoPilot) ([]*autopilot.WeightedHeuristic,
|
2018-12-19 20:56:43 +03:00
|
|
|
error) {
|
|
|
|
|
|
|
|
var (
|
|
|
|
heuristicsStr string
|
|
|
|
sum float64
|
|
|
|
heuristics []*autopilot.WeightedHeuristic
|
|
|
|
)
|
|
|
|
|
|
|
|
// Create a help text that we can return in case the config is not
|
|
|
|
// correct.
|
|
|
|
for _, a := range autopilot.AvailableHeuristics {
|
|
|
|
heuristicsStr += fmt.Sprintf(" '%v' ", a.Name())
|
|
|
|
}
|
2019-09-18 14:28:27 +03:00
|
|
|
availStr := fmt.Sprintf("Available heuristics are: [%v]", heuristicsStr)
|
2018-12-19 20:56:43 +03:00
|
|
|
|
|
|
|
// We'll go through the config and make sure all the heuristics exists,
|
|
|
|
// and that the sum of their weights is 1.0.
|
|
|
|
for name, weight := range cfg.Heuristic {
|
|
|
|
a, ok := autopilot.AvailableHeuristics[name]
|
|
|
|
if !ok {
|
|
|
|
// No heuristic matching this config option was found.
|
2020-04-14 20:56:05 +03:00
|
|
|
return nil, fmt.Errorf("heuristic %v not available. %v",
|
2018-12-19 20:56:43 +03:00
|
|
|
name, availStr)
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this heuristic was among the registered ones, we add it
|
|
|
|
// to the list we'll give to the agent, and keep track of the
|
|
|
|
// sum of weights.
|
|
|
|
heuristics = append(
|
|
|
|
heuristics,
|
|
|
|
&autopilot.WeightedHeuristic{
|
|
|
|
Weight: weight,
|
|
|
|
AttachmentHeuristic: a,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
sum += weight
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check found heuristics. We must have at least one to operate.
|
|
|
|
if len(heuristics) == 0 {
|
2020-04-14 20:56:05 +03:00
|
|
|
return nil, fmt.Errorf("no active heuristics: %v", availStr)
|
2018-12-19 20:56:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if sum != 1.0 {
|
2020-04-14 20:56:05 +03:00
|
|
|
return nil, fmt.Errorf("heuristic weights must sum to 1.0")
|
2018-12-19 20:56:43 +03:00
|
|
|
}
|
|
|
|
return heuristics, nil
|
|
|
|
}
|
|
|
|
|
2017-08-11 07:39:08 +03:00
|
|
|
// chanController is an implementation of the autopilot.ChannelController
|
|
|
|
// interface that's backed by a running lnd instance.
|
|
|
|
type chanController struct {
|
2019-11-25 15:36:40 +03:00
|
|
|
server *server
|
|
|
|
private bool
|
|
|
|
minConfs int32
|
|
|
|
confTarget uint32
|
|
|
|
chanMinHtlcIn lnwire.MilliSatoshi
|
2020-08-04 21:56:31 +03:00
|
|
|
netParams bitcoinNetParams
|
2017-08-11 07:39:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// OpenChannel opens a channel to a target peer, with a capacity of the
|
|
|
|
// specified amount. This function should un-block immediately after the
|
|
|
|
// funding transaction that marks the channel open has been broadcast.
|
|
|
|
func (c *chanController) OpenChannel(target *btcec.PublicKey,
|
2018-08-07 04:58:36 +03:00
|
|
|
amt btcutil.Amount) error {
|
2017-08-11 07:39:08 +03:00
|
|
|
|
|
|
|
// With the connection established, we'll now establish our connection
|
|
|
|
// to the target peer, waiting for the first update before we exit.
|
2019-04-26 16:32:29 +03:00
|
|
|
feePerKw, err := c.server.cc.feeEstimator.EstimateFeePerKW(
|
2019-06-08 18:29:33 +03:00
|
|
|
c.confTarget,
|
2019-04-26 16:32:29 +03:00
|
|
|
)
|
2017-11-23 22:38:08 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-12-17 02:01:08 +03:00
|
|
|
|
2018-08-10 05:17:16 +03:00
|
|
|
// Construct the open channel request and send it to the server to begin
|
|
|
|
// the funding workflow.
|
|
|
|
req := &openChanReq{
|
2020-07-08 11:30:09 +03:00
|
|
|
targetPubkey: target,
|
2020-08-04 21:56:31 +03:00
|
|
|
chainHash: *c.netParams.GenesisHash,
|
2020-07-08 11:30:09 +03:00
|
|
|
subtractFees: true,
|
|
|
|
localFundingAmt: amt,
|
|
|
|
pushAmt: 0,
|
|
|
|
minHtlcIn: c.chanMinHtlcIn,
|
|
|
|
fundingFeePerKw: feePerKw,
|
|
|
|
private: c.private,
|
|
|
|
remoteCsvDelay: 0,
|
|
|
|
minConfs: c.minConfs,
|
2018-08-30 00:44:23 +03:00
|
|
|
maxValueInFlight: 0,
|
2018-08-10 05:17:16 +03:00
|
|
|
}
|
2017-08-11 07:39:08 +03:00
|
|
|
|
2018-08-10 05:17:16 +03:00
|
|
|
updateStream, errChan := c.server.OpenChannel(req)
|
2017-08-11 07:39:08 +03:00
|
|
|
select {
|
|
|
|
case err := <-errChan:
|
|
|
|
return err
|
|
|
|
case <-updateStream:
|
|
|
|
return nil
|
|
|
|
case <-c.server.quit:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *chanController) CloseChannel(chanPoint *wire.OutPoint) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// A compile time assertion to ensure chanController meets the
|
|
|
|
// autopilot.ChannelController interface.
|
|
|
|
var _ autopilot.ChannelController = (*chanController)(nil)
|
|
|
|
|
2019-11-25 15:36:40 +03:00
|
|
|
// initAutoPilot initializes a new autopilot.ManagerCfg to manage an autopilot.
|
|
|
|
// Agent instance based on the passed configuration structs. The agent and all
|
|
|
|
// interfaces needed to drive it won't be launched before the Manager's
|
2018-12-13 14:26:29 +03:00
|
|
|
// StartAgent method is called.
|
2020-05-13 16:57:30 +03:00
|
|
|
func initAutoPilot(svr *server, cfg *lncfg.AutoPilot,
|
2020-08-04 21:56:31 +03:00
|
|
|
chainCfg *lncfg.Chain, netParams bitcoinNetParams) (*autopilot.ManagerCfg,
|
|
|
|
error) {
|
2019-11-25 15:36:40 +03:00
|
|
|
|
2020-03-11 14:56:16 +03:00
|
|
|
atplLog.Infof("Instantiating autopilot with active=%v, "+
|
|
|
|
"max_channels=%d, allocation=%f, min_chan_size=%d, "+
|
|
|
|
"max_chan_size=%d, private=%t, min_confs=%d, conf_target=%d",
|
|
|
|
cfg.Active, cfg.MaxChannels, cfg.Allocation, cfg.MinChannelSize,
|
2019-09-06 17:04:47 +03:00
|
|
|
cfg.MaxChannelSize, cfg.Private, cfg.MinConfs, cfg.ConfTarget)
|
2017-08-11 07:39:08 +03:00
|
|
|
|
2018-11-23 01:18:08 +03:00
|
|
|
// Set up the constraints the autopilot heuristics must adhere to.
|
2018-12-19 16:54:53 +03:00
|
|
|
atplConstraints := autopilot.NewConstraints(
|
|
|
|
btcutil.Amount(cfg.MinChannelSize),
|
|
|
|
btcutil.Amount(cfg.MaxChannelSize),
|
|
|
|
uint16(cfg.MaxChannels),
|
|
|
|
10,
|
|
|
|
cfg.Allocation,
|
|
|
|
)
|
2018-12-19 20:57:32 +03:00
|
|
|
heuristics, err := validateAtplCfg(cfg)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-08-11 07:39:08 +03:00
|
|
|
|
2019-01-09 11:14:45 +03:00
|
|
|
weightedAttachment, err := autopilot.NewWeightedCombAttachment(
|
2018-12-19 20:57:32 +03:00
|
|
|
heuristics...,
|
2019-01-09 11:14:45 +03:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-08-11 07:39:08 +03:00
|
|
|
// With the heuristic itself created, we can now populate the remainder
|
|
|
|
// of the items that the autopilot agent needs to perform its duties.
|
2020-04-28 11:06:21 +03:00
|
|
|
self := svr.identityECDH.PubKey()
|
2017-08-11 07:39:08 +03:00
|
|
|
pilotCfg := autopilot.Config{
|
2018-08-07 00:02:31 +03:00
|
|
|
Self: self,
|
2019-01-09 11:14:45 +03:00
|
|
|
Heuristic: weightedAttachment,
|
2018-08-07 00:02:31 +03:00
|
|
|
ChanController: &chanController{
|
2019-11-25 15:36:40 +03:00
|
|
|
server: svr,
|
|
|
|
private: cfg.Private,
|
|
|
|
minConfs: cfg.MinConfs,
|
|
|
|
confTarget: cfg.ConfTarget,
|
|
|
|
chanMinHtlcIn: chainCfg.MinHTLCIn,
|
2020-08-04 21:56:31 +03:00
|
|
|
netParams: netParams,
|
2018-08-07 00:02:31 +03:00
|
|
|
},
|
2017-08-11 07:39:08 +03:00
|
|
|
WalletBalance: func() (btcutil.Amount, error) {
|
2018-08-10 05:03:08 +03:00
|
|
|
return svr.cc.wallet.ConfirmedBalance(cfg.MinConfs)
|
2017-08-11 07:39:08 +03:00
|
|
|
},
|
2020-05-07 06:49:36 +03:00
|
|
|
Graph: autopilot.ChannelGraphFromDatabase(svr.localChanDB.ChannelGraph()),
|
2018-11-23 01:18:08 +03:00
|
|
|
Constraints: atplConstraints,
|
2018-08-07 04:58:36 +03:00
|
|
|
ConnectToPeer: func(target *btcec.PublicKey, addrs []net.Addr) (bool, error) {
|
|
|
|
// First, we'll check if we're already connected to the
|
|
|
|
// target peer. If we are, we can exit early. Otherwise,
|
|
|
|
// we'll need to establish a connection.
|
|
|
|
if _, err := svr.FindPeer(target); err == nil {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
2018-08-17 10:26:54 +03:00
|
|
|
// We can't establish a channel if no addresses were
|
|
|
|
// provided for the peer.
|
|
|
|
if len(addrs) == 0 {
|
|
|
|
return false, errors.New("no addresses specified")
|
|
|
|
}
|
|
|
|
|
2018-08-07 04:58:36 +03:00
|
|
|
atplLog.Tracef("Attempting to connect to %x",
|
|
|
|
target.SerializeCompressed())
|
|
|
|
|
|
|
|
lnAddr := &lnwire.NetAddress{
|
|
|
|
IdentityKey: target,
|
2020-08-04 21:56:31 +03:00
|
|
|
ChainNet: netParams.Net,
|
2018-08-07 04:58:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// We'll attempt to successively connect to each of the
|
|
|
|
// advertised IP addresses until we've either exhausted
|
|
|
|
// the advertised IP addresses, or have made a
|
|
|
|
// connection.
|
|
|
|
var connected bool
|
|
|
|
for _, addr := range addrs {
|
|
|
|
switch addr.(type) {
|
|
|
|
case *net.TCPAddr, *tor.OnionAddr:
|
|
|
|
lnAddr.Address = addr
|
|
|
|
default:
|
|
|
|
return false, fmt.Errorf("unknown "+
|
|
|
|
"address type %T", addr)
|
|
|
|
}
|
|
|
|
|
2020-08-25 07:50:48 +03:00
|
|
|
err := svr.ConnectToPeer(
|
|
|
|
lnAddr, false, svr.cfg.ConnectionTimeout,
|
|
|
|
)
|
2018-08-07 04:58:36 +03:00
|
|
|
if err != nil {
|
|
|
|
// If we weren't able to connect to the
|
|
|
|
// peer at this address, then we'll move
|
|
|
|
// onto the next.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
connected = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we weren't able to establish a connection at all,
|
|
|
|
// then we'll error out.
|
|
|
|
if !connected {
|
2018-08-29 12:04:54 +03:00
|
|
|
return false, errors.New("exhausted all " +
|
|
|
|
"advertised addresses")
|
2018-08-07 04:58:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return false, nil
|
|
|
|
},
|
|
|
|
DisconnectPeer: svr.DisconnectPeer,
|
2017-08-11 07:39:08 +03:00
|
|
|
}
|
|
|
|
|
2018-12-13 14:26:29 +03:00
|
|
|
// Create and return the autopilot.ManagerCfg that administrates this
|
|
|
|
// agent-pilot instance.
|
|
|
|
return &autopilot.ManagerCfg{
|
|
|
|
Self: self,
|
|
|
|
PilotCfg: &pilotCfg,
|
2020-10-02 14:56:43 +03:00
|
|
|
ChannelState: func() ([]autopilot.LocalChannel, error) {
|
2018-12-13 14:26:29 +03:00
|
|
|
// We'll fetch the current state of open
|
|
|
|
// channels from the database to use as initial
|
|
|
|
// state for the auto-pilot agent.
|
2020-05-07 06:49:36 +03:00
|
|
|
activeChannels, err := svr.remoteChanDB.FetchAllChannels()
|
2018-12-13 14:26:29 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2018-01-09 04:44:28 +03:00
|
|
|
}
|
2020-10-02 14:56:43 +03:00
|
|
|
chanState := make([]autopilot.LocalChannel,
|
2018-12-13 14:26:29 +03:00
|
|
|
len(activeChannels))
|
|
|
|
for i, channel := range activeChannels {
|
2020-10-02 15:49:02 +03:00
|
|
|
localCommit := channel.LocalCommitment
|
|
|
|
balance := localCommit.LocalBalance.ToSatoshis()
|
|
|
|
|
2020-10-02 14:56:43 +03:00
|
|
|
chanState[i] = autopilot.LocalChannel{
|
2020-10-02 15:49:02 +03:00
|
|
|
ChanID: channel.ShortChanID(),
|
|
|
|
Balance: balance,
|
2018-12-13 14:26:29 +03:00
|
|
|
Node: autopilot.NewNodeID(
|
2020-10-02 15:49:02 +03:00
|
|
|
channel.IdentityPub,
|
|
|
|
),
|
2017-08-11 07:39:08 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-13 14:26:29 +03:00
|
|
|
return chanState, nil
|
|
|
|
},
|
2020-10-02 15:49:02 +03:00
|
|
|
ChannelInfo: func(chanPoint wire.OutPoint) (
|
|
|
|
*autopilot.LocalChannel, error) {
|
|
|
|
|
|
|
|
channel, err := svr.remoteChanDB.FetchChannel(chanPoint)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
localCommit := channel.LocalCommitment
|
|
|
|
return &autopilot.LocalChannel{
|
|
|
|
ChanID: channel.ShortChanID(),
|
|
|
|
Balance: localCommit.LocalBalance.ToSatoshis(),
|
|
|
|
Node: autopilot.NewNodeID(channel.IdentityPub),
|
|
|
|
}, nil
|
|
|
|
},
|
2018-12-13 14:26:29 +03:00
|
|
|
SubscribeTransactions: svr.cc.wallet.SubscribeTransactions,
|
|
|
|
SubscribeTopology: svr.chanRouter.SubscribeTopology,
|
2019-01-09 11:14:45 +03:00
|
|
|
}, nil
|
2017-08-11 07:39:08 +03:00
|
|
|
}
|