commit
124e9fa1bc
|
@ -36,6 +36,7 @@ type Client struct {
|
||||||
nextRequestID int
|
nextRequestID int
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
log *utils.Logger
|
log *utils.Logger
|
||||||
|
requireTls bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request models the structure of all Electrum protocol requests.
|
// Request models the structure of all Electrum protocol requests.
|
||||||
|
@ -110,9 +111,10 @@ type ServerFeatures struct {
|
||||||
type Param = interface{}
|
type Param = interface{}
|
||||||
|
|
||||||
// NewClient creates an initialized Client instance.
|
// NewClient creates an initialized Client instance.
|
||||||
func NewClient() *Client {
|
func NewClient(requireTls bool) *Client {
|
||||||
return &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 {
|
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
|
// TODO: check if insecure is necessary
|
||||||
config := &tls.Config{
|
config := &tls.Config{
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
|
@ -343,12 +348,22 @@ func (c *Client) establishConnection() error {
|
||||||
Timeout: connectionTimeout,
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.conn = conn
|
c.conn = conn
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,11 @@ type Pool struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPool creates an initialized Pool with a `size` number of clients.
|
// 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)
|
nextClient := make(chan *Client, size)
|
||||||
|
|
||||||
for i := 0; i < size; i++ {
|
for i := 0; i < size; i++ {
|
||||||
nextClient <- NewClient()
|
nextClient <- NewClient(requireTls)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Pool{nextClient}
|
return &Pool{nextClient}
|
||||||
|
|
|
@ -5,17 +5,21 @@ import "sync/atomic"
|
||||||
// ServerProvider manages a rotating server list, from which callers can pull server addresses.
|
// ServerProvider manages a rotating server list, from which callers can pull server addresses.
|
||||||
type ServerProvider struct {
|
type ServerProvider struct {
|
||||||
nextIndex int32
|
nextIndex int32
|
||||||
|
servers []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServerProvider returns an initialized ServerProvider.
|
// NewServerProvider returns an initialized ServerProvider.
|
||||||
func NewServerProvider() *ServerProvider {
|
func NewServerProvider(servers []string) *ServerProvider {
|
||||||
return &ServerProvider{-1}
|
return &ServerProvider{
|
||||||
|
nextIndex: -1,
|
||||||
|
servers: servers,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NextServer returns an address from the rotating list. It's thread-safe.
|
// NextServer returns an address from the rotating list. It's thread-safe.
|
||||||
func (p *ServerProvider) NextServer() string {
|
func (p *ServerProvider) NextServer() string {
|
||||||
index := int(atomic.AddInt32(&p.nextIndex, 1))
|
index := int(atomic.AddInt32(&p.nextIndex, 1))
|
||||||
return PublicServers[index%len(PublicServers)]
|
return p.servers[index%len(p.servers)]
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicServers list.
|
// PublicServers list.
|
||||||
|
@ -29,72 +33,73 @@ func (p *ServerProvider) NextServer() string {
|
||||||
// See `cmd/survey/main.go`
|
// See `cmd/survey/main.go`
|
||||||
var PublicServers = []string{
|
var PublicServers = []string{
|
||||||
// Fast servers with batching
|
// 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
|
"electrum.coinext.com.br:50002", // impl: ElectrumX 1.14.0, batching: true, ttc: 0.15, speed: 113, from:
|
||||||
"fullnode.titanconnect.ca:50002", // impl: Fulcrum 1.6.0, batching: true, ttc: 0.66, speed: 86, from: fortress.qtornado.com:443
|
"fulcrum.sethforprivacy.com:50002", // impl: Fulcrum 1.9.0, batching: true, ttc: 0.55, speed: 96, from:
|
||||||
"de.poiuty.com:50002", // impl: Fulcrum 1.6.0, batching: true, ttc: 0.76, speed: 78, from: fortress.qtornado.com:443
|
"mainnet.foundationdevices.com:50002", // impl: Fulcrum 1.8.2, batching: true, ttc: 0.54, speed: 88, from:
|
||||||
"2ex.digitaleveryware.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.68, speed: 68, from: fortress.qtornado.com:443
|
"btc.lastingcoin.net:50002", // impl: Fulcrum 1.7.0, batching: true, ttc: 0.74, speed: 73, from:
|
||||||
"fortress.qtornado.com:443", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.77, speed: 67, from:
|
"vmd71287.contaboserver.net:50002", // impl: Fulcrum 1.9.0, batching: true, ttc: 0.60, speed: 70, from:
|
||||||
"hodlers.beer:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.74, speed: 64, from: fortress.qtornado.com:443
|
"de.poiuty.com:50002", // impl: Fulcrum 1.8.2, batching: true, ttc: 0.87, speed: 70, from:
|
||||||
"f.keff.org:50002", // impl: Fulcrum 1.6.0, batching: true, ttc: 0.76, speed: 60, from: fortress.qtornado.com:443
|
"electrum.jochen-hoenicke.de:50006", // impl: Fulcrum 1.8.2, batching: true, ttc: 0.83, speed: 69, from:
|
||||||
"e2.keff.org:50002", // impl: Fulcrum 1.6.0, batching: true, ttc: 0.75, speed: 58, from:
|
"btc.cr.ypto.tech:50002", // impl: Fulcrum 1.9.0, batching: true, ttc: 0.81, speed: 65, from:
|
||||||
"electrum.stippy.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.75, speed: 56, from: fortress.qtornado.com:443
|
"e.keff.org:50002", // impl: Fulcrum 1.8.2, batching: true, ttc: 0.82, speed: 65, from:
|
||||||
"electrum.privateservers.network:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 0.83, speed: 52, from: fortress.qtornado.com:443
|
"vmd104014.contaboserver.net:50002", // impl: Fulcrum 1.9.0, batching: true, ttc: 0.58, speed: 64, from:
|
||||||
"fulcrum.grey.pw:51002", // impl: Fulcrum 1.6.0, batching: true, ttc: 0.79, speed: 49, from: fortress.qtornado.com:443
|
"e2.keff.org:50002", // impl: Fulcrum 1.8.2, batching: true, ttc: 0.83, speed: 64, from:
|
||||||
"btc.electroncash.dk:60002", // impl: Fulcrum 1.6.0, batching: true, ttc: 0.79, speed: 49, from: fortress.qtornado.com:443
|
"fulcrum.grey.pw:51002", // impl: Fulcrum 1.9.0, batching: true, ttc: 0.81, speed: 63, from:
|
||||||
"node.degga.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.59, speed: 46, from: fortress.qtornado.com:443
|
"fortress.qtornado.com:443", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.84, speed: 62, from:
|
||||||
"e.keff.org:50002", // impl: ElectrumX 1.10.0, batching: true, ttc: 0.76, speed: 45, from:
|
"f.keff.org:50002", // impl: Fulcrum 1.8.2, batching: true, ttc: 0.89, speed: 62, from:
|
||||||
"bitcoin.aranguren.org:50002", // impl: Fulcrum 1.6.0, batching: true, ttc: 1.25, speed: 41, from:
|
"2ex.digitaleveryware.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.71, speed: 61, from:
|
||||||
"electrum.helali.me:50002", // impl: Fulcrum 1.6.0, batching: true, ttc: 1.20, speed: 38, from: fortress.qtornado.com:443
|
"electrum.petrkr.net:50002", // impl: Fulcrum 1.9.0, batching: true, ttc: 0.84, speed: 58, from:
|
||||||
"node1.btccuracao.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.79, speed: 33, from: fortress.qtornado.com:443
|
"electrum.stippy.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.80, speed: 57, from:
|
||||||
"ASSUREDLY.not.fyi:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.66, speed: 20, from: electrumx.erbium.eu:50002
|
"electrum0.snel.it:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.80, speed: 56, from:
|
||||||
"assuredly.not.fyi:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.74, speed: 20, from: fortress.qtornado.com:443
|
"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
|
// Other servers
|
||||||
"smmalis37.ddns.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.56, speed: 19, 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
|
||||||
"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: 1.04, speed: 19, from:
|
||||||
"electrum.bitaroo.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.98, speed: 19, from: fortress.qtornado.com:443
|
"btce.iiiiiii.biz:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.07, speed: 19, from: electrum.coinext.com.br:50002
|
||||||
"electrum.exan.tech:443", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.08, speed: 19, from: fortress.qtornado.com:443
|
"electrum.emzy.de:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.17, speed: 19, from:
|
||||||
"electrum-fulcrum.toggen.net:50002", // impl: Fulcrum 1.6.0, batching: true, ttc: 1.47, speed: 16, from: fortress.qtornado.com:443
|
"alviss.coinjoined.com:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 1.15, speed: 17, from:
|
||||||
"vmd84592.contaboserver.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.79, speed: 14, from: fortress.qtornado.com:443
|
"2AZZARITA.hopto.org:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.81, speed: 16, from: electrum.coinext.com.br:50002
|
||||||
"gd42.org:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.50, speed: 12, from: fortress.qtornado.com:443
|
"vmd104012.contaboserver.net:50002", // impl: Fulcrum 1.9.0, batching: true, ttc: 1.25, speed: 16, from:
|
||||||
"vmd63185.contaboserver.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.75, speed: 12, from: fortress.qtornado.com:443
|
"electrum.bitcoinlizard.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 4.99, speed: 14, from:
|
||||||
"alfa.cryptotrek.online:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.75, speed: 12, from: fortress.qtornado.com:443
|
"btc.ocf.sh:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.19, speed: 12, from:
|
||||||
"electrumx.ultracloud.tk:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.76, speed: 12, from: fortress.qtornado.com:443
|
"bitcoins.sk:56002", // impl: ElectrumX 1.14.0, batching: true, ttc: 0.80, speed: 11, from:
|
||||||
"electrum.brainshome.de:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.89, speed: 12, from: fortress.qtornado.com:443
|
"electrum-btc.leblancnet.us:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.04, speed: 11, from:
|
||||||
"ragtor.duckdns.org:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.91, speed: 12, from: bitcoin.aranguren.org:50002
|
"helicarrier.bauerj.eu:50002", // impl: ElectrumX 1.10.0, batching: true, ttc: 0.96, speed: 10, from:
|
||||||
"94.23.247.135:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 6.80, speed: 12, from: fortress.qtornado.com:443
|
"electrum.neocrypto.io:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.69, speed: 7, from:
|
||||||
"eai.coincited.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.12, speed: 11, from: electrumx.erbium.eu:50002
|
"caleb.vegas:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.71, speed: 7, from:
|
||||||
"142.93.6.38:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.56, speed: 11, from: fortress.qtornado.com:443
|
"smmalis37.ddns.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.77, speed: 7, from:
|
||||||
"157.245.172.236:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.62, speed: 11, from: fortress.qtornado.com:443
|
"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.68, speed: 11, from: fortress.qtornado.com:443
|
"electrum.kendigisland.xyz:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.79, speed: 7, from:
|
||||||
"btc.lastingcoin.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.74, speed: 11, from: fortress.qtornado.com:443
|
"electrum.hsmiths.com:50002", // impl: ElectrumX 1.10.0, batching: true, ttc: 0.90, speed: 7, from:
|
||||||
"xtrum.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.75, speed: 11, from: electrumx.erbium.eu:50002
|
"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.76, speed: 11, from: fortress.qtornado.com:443
|
"blkhub.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.94, speed: 7, from:
|
||||||
"electrumx.erbium.eu:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.78, speed: 11, from:
|
"electrum.mmitech.info:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.95, speed: 7, from:
|
||||||
"185.64.116.15:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.79, speed: 11, from: bitcoin.aranguren.org:50002
|
"elx.bitske.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.17, speed: 7, from:
|
||||||
"188.165.206.215:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.81, speed: 11, from: fortress.qtornado.com:443
|
"bitcoin.lu.ke:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.21, speed: 7, from:
|
||||||
"ex03.axalgo.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.92, speed: 11, from: fortress.qtornado.com:443
|
"ex05.axalgo.com:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.21, speed: 7, from:
|
||||||
"electrum.bitcoinlizard.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.98, speed: 11, from: fortress.qtornado.com:443
|
"walle.dedyn.io:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.23, speed: 7, from:
|
||||||
"btce.iiiiiii.biz:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.02, speed: 11, from: fortress.qtornado.com:443
|
"eai.coincited.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.34, speed: 6, from:
|
||||||
"btc.ocf.sh:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.15, speed: 11, from: fortress.qtornado.com:443
|
"2electrumx.hopto.me:56022", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.93, speed: 6, from:
|
||||||
"68.183.188.105:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.36, speed: 11, from: fortress.qtornado.com:443
|
"hodlers.beer:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.03, speed: 6, from:
|
||||||
"vmd71287.contaboserver.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.60, speed: 10, from: fortress.qtornado.com:443
|
"kareoke.qoppa.org:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.13, speed: 6, from:
|
||||||
"2electrumx.hopto.me:56022", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.89, speed: 10, from: fortress.qtornado.com:443
|
"ASSUREDLY.not.fyi:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.85, speed: 4, from:
|
||||||
"electrum.emzy.de:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.15, speed: 10, from:
|
"electrumx.alexridevski.net:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.87, speed: 4, from:
|
||||||
"electrumx.electricnewyear.net:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 0.66, speed: 9, from:
|
"assuredly.not.fyi:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.90, speed: 4, from:
|
||||||
"walle.dedyn.io:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.76, speed: 8, from: fortress.qtornado.com:443
|
"ragtor.duckdns.org:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.07, speed: 4, from:
|
||||||
"caleb.vegas:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.80, speed: 8, from: fortress.qtornado.com:443
|
"surely.not.fyi:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.20, speed: 4, from:
|
||||||
"electrum.neocrypto.io:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.69, speed: 7, from: fortress.qtornado.com:443
|
"btc.electrum.bitbitnet.net:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 0.71, speed: 3, from:
|
||||||
"guichet.centure.cc:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.71, speed: 7, from: fortress.qtornado.com:443
|
"gods-of-rock.screaminglemur.net:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 0.92, speed: 3, from:
|
||||||
"167.172.42.31:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.75, speed: 7, from: fortress.qtornado.com:443
|
"SURELY.not.fyi:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 1.35, speed: 3, from:
|
||||||
"2AZZARITA.hopto.org:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.77, speed: 7, from: fortress.qtornado.com:443
|
"horsey.cryptocowboys.net:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 0.61, speed: 2, from:
|
||||||
"jonas.reptiles.se:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.95, speed: 7, from: fortress.qtornado.com:443
|
"electrumx-btc.cryptonermal.net:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 0.83, speed: 1, from:
|
||||||
"167.172.226.175:50002", // impl: ElectrumX 1.16.0, batching: true, ttc: 0.61, speed: 6, from: fortress.qtornado.com:443
|
"electrum.coineuskal.com:50002", // impl: ElectrumX 1.15.0, batching: true, ttc: 1.73, speed: 0, from: electrum.coinext.com.br:50002
|
||||||
"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
|
|
||||||
}
|
}
|
||||||
|
|
81
main.go
81
main.go
|
@ -14,19 +14,31 @@ import (
|
||||||
"github.com/muun/libwallet"
|
"github.com/muun/libwallet"
|
||||||
"github.com/muun/libwallet/btcsuitew/btcutilw"
|
"github.com/muun/libwallet/btcsuitew/btcutilw"
|
||||||
"github.com/muun/libwallet/emergencykit"
|
"github.com/muun/libwallet/emergencykit"
|
||||||
|
"github.com/muun/recovery/electrum"
|
||||||
"github.com/muun/recovery/scanner"
|
"github.com/muun/recovery/scanner"
|
||||||
"github.com/muun/recovery/utils"
|
"github.com/muun/recovery/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const electrumPoolSize = 6
|
||||||
|
|
||||||
var debugOutputStream = bytes.NewBuffer(nil)
|
var debugOutputStream = bytes.NewBuffer(nil)
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
generateContacts bool
|
||||||
|
providedElectrum string
|
||||||
|
usesProvidedElectrum bool
|
||||||
|
onlyScan bool
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
utils.SetOutputStream(debugOutputStream)
|
utils.SetOutputStream(debugOutputStream)
|
||||||
|
|
||||||
var generateContacts bool
|
var config config
|
||||||
|
|
||||||
// Pick up command-line arguments:
|
// 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.Usage = printUsage
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
args := flag.Args()
|
args := flag.Args()
|
||||||
|
@ -40,6 +52,11 @@ func main() {
|
||||||
// Welcome!
|
// Welcome!
|
||||||
printWelcomeMessage()
|
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
|
// 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:
|
// so we keep them in mind:
|
||||||
var recoveryCode string
|
var recoveryCode string
|
||||||
|
@ -62,25 +79,41 @@ func main() {
|
||||||
|
|
||||||
decryptedKeys[0].Key.Path = "m/1'/1'" // a little adjustment for legacy users.
|
decryptedKeys[0].Key.Path = "m/1'/1'" // a little adjustment for legacy users.
|
||||||
|
|
||||||
// Finally, we need the destination address to sweep the funds:
|
if !config.onlyScan {
|
||||||
destinationAddress = readAddress()
|
// Finally, we need the destination address to sweep the funds:
|
||||||
|
destinationAddress = readAddress()
|
||||||
|
}
|
||||||
|
|
||||||
sayBlock(`
|
sayBlock(`
|
||||||
Starting scan of all possible addresses. This will take a few minutes.
|
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")
|
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.
|
// doRecovery runs the scan & sweep process, and returns the ID of the broadcasted transaction.
|
||||||
func doRecovery(
|
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)
|
addrGen := NewAddressGenerator(decryptedKeys[0].Key, decryptedKeys[1].Key, config.generateContacts)
|
||||||
utxoScanner := scanner.NewScanner()
|
|
||||||
|
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()
|
addresses := addrGen.Stream()
|
||||||
|
|
||||||
|
@ -122,6 +155,9 @@ func doRecovery(
|
||||||
}
|
}
|
||||||
|
|
||||||
say("\n— {white %d} sats total\n", total)
|
say("\n— {white %d} sats total\n", total)
|
||||||
|
if config.onlyScan {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
txOutputAmount, txWeightInBytes, err := sweeper.GetSweepTxAmountAndWeightInBytes(utxos)
|
txOutputAmount, txWeightInBytes, err := sweeper.GetSweepTxAmountAndWeightInBytes(utxos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -145,11 +181,36 @@ func doRecovery(
|
||||||
|
|
||||||
sayBlock(`
|
sayBlock(`
|
||||||
Transaction sent! You can check the status here: https://mempool.space/tx/%v
|
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())
|
`, 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) {
|
func exitWithError(err error) {
|
||||||
sayBlock(`
|
sayBlock(`
|
||||||
{red Error!}
|
{red Error!}
|
||||||
|
@ -351,7 +412,7 @@ func readAddress() btcutil.Address {
|
||||||
func readFee(totalBalance, weight int64) int64 {
|
func readFee(totalBalance, weight int64) int64 {
|
||||||
sayBlock(`
|
sayBlock(`
|
||||||
{yellow Enter the fee rate (sats/byte)}
|
{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)
|
`, weight)
|
||||||
|
|
||||||
var userInput string
|
var userInput string
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/muun/recovery/utils"
|
"github.com/muun/recovery/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
const electrumPoolSize = 6
|
|
||||||
const taskTimeout = 15 * time.Minute
|
const taskTimeout = 15 * time.Minute
|
||||||
const batchSize = 100
|
const batchSize = 100
|
||||||
|
|
||||||
|
@ -70,10 +69,10 @@ type scanContext struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewScanner creates an initialized Scanner.
|
// NewScanner creates an initialized Scanner.
|
||||||
func NewScanner() *Scanner {
|
func NewScanner(connectionPool *electrum.Pool, electrumProvider *electrum.ServerProvider) *Scanner {
|
||||||
return &Scanner{
|
return &Scanner{
|
||||||
pool: electrum.NewPool(electrumPoolSize),
|
pool: connectionPool,
|
||||||
servers: electrum.NewServerProvider(),
|
servers: electrumProvider,
|
||||||
log: utils.NewLogger("Scanner"),
|
log: utils.NewLogger("Scanner"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
// 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"
|
var mainnetGenesisHash = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
|
||||||
|
|
||||||
func NewSurvey(config *Config) *Survey {
|
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
|
// testConnection returns the server implementation, protocol version and time to connect
|
||||||
func testConnection(task *surveyTask) (string, string, time.Duration, error) {
|
func testConnection(task *surveyTask) (string, string, time.Duration, error) {
|
||||||
client := electrum.NewClient()
|
client := electrum.NewClient(true)
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
err := client.Connect(task.server)
|
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
|
// testsBlockchain returns whether this server is operating on Bitcoin mainnet
|
||||||
func testBitcoinMainnet(task *surveyTask) (bool, error) {
|
func testBitcoinMainnet(task *surveyTask) (bool, error) {
|
||||||
client := electrum.NewClient()
|
client := electrum.NewClient(true)
|
||||||
|
|
||||||
err := client.Connect(task.server)
|
err := client.Connect(task.server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -229,7 +229,7 @@ func testBitcoinMainnet(task *surveyTask) (bool, error) {
|
||||||
|
|
||||||
// testBatchSupport returns whether the server successfully responded to a batched request
|
// testBatchSupport returns whether the server successfully responded to a batched request
|
||||||
func testBatchSupport(task *surveyTask) (bool, error) {
|
func testBatchSupport(task *surveyTask) (bool, error) {
|
||||||
client := electrum.NewClient()
|
client := electrum.NewClient(true)
|
||||||
|
|
||||||
err := client.Connect(task.server)
|
err := client.Connect(task.server)
|
||||||
if err != nil {
|
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
|
// measureSpeed returns the amount of successful ListUnspentBatch calls in SPEED_TEST_DURATION
|
||||||
// seconds. It assumes batch support was verified beforehand.
|
// seconds. It assumes batch support was verified beforehand.
|
||||||
func (s *Survey) measureSpeed(task *surveyTask) (int, error) {
|
func (s *Survey) measureSpeed(task *surveyTask) (int, error) {
|
||||||
client := electrum.NewClient()
|
client := electrum.NewClient(true)
|
||||||
|
|
||||||
err := client.Connect(task.server)
|
err := client.Connect(task.server)
|
||||||
if err != nil {
|
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
|
// 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) {
|
func getPeers(task *surveyTask) ([]string, error) {
|
||||||
client := electrum.NewClient()
|
client := electrum.NewClient(true)
|
||||||
|
|
||||||
err := client.Connect(task.server)
|
err := client.Connect(task.server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -54,8 +54,8 @@ func (s *Sweeper) BuildSweepTx(utxos []*scanner.Utxo, fee int64) (*wire.MsgTx, e
|
||||||
|
|
||||||
func (s *Sweeper) BroadcastTx(tx *wire.MsgTx) error {
|
func (s *Sweeper) BroadcastTx(tx *wire.MsgTx) error {
|
||||||
// Connect to an Electurm server using a fresh client and provider pair:
|
// Connect to an Electurm server using a fresh client and provider pair:
|
||||||
sp := electrum.NewServerProvider() // TODO create servers module, for provider and pool
|
sp := electrum.NewServerProvider(electrum.PublicServers) // TODO create servers module, for provider and pool
|
||||||
client := electrum.NewClient()
|
client := electrum.NewClient(true)
|
||||||
|
|
||||||
for !client.IsConnected() {
|
for !client.IsConnected() {
|
||||||
client.Connect(sp.NextServer())
|
client.Connect(sp.NextServer())
|
||||||
|
|
|
@ -3,4 +3,4 @@ package main
|
||||||
// The OSS tool depends on this file with this content. If you need to change
|
// The OSS tool depends on this file with this content. If you need to change
|
||||||
// it, change the tool too.
|
// it, change the tool too.
|
||||||
|
|
||||||
const version = "2.2.3"
|
const version = "2.2.4"
|
||||||
|
|
Loading…
Reference in New Issue