diff --git a/electrum/client.go b/electrum/client.go index 1fa783d..790c2aa 100644 --- a/electrum/client.go +++ b/electrum/client.go @@ -36,6 +36,7 @@ type Client struct { nextRequestID int conn net.Conn log *utils.Logger + requireTls bool } // Request models the structure of all Electrum protocol requests. @@ -110,9 +111,10 @@ type ServerFeatures struct { type Param = interface{} // NewClient creates an initialized Client instance. -func NewClient() *Client { +func NewClient(requireTls bool) *Client { return &Client{ - log: utils.NewLogger(defaultLoggerTag), + log: utils.NewLogger(defaultLoggerTag), + requireTls: requireTls, } } @@ -334,6 +336,9 @@ func (c *Client) ListUnspentBatch(indexHashes []string) ([][]UnspentRef, error) } func (c *Client) establishConnection() error { + // We first try to connect over TCP+TLS + // If we fail and requireTls is false, we try over TCP + // TODO: check if insecure is necessary config := &tls.Config{ InsecureSkipVerify: true, @@ -343,12 +348,22 @@ func (c *Client) establishConnection() error { Timeout: connectionTimeout, } - conn, err := tls.DialWithDialer(dialer, "tcp", c.Server, config) + tlsConn, err := tls.DialWithDialer(dialer, "tcp", c.Server, config) + if err == nil { + c.conn = tlsConn + return nil + } + if c.requireTls { + return err + } + + conn, err := net.DialTimeout("tcp", c.Server, connectionTimeout) if err != nil { return err } c.conn = conn + return nil } diff --git a/electrum/pool.go b/electrum/pool.go index d728bb3..c05e3ce 100644 --- a/electrum/pool.go +++ b/electrum/pool.go @@ -7,11 +7,11 @@ type Pool struct { } // NewPool creates an initialized Pool with a `size` number of clients. -func NewPool(size int) *Pool { +func NewPool(size int, requireTls bool) *Pool { nextClient := make(chan *Client, size) for i := 0; i < size; i++ { - nextClient <- NewClient() + nextClient <- NewClient(requireTls) } return &Pool{nextClient} diff --git a/electrum/servers.go b/electrum/servers.go index 2d385a7..598f284 100644 --- a/electrum/servers.go +++ b/electrum/servers.go @@ -5,17 +5,21 @@ import "sync/atomic" // ServerProvider manages a rotating server list, from which callers can pull server addresses. type ServerProvider struct { nextIndex int32 + servers []string } // NewServerProvider returns an initialized ServerProvider. -func NewServerProvider() *ServerProvider { - return &ServerProvider{-1} +func NewServerProvider(servers []string) *ServerProvider { + return &ServerProvider{ + nextIndex: -1, + servers: servers, + } } // NextServer returns an address from the rotating list. It's thread-safe. func (p *ServerProvider) NextServer() string { index := int(atomic.AddInt32(&p.nextIndex, 1)) - return PublicServers[index%len(PublicServers)] + return p.servers[index%len(p.servers)] } // PublicServers list. @@ -29,72 +33,73 @@ func (p *ServerProvider) NextServer() string { // See `cmd/survey/main.go` var PublicServers = []string{ // Fast servers with batching - "blackie.c3-soft.com:57002", // impl: Fulcrum 1.6.0, batching: true, ttc: 0.72, speed: 97, from: fortress.qtornado.com:443 - "fullnode.titanconnect.ca:50002", // impl: Fulcrum 1.6.0, batching: true, ttc: 0.66, speed: 86, from: fortress.qtornado.com:443 - "de.poiuty.com:50002", // impl: Fulcrum 1.6.0, batching: true, ttc: 0.76, speed: 78, from: fortress.qtornado.com:443 - "2ex.digitaleveryware.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.68, speed: 68, from: fortress.qtornado.com:443 - "fortress.qtornado.com:443", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.77, speed: 67, from: - "hodlers.beer:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.74, speed: 64, from: fortress.qtornado.com:443 - "f.keff.org:50002", // impl: Fulcrum 1.6.0, batching: true, ttc: 0.76, speed: 60, from: fortress.qtornado.com:443 - "e2.keff.org:50002", // impl: Fulcrum 1.6.0, batching: true, ttc: 0.75, speed: 58, from: - "electrum.stippy.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.75, speed: 56, from: fortress.qtornado.com:443 - "electrum.privateservers.network:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 0.83, speed: 52, from: fortress.qtornado.com:443 - "fulcrum.grey.pw:51002", // impl: Fulcrum 1.6.0, batching: true, ttc: 0.79, speed: 49, from: fortress.qtornado.com:443 - "btc.electroncash.dk:60002", // impl: Fulcrum 1.6.0, batching: true, ttc: 0.79, speed: 49, from: fortress.qtornado.com:443 - "node.degga.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.59, speed: 46, from: fortress.qtornado.com:443 - "e.keff.org:50002", // impl: ElectrumX 1.10.0, batching: true, ttc: 0.76, speed: 45, from: - "bitcoin.aranguren.org:50002", // impl: Fulcrum 1.6.0, batching: true, ttc: 1.25, speed: 41, from: - "electrum.helali.me:50002", // impl: Fulcrum 1.6.0, batching: true, ttc: 1.20, speed: 38, from: fortress.qtornado.com:443 - "node1.btccuracao.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.79, speed: 33, from: fortress.qtornado.com:443 - "ASSUREDLY.not.fyi:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.66, speed: 20, from: electrumx.erbium.eu:50002 - "assuredly.not.fyi:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.74, speed: 20, from: fortress.qtornado.com:443 + "electrum.coinext.com.br:50002", // impl: ElectrumX 1.14.0, batching: true, ttc: 0.15, speed: 113, from: + "fulcrum.sethforprivacy.com:50002", // impl: Fulcrum 1.9.0, batching: true, ttc: 0.55, speed: 96, from: + "mainnet.foundationdevices.com:50002", // impl: Fulcrum 1.8.2, batching: true, ttc: 0.54, speed: 88, from: + "btc.lastingcoin.net:50002", // impl: Fulcrum 1.7.0, batching: true, ttc: 0.74, speed: 73, from: + "vmd71287.contaboserver.net:50002", // impl: Fulcrum 1.9.0, batching: true, ttc: 0.60, speed: 70, from: + "de.poiuty.com:50002", // impl: Fulcrum 1.8.2, batching: true, ttc: 0.87, speed: 70, from: + "electrum.jochen-hoenicke.de:50006", // impl: Fulcrum 1.8.2, batching: true, ttc: 0.83, speed: 69, from: + "btc.cr.ypto.tech:50002", // impl: Fulcrum 1.9.0, batching: true, ttc: 0.81, speed: 65, from: + "e.keff.org:50002", // impl: Fulcrum 1.8.2, batching: true, ttc: 0.82, speed: 65, from: + "vmd104014.contaboserver.net:50002", // impl: Fulcrum 1.9.0, batching: true, ttc: 0.58, speed: 64, from: + "e2.keff.org:50002", // impl: Fulcrum 1.8.2, batching: true, ttc: 0.83, speed: 64, from: + "fulcrum.grey.pw:51002", // impl: Fulcrum 1.9.0, batching: true, ttc: 0.81, speed: 63, from: + "fortress.qtornado.com:443", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.84, speed: 62, from: + "f.keff.org:50002", // impl: Fulcrum 1.8.2, batching: true, ttc: 0.89, speed: 62, from: + "2ex.digitaleveryware.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.71, speed: 61, from: + "electrum.petrkr.net:50002", // impl: Fulcrum 1.9.0, batching: true, ttc: 0.84, speed: 58, from: + "electrum.stippy.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.80, speed: 57, from: + "electrum0.snel.it:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.80, speed: 56, from: + "ru.poiuty.com:50002", // impl: Fulcrum 1.8.2, batching: true, ttc: 0.99, speed: 56, from: + "electrum.privateservers.network:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 0.85, speed: 49, from: + "btc.electroncash.dk:60002", // impl: Fulcrum 1.9.0, batching: true, ttc: 0.92, speed: 48, from: + "bitcoin.aranguren.org:50002", // impl: Fulcrum 1.8.2, batching: true, ttc: 1.19, speed: 48, from: + "electrum.bitcoinserver.nl:50514", // impl: Fulcrum 1.8.1, batching: true, ttc: 0.85, speed: 44, from: + "btc.prompt.cash:61002", // impl: Fulcrum 1.8.1, batching: true, ttc: 1.22, speed: 44, from: + "fulc.bot.nu:50002", // impl: Fulcrum 1.7.0, batching: true, ttc: 1.04, speed: 35, from: + "bolt.schulzemic.net:50002", // impl: Fulcrum 1.8.2, batching: true, ttc: 0.96, speed: 33, from: + "node1.btccuracao.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.90, speed: 25, from: // Other servers - "smmalis37.ddns.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.56, speed: 19, from: fortress.qtornado.com:443 - "electrumx.papabyte.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.95, speed: 19, from: fortress.qtornado.com:443 - "electrum.bitaroo.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.98, speed: 19, from: fortress.qtornado.com:443 - "electrum.exan.tech:443", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.08, speed: 19, from: fortress.qtornado.com:443 - "electrum-fulcrum.toggen.net:50002", // impl: Fulcrum 1.6.0, batching: true, ttc: 1.47, speed: 16, from: fortress.qtornado.com:443 - "vmd84592.contaboserver.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.79, speed: 14, from: fortress.qtornado.com:443 - "gd42.org:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.50, speed: 12, from: fortress.qtornado.com:443 - "vmd63185.contaboserver.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.75, speed: 12, from: fortress.qtornado.com:443 - "alfa.cryptotrek.online:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.75, speed: 12, from: fortress.qtornado.com:443 - "electrumx.ultracloud.tk:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.76, speed: 12, from: fortress.qtornado.com:443 - "electrum.brainshome.de:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.89, speed: 12, from: fortress.qtornado.com:443 - "ragtor.duckdns.org:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.91, speed: 12, from: bitcoin.aranguren.org:50002 - "94.23.247.135:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 6.80, speed: 12, from: fortress.qtornado.com:443 - "eai.coincited.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.12, speed: 11, from: electrumx.erbium.eu:50002 - "142.93.6.38:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.56, speed: 11, from: fortress.qtornado.com:443 - "157.245.172.236:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.62, speed: 11, from: fortress.qtornado.com:443 - "electrum.kendigisland.xyz:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.68, speed: 11, from: fortress.qtornado.com:443 - "btc.lastingcoin.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.74, speed: 11, from: fortress.qtornado.com:443 - "xtrum.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.75, speed: 11, from: electrumx.erbium.eu:50002 - "blkhub.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.76, speed: 11, from: fortress.qtornado.com:443 - "electrumx.erbium.eu:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.78, speed: 11, from: - "185.64.116.15:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.79, speed: 11, from: bitcoin.aranguren.org:50002 - "188.165.206.215:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.81, speed: 11, from: fortress.qtornado.com:443 - "ex03.axalgo.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.92, speed: 11, from: fortress.qtornado.com:443 - "electrum.bitcoinlizard.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.98, speed: 11, from: fortress.qtornado.com:443 - "btce.iiiiiii.biz:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.02, speed: 11, from: fortress.qtornado.com:443 - "btc.ocf.sh:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.15, speed: 11, from: fortress.qtornado.com:443 - "68.183.188.105:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.36, speed: 11, from: fortress.qtornado.com:443 - "vmd71287.contaboserver.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.60, speed: 10, from: fortress.qtornado.com:443 - "2electrumx.hopto.me:56022", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.89, speed: 10, from: fortress.qtornado.com:443 - "electrum.emzy.de:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.15, speed: 10, from: - "electrumx.electricnewyear.net:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 0.66, speed: 9, from: - "walle.dedyn.io:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.76, speed: 8, from: fortress.qtornado.com:443 - "caleb.vegas:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.80, speed: 8, from: fortress.qtornado.com:443 - "electrum.neocrypto.io:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.69, speed: 7, from: fortress.qtornado.com:443 - "guichet.centure.cc:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.71, speed: 7, from: fortress.qtornado.com:443 - "167.172.42.31:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.75, speed: 7, from: fortress.qtornado.com:443 - "2AZZARITA.hopto.org:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.77, speed: 7, from: fortress.qtornado.com:443 - "jonas.reptiles.se:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.95, speed: 7, from: fortress.qtornado.com:443 - "167.172.226.175:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.61, speed: 6, from: fortress.qtornado.com:443 - "electrum-btc.leblancnet.us:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 0.64, speed: 6, from: fortress.qtornado.com:443 - "104.248.139.211:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.90, speed: 6, from: fortress.qtornado.com:443 - "elx.bitske.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.01, speed: 6, from: fortress.qtornado.com:443 - "kareoke.qoppa.org:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.18, speed: 6, from: electrumx.erbium.eu:50002 - "ex.btcmp.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.12, speed: 5, from: fortress.qtornado.com:443 - "bitcoins.sk:56002", // impl: ElectrumX 1.14.0, batching: true, ttc: 0.77, speed: 3, from: fortress.qtornado.com:443 - "73.92.198.54:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 5.57, speed: 0, from: fortress.qtornado.com:443 + "xtrum.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.91, speed: 19, from: fulcrum.sethforprivacy.com:50002 + "electrum.bitaroo.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.04, speed: 19, from: + "btce.iiiiiii.biz:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.07, speed: 19, from: electrum.coinext.com.br:50002 + "electrum.emzy.de:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.17, speed: 19, from: + "alviss.coinjoined.com:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 1.15, speed: 17, from: + "2AZZARITA.hopto.org:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.81, speed: 16, from: electrum.coinext.com.br:50002 + "vmd104012.contaboserver.net:50002", // impl: Fulcrum 1.9.0, batching: true, ttc: 1.25, speed: 16, from: + "electrum.bitcoinlizard.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 4.99, speed: 14, from: + "btc.ocf.sh:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.19, speed: 12, from: + "bitcoins.sk:56002", // impl: ElectrumX 1.14.0, batching: true, ttc: 0.80, speed: 11, from: + "electrum-btc.leblancnet.us:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.04, speed: 11, from: + "helicarrier.bauerj.eu:50002", // impl: ElectrumX 1.10.0, batching: true, ttc: 0.96, speed: 10, from: + "electrum.neocrypto.io:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.69, speed: 7, from: + "caleb.vegas:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.71, speed: 7, from: + "smmalis37.ddns.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.77, speed: 7, from: + "2azzarita.hopto.org:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.78, speed: 7, from: fulcrum.sethforprivacy.com:50002 + "electrum.kendigisland.xyz:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.79, speed: 7, from: + "electrum.hsmiths.com:50002", // impl: ElectrumX 1.10.0, batching: true, ttc: 0.90, speed: 7, from: + "vmd63185.contaboserver.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.92, speed: 7, from: + "blkhub.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.94, speed: 7, from: + "electrum.mmitech.info:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.95, speed: 7, from: + "elx.bitske.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.17, speed: 7, from: + "bitcoin.lu.ke:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.21, speed: 7, from: + "ex05.axalgo.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.21, speed: 7, from: + "walle.dedyn.io:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.23, speed: 7, from: + "eai.coincited.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.34, speed: 6, from: + "2electrumx.hopto.me:56022", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.93, speed: 6, from: + "hodlers.beer:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.03, speed: 6, from: + "kareoke.qoppa.org:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.13, speed: 6, from: + "ASSUREDLY.not.fyi:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.85, speed: 4, from: + "electrumx.alexridevski.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.87, speed: 4, from: + "assuredly.not.fyi:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.90, speed: 4, from: + "ragtor.duckdns.org:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.07, speed: 4, from: + "surely.not.fyi:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.20, speed: 4, from: + "btc.electrum.bitbitnet.net:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 0.71, speed: 3, from: + "gods-of-rock.screaminglemur.net:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 0.92, speed: 3, from: + "SURELY.not.fyi:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.35, speed: 3, from: + "horsey.cryptocowboys.net:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 0.61, speed: 2, from: + "electrumx-btc.cryptonermal.net:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 0.83, speed: 1, from: + "electrum.coineuskal.com:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 1.73, speed: 0, from: electrum.coinext.com.br:50002 } diff --git a/main.go b/main.go index c80f060..5514feb 100644 --- a/main.go +++ b/main.go @@ -14,19 +14,31 @@ import ( "github.com/muun/libwallet" "github.com/muun/libwallet/btcsuitew/btcutilw" "github.com/muun/libwallet/emergencykit" + "github.com/muun/recovery/electrum" "github.com/muun/recovery/scanner" "github.com/muun/recovery/utils" ) +const electrumPoolSize = 6 + var debugOutputStream = bytes.NewBuffer(nil) +type config struct { + generateContacts bool + providedElectrum string + usesProvidedElectrum bool + onlyScan bool +} + func main() { utils.SetOutputStream(debugOutputStream) - var generateContacts bool + var config config // Pick up command-line arguments: - flag.BoolVar(&generateContacts, "generate-contacts", false, "Generate contact addresses") + flag.BoolVar(&config.generateContacts, "generate-contacts", false, "Generate contact addresses") + flag.StringVar(&config.providedElectrum, "electrum-server", "", "Connect to this electrum server to find funds") + flag.BoolVar(&config.onlyScan, "only-scan", false, "Only scan for UTXOs without generating a transaction") flag.Usage = printUsage flag.Parse() args := flag.Args() @@ -40,6 +52,11 @@ func main() { // Welcome! printWelcomeMessage() + config.usesProvidedElectrum = len(strings.TrimSpace(config.providedElectrum)) > 0 + if config.usesProvidedElectrum { + validateProvidedElectrum(config.providedElectrum) + } + // We're going to need a few things to move forward with the recovery process. Let's make a list // so we keep them in mind: var recoveryCode string @@ -62,25 +79,41 @@ func main() { decryptedKeys[0].Key.Path = "m/1'/1'" // a little adjustment for legacy users. - // Finally, we need the destination address to sweep the funds: - destinationAddress = readAddress() + if !config.onlyScan { + // Finally, we need the destination address to sweep the funds: + destinationAddress = readAddress() + } sayBlock(` Starting scan of all possible addresses. This will take a few minutes. `) - doRecovery(decryptedKeys, destinationAddress, generateContacts) + doRecovery(decryptedKeys, destinationAddress, config) sayBlock("We appreciate all kinds of feedback. If you have any, send it to {blue contact@muun.com}\n") } // doRecovery runs the scan & sweep process, and returns the ID of the broadcasted transaction. func doRecovery( - decryptedKeys []*libwallet.DecryptedPrivateKey, destinationAddress btcutil.Address, generateContacts bool, + decryptedKeys []*libwallet.DecryptedPrivateKey, + destinationAddress btcutil.Address, + config config, ) { - addrGen := NewAddressGenerator(decryptedKeys[0].Key, decryptedKeys[1].Key, generateContacts) - utxoScanner := scanner.NewScanner() + addrGen := NewAddressGenerator(decryptedKeys[0].Key, decryptedKeys[1].Key, config.generateContacts) + + var electrumProvider *electrum.ServerProvider + if config.usesProvidedElectrum { + electrumProvider = electrum.NewServerProvider([]string{ + config.providedElectrum, + }) + } else { + electrumProvider = electrum.NewServerProvider(electrum.PublicServers) + } + + connectionPool := electrum.NewPool(electrumPoolSize, !config.usesProvidedElectrum) + + utxoScanner := scanner.NewScanner(connectionPool, electrumProvider) addresses := addrGen.Stream() @@ -122,6 +155,9 @@ func doRecovery( } say("\n— {white %d} sats total\n", total) + if config.onlyScan { + return + } txOutputAmount, txWeightInBytes, err := sweeper.GetSweepTxAmountAndWeightInBytes(utxos) if err != nil { @@ -145,11 +181,36 @@ func doRecovery( sayBlock(` Transaction sent! You can check the status here: https://mempool.space/tx/%v - (it will appear in Blockstream after a short delay) + (it will appear in mempool.space after a short delay) `, sweepTx.TxHash().String()) } +func validateProvidedElectrum(providedElectrum string) { + client := electrum.NewClient(false) + err := client.Connect(providedElectrum) + defer func(client *electrum.Client) { + _ = client.Disconnect() + }(client) + + if err != nil { + sayBlock(` + {red Error!} + The Recovery Tool couldn't connect to the provided Electrum server %v. + + If the problem persists, contact {blue support@muun.com}. + + ――― {white error report} ――― + %v + ―――――――――――――――――――― + + We're always there to help. + `, providedElectrum, err) + + os.Exit(2) + } +} + func exitWithError(err error) { sayBlock(` {red Error!} @@ -351,7 +412,7 @@ func readAddress() btcutil.Address { func readFee(totalBalance, weight int64) int64 { sayBlock(` {yellow Enter the fee rate (sats/byte)} - Your transaction weighs %v bytes. You can get suggestions in https://bitcoinfees.earn.com/#fees + Your transaction weighs %v bytes. You can get suggestions in https://mempool.space/ under "Transaction fees". `, weight) var userInput string diff --git a/scanner/scanner.go b/scanner/scanner.go index f9da0dc..ffce52a 100644 --- a/scanner/scanner.go +++ b/scanner/scanner.go @@ -9,7 +9,6 @@ import ( "github.com/muun/recovery/utils" ) -const electrumPoolSize = 6 const taskTimeout = 15 * time.Minute const batchSize = 100 @@ -70,10 +69,10 @@ type scanContext struct { } // NewScanner creates an initialized Scanner. -func NewScanner() *Scanner { +func NewScanner(connectionPool *electrum.Pool, electrumProvider *electrum.ServerProvider) *Scanner { return &Scanner{ - pool: electrum.NewPool(electrumPoolSize), - servers: electrum.NewServerProvider(), + pool: connectionPool, + servers: electrumProvider, log: utils.NewLogger("Scanner"), } } diff --git a/survey/survey.go b/survey/survey.go index 5024c2f..316bca8 100644 --- a/survey/survey.go +++ b/survey/survey.go @@ -47,7 +47,7 @@ type surveyTask struct { } // Values to check whether we're in the same chain (in a previous version, SV servers snuck in) -var mainnetSomeTx = "1712426823cc94935287a6834f7982723fbb5c808cbe00ec2cf3f582582be4c5" +var mainnetSomeTx = "985eb411473fa1bbd73efa5e3685edc00366c86b8d4d3f5b969ad59c23f4d959" var mainnetGenesisHash = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" func NewSurvey(config *Config) *Survey { @@ -194,7 +194,7 @@ func (s *Survey) processTask(task *surveyTask) *Result { // testConnection returns the server implementation, protocol version and time to connect func testConnection(task *surveyTask) (string, string, time.Duration, error) { - client := electrum.NewClient() + client := electrum.NewClient(true) start := time.Now() err := client.Connect(task.server) @@ -207,7 +207,7 @@ func testConnection(task *surveyTask) (string, string, time.Duration, error) { // testsBlockchain returns whether this server is operating on Bitcoin mainnet func testBitcoinMainnet(task *surveyTask) (bool, error) { - client := electrum.NewClient() + client := electrum.NewClient(true) err := client.Connect(task.server) if err != nil { @@ -229,7 +229,7 @@ func testBitcoinMainnet(task *surveyTask) (bool, error) { // testBatchSupport returns whether the server successfully responded to a batched request func testBatchSupport(task *surveyTask) (bool, error) { - client := electrum.NewClient() + client := electrum.NewClient(true) err := client.Connect(task.server) if err != nil { @@ -247,7 +247,7 @@ func testBatchSupport(task *surveyTask) (bool, error) { // measureSpeed returns the amount of successful ListUnspentBatch calls in SPEED_TEST_DURATION // seconds. It assumes batch support was verified beforehand. func (s *Survey) measureSpeed(task *surveyTask) (int, error) { - client := electrum.NewClient() + client := electrum.NewClient(true) err := client.Connect(task.server) if err != nil { @@ -273,7 +273,7 @@ func (s *Survey) measureSpeed(task *surveyTask) (int, error) { // getPeers returns the list of peers from a server, or empty if it doesn't responds to the request func getPeers(task *surveyTask) ([]string, error) { - client := electrum.NewClient() + client := electrum.NewClient(true) err := client.Connect(task.server) if err != nil { diff --git a/sweeper.go b/sweeper.go index a9455e1..dec01fa 100644 --- a/sweeper.go +++ b/sweeper.go @@ -54,8 +54,8 @@ func (s *Sweeper) BuildSweepTx(utxos []*scanner.Utxo, fee int64) (*wire.MsgTx, e func (s *Sweeper) BroadcastTx(tx *wire.MsgTx) error { // Connect to an Electurm server using a fresh client and provider pair: - sp := electrum.NewServerProvider() // TODO create servers module, for provider and pool - client := electrum.NewClient() + sp := electrum.NewServerProvider(electrum.PublicServers) // TODO create servers module, for provider and pool + client := electrum.NewClient(true) for !client.IsConnected() { client.Connect(sp.NextServer()) diff --git a/version.go b/version.go index 1ff5fe2..bc074c3 100644 --- a/version.go +++ b/version.go @@ -3,4 +3,4 @@ package main // The OSS tool depends on this file with this content. If you need to change // it, change the tool too. -const version = "2.2.3" +const version = "2.2.4"