123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- package lnd
- import (
- "errors"
- "fmt"
- "net"
- "github.com/btcsuite/btcd/btcec/v2"
- "github.com/btcsuite/btcd/btcutil"
- "github.com/btcsuite/btcd/wire"
- "github.com/lightningnetwork/lnd/autopilot"
- "github.com/lightningnetwork/lnd/chainreg"
- "github.com/lightningnetwork/lnd/funding"
- "github.com/lightningnetwork/lnd/lncfg"
- "github.com/lightningnetwork/lnd/lnwallet"
- "github.com/lightningnetwork/lnd/lnwire"
- "github.com/lightningnetwork/lnd/tor"
- )
- // validateAtplCfg 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.
- func validateAtplCfg(cfg *lncfg.AutoPilot) ([]*autopilot.WeightedHeuristic,
- 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())
- }
- availStr := fmt.Sprintf("Available heuristics are: [%v]", heuristicsStr)
- // 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.
- return nil, fmt.Errorf("heuristic %v not available. %v",
- 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 {
- 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
- }
- // chanController is an implementation of the autopilot.ChannelController
- // interface that's backed by a running lnd instance.
- type chanController struct {
- server *server
- private bool
- minConfs int32
- confTarget uint32
- chanMinHtlcIn lnwire.MilliSatoshi
- netParams chainreg.BitcoinNetParams
- }
- // 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,
- amt btcutil.Amount) error {
- // With the connection established, we'll now establish our connection
- // to the target peer, waiting for the first update before we exit.
- feePerKw, err := c.server.cc.FeeEstimator.EstimateFeePerKW(
- c.confTarget,
- )
- if err != nil {
- return err
- }
- // Construct the open channel request and send it to the server to begin
- // the funding workflow.
- req := &funding.InitFundingMsg{
- TargetPubkey: target,
- ChainHash: *c.netParams.GenesisHash,
- SubtractFees: true,
- LocalFundingAmt: amt,
- PushAmt: 0,
- MinHtlcIn: c.chanMinHtlcIn,
- FundingFeePerKw: feePerKw,
- Private: c.private,
- RemoteCsvDelay: 0,
- MinConfs: c.minConfs,
- MaxValueInFlight: 0,
- }
- 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
- }
- // A compile time assertion to ensure chanController meets the
- // autopilot.ChannelController interface.
- var _ autopilot.ChannelController = (*chanController)(nil)
- // 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
- // StartAgent method is called.
- func initAutoPilot(svr *server, cfg *lncfg.AutoPilot,
- minHTLCIn lnwire.MilliSatoshi, netParams chainreg.BitcoinNetParams) (
- *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)
- // Set up the constraints the autopilot heuristics must adhere to.
- 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
- }
- // With the heuristic itself created, we can now populate the remainder
- // of the items that the autopilot agent needs to perform its duties.
- self := svr.identityECDH.PubKey()
- pilotCfg := autopilot.Config{
- Self: self,
- Heuristic: weightedAttachment,
- ChanController: &chanController{
- server: svr,
- private: cfg.Private,
- minConfs: cfg.MinConfs,
- confTarget: cfg.ConfTarget,
- chanMinHtlcIn: minHTLCIn,
- netParams: netParams,
- },
- WalletBalance: func() (btcutil.Amount, error) {
- return svr.cc.Wallet.ConfirmedBalance(
- cfg.MinConfs, lnwallet.DefaultAccountName,
- )
- },
- Graph: autopilot.ChannelGraphFromDatabase(svr.graphDB),
- Constraints: atplConstraints,
- 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
- }
- // 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")
- }
- atplLog.Tracef("Attempting to connect to %x",
- target.SerializeCompressed())
- lnAddr := &lnwire.NetAddress{
- IdentityKey: target,
- ChainNet: netParams.Net,
- }
- // 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)
- }
- err := svr.ConnectToPeer(
- lnAddr, false, svr.cfg.ConnectionTimeout,
- )
- 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 {
- return false, errors.New("exhausted all " +
- "advertised addresses")
- }
- return false, nil
- },
- DisconnectPeer: svr.DisconnectPeer,
- }
- // Create and return the autopilot.ManagerCfg that administrates this
- // agent-pilot instance.
- return &autopilot.ManagerCfg{
- Self: self,
- PilotCfg: &pilotCfg,
- ChannelState: func() ([]autopilot.LocalChannel, error) {
- // We'll fetch the current state of open
- // channels from the database to use as initial
- // state for the auto-pilot agent.
- activeChannels, err := svr.chanStateDB.FetchAllChannels()
- if err != nil {
- return nil, err
- }
- chanState := make([]autopilot.LocalChannel,
- len(activeChannels))
- for i, channel := range activeChannels {
- localCommit := channel.LocalCommitment
- balance := localCommit.LocalBalance.ToSatoshis()
- chanState[i] = autopilot.LocalChannel{
- ChanID: channel.ShortChanID(),
- Balance: balance,
- Node: autopilot.NewNodeID(
- channel.IdentityPub,
- ),
- }
- }
- return chanState, nil
- },
- ChannelInfo: func(chanPoint wire.OutPoint) (
- *autopilot.LocalChannel, error) {
- channel, err := svr.chanStateDB.FetchChannel(nil, 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
- },
- SubscribeTransactions: svr.cc.Wallet.SubscribeTransactions,
- SubscribeTopology: svr.chanRouter.SubscribeTopology,
- }, nil
- }
|