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 }