package lnd
import (
"errors"
"fmt"
"net"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/autopilot"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/tor"
)
func validateAtplCfg(cfg *autoPilotConfig) ([]*autopilot.WeightedHeuristic,
error) {
var (
heuristicsStr string
sum float64
heuristics []*autopilot.WeightedHeuristic
)
for _, a := range autopilot.AvailableHeuristics {
heuristicsStr += fmt.Sprintf(" '%v' ", a.Name())
}
availStr := fmt.Sprintf("Available heuristics are: [%v]", heuristicsStr)
for name, weight := range cfg.Heuristic {
a, ok := autopilot.AvailableHeuristics[name]
if !ok {
return nil, fmt.Errorf("heuristic %v not available. %v",
name, availStr)
}
heuristics = append(
heuristics,
&autopilot.WeightedHeuristic{
Weight: weight,
AttachmentHeuristic: a,
},
)
sum += weight
}
if len(heuristics) == 0 {
return nil, fmt.Errorf("no active heuristics: %v", availStr)
}
if sum != 1.0 {
return nil, fmt.Errorf("heuristic weights must sum to 1.0")
}
return heuristics, nil
}
type chanController struct {
server *server
private bool
minConfs int32
confTarget uint32
chanMinHtlcIn lnwire.MilliSatoshi
}
func (c *chanController) OpenChannel(target *btcec.PublicKey,
amt btcutil.Amount) error {
feePerKw, err := c.server.cc.feeEstimator.EstimateFeePerKW(
c.confTarget,
)
if err != nil {
return err
}
req := &openChanReq{
targetPubkey: target,
chainHash: *activeNetParams.GenesisHash,
subtractFees: true,
localFundingAmt: amt,
pushAmt: 0,
minHtlcIn: c.chanMinHtlcIn,
fundingFeePerKw: feePerKw,
private: c.private,
remoteCsvDelay: 0,
minConfs: c.minConfs,
}
updateStream, errChan := c.server.OpenChannel(req)
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
}
func (c *chanController) SpliceIn(chanPoint *wire.OutPoint,
amt btcutil.Amount) (*autopilot.Channel, error) {
return nil, nil
}
func (c *chanController) SpliceOut(chanPoint *wire.OutPoint,
amt btcutil.Amount) (*autopilot.Channel, error) {
return nil, nil
}
var _ autopilot.ChannelController = (*chanController)(nil)
func initAutoPilot(svr *server, cfg *autoPilotConfig, chainCfg *chainConfig) (
*autopilot.ManagerCfg, error) {
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,
cfg.MaxChannelSize, cfg.Private, cfg.MinConfs, cfg.ConfTarget)
atplConstraints := autopilot.NewConstraints(
btcutil.Amount(cfg.MinChannelSize),
btcutil.Amount(cfg.MaxChannelSize),
uint16(cfg.MaxChannels),
10,
cfg.Allocation,
)
heuristics, err := validateAtplCfg(cfg)
if err != nil {
return nil, err
}
weightedAttachment, err := autopilot.NewWeightedCombAttachment(
heuristics...,
)
if err != nil {
return nil, err
}
self := svr.identityPriv.PubKey()
pilotCfg := autopilot.Config{
Self: self,
Heuristic: weightedAttachment,
ChanController: &chanController{
server: svr,
private: cfg.Private,
minConfs: cfg.MinConfs,
confTarget: cfg.ConfTarget,
chanMinHtlcIn: chainCfg.MinHTLCIn,
},
WalletBalance: func() (btcutil.Amount, error) {
return svr.cc.wallet.ConfirmedBalance(cfg.MinConfs)
},
Graph: autopilot.ChannelGraphFromDatabase(svr.chanDB.ChannelGraph()),
Constraints: atplConstraints,
ConnectToPeer: func(target *btcec.PublicKey, addrs []net.Addr) (bool, error) {
if _, err := svr.FindPeer(target); err == nil {
return true, nil
}
if len(addrs) == 0 {
return false, errors.New("no addresses specified")
}
atplLog.Tracef("Attempting to connect to %x",
target.SerializeCompressed())
lnAddr := &lnwire.NetAddress{
IdentityKey: target,
ChainNet: activeNetParams.Net,
}
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)
}
err := svr.ConnectToPeer(lnAddr, false)
if err != nil {
continue
}
connected = true
break
}
if !connected {
return false, errors.New("exhausted all " +
"advertised addresses")
}
return false, nil
},
DisconnectPeer: svr.DisconnectPeer,
}
return &autopilot.ManagerCfg{
Self: self,
PilotCfg: &pilotCfg,
ChannelState: func() ([]autopilot.Channel, error) {
activeChannels, err := svr.chanDB.FetchAllChannels()
if err != nil {
return nil, err
}
chanState := make([]autopilot.Channel,
len(activeChannels))
for i, channel := range activeChannels {
chanState[i] = autopilot.Channel{
ChanID: channel.ShortChanID(),
Capacity: channel.Capacity,
Node: autopilot.NewNodeID(
channel.IdentityPub),
}
}
return chanState, nil
},
SubscribeTransactions: svr.cc.wallet.SubscribeTransactions,
SubscribeTopology: svr.chanRouter.SubscribeTopology,
}, nil
}