decred.org/dcrdex@v1.0.5/dex/testing/firo/test/electrumx_network_test.go (about) 1 //go:build live 2 3 // This code is available on the terms of the project LICENSE.md file, 4 // also available online at https://blueoakcouncil.org/license/1.0.0. 5 6 // Connects to one ElectrumX-Firo testnet server and one ElectrumX-Firo 7 // mainnet server and tests a subset of electrumx-firo commands. 8 // 9 // See also: 10 // https://electrumx.readthedocs.io/en/latest/protocol-methods.html 11 // 12 // To also run on Regtest network: 13 // 14 // Chain: dex/testing/firo/harness.sh 15 // ElectrumX-Firo server: dex/testing/firo/electrumx.sh 16 // export REGTEST=1 17 18 package firo 19 20 import ( 21 "context" 22 "crypto/tls" 23 "crypto/x509" 24 "encoding/hex" 25 "fmt" 26 "net" 27 "os" 28 "sort" 29 "strings" 30 "testing" 31 "time" 32 33 "decred.org/dcrdex/client/asset/btc/electrum" 34 "github.com/btcsuite/btcd/wire" 35 "github.com/davecgh/go-spew/spew" 36 ) 37 38 type electrumNetwork string 39 40 var regtest electrumNetwork = "regtest" 41 var testnet electrumNetwork = "testnet" 42 var mainnet electrumNetwork = "mainnet" 43 44 func TestServerConn(t *testing.T) { 45 var serverAddr string 46 var genesis string 47 var randomTx string 48 var blockHeight uint32 49 var blockCount uint32 50 51 if os.Getenv("REGTEST") != "" { 52 serverAddr = "127.0.0.1:50002" // electrumx-firo regtest 53 genesis = "a42b98f04cc2916e8adfb5d9db8a2227c4629bc205748ed2f33180b636ee885b" 54 randomTx = "7e3d477aaac3534da5624aecf82583a1381508bf672ec18dfc0b9e7b7b0e2dfe" 55 blockHeight = 330 56 blockCount = 5 57 runOneNet(t, regtest, serverAddr, genesis, randomTx, blockHeight, blockCount) 58 } 59 60 serverAddr = "95.179.164.13:51002" 61 genesis = "aa22adcc12becaf436027ffe62a8fb21b234c58c23865291e5dc52cf53f64fca" 62 randomTx = "af5bc24f8f995ff0439af0c9f3ba607995f74a84d8cc06e4a6ba7f3c7692a41a" 63 blockHeight = 105793 64 blockCount = 3 65 runOneNet(t, testnet, serverAddr, genesis, randomTx, blockHeight, blockCount) 66 67 serverAddr = "electrumx.firo.org:50002" 68 genesis = "4381deb85b1b2c9843c222944b616d997516dcbd6a964e1eaf0def0830695233" 69 randomTx = "3cbf438c07938a5da6293f3a9be9b786e01c14b459d56cd595dcd534db128ed3" 70 blockHeight = 676700 71 blockCount = 3 72 runOneNet(t, mainnet, serverAddr, genesis, randomTx, blockHeight, blockCount) 73 } 74 75 func runOneNet(t *testing.T, electrumNetwork electrumNetwork, 76 addr, genesis, randomTx string, blockHeight, blockCount uint32) { 77 78 ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second) 79 defer cancel() 80 81 host, _, err := net.SplitHostPort(addr) 82 if err != nil { 83 t.Fatal(err) 84 } 85 86 rootCAs, _ := x509.SystemCertPool() 87 tlsConfig := &tls.Config{ 88 InsecureSkipVerify: true, 89 RootCAs: rootCAs, 90 MinVersion: tls.VersionTLS12, // works ok 91 ServerName: host, 92 } 93 94 opts := &electrum.ConnectOpts{ 95 TLSConfig: tlsConfig, 96 DebugLogger: electrum.StdoutPrinter, 97 } 98 99 sc, err := electrum.ConnectServer(ctx, addr, opts) 100 if err != nil { 101 t.Fatal(err) 102 } 103 t.Log(sc.Proto()) 104 105 fmt.Printf("\n\n ** Connected to %s **\n\n", electrumNetwork) 106 107 banner, err := sc.Banner(ctx) 108 if err != nil { 109 t.Fatal(err) 110 } 111 spew.Dump(banner) 112 113 feats, err := sc.Features(ctx) 114 if err != nil { 115 t.Fatal(err) 116 } 117 spew.Dump(feats) 118 119 if feats.Genesis != genesis { 120 t.Fatalf("wrong genesis hash for Firo-Electrum on %s: %v", 121 feats.Genesis, electrumNetwork) 122 } 123 t.Log("Genesis correct") 124 125 // All tx hashes will change on each regtest startup. So use 126 // listtransactions or listunspent on the harness to get a 127 // new random tx. 128 txres, err := sc.GetTransaction(ctx, randomTx) 129 if err != nil { 130 t.Fatal(err) 131 } 132 spew.Dump(txres) 133 134 // Do not make block count too big or electrumX may throttle response 135 // as an anti ddos measure 136 hdrsRes, err := sc.BlockHeaders(ctx, blockHeight, blockCount) 137 if err != nil { 138 t.Fatal(err) 139 } 140 spew.Dump(hdrsRes) 141 142 hdrReader := hex.NewDecoder(strings.NewReader(hdrsRes.HexConcat)) 143 var lastHdr *wire.BlockHeader 144 timestamps := make([]int64, 0, hdrsRes.Count) 145 for i := uint32(0); i < hdrsRes.Count; i++ { 146 hdr := &wire.BlockHeader{} 147 err = hdr.Deserialize(hdrReader) 148 if err != nil { 149 if i > 0 { 150 t.Fatalf("Failed to deserialize header for block %d: %v", 151 blockHeight+i, err) 152 break // we have at least one time stamp, work with it 153 } 154 t.Fatal(err) 155 } 156 timestamps = append(timestamps, hdr.Timestamp.Unix()) 157 if i == blockCount-1 && blockCount == hdrsRes.Count { 158 lastHdr = hdr 159 } 160 } 161 spew.Dump(lastHdr) 162 163 sort.Slice(timestamps, func(i, j int) bool { 164 return timestamps[i] < timestamps[j] 165 }) 166 167 medianTimestamp := timestamps[len(timestamps)/2] 168 t.Logf("Median time for block %d: %v", blockHeight+hdrsRes.Count-1, time.Unix(medianTimestamp, 0)) 169 170 hdrRes, _, _ := sc.SubscribeHeaders(ctx) 171 spew.Dump(hdrRes) 172 173 height := blockHeight 174 out: 175 for { 176 select { 177 case <-ctx.Done(): 178 break out 179 case <-time.After(5 * time.Second): 180 hdr, err := sc.BlockHeader(ctx, height) 181 if err != nil { 182 t.Log(err) 183 break out 184 } 185 t.Log(hdr) 186 } 187 height++ 188 } 189 sc.Shutdown() 190 191 <-sc.Done() 192 }