decred.org/dcrdex@v1.0.3/client/asset/btc/electrum/example/server/main.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org/license/1.0.0.
     3  
     4  package main
     5  
     6  import (
     7  	"context"
     8  	"crypto/tls"
     9  	"crypto/x509"
    10  	"encoding/hex"
    11  	"fmt"
    12  	"net"
    13  	"os"
    14  	"os/signal"
    15  	"strings"
    16  	"time"
    17  
    18  	"decred.org/dcrdex/client/asset/btc/electrum"
    19  	"github.com/btcsuite/btcd/wire"
    20  	"github.com/davecgh/go-spew/spew"
    21  )
    22  
    23  // This example app requires a tor proxy listening on 127.0.0.1:9050.
    24  
    25  func main() {
    26  	ctx, cancel := context.WithCancel(context.Background())
    27  	defer cancel()
    28  
    29  	killChan := make(chan os.Signal, 1)
    30  	signal.Notify(killChan, os.Interrupt)
    31  	go func() {
    32  		<-killChan
    33  		fmt.Println("Shutting down...")
    34  		cancel()
    35  	}()
    36  
    37  	err := run(ctx)
    38  	if err != nil {
    39  		fmt.Fprintln(os.Stderr, err)
    40  		os.Exit(1)
    41  	}
    42  	os.Exit(0)
    43  }
    44  
    45  func startConn(ctx context.Context, addr, torProxy string, ssl bool) (*electrum.ServerConn, error) {
    46  	host, _, err := net.SplitHostPort(addr)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  
    51  	opts := &electrum.ConnectOpts{
    52  		TorProxy:    torProxy,
    53  		DebugLogger: electrum.StdoutPrinter,
    54  	}
    55  	if ssl {
    56  		rootCAs, _ := x509.SystemCertPool()
    57  		opts.TLSConfig = &tls.Config{
    58  			InsecureSkipVerify: true, // they are almost all self-signed, most without fqdn
    59  			RootCAs:            rootCAs,
    60  			// MinVersion:         tls.VersionTLS12,
    61  			ServerName: host,
    62  		}
    63  	}
    64  	sc, err := electrum.ConnectServer(ctx, addr, opts)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	fmt.Printf("Connected to Electrum server %v, protocol version %v\n", addr, sc.Proto())
    69  
    70  	banner, err := sc.Banner(ctx)
    71  	if err != nil {
    72  		sc.Shutdown()
    73  		return nil, err
    74  	}
    75  	fmt.Println(banner)
    76  
    77  	txInfo, err := sc.GetTransaction(ctx, "6356ad5ac46daabfd0a0b607e44ce78b77ad0fd17a3376aaca73ef230303afbf")
    78  	if err != nil {
    79  		sc.Shutdown()
    80  		return nil, err
    81  	}
    82  	spew.Dump(txInfo)
    83  
    84  	peers, err := sc.Peers(ctx)
    85  	if err != nil {
    86  		sc.Shutdown()
    87  		return nil, err
    88  	}
    89  	for i, peer := range peers {
    90  		fmt.Printf(" - Peer %3d: %s (%s), features: %v\n", i, peer.Addr, peer.Host, peer.Feats)
    91  	}
    92  	sslPeers, tcpOnionPeers := electrum.SSLPeerAddrs(peers, true)
    93  	fmt.Printf("%d SSL peers / %d no-SSL onion peers\n", len(sslPeers), len(tcpOnionPeers))
    94  
    95  	hdrRes, hdrs, err := sc.SubscribeHeaders(ctx)
    96  	if err != nil {
    97  		sc.Shutdown()
    98  		return nil, err
    99  	}
   100  	wireHdr := &wire.BlockHeader{}
   101  	err = wireHdr.Deserialize(hex.NewDecoder(strings.NewReader(hdrRes.Hex)))
   102  	if err != nil {
   103  		sc.Shutdown()
   104  		return nil, err
   105  	}
   106  	fmt.Printf("Best block %v (%d)\n", wireHdr.BlockHash(), hdrRes.Height)
   107  
   108  	go func() {
   109  		for hdr := range hdrs {
   110  			wireHdr := &wire.BlockHeader{}
   111  			err := wireHdr.Deserialize(hex.NewDecoder(strings.NewReader(hdr.Hex)))
   112  			if err != nil {
   113  				fmt.Printf("Failed to deserialize block header for block at height %d: %v", hdr.Height, err)
   114  				continue
   115  			}
   116  			fmt.Printf("New best block %v (%d)\n", wireHdr.BlockHash(), hdr.Height)
   117  		}
   118  
   119  		sc.Shutdown() // should be shut down already
   120  	}()
   121  
   122  	return sc, nil
   123  }
   124  
   125  func run(ctx context.Context) error {
   126  	addrs := map[string]bool{ // addr => ssl
   127  		// "127.0.0.1:51002": true,
   128  		"electrum.ltc.xurious.com:51002": true,
   129  		"electrum-ltc.bysh.me:51002":     true,
   130  		"electrum3.hodlister.co:50002":   true,
   131  		"node.degga.net:50002":           true,
   132  		"kittycp2gatrqhlwpmbczk5rblw62enrpo2rzwtkfrrr27hq435d4vid.onion:50001": false,
   133  		"kittycp2gatrqhlwpmbczk5rblw62enrpo2rzwtkfrrr27hq435d4vid.onion:50002": true,
   134  		"hodlers.beer:50002":          true,
   135  		"electrum.neocrypto.io:50001": false,
   136  	}
   137  	for {
   138  		var addr string
   139  		var ssl bool
   140  		for addr, ssl = range addrs {
   141  			break
   142  		}
   143  		sc, err := startConn(ctx, addr, "127.0.0.1:9050", ssl)
   144  		if err != nil {
   145  			if ctx.Err() != nil {
   146  				return ctx.Err()
   147  			}
   148  			fmt.Printf("%v, %T\n", err, err)
   149  			time.Sleep(10 * time.Second)
   150  			continue
   151  		}
   152  
   153  		<-sc.Done()
   154  
   155  		select {
   156  		case <-time.After(10 * time.Second):
   157  		case <-ctx.Done():
   158  			return ctx.Err()
   159  		}
   160  	}
   161  }