From e3285daf5b18e80c8a32b1b49ba24fe5f2b19976 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Sun, 28 Apr 2024 16:03:01 +0200 Subject: [PATCH] triggerforceclose: support Tor connections --- cmd/chantools/triggerforceclose.go | 67 +++++++++++++++++++++++------- doc/chantools_triggerforceclose.md | 1 + 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/cmd/chantools/triggerforceclose.go b/cmd/chantools/triggerforceclose.go index 53492ec..366a046 100644 --- a/cmd/chantools/triggerforceclose.go +++ b/cmd/chantools/triggerforceclose.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "net" "strconv" "strings" "time" @@ -16,12 +15,15 @@ import ( "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/peer" "github.com/lightningnetwork/lnd/tor" "github.com/spf13/cobra" ) var ( dialTimeout = time.Minute + + defaultTorDNSHostPort = "soa.nodes.lightning.directory:53" ) type triggerForceCloseCommand struct { @@ -30,6 +32,8 @@ type triggerForceCloseCommand struct { APIURL string + TorProxy string + rootKey *rootKey cmd *cobra.Command } @@ -63,6 +67,10 @@ does not properly respond to a Data Loss Protection re-establish message).'`, &cc.APIURL, "apiurl", defaultAPIURL, "API URL to use (must "+ "be esplora compatible)", ) + cc.cmd.Flags().StringVar( + &cc.TorProxy, "torproxy", "", "SOCKS5 proxy to use for Tor "+ + "connections (to .onion addresses)", + ) cc.rootKey = newRootKey(cc.cmd, "deriving the identity key") return cc.cmd @@ -94,7 +102,9 @@ func (c *triggerForceCloseCommand) Execute(_ *cobra.Command, _ []string) error { return fmt.Errorf("error parsing channel point: %w", err) } - err = requestForceClose(c.Peer, pubKey, outPoint, identityECDH) + err = requestForceClose( + c.Peer, c.TorProxy, pubKey, outPoint, identityECDH, + ) if err != nil { return fmt.Errorf("error requesting force close: %w", err) } @@ -134,34 +144,44 @@ func noiseDial(idKey keychain.SingleKeyECDH, lnAddr *lnwire.NetAddress, return brontide.Dial(idKey, lnAddr, timeout, netCfg.Dial) } -func requestForceClose(peerHost string, peerPubKey *btcec.PublicKey, - channelPoint *wire.OutPoint, identity keychain.SingleKeyECDH) error { +func connectPeer(peerHost, torProxy string, peerPubKey *btcec.PublicKey, + identity keychain.SingleKeyECDH, + dialTimeout time.Duration) (*peer.Brontide, error) { + + var dialNet tor.Net = &tor.ClearNet{} + if torProxy != "" { + dialNet = &tor.ProxyNet{ + SOCKS: torProxy, + DNS: defaultTorDNSHostPort, + StreamIsolation: false, + SkipProxyForClearNetTargets: true, + } + } + log.Debugf("Attempting to resolve peer address %v", peerHost) peerAddr, err := lncfg.ParseLNAddressString( - peerHost, "9735", net.ResolveTCPAddr, + peerHost, "9735", dialNet.ResolveTCPAddr, ) if err != nil { - return fmt.Errorf("error parsing peer address: %w", err) + return nil, fmt.Errorf("error parsing peer address: %w", err) } - channelID := lnwire.NewChanIDFromOutPoint(channelPoint) - - conn, err := noiseDial( - identity, peerAddr, &tor.ClearNet{}, dialTimeout, - ) + log.Debugf("Attempting to dial resolved peer address %v", + peerAddr.String()) + conn, err := noiseDial(identity, peerAddr, dialNet, dialTimeout) if err != nil { - return fmt.Errorf("error dialing peer: %w", err) + return nil, fmt.Errorf("error dialing peer: %w", err) } - log.Infof("Attempting to connect to peer %x, dial timeout is %v", - peerPubKey.SerializeCompressed(), dialTimeout) + log.Infof("Attempting to establish p2p connection to peer %x, dial"+ + "timeout is %v", peerPubKey.SerializeCompressed(), dialTimeout) req := &connmgr.ConnReq{ Addr: peerAddr, Permanent: false, } p, err := lnd.ConnectPeer(conn, req, chainParams, identity) if err != nil { - return fmt.Errorf("error connecting to peer: %w", err) + return nil, fmt.Errorf("error connecting to peer: %w", err) } log.Infof("Connection established to peer %x", @@ -171,10 +191,25 @@ func requestForceClose(peerHost string, peerPubKey *btcec.PublicKey, select { case <-p.ActiveSignal(): case <-p.QuitSignal(): - return fmt.Errorf("peer %x disconnected", + return nil, fmt.Errorf("peer %x disconnected", peerPubKey.SerializeCompressed()) } + return p, nil +} + +func requestForceClose(peerHost, torProxy string, peerPubKey *btcec.PublicKey, + channelPoint *wire.OutPoint, identity keychain.SingleKeyECDH) error { + + p, err := connectPeer( + peerHost, torProxy, peerPubKey, identity, dialTimeout, + ) + if err != nil { + return fmt.Errorf("error connecting to peer: %w", err) + } + + channelID := lnwire.NewChanIDFromOutPoint(channelPoint) + // Channel ID (32 byte) + u16 for the data length (which will be 0). data := make([]byte, 34) copy(data[:32], channelID[:]) diff --git a/doc/chantools_triggerforceclose.md b/doc/chantools_triggerforceclose.md index 58bd4e3..695752c 100644 --- a/doc/chantools_triggerforceclose.md +++ b/doc/chantools_triggerforceclose.md @@ -30,6 +30,7 @@ chantools triggerforceclose \ -h, --help help for triggerforceclose --peer string remote peer address (@[:]) --rootkey string BIP32 HD root key of the wallet to use for deriving the identity key; leave empty to prompt for lnd 24 word aezeed + --torproxy string SOCKS5 proxy to use for Tor connections (to .onion addresses) --walletdb string read the seed/master root key to use fro deriving the identity key from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ```