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  }