github.com/grupokindynos/coins-explorer@v0.0.0-20210507172551-fa8983d19250/server/public_test.go (about)

     1  // +build unittest
     2  
     3  package server
     4  
     5  import (
     6  	"encoding/json"
     7  	"github.com/grupokindynos/coins-explorer/bchain"
     8  	"github.com/grupokindynos/coins-explorer/bchain/coins/btc"
     9  	"github.com/grupokindynos/coins-explorer/common"
    10  	"github.com/grupokindynos/coins-explorer/db"
    11  	"github.com/grupokindynos/coins-explorer/tests/dbtestdata"
    12  	"io/ioutil"
    13  	"net/http"
    14  	"net/http/httptest"
    15  	"net/url"
    16  	"os"
    17  	"strconv"
    18  	"strings"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/golang/glog"
    23  	"github.com/gorilla/websocket"
    24  	"github.com/martinboehm/btcutil/chaincfg"
    25  	gosocketio "github.com/martinboehm/golang-socketio"
    26  	"github.com/martinboehm/golang-socketio/transport"
    27  )
    28  
    29  func TestMain(m *testing.M) {
    30  	// set the current directory to blockbook root so that ./static/ works
    31  	if err := os.Chdir(".."); err != nil {
    32  		glog.Fatal("Chdir error:", err)
    33  	}
    34  	c := m.Run()
    35  	chaincfg.ResetParams()
    36  	os.Exit(c)
    37  }
    38  
    39  func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *common.InternalState, string) {
    40  	tmp, err := ioutil.TempDir("", "testdb")
    41  	if err != nil {
    42  		t.Fatal(err)
    43  	}
    44  	d, err := db.NewRocksDB(tmp, 100000, -1, parser, nil)
    45  	if err != nil {
    46  		t.Fatal(err)
    47  	}
    48  	is, err := d.LoadInternalState("fakecoin")
    49  	if err != nil {
    50  		t.Fatal(err)
    51  	}
    52  	d.SetInternalState(is)
    53  	block1 := dbtestdata.GetTestBitcoinTypeBlock1(parser)
    54  	// setup internal state BlockTimes
    55  	for i := uint32(0); i < block1.Height; i++ {
    56  		is.BlockTimes = append(is.BlockTimes, 0)
    57  	}
    58  	// import data
    59  	if err := d.ConnectBlock(block1); err != nil {
    60  		t.Fatal(err)
    61  	}
    62  	block2 := dbtestdata.GetTestBitcoinTypeBlock2(parser)
    63  	if err := d.ConnectBlock(block2); err != nil {
    64  		t.Fatal(err)
    65  	}
    66  	if err := InitTestFiatRates(d); err != nil {
    67  		t.Fatal(err)
    68  	}
    69  	is.FinishedSync(block2.Height)
    70  	return d, is, tmp
    71  }
    72  
    73  func setupPublicHTTPServer(t *testing.T) (*PublicServer, string) {
    74  	parser := btc.NewBitcoinParser(
    75  		btc.GetChainParams("test"),
    76  		&btc.Configuration{
    77  			BlockAddressesToKeep:  1,
    78  			XPubMagic:             70617039,
    79  			XPubMagicSegwitP2sh:   71979618,
    80  			XPubMagicSegwitNative: 73342198,
    81  			Slip44:                1,
    82  		})
    83  
    84  	d, is, path := setupRocksDB(t, parser)
    85  	// setup internal state and match BestHeight to test data
    86  	is.Coin = "Fakecoin"
    87  	is.CoinLabel = "Fake Coin"
    88  	is.CoinShortcut = "FAKE"
    89  
    90  	metrics, err := common.GetMetrics("Fakecoin")
    91  	if err != nil {
    92  		glog.Fatal("metrics: ", err)
    93  	}
    94  
    95  	chain, err := dbtestdata.NewFakeBlockChain(parser)
    96  	if err != nil {
    97  		glog.Fatal("fakechain: ", err)
    98  	}
    99  
   100  	mempool, err := chain.CreateMempool(chain)
   101  	if err != nil {
   102  		glog.Fatal("mempool: ", err)
   103  	}
   104  
   105  	// caching is switched off because test transactions do not have hex data
   106  	txCache, err := db.NewTxCache(d, chain, metrics, is, false)
   107  	if err != nil {
   108  		glog.Fatal("txCache: ", err)
   109  	}
   110  
   111  	// s.Run is never called, binding can be to any port
   112  	s, err := NewPublicServer("localhost:12345", "", d, chain, mempool, txCache, "", metrics, is, false)
   113  	if err != nil {
   114  		t.Fatal(err)
   115  	}
   116  	return s, path
   117  }
   118  
   119  func closeAndDestroyPublicServer(t *testing.T, s *PublicServer, dbpath string) {
   120  	// destroy db
   121  	if err := s.db.Close(); err != nil {
   122  		t.Fatal(err)
   123  	}
   124  	os.RemoveAll(dbpath)
   125  }
   126  
   127  func newGetRequest(u string) *http.Request {
   128  	r, err := http.NewRequest("GET", u, nil)
   129  	if err != nil {
   130  		glog.Fatal(err)
   131  	}
   132  	return r
   133  }
   134  
   135  func newPostFormRequest(u string, formdata ...string) *http.Request {
   136  	form := url.Values{}
   137  	for i := 0; i < len(formdata)-1; i += 2 {
   138  		form.Add(formdata[i], formdata[i+1])
   139  	}
   140  	r, err := http.NewRequest("POST", u, strings.NewReader(form.Encode()))
   141  	if err != nil {
   142  		glog.Fatal(err)
   143  	}
   144  	r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
   145  	return r
   146  }
   147  
   148  func newPostRequest(u string, body string) *http.Request {
   149  	r, err := http.NewRequest("POST", u, strings.NewReader(body))
   150  	if err != nil {
   151  		glog.Fatal(err)
   152  	}
   153  	r.Header.Add("Content-Type", "application/octet-stream")
   154  	return r
   155  }
   156  
   157  func insertFiatRate(date string, rates map[string]float64, d *db.RocksDB) error {
   158  	convertedDate, err := db.FiatRatesConvertDate(date)
   159  	if err != nil {
   160  		return err
   161  	}
   162  	ticker := &db.CurrencyRatesTicker{
   163  		Timestamp: convertedDate,
   164  		Rates:     rates,
   165  	}
   166  	return d.FiatRatesStoreTicker(ticker)
   167  }
   168  
   169  // InitTestFiatRates initializes test data for /api/v2/tickers endpoint
   170  func InitTestFiatRates(d *db.RocksDB) error {
   171  	if err := insertFiatRate("20180320020000", map[string]float64{
   172  		"usd": 2000.0,
   173  		"eur": 1300.0,
   174  	}, d); err != nil {
   175  		return err
   176  	}
   177  	if err := insertFiatRate("20180320030000", map[string]float64{
   178  		"usd": 2001.0,
   179  		"eur": 1301.0,
   180  	}, d); err != nil {
   181  		return err
   182  	}
   183  	if err := insertFiatRate("20180320040000", map[string]float64{
   184  		"usd": 2002.0,
   185  		"eur": 1302.0,
   186  	}, d); err != nil {
   187  		return err
   188  	}
   189  	if err := insertFiatRate("20180321055521", map[string]float64{
   190  		"usd": 2003.0,
   191  		"eur": 1303.0,
   192  	}, d); err != nil {
   193  		return err
   194  	}
   195  	if err := insertFiatRate("20191121140000", map[string]float64{
   196  		"usd": 7814.5,
   197  		"eur": 7100.0,
   198  	}, d); err != nil {
   199  		return err
   200  	}
   201  	return insertFiatRate("20191121143015", map[string]float64{
   202  		"usd": 7914.5,
   203  		"eur": 7134.1,
   204  	}, d)
   205  }
   206  
   207  func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
   208  	tests := []struct {
   209  		name        string
   210  		r           *http.Request
   211  		status      int
   212  		contentType string
   213  		body        []string
   214  	}{
   215  		{
   216  			name:        "explorerTx",
   217  			r:           newGetRequest(ts.URL + "/tx/fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db"),
   218  			status:      http.StatusOK,
   219  			contentType: "text/html; charset=utf-8",
   220  			body: []string{
   221  				`<a class="navbar-brand" href="/">Fake Coin Explorer</a>`,
   222  				`<h1>Transaction</h1>`,
   223  				`<span class="data">fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db</span>`,
   224  				`td class="data">0 FAKE</td>`,
   225  				`<a href="/address/mzVznVsCHkVHX9UN8WPFASWUUHtxnNn4Jj">mzVznVsCHkVHX9UN8WPFASWUUHtxnNn4Jj</a>`,
   226  				`13.60030331 FAKE`,
   227  				`<td><span class="tx-addr">No Inputs (Newly Generated Coins)</span></td>`,
   228  				`</html>`,
   229  			},
   230  		},
   231  		{
   232  			name:        "explorerAddress",
   233  			r:           newGetRequest(ts.URL + "/address/mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"),
   234  			status:      http.StatusOK,
   235  			contentType: "text/html; charset=utf-8",
   236  			body: []string{
   237  				`<a class="navbar-brand" href="/">Fake Coin Explorer</a>`,
   238  				`<h1>Address`,
   239  				`<small class="text-muted">0.00012345 FAKE</small>`,
   240  				`<span class="data">mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz</span>`,
   241  				`<td class="data">0.00012345 FAKE</td>`,
   242  				`<a href="/tx/7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25">7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25</a>`,
   243  				`3172.83951061 FAKE <a class="text-danger" href="/spending/7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25/0" title="Spent">➡</a></span>`,
   244  				`<td><span class="ellipsis tx-addr"><a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a></span><span class="tx-amt">`,
   245  				`td><span class="ellipsis tx-addr"><a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a></span><span class="tx-amt">`,
   246  				`9172.83951061 FAKE <span class="text-success" title="Unspent"> <b>×</b></span></span>`,
   247  				`<a href="/tx/00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840">00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840</a>`,
   248  				`</html>`,
   249  			},
   250  		},
   251  		{
   252  			name:        "explorerSpendingTx",
   253  			r:           newGetRequest(ts.URL + "/spending/7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25/0"),
   254  			status:      http.StatusOK,
   255  			contentType: "text/html; charset=utf-8",
   256  			body: []string{
   257  				`<a class="navbar-brand" href="/">Fake Coin Explorer</a>`,
   258  				`<h1>Transaction</h1>`,
   259  				`<span class="data">3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71</span>`,
   260  				`<td class="data">0.00000062 FAKE</td>`,
   261  				`</html>`,
   262  			},
   263  		},
   264  		{
   265  			name:        "explorerSpendingTx - not found",
   266  			r:           newGetRequest(ts.URL + "/spending/123be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25/0"),
   267  			status:      http.StatusOK,
   268  			contentType: "text/html; charset=utf-8",
   269  			body: []string{
   270  				`<a class="navbar-brand" href="/">Fake Coin Explorer</a>`,
   271  				`<h1>Error</h1>`,
   272  				`<h4>Transaction not found</h4>`,
   273  				`</html>`,
   274  			},
   275  		},
   276  		{
   277  			name:        "explorerBlocks",
   278  			r:           newGetRequest(ts.URL + "/blocks"),
   279  			status:      http.StatusOK,
   280  			contentType: "text/html; charset=utf-8",
   281  			body: []string{
   282  				`<a class="navbar-brand" href="/">Fake Coin Explorer</a>`,
   283  				`<h1>Blocks`,
   284  				`<td><a href="/block/225494">225494</a></td>`,
   285  				`<td class="ellipsis">00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6</td>`,
   286  				`<td class="ellipsis">0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997</td>`,
   287  				`<td class="text-right">2</td>`,
   288  				`<td class="text-right">1234567</td>`,
   289  				`</html>`,
   290  			},
   291  		},
   292  		{
   293  			name:        "explorerBlock",
   294  			r:           newGetRequest(ts.URL + "/block/225494"),
   295  			status:      http.StatusOK,
   296  			contentType: "text/html; charset=utf-8",
   297  			body: []string{
   298  				`<a class="navbar-brand" href="/">Fake Coin Explorer</a>`,
   299  				`<h1>Block 225494</h1>`,
   300  				`<span class="data">00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6</span>`,
   301  				`<td class="data">4</td>`, // number of transactions
   302  				`<a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a>`,
   303  				`9172.83951061 FAKE`,
   304  				`<a href="/tx/fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db">fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db</a>`,
   305  				`</html>`,
   306  			},
   307  		},
   308  		{
   309  			name:        "explorerIndex",
   310  			r:           newGetRequest(ts.URL + "/"),
   311  			status:      http.StatusOK,
   312  			contentType: "text/html; charset=utf-8",
   313  			body: []string{
   314  				`<a class="navbar-brand" href="/">Fake Coin Explorer</a>`,
   315  				`<h1>Application status</h1>`,
   316  				`<h3 class="bg-warning text-white" style="padding: 20px;">Synchronization with backend is disabled, the state of index is not up to date.</h3>`,
   317  				`<a href="/block/225494">225494</a>`,
   318  				`<td class="data">/Fakecoin:0.0.1/</td>`,
   319  				`</html>`,
   320  			},
   321  		},
   322  		{
   323  			name:        "explorerSearch block height",
   324  			r:           newGetRequest(ts.URL + "/search?q=225494"),
   325  			status:      http.StatusOK,
   326  			contentType: "text/html; charset=utf-8",
   327  			body: []string{
   328  				`<a class="navbar-brand" href="/">Fake Coin Explorer</a>`,
   329  				`<h1>Block 225494</h1>`,
   330  				`<span class="data">00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6</span>`,
   331  				`<td class="data">4</td>`, // number of transactions
   332  				`<a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a>`,
   333  				`9172.83951061 FAKE`,
   334  				`<a href="/tx/fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db">fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db</a>`,
   335  				`</html>`,
   336  			},
   337  		},
   338  		{
   339  			name:        "explorerSearch block hash",
   340  			r:           newGetRequest(ts.URL + "/search?q=00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6"),
   341  			status:      http.StatusOK,
   342  			contentType: "text/html; charset=utf-8",
   343  			body: []string{
   344  				`<a class="navbar-brand" href="/">Fake Coin Explorer</a>`,
   345  				`<h1>Block 225494</h1>`,
   346  				`<span class="data">00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6</span>`,
   347  				`<td class="data">4</td>`, // number of transactions
   348  				`<a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a>`,
   349  				`9172.83951061 FAKE`,
   350  				`<a href="/tx/fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db">fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db</a>`,
   351  				`</html>`,
   352  			},
   353  		},
   354  		{
   355  			name:        "explorerSearch tx",
   356  			r:           newGetRequest(ts.URL + "/search?q=fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db"),
   357  			status:      http.StatusOK,
   358  			contentType: "text/html; charset=utf-8",
   359  			body: []string{
   360  				`<a class="navbar-brand" href="/">Fake Coin Explorer</a>`,
   361  				`<h1>Transaction</h1>`,
   362  				`<span class="data">fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db</span>`,
   363  				`td class="data">0 FAKE</td>`,
   364  				`<a href="/address/mzVznVsCHkVHX9UN8WPFASWUUHtxnNn4Jj">mzVznVsCHkVHX9UN8WPFASWUUHtxnNn4Jj</a>`,
   365  				`13.60030331 FAKE`,
   366  				`<td><span class="tx-addr">No Inputs (Newly Generated Coins)</span></td>`,
   367  				`</html>`,
   368  			},
   369  		},
   370  		{
   371  			name:        "explorerSearch address",
   372  			r:           newGetRequest(ts.URL + "/search?q=mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"),
   373  			status:      http.StatusOK,
   374  			contentType: "text/html; charset=utf-8",
   375  			body: []string{
   376  				`<a class="navbar-brand" href="/">Fake Coin Explorer</a>`,
   377  				`<h1>Address`,
   378  				`<small class="text-muted">0.00012345 FAKE</small>`,
   379  				`<span class="data">mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz</span>`,
   380  				`<td class="data">0.00012345 FAKE</td>`,
   381  				`<a href="/tx/7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25">7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25</a>`,
   382  				`3172.83951061 FAKE <a class="text-danger" href="/spending/7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25/0" title="Spent">➡</a></span>`,
   383  				`<td><span class="ellipsis tx-addr"><a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a></span><span class="tx-amt">`,
   384  				`td><span class="ellipsis tx-addr"><a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a></span><span class="tx-amt">`,
   385  				`9172.83951061 FAKE <span class="text-success" title="Unspent"> <b>×</b></span></span>`,
   386  				`<a href="/tx/00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840">00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840</a>`,
   387  				`</html>`,
   388  			},
   389  		},
   390  		{
   391  			name:        "explorerSearch xpub",
   392  			r:           newGetRequest(ts.URL + "/search?q=" + dbtestdata.Xpub),
   393  			status:      http.StatusOK,
   394  			contentType: "text/html; charset=utf-8",
   395  			body: []string{
   396  				`<a class="navbar-brand" href="/">Fake Coin Explorer</a>`,
   397  				`<h1>XPUB <small class="text-muted">1186.419755 FAKE</small></h1><div class="alert alert-data ellipsis"><span class="data">upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q</span></div>`,
   398  				`<td style="width: 25%;">Total Received</td><td class="data">1186.41975501 FAKE</td>`,
   399  				`<td>Total Sent</td><td class="data">0.00000001 FAKE</td>`,
   400  				`<td>Used XPUB Addresses</td><td class="data">2</td>`,
   401  				`<td class="data ellipsis"><a href="/address/2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu">2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu</a></td><td class="data">1186.419755 FAKE</td><td class="data">1</td><td>m/49&#39;/1&#39;/33&#39;/1/3</td>`,
   402  				`<div class="col-xs-7 col-md-8 ellipsis"><a href="/tx/3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71">3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71</a></div>`,
   403  				`<div class="col-xs-7 col-md-8 ellipsis"><a href="/tx/effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75">effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75</a></div>`,
   404  				`<td><span class="ellipsis tx-addr"><a href="/address/2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1">2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1</a></span><span class="tx-amt">0.00009876 FAKE <a class="text-danger" href="/spending/effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75/2" title="Spent">➡</a></span></td>`,
   405  				`</html>`,
   406  			},
   407  		},
   408  		{
   409  			name:        "explorerSearch not found",
   410  			r:           newGetRequest(ts.URL + "/search?q=1234"),
   411  			status:      http.StatusOK,
   412  			contentType: "text/html; charset=utf-8",
   413  			body: []string{
   414  				`<a class="navbar-brand" href="/">Fake Coin Explorer</a>`,
   415  				`<h1>Error</h1>`,
   416  				`<h4>No matching records found for &#39;1234&#39;</h4>`,
   417  				`</html>`,
   418  			},
   419  		},
   420  		{
   421  			name:        "explorerSendTx",
   422  			r:           newGetRequest(ts.URL + "/sendtx"),
   423  			status:      http.StatusOK,
   424  			contentType: "text/html; charset=utf-8",
   425  			body: []string{
   426  				`<a class="navbar-brand" href="/">Fake Coin Explorer</a>`,
   427  				`<h1>Send Raw Transaction</h1>`,
   428  				`<textarea class="form-control" rows="8" name="hex"></textarea>`,
   429  				`</html>`,
   430  			},
   431  		},
   432  		{
   433  			name:        "explorerSendTx POST",
   434  			r:           newPostFormRequest(ts.URL+"/sendtx", "hex", "12341234"),
   435  			status:      http.StatusOK,
   436  			contentType: "text/html; charset=utf-8",
   437  			body: []string{
   438  				`<a class="navbar-brand" href="/">Fake Coin Explorer</a>`,
   439  				`<h1>Send Raw Transaction</h1>`,
   440  				`<textarea class="form-control" rows="8" name="hex">12341234</textarea>`,
   441  				`<div class="alert alert-danger">Invalid data</div>`,
   442  				`</html>`,
   443  			},
   444  		},
   445  		{
   446  			name:        "apiIndex",
   447  			r:           newGetRequest(ts.URL + "/api"),
   448  			status:      http.StatusOK,
   449  			contentType: "application/json; charset=utf-8",
   450  			body: []string{
   451  				`{"blockbook":{"coin":"Fakecoin"`,
   452  				`"bestHeight":225494`,
   453  				`"decimals":8`,
   454  				`"backend":{"chain":"fakecoin","blocks":2,"headers":2,"bestBlockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6"`,
   455  				`"version":"001001","subversion":"/Fakecoin:0.0.1/"`,
   456  			},
   457  		},
   458  		{
   459  			name:        "apiBlockIndex",
   460  			r:           newGetRequest(ts.URL + "/api/block-index/"),
   461  			status:      http.StatusOK,
   462  			contentType: "application/json; charset=utf-8",
   463  			body: []string{
   464  				`{"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6"}`,
   465  			},
   466  		},
   467  		{
   468  			name:        "apiTx v1",
   469  			r:           newGetRequest(ts.URL + "/api/v1/tx/05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07"),
   470  			status:      http.StatusOK,
   471  			contentType: "application/json; charset=utf-8",
   472  			body: []string{
   473  				`{"txid":"05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07","vin":[{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vout":2,"n":0,"scriptSig":{},"addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"value":"0.00009876"}],"vout":[{"value":"0.00009","n":0,"scriptPubKey":{"hex":"a914e921fc4912a315078f370d959f2c4f7b6d2a683c87","addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"]},"spent":false}],"blockhash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockheight":225494,"confirmations":1,"time":1521595678,"blocktime":1521595678,"valueOut":"0.00009","valueIn":"0.00009876","fees":"0.00000876","hex":""}`,
   474  			},
   475  		},
   476  		{
   477  			name:        "apiTx - not found v1",
   478  			r:           newGetRequest(ts.URL + "/api/v1/tx/1232e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07"),
   479  			status:      http.StatusBadRequest,
   480  			contentType: "application/json; charset=utf-8",
   481  			body: []string{
   482  				`{"error":"Transaction '1232e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07' not found"}`,
   483  			},
   484  		},
   485  		{
   486  			name:        "apiTx v2",
   487  			r:           newGetRequest(ts.URL + "/api/v2/tx/05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07"),
   488  			status:      http.StatusOK,
   489  			contentType: "application/json; charset=utf-8",
   490  			body: []string{
   491  				`{"txid":"05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07","vin":[{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vout":2,"n":0,"addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"isAddress":true,"value":"9876"}],"vout":[{"value":"9000","n":0,"hex":"a914e921fc4912a315078f370d959f2c4f7b6d2a683c87","addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"isAddress":true}],"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockHeight":225494,"confirmations":1,"blockTime":1521595678,"value":"9000","valueIn":"9876","fees":"876"}`,
   492  			},
   493  		},
   494  		{
   495  			name:        "apiTx - not found v2",
   496  			r:           newGetRequest(ts.URL + "/api/v2/tx/1232e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07"),
   497  			status:      http.StatusBadRequest,
   498  			contentType: "application/json; charset=utf-8",
   499  			body: []string{
   500  				`{"error":"Transaction '1232e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07' not found"}`,
   501  			},
   502  		},
   503  		{
   504  			name:        "apiTxSpecific",
   505  			r:           newGetRequest(ts.URL + "/api/tx-specific/00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840"),
   506  			status:      http.StatusOK,
   507  			contentType: "application/json; charset=utf-8",
   508  			body: []string{
   509  				`{"hex":"","txid":"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840","version":0,"locktime":0,"vin":[],"vout":[{"ValueSat":100000000,"value":0,"n":0,"scriptPubKey":{"hex":"76a914010d39800f86122416e28f485029acf77507169288ac","addresses":null}},{"ValueSat":12345,"value":0,"n":1,"scriptPubKey":{"hex":"76a9148bdf0aa3c567aa5975c2e61321b8bebbe7293df688ac","addresses":null}},{"ValueSat":12345,"value":0,"n":2,"scriptPubKey":{"hex":"76a9148bdf0aa3c567aa5975c2e61321b8bebbe7293df688ac","addresses":null}}],"confirmations":2,"time":1521515026,"blocktime":1521515026}`,
   510  			},
   511  		},
   512  		{
   513  			name:        "apiFeeStats",
   514  			r:           newGetRequest(ts.URL + "/api/v2/feestats/225494"),
   515  			status:      http.StatusOK,
   516  			contentType: "application/json; charset=utf-8",
   517  			body: []string{
   518  				`{"txCount":3,"totalFeesSat":"1284","averageFeePerKb":1398,"decilesFeePerKb":[155,155,155,155,1679,1679,1679,2361,2361,2361,2361]}`,
   519  			},
   520  		},
   521  		{
   522  			name:        "apiFiatRates missing currency",
   523  			r:           newGetRequest(ts.URL + "/api/v2/tickers"),
   524  			status:      http.StatusOK,
   525  			contentType: "application/json; charset=utf-8",
   526  			body: []string{
   527  				`{"ts":1574346615,"rates":{"eur":7134.1,"usd":7914.5}}`,
   528  			},
   529  		},
   530  		{
   531  			name:        "apiFiatRates get last rate",
   532  			r:           newGetRequest(ts.URL + "/api/v2/tickers?currency=usd"),
   533  			status:      http.StatusOK,
   534  			contentType: "application/json; charset=utf-8",
   535  			body: []string{
   536  				`{"ts":1574346615,"rates":{"usd":7914.5}}`,
   537  			},
   538  		},
   539  		{
   540  			name:        "apiFiatRates get rate by exact timestamp",
   541  			r:           newGetRequest(ts.URL + "/api/v2/tickers?currency=usd&timestamp=1574344800"),
   542  			status:      http.StatusOK,
   543  			contentType: "application/json; charset=utf-8",
   544  			body: []string{
   545  				`{"ts":1574344800,"rates":{"usd":7814.5}}`,
   546  			},
   547  		},
   548  		{
   549  			name:        "apiFiatRates incorrect timestamp",
   550  			r:           newGetRequest(ts.URL + "/api/v2/tickers?currency=usd&timestamp=yesterday"),
   551  			status:      http.StatusBadRequest,
   552  			contentType: "application/json; charset=utf-8",
   553  			body: []string{
   554  				`{"error":"Parameter \"timestamp\" is not a valid Unix timestamp."}`,
   555  			},
   556  		},
   557  		{
   558  			name:        "apiFiatRates future timestamp",
   559  			r:           newGetRequest(ts.URL + "/api/v2/tickers?currency=usd&timestamp=7980386400"),
   560  			status:      http.StatusOK,
   561  			contentType: "application/json; charset=utf-8",
   562  			body: []string{
   563  				`{"ts":7980386400,"rates":{"usd":-1}}`,
   564  			},
   565  		},
   566  		{
   567  			name:        "apiFiatRates future timestamp, all currencies",
   568  			r:           newGetRequest(ts.URL + "/api/v2/tickers?timestamp=7980386400"),
   569  			status:      http.StatusOK,
   570  			contentType: "application/json; charset=utf-8",
   571  			body: []string{
   572  				`{"ts":7980386400,"rates":{}}`,
   573  			},
   574  		},
   575  		{
   576  			name:        "apiFiatRates get EUR rate (exact timestamp)",
   577  			r:           newGetRequest(ts.URL + "/api/v2/tickers?timestamp=1574344800&currency=eur"),
   578  			status:      http.StatusOK,
   579  			contentType: "application/json; charset=utf-8",
   580  			body: []string{
   581  				`{"ts":1574344800,"rates":{"eur":7100}}`,
   582  			},
   583  		},
   584  		{
   585  			name:        "apiFiatRates get closest rate",
   586  			r:           newGetRequest(ts.URL + "/api/v2/tickers?timestamp=1357045200&currency=usd"),
   587  			status:      http.StatusOK,
   588  			contentType: "application/json; charset=utf-8",
   589  			body: []string{
   590  				`{"ts":1521511200,"rates":{"usd":2000}}`,
   591  			},
   592  		},
   593  		{
   594  			name:        "apiFiatRates get rate by block height",
   595  			r:           newGetRequest(ts.URL + "/api/v2/tickers?block=225494&currency=usd"),
   596  			status:      http.StatusOK,
   597  			contentType: "application/json; charset=utf-8",
   598  			body: []string{
   599  				`{"ts":1521611721,"rates":{"usd":2003}}`,
   600  			},
   601  		},
   602  		{
   603  			name:        "apiFiatRates get rate for EUR",
   604  			r:           newGetRequest(ts.URL + "/api/v2/tickers?timestamp=1574346615&currency=eur"),
   605  			status:      http.StatusOK,
   606  			contentType: "application/json; charset=utf-8",
   607  			body: []string{
   608  				`{"ts":1574346615,"rates":{"eur":7134.1}}`,
   609  			},
   610  		},
   611  		{
   612  			name:        "apiFiatRates get exact rate for an incorrect currency",
   613  			r:           newGetRequest(ts.URL + "/api/v2/tickers?timestamp=1574346615&currency=does_not_exist"),
   614  			status:      http.StatusOK,
   615  			contentType: "application/json; charset=utf-8",
   616  			body: []string{
   617  				`{"ts":1574346615,"rates":{"does_not_exist":-1}}`,
   618  			},
   619  		},
   620  		{
   621  			name:        "apiTickerList",
   622  			r:           newGetRequest(ts.URL + "/api/v2/tickers-list?timestamp=1574346615"),
   623  			status:      http.StatusOK,
   624  			contentType: "application/json; charset=utf-8",
   625  			body: []string{
   626  				`{"ts":1574346615,"available_currencies":["eur","usd"]}`,
   627  			},
   628  		},
   629  		{
   630  			name:        "apiAddress v1",
   631  			r:           newGetRequest(ts.URL + "/api/v1/address/mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"),
   632  			status:      http.StatusOK,
   633  			contentType: "application/json; charset=utf-8",
   634  			body: []string{
   635  				`{"page":1,"totalPages":1,"itemsOnPage":1000,"addrStr":"mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw","balance":"0","totalReceived":"12345.67890123","totalSent":"12345.67890123","unconfirmedBalance":"0","unconfirmedTxApperances":0,"txApperances":2,"transactions":["7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75"]}`,
   636  			},
   637  		},
   638  		{
   639  			name:        "apiAddress v2",
   640  			r:           newGetRequest(ts.URL + "/api/v2/address/mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"),
   641  			status:      http.StatusOK,
   642  			contentType: "application/json; charset=utf-8",
   643  			body: []string{
   644  				`{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw","balance":"0","totalReceived":"1234567890123","totalSent":"1234567890123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"txids":["7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75"]}`,
   645  			},
   646  		},
   647  		{
   648  			name:        "apiAddress v2 details=basic",
   649  			r:           newGetRequest(ts.URL + "/api/v2/address/mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw?details=basic"),
   650  			status:      http.StatusOK,
   651  			contentType: "application/json; charset=utf-8",
   652  			body: []string{
   653  				`{"address":"mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw","balance":"0","totalReceived":"1234567890123","totalSent":"1234567890123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2}`,
   654  			},
   655  		},
   656  		{
   657  			name:        "apiAddress v2 details=txs",
   658  			r:           newGetRequest(ts.URL + "/api/v2/address/mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw?details=txs"),
   659  			status:      http.StatusOK,
   660  			contentType: "application/json; charset=utf-8",
   661  			body: []string{
   662  				`{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw","balance":"0","totalReceived":"1234567890123","totalSent":"1234567890123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","vin":[{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","n":0,"addresses":["mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"],"isAddress":true,"value":"1234567890123"},{"txid":"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840","vout":1,"n":1,"addresses":["mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"],"isAddress":true,"value":"12345"}],"vout":[{"value":"317283951061","n":0,"spent":true,"hex":"76a914ccaaaf374e1b06cb83118453d102587b4273d09588ac","addresses":["mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX"],"isAddress":true},{"value":"917283951061","n":1,"hex":"76a9148d802c045445df49613f6a70ddd2e48526f3701f88ac","addresses":["mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL"],"isAddress":true},{"value":"0","n":2,"hex":"6a072020f1686f6a20","addresses":["OP_RETURN 2020f1686f6a20"],"isAddress":false}],"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockHeight":225494,"confirmations":1,"blockTime":1521595678,"value":"1234567902122","valueIn":"1234567902468","fees":"346"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vin":[],"vout":[{"value":"1234567890123","n":0,"spent":true,"hex":"76a914a08eae93007f22668ab5e4a9c83c8cd1c325e3e088ac","addresses":["mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"],"isAddress":true},{"value":"1","n":1,"spent":true,"hex":"a91452724c5178682f70e0ba31c6ec0633755a3b41d987","addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true},{"value":"9876","n":2,"spent":true,"hex":"a914e921fc4912a315078f370d959f2c4f7b6d2a683c87","addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"isAddress":true}],"blockHash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","blockHeight":225493,"confirmations":2,"blockTime":1521515026,"value":"1234567900000","valueIn":"0","fees":"0"}]}`,
   663  			},
   664  		},
   665  		{
   666  			name:        "apiAddress v2 missing address",
   667  			r:           newGetRequest(ts.URL + "/api/v2/address/"),
   668  			status:      http.StatusBadRequest,
   669  			contentType: "application/json; charset=utf-8",
   670  			body: []string{
   671  				`{"error":"Missing address"}`,
   672  			},
   673  		},
   674  		{
   675  			name:        "apiXpub v2 default",
   676  			r:           newGetRequest(ts.URL + "/api/v2/xpub/" + dbtestdata.Xpub),
   677  			status:      http.StatusOK,
   678  			contentType: "application/json; charset=utf-8",
   679  			body: []string{
   680  				`{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"txids":["3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75"],"usedTokens":2,"tokens":[{"type":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"}]}`,
   681  			},
   682  		},
   683  		{
   684  			name:        "apiXpub v2 tokens=used",
   685  			r:           newGetRequest(ts.URL + "/api/v2/xpub/" + dbtestdata.Xpub + "?tokens=used"),
   686  			status:      http.StatusOK,
   687  			contentType: "application/json; charset=utf-8",
   688  			body: []string{
   689  				`{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"txids":["3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75"],"usedTokens":2,"tokens":[{"type":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8,"balance":"0","totalReceived":"1","totalSent":"1"},{"type":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"}]}`,
   690  			},
   691  		},
   692  		{
   693  			name:        "apiXpub v2 tokens=derived",
   694  			r:           newGetRequest(ts.URL + "/api/v2/xpub/" + dbtestdata.Xpub + "?tokens=derived"),
   695  			status:      http.StatusOK,
   696  			contentType: "application/json; charset=utf-8",
   697  			body: []string{
   698  				`{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"txids":["3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75"],"usedTokens":2,"tokens":[{"type":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8,"balance":"0","totalReceived":"1","totalSent":"1"},{"type":"XPUBAddress","name":"2MsYfbi6ZdVXLDNrYAQ11ja9Sd3otMk4Pmj","path":"m/49'/1'/33'/0/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuAZNAjLSo6RLFad2fvHSfgqBD7BoEVy4T","path":"m/49'/1'/33'/0/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NEqKzw3BosGnBE9by5uaDy5QgwjHac4Zbg","path":"m/49'/1'/33'/0/3","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mw7vJNC8zUK6VNN4CEjtoTYmuNPLewxZzV","path":"m/49'/1'/33'/0/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N1kvo97NFASPXiwephZUxE9PRXunjTxEc4","path":"m/49'/1'/33'/0/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuWrWMzoBt8VDFNvPmpJf42M1GTUs85fPx","path":"m/49'/1'/33'/0/6","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuVZ2Ca6Da9zmYynt49Rx7uikAgubGcymF","path":"m/49'/1'/33'/0/7","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzRGWDUmrPP9HwYu4B43QGCTLwoop5cExa","path":"m/49'/1'/33'/0/8","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5C9EEWJzyBXhpyPHqa3UNed73Amsi5b3L","path":"m/49'/1'/33'/0/9","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzNawz2zjwq1L85GDE3YydEJGJYfXxaWkk","path":"m/49'/1'/33'/0/10","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N7NdeuAMgL57WE7QCeV2gTWi2Um8iAu5dA","path":"m/49'/1'/33'/0/11","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8JQEP6DSHEZHNsSDPA1gHMUq9YFndhkfV","path":"m/49'/1'/33'/0/12","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mvbn3YXqKZVpQKugaoQrfjSYPvz76RwZkC","path":"m/49'/1'/33'/0/13","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8MRNxCfwUY9TSW27X9ooGYtqgrGCfLRHx","path":"m/49'/1'/33'/0/14","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N6HvwrHC113KYZAmCtJ9XJNWgaTcnFunCM","path":"m/49'/1'/33'/0/15","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NEo3oNyHUoi7rmRWee7wki37jxPWsWCopJ","path":"m/49'/1'/33'/0/16","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mzm5KY8qdFbDHsQfy4akXbFvbR3FAwDuVo","path":"m/49'/1'/33'/0/17","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NGMwftmQCogp6XZNGvgiybz3WZysvsJzqC","path":"m/49'/1'/33'/0/18","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N3fJrrefndYjLGycvFFfYgevpZtcRKCkRD","path":"m/49'/1'/33'/0/19","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N1T7TnHBwfdpBoyw53EGUL7vuJmb2mU6jF","path":"m/49'/1'/33'/0/20","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzSBtRWHbBjeUcu3H5VRDqkvz5sfmDxJKo","path":"m/49'/1'/33'/1/0","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MtShtAJYb1afWduUTwF1SixJjan7urZKke","path":"m/49'/1'/33'/1/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N3cP668SeqyBEr9gnB4yQEmU3VyxeRYith","path":"m/49'/1'/33'/1/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"},{"type":"XPUBAddress","name":"2NEzatauNhf9kPTwwj6ZfYKjUdy52j4hVUL","path":"m/49'/1'/33'/1/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4RjsDp4LBpkNqyF91aNjgpF9CwDwBkJZq","path":"m/49'/1'/33'/1/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8XygTmQc4NoBBPEy3yybnfCYhsxFtzPDY","path":"m/49'/1'/33'/1/6","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5BjBomZvb48sccK2vwLMiQ5ETKp1fdPVn","path":"m/49'/1'/33'/1/7","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MybMwbZRPCGU3SMWPwQCpDkbcQFw5Hbwen","path":"m/49'/1'/33'/1/8","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N7HexL4dyAQc7Th4iqcCW4hZuyiZsLWf74","path":"m/49'/1'/33'/1/9","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NF6X5FDGWrQj4nQrfP6hA77zB5WAc1DGup","path":"m/49'/1'/33'/1/10","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4ZRPdvc7BVioBTohy4F6QtxreqcjNj26b","path":"m/49'/1'/33'/1/11","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mtfho1rLmevh4qTnkYWxZEFCWteDMtTcUF","path":"m/49'/1'/33'/1/12","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NFUCphKYvmMcNZRZrF261mRX6iADVB9Qms","path":"m/49'/1'/33'/1/13","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5kBNMB8qgxE4Y4f8J19fScsE49J4aNvoJ","path":"m/49'/1'/33'/1/14","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NANWCaefhCKdXMcW8NbZnnrFRDvhJN2wPy","path":"m/49'/1'/33'/1/15","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NFHw7Yo2Bz8D2wGAYHW9qidbZFLpfJ72qB","path":"m/49'/1'/33'/1/16","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NBDSsBgy5PpFniLCb1eAFHcSxgxwPSDsZa","path":"m/49'/1'/33'/1/17","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NDWCSQHogc7sCuc2WoYt9PX2i2i6a5k6dX","path":"m/49'/1'/33'/1/18","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8vNyDP7iSDjm3BKpXrbDjAxyphqfvnJz8","path":"m/49'/1'/33'/1/19","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4tFKLurSbMusAyq1tv4tzymVjveAFV1Vb","path":"m/49'/1'/33'/1/20","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NBx5WwjAr2cH6Yqrp3Vsf957HtRKwDUVdX","path":"m/49'/1'/33'/1/21","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NBu1seHTaFhQxbcW5L5BkZzqFLGmZqpxsa","path":"m/49'/1'/33'/1/22","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NCDLoea22jGsXuarfT1n2QyCUh6RFhAPnT","path":"m/49'/1'/33'/1/23","transfers":0,"decimals":8}]}`,
   699  			},
   700  		},
   701  		{
   702  			name:        "apiXpub v2 details=basic",
   703  			r:           newGetRequest(ts.URL + "/api/v2/xpub/" + dbtestdata.Xpub + "?details=basic"),
   704  			status:      http.StatusOK,
   705  			contentType: "application/json; charset=utf-8",
   706  			body: []string{
   707  				`{"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":3,"usedTokens":2}`,
   708  			},
   709  		},
   710  		{
   711  			name:        "apiXpub v2 details=tokens?tokens=used",
   712  			r:           newGetRequest(ts.URL + "/api/v2/xpub/" + dbtestdata.Xpub + "?details=tokens&tokens=used"),
   713  			status:      http.StatusOK,
   714  			contentType: "application/json; charset=utf-8",
   715  			body: []string{
   716  				`{"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":3,"usedTokens":2,"tokens":[{"type":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8},{"type":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8}]}`,
   717  			},
   718  		},
   719  		{
   720  			name:        "apiXpub v2 details=tokenBalances",
   721  			r:           newGetRequest(ts.URL + "/api/v2/xpub/" + dbtestdata.Xpub + "?details=tokenBalances"),
   722  			status:      http.StatusOK,
   723  			contentType: "application/json; charset=utf-8",
   724  			body: []string{
   725  				`{"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":3,"usedTokens":2,"tokens":[{"type":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"}]}`,
   726  			},
   727  		},
   728  		{
   729  			name:        "apiXpub v2 details=txs&tokens=derived&gap=5&from=225494&to=225494&pageSize=3",
   730  			r:           newGetRequest(ts.URL + "/api/v2/xpub/" + dbtestdata.Xpub + "?details=txs&tokens=derived&gap=5&from=225494&to=225494&pageSize=3"),
   731  			status:      http.StatusOK,
   732  			contentType: "application/json; charset=utf-8",
   733  			body: []string{
   734  				`{"page":1,"totalPages":1,"itemsOnPage":3,"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","vin":[{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","n":0,"addresses":["mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX"],"isAddress":true,"value":"317283951061"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vout":1,"n":1,"addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true,"value":"1"}],"vout":[{"value":"118641975500","n":0,"hex":"a91495e9fbe306449c991d314afe3c3567d5bf78efd287","addresses":["2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu"],"isAddress":true},{"value":"198641975500","n":1,"hex":"76a9143f8ba3fda3ba7b69f5818086e12223c6dd25e3c888ac","addresses":["mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP"],"isAddress":true}],"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockHeight":225494,"confirmations":1,"blockTime":1521595678,"value":"317283951000","valueIn":"317283951062","fees":"62"}],"usedTokens":2,"tokens":[{"type":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8,"balance":"0","totalReceived":"1","totalSent":"1"},{"type":"XPUBAddress","name":"2MsYfbi6ZdVXLDNrYAQ11ja9Sd3otMk4Pmj","path":"m/49'/1'/33'/0/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuAZNAjLSo6RLFad2fvHSfgqBD7BoEVy4T","path":"m/49'/1'/33'/0/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NEqKzw3BosGnBE9by5uaDy5QgwjHac4Zbg","path":"m/49'/1'/33'/0/3","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mw7vJNC8zUK6VNN4CEjtoTYmuNPLewxZzV","path":"m/49'/1'/33'/0/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N1kvo97NFASPXiwephZUxE9PRXunjTxEc4","path":"m/49'/1'/33'/0/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzSBtRWHbBjeUcu3H5VRDqkvz5sfmDxJKo","path":"m/49'/1'/33'/1/0","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MtShtAJYb1afWduUTwF1SixJjan7urZKke","path":"m/49'/1'/33'/1/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N3cP668SeqyBEr9gnB4yQEmU3VyxeRYith","path":"m/49'/1'/33'/1/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"},{"type":"XPUBAddress","name":"2NEzatauNhf9kPTwwj6ZfYKjUdy52j4hVUL","path":"m/49'/1'/33'/1/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4RjsDp4LBpkNqyF91aNjgpF9CwDwBkJZq","path":"m/49'/1'/33'/1/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8XygTmQc4NoBBPEy3yybnfCYhsxFtzPDY","path":"m/49'/1'/33'/1/6","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5BjBomZvb48sccK2vwLMiQ5ETKp1fdPVn","path":"m/49'/1'/33'/1/7","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MybMwbZRPCGU3SMWPwQCpDkbcQFw5Hbwen","path":"m/49'/1'/33'/1/8","transfers":0,"decimals":8}]}`,
   735  			},
   736  		},
   737  		{
   738  			name:        "apiXpub v2 missing xpub",
   739  			r:           newGetRequest(ts.URL + "/api/v2/xpub/"),
   740  			status:      http.StatusBadRequest,
   741  			contentType: "application/json; charset=utf-8",
   742  			body: []string{
   743  				`{"error":"Missing xpub"}`,
   744  			},
   745  		},
   746  		{
   747  			name:        "apiUtxo v1",
   748  			r:           newGetRequest(ts.URL + "/api/v1/utxo/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL"),
   749  			status:      http.StatusOK,
   750  			contentType: "application/json; charset=utf-8",
   751  			body: []string{
   752  				`[{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","vout":1,"amount":"9172.83951061","satoshis":917283951061,"height":225494,"confirmations":1}]`,
   753  			},
   754  		},
   755  		{
   756  			name:        "apiUtxo v2",
   757  			r:           newGetRequest(ts.URL + "/api/v2/utxo/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL"),
   758  			status:      http.StatusOK,
   759  			contentType: "application/json; charset=utf-8",
   760  			body: []string{
   761  				`[{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","vout":1,"value":"917283951061","height":225494,"confirmations":1}]`,
   762  			},
   763  		},
   764  		{
   765  			name:        "apiUtxo v2 xpub",
   766  			r:           newGetRequest(ts.URL + "/api/v2/utxo/" + dbtestdata.Xpub),
   767  			status:      http.StatusOK,
   768  			contentType: "application/json; charset=utf-8",
   769  			body: []string{
   770  				`[{"txid":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","vout":0,"value":"118641975500","height":225494,"confirmations":1,"address":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3"}]`,
   771  			},
   772  		},
   773  		{
   774  			name:        "apiBalanceHistory Addr2 v2",
   775  			r:           newGetRequest(ts.URL + "/api/v2/balancehistory/mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"),
   776  			status:      http.StatusOK,
   777  			contentType: "application/json; charset=utf-8",
   778  			body: []string{
   779  				`[{"time":1521514800,"txs":1,"received":"24690","sent":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"0","sent":"12345","rates":{"eur":1303,"usd":2003}}]`,
   780  			},
   781  		},
   782  		{
   783  			name:        "apiBalanceHistory Addr5 v2",
   784  			r:           newGetRequest(ts.URL + "/api/v2/balancehistory/2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"),
   785  			status:      http.StatusOK,
   786  			contentType: "application/json; charset=utf-8",
   787  			body: []string{
   788  				`[{"time":1521514800,"txs":1,"received":"9876","sent":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"9000","sent":"9876","rates":{"eur":1303,"usd":2003}}]`,
   789  			},
   790  		},
   791  		{
   792  			name:        "apiBalanceHistory Addr5 v2 fiatcurrency=eur",
   793  			r:           newGetRequest(ts.URL + "/api/v2/balancehistory/2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1?fiatcurrency=eur"),
   794  			status:      http.StatusOK,
   795  			contentType: "application/json; charset=utf-8",
   796  			body: []string{
   797  				`[{"time":1521514800,"txs":1,"received":"9876","sent":"0","rates":{"eur":1301}},{"time":1521594000,"txs":1,"received":"9000","sent":"9876","rates":{"eur":1303}}]`,
   798  			},
   799  		},
   800  		{
   801  			name:        "apiBalanceHistory Addr2 v2 from=1521504000&to=1521590400",
   802  			r:           newGetRequest(ts.URL + "/api/v2/balancehistory/mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz?from=1521504000&to=1521590400"),
   803  			status:      http.StatusOK,
   804  			contentType: "application/json; charset=utf-8",
   805  			body: []string{
   806  				`[{"time":1521514800,"txs":1,"received":"24690","sent":"0","rates":{"eur":1301,"usd":2001}}]`,
   807  			},
   808  		},
   809  		{
   810  			name:        "apiBalanceHistory xpub v2",
   811  			r:           newGetRequest(ts.URL + "/api/v2/balancehistory/" + dbtestdata.Xpub),
   812  			status:      http.StatusOK,
   813  			contentType: "application/json; charset=utf-8",
   814  			body: []string{
   815  				`[{"time":1521514800,"txs":1,"received":"1","sent":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"118641975500","sent":"1","rates":{"eur":1303,"usd":2003}}]`,
   816  			},
   817  		},
   818  		{
   819  			name:        "apiBalanceHistory xpub v2 from=1521504000&to=1521590400",
   820  			r:           newGetRequest(ts.URL + "/api/v2/balancehistory/" + dbtestdata.Xpub + "?from=1521504000&to=1521590400"),
   821  			status:      http.StatusOK,
   822  			contentType: "application/json; charset=utf-8",
   823  			body: []string{
   824  				`[{"time":1521514800,"txs":1,"received":"1","sent":"0","rates":{"eur":1301,"usd":2001}}]`,
   825  			},
   826  		},
   827  		{
   828  			name:        "apiBalanceHistory xpub v2 from=1521504000&to=1521590400&fiatcurrency=usd",
   829  			r:           newGetRequest(ts.URL + "/api/v2/balancehistory/" + dbtestdata.Xpub + "?from=1521504000&to=1521590400&fiatcurrency=usd"),
   830  			status:      http.StatusOK,
   831  			contentType: "application/json; charset=utf-8",
   832  			body: []string{
   833  				`[{"time":1521514800,"txs":1,"received":"1","sent":"0","rates":{"usd":2001}}]`,
   834  			},
   835  		},
   836  		{
   837  			name:        "apiBalanceHistory xpub v2 from=1521590400",
   838  			r:           newGetRequest(ts.URL + "/api/v2/balancehistory/" + dbtestdata.Xpub + "?from=1521590400"),
   839  			status:      http.StatusOK,
   840  			contentType: "application/json; charset=utf-8",
   841  			body: []string{
   842  				`[{"time":1521594000,"txs":1,"received":"118641975500","sent":"1","rates":{"eur":1303,"usd":2003}}]`,
   843  			},
   844  		},
   845  		{
   846  			name:        "apiSendTx",
   847  			r:           newGetRequest(ts.URL + "/api/v2/sendtx/1234567890"),
   848  			status:      http.StatusBadRequest,
   849  			contentType: "application/json; charset=utf-8",
   850  			body: []string{
   851  				`{"error":"Invalid data"}`,
   852  			},
   853  		},
   854  		{
   855  			name:        "apiSendTx POST",
   856  			r:           newPostRequest(ts.URL+"/api/v2/sendtx/", "123456"),
   857  			status:      http.StatusOK,
   858  			contentType: "application/json; charset=utf-8",
   859  			body: []string{
   860  				`{"result":"9876"}`,
   861  			},
   862  		},
   863  		{
   864  			name:        "apiSendTx POST empty",
   865  			r:           newPostRequest(ts.URL+"/api/v2/sendtx", ""),
   866  			status:      http.StatusBadRequest,
   867  			contentType: "application/json; charset=utf-8",
   868  			body: []string{
   869  				`{"error":"Missing tx blob"}`,
   870  			},
   871  		},
   872  		{
   873  			name:        "apiEstimateFee",
   874  			r:           newGetRequest(ts.URL + "/api/estimatefee/123?conservative=false"),
   875  			status:      http.StatusOK,
   876  			contentType: "application/json; charset=utf-8",
   877  			body: []string{
   878  				`{"result":"0.00012299"}`,
   879  			},
   880  		},
   881  		{
   882  			name:        "apiGetBlock",
   883  			r:           newGetRequest(ts.URL + "/api/v2/block/225493"),
   884  			status:      http.StatusOK,
   885  			contentType: "application/json; charset=utf-8",
   886  			body: []string{
   887  				`{"page":1,"totalPages":1,"itemsOnPage":1000,"hash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","nextBlockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","height":225493,"confirmations":2,"size":1234567,"time":1521515026,"version":0,"merkleRoot":"","nonce":"","bits":"","difficulty":"","txCount":2,"txs":[{"txid":"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840","vin":[],"vout":[{"value":"100000000","n":0,"addresses":["mfcWp7DB6NuaZsExybTTXpVgWz559Np4Ti"],"isAddress":true},{"value":"12345","n":1,"spent":true,"addresses":["mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"],"isAddress":true},{"value":"12345","n":2,"addresses":["mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"],"isAddress":true}],"blockHash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","blockHeight":225493,"confirmations":2,"blockTime":1521515026,"value":"100024690","valueIn":"0","fees":"0"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vin":[],"vout":[{"value":"1234567890123","n":0,"spent":true,"addresses":["mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"],"isAddress":true},{"value":"1","n":1,"spent":true,"addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true},{"value":"9876","n":2,"spent":true,"addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"isAddress":true}],"blockHash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","blockHeight":225493,"confirmations":2,"blockTime":1521515026,"value":"1234567900000","valueIn":"0","fees":"0"}]}`,
   888  			},
   889  		},
   890  	}
   891  
   892  	for _, tt := range tests {
   893  		t.Run(tt.name, func(t *testing.T) {
   894  			resp, err := http.DefaultClient.Do(tt.r)
   895  			if err != nil {
   896  				t.Fatal(err)
   897  			}
   898  			defer resp.Body.Close()
   899  			if resp.StatusCode != tt.status {
   900  				t.Errorf("StatusCode = %v, want %v", resp.StatusCode, tt.status)
   901  			}
   902  			if resp.Header["Content-Type"][0] != tt.contentType {
   903  				t.Errorf("Content-Type = %v, want %v", resp.Header["Content-Type"][0], tt.contentType)
   904  			}
   905  			bb, err := ioutil.ReadAll(resp.Body)
   906  			if err != nil {
   907  				t.Fatal(err)
   908  			}
   909  			b := string(bb)
   910  			for _, c := range tt.body {
   911  				if !strings.Contains(b, c) {
   912  					t.Errorf("got %v, want to contain %v", b, c)
   913  					break
   914  				}
   915  			}
   916  		})
   917  	}
   918  }
   919  
   920  func socketioTestsBitcoinType(t *testing.T, ts *httptest.Server) {
   921  	type socketioReq struct {
   922  		Method string        `json:"method"`
   923  		Params []interface{} `json:"params"`
   924  	}
   925  
   926  	url := strings.Replace(ts.URL, "http://", "ws://", 1) + "/socket.io/"
   927  	s, err := gosocketio.Dial(url, transport.GetDefaultWebsocketTransport())
   928  	if err != nil {
   929  		t.Fatal(err)
   930  	}
   931  	defer s.Close()
   932  
   933  	tests := []struct {
   934  		name string
   935  		req  socketioReq
   936  		want string
   937  	}{
   938  		{
   939  			name: "socketio getInfo",
   940  			req:  socketioReq{"getInfo", []interface{}{}},
   941  			want: `{"result":{"blocks":225494,"testnet":true,"network":"fakecoin","subversion":"/Fakecoin:0.0.1/","coin_name":"Fakecoin","about":"Blockbook - blockchain indexer for created by Trezor and used by PolisPay Wallet https://polispay.com/."}}`,
   942  		},
   943  		{
   944  			name: "socketio estimateFee",
   945  			req:  socketioReq{"estimateFee", []interface{}{17}},
   946  			want: `{"result":0.000034}`,
   947  		},
   948  		{
   949  			name: "socketio estimateSmartFee",
   950  			req:  socketioReq{"estimateSmartFee", []interface{}{19, true}},
   951  			want: `{"result":0.000019}`,
   952  		},
   953  		{
   954  			name: "socketio getAddressTxids",
   955  			req: socketioReq{"getAddressTxids", []interface{}{
   956  				[]string{"mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"},
   957  				map[string]interface{}{
   958  					"start":        2000000,
   959  					"end":          0,
   960  					"queryMempool": false,
   961  				},
   962  			}},
   963  			want: `{"result":["7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840"]}`,
   964  		},
   965  		{
   966  			name: "socketio getAddressTxids limited range",
   967  			req: socketioReq{"getAddressTxids", []interface{}{
   968  				[]string{"mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"},
   969  				map[string]interface{}{
   970  					"start":        225494,
   971  					"end":          225494,
   972  					"queryMempool": false,
   973  				},
   974  			}},
   975  			want: `{"result":["7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25"]}`,
   976  		},
   977  		{
   978  			name: "socketio getAddressHistory",
   979  			req: socketioReq{"getAddressHistory", []interface{}{
   980  				[]string{"mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"},
   981  				map[string]interface{}{
   982  					"start":        2000000,
   983  					"end":          0,
   984  					"queryMempool": false,
   985  					"from":         0,
   986  					"to":           5,
   987  				},
   988  			}},
   989  			want: `{"result":{"totalCount":2,"items":[{"addresses":{"mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz":{"inputIndexes":[1],"outputIndexes":[]}},"satoshis":-12345,"confirmations":1,"tx":{"hex":"","height":225494,"blockTimestamp":1521595678,"version":0,"hash":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","inputs":[{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","outputIndex":0,"script":"","sequence":0,"address":"mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw","satoshis":1234567890123},{"txid":"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840","outputIndex":1,"script":"","sequence":0,"address":"mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz","satoshis":12345}],"inputSatoshis":1234567902468,"outputs":[{"satoshis":317283951061,"script":"76a914ccaaaf374e1b06cb83118453d102587b4273d09588ac","address":"mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX"},{"satoshis":917283951061,"script":"76a9148d802c045445df49613f6a70ddd2e48526f3701f88ac","address":"mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL"},{"satoshis":0,"script":"6a072020f1686f6a20","address":"OP_RETURN 2020f1686f6a20"}],"outputSatoshis":1234567902122,"feeSatoshis":346}},{"addresses":{"mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz":{"inputIndexes":[],"outputIndexes":[1,2]}},"satoshis":24690,"confirmations":2,"tx":{"hex":"","height":225493,"blockTimestamp":1521515026,"version":0,"hash":"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840","inputs":[],"outputs":[{"satoshis":100000000,"script":"76a914010d39800f86122416e28f485029acf77507169288ac","address":"mfcWp7DB6NuaZsExybTTXpVgWz559Np4Ti"},{"satoshis":12345,"script":"76a9148bdf0aa3c567aa5975c2e61321b8bebbe7293df688ac","address":"mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"},{"satoshis":12345,"script":"76a9148bdf0aa3c567aa5975c2e61321b8bebbe7293df688ac","address":"mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"}],"outputSatoshis":100024690}}]}}`,
   990  		},
   991  		{
   992  			name: "socketio getBlockHeader",
   993  			req:  socketioReq{"getBlockHeader", []interface{}{225493}},
   994  			want: `{"result":{"hash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","version":0,"confirmations":0,"height":0,"chainWork":"","nextHash":"","merkleRoot":"","time":0,"medianTime":0,"nonce":0,"bits":"","difficulty":0}}`,
   995  		},
   996  		{
   997  			name: "socketio getDetailedTransaction",
   998  			req:  socketioReq{"getDetailedTransaction", []interface{}{"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71"}},
   999  			want: `{"result":{"hex":"","height":225494,"blockTimestamp":1521595678,"version":0,"hash":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","inputs":[{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","outputIndex":0,"script":"","sequence":0,"address":"mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX","satoshis":317283951061},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","outputIndex":1,"script":"","sequence":0,"address":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","satoshis":1}],"inputSatoshis":317283951062,"outputs":[{"satoshis":118641975500,"script":"a91495e9fbe306449c991d314afe3c3567d5bf78efd287","address":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu"},{"satoshis":198641975500,"script":"76a9143f8ba3fda3ba7b69f5818086e12223c6dd25e3c888ac","address":"mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP"}],"outputSatoshis":317283951000,"feeSatoshis":62}}`,
  1000  		},
  1001  		{
  1002  			name: "socketio sendTransaction",
  1003  			req:  socketioReq{"sendTransaction", []interface{}{"010000000001019d64f0c72a0d206001decbffaa722eb1044534c"}},
  1004  			want: `{"error":{"message":"Invalid data"}}`,
  1005  		},
  1006  	}
  1007  
  1008  	for _, tt := range tests {
  1009  		t.Run(tt.name, func(t *testing.T) {
  1010  			resp, err := s.Ack("message", tt.req, time.Second*3)
  1011  			if err != nil {
  1012  				t.Errorf("Socketio error %v", err)
  1013  			}
  1014  			if resp != tt.want {
  1015  				t.Errorf("got %v, want %v", resp, tt.want)
  1016  			}
  1017  		})
  1018  	}
  1019  }
  1020  
  1021  func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
  1022  	type websocketReq struct {
  1023  		ID     string      `json:"id"`
  1024  		Method string      `json:"method"`
  1025  		Params interface{} `json:"params,omitempty"`
  1026  	}
  1027  	type websocketResp struct {
  1028  		ID string `json:"id"`
  1029  	}
  1030  	url := strings.Replace(ts.URL, "http://", "ws://", 1) + "/websocket"
  1031  	s, _, err := websocket.DefaultDialer.Dial(url, nil)
  1032  	if err != nil {
  1033  		t.Fatal(err)
  1034  	}
  1035  	defer s.Close()
  1036  
  1037  	tests := []struct {
  1038  		name string
  1039  		req  websocketReq
  1040  		want string
  1041  	}{
  1042  		{
  1043  			name: "websocket getInfo",
  1044  			req: websocketReq{
  1045  				Method: "getInfo",
  1046  			},
  1047  			want: `{"id":"0","data":{"name":"Fakecoin","shortcut":"FAKE","decimals":8,"version":"unknown","bestHeight":225494,"bestHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","block0Hash":"","testnet":true}}`,
  1048  		},
  1049  		{
  1050  			name: "websocket getBlockHash",
  1051  			req: websocketReq{
  1052  				Method: "getBlockHash",
  1053  				Params: map[string]interface{}{
  1054  					"height": 225494,
  1055  				},
  1056  			},
  1057  			want: `{"id":"1","data":{"hash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6"}}`,
  1058  		},
  1059  		{
  1060  			name: "websocket getAccountInfo",
  1061  			req: websocketReq{
  1062  				Method: "getAccountInfo",
  1063  				Params: map[string]interface{}{
  1064  					"descriptor": dbtestdata.Xpub,
  1065  					"details":    "txs",
  1066  				},
  1067  			},
  1068  			want: `{"id":"2","data":{"page":1,"totalPages":1,"itemsOnPage":25,"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","vin":[{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","n":0,"addresses":["mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX"],"isAddress":true,"value":"317283951061"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vout":1,"n":1,"addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true,"value":"1"}],"vout":[{"value":"118641975500","n":0,"hex":"a91495e9fbe306449c991d314afe3c3567d5bf78efd287","addresses":["2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu"],"isAddress":true},{"value":"198641975500","n":1,"hex":"76a9143f8ba3fda3ba7b69f5818086e12223c6dd25e3c888ac","addresses":["mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP"],"isAddress":true}],"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockHeight":225494,"confirmations":1,"blockTime":1521595678,"value":"317283951000","valueIn":"317283951062","fees":"62"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vin":[],"vout":[{"value":"1234567890123","n":0,"spent":true,"hex":"76a914a08eae93007f22668ab5e4a9c83c8cd1c325e3e088ac","addresses":["mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"],"isAddress":true},{"value":"1","n":1,"spent":true,"hex":"a91452724c5178682f70e0ba31c6ec0633755a3b41d987","addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true},{"value":"9876","n":2,"spent":true,"hex":"a914e921fc4912a315078f370d959f2c4f7b6d2a683c87","addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"isAddress":true}],"blockHash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","blockHeight":225493,"confirmations":2,"blockTime":1521515026,"value":"1234567900000","valueIn":"0","fees":"0"}],"usedTokens":2,"tokens":[{"type":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8,"balance":"0","totalReceived":"1","totalSent":"1"},{"type":"XPUBAddress","name":"2MsYfbi6ZdVXLDNrYAQ11ja9Sd3otMk4Pmj","path":"m/49'/1'/33'/0/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuAZNAjLSo6RLFad2fvHSfgqBD7BoEVy4T","path":"m/49'/1'/33'/0/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NEqKzw3BosGnBE9by5uaDy5QgwjHac4Zbg","path":"m/49'/1'/33'/0/3","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mw7vJNC8zUK6VNN4CEjtoTYmuNPLewxZzV","path":"m/49'/1'/33'/0/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N1kvo97NFASPXiwephZUxE9PRXunjTxEc4","path":"m/49'/1'/33'/0/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuWrWMzoBt8VDFNvPmpJf42M1GTUs85fPx","path":"m/49'/1'/33'/0/6","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuVZ2Ca6Da9zmYynt49Rx7uikAgubGcymF","path":"m/49'/1'/33'/0/7","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzRGWDUmrPP9HwYu4B43QGCTLwoop5cExa","path":"m/49'/1'/33'/0/8","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5C9EEWJzyBXhpyPHqa3UNed73Amsi5b3L","path":"m/49'/1'/33'/0/9","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzNawz2zjwq1L85GDE3YydEJGJYfXxaWkk","path":"m/49'/1'/33'/0/10","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N7NdeuAMgL57WE7QCeV2gTWi2Um8iAu5dA","path":"m/49'/1'/33'/0/11","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8JQEP6DSHEZHNsSDPA1gHMUq9YFndhkfV","path":"m/49'/1'/33'/0/12","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mvbn3YXqKZVpQKugaoQrfjSYPvz76RwZkC","path":"m/49'/1'/33'/0/13","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8MRNxCfwUY9TSW27X9ooGYtqgrGCfLRHx","path":"m/49'/1'/33'/0/14","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N6HvwrHC113KYZAmCtJ9XJNWgaTcnFunCM","path":"m/49'/1'/33'/0/15","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NEo3oNyHUoi7rmRWee7wki37jxPWsWCopJ","path":"m/49'/1'/33'/0/16","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mzm5KY8qdFbDHsQfy4akXbFvbR3FAwDuVo","path":"m/49'/1'/33'/0/17","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NGMwftmQCogp6XZNGvgiybz3WZysvsJzqC","path":"m/49'/1'/33'/0/18","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N3fJrrefndYjLGycvFFfYgevpZtcRKCkRD","path":"m/49'/1'/33'/0/19","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N1T7TnHBwfdpBoyw53EGUL7vuJmb2mU6jF","path":"m/49'/1'/33'/0/20","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzSBtRWHbBjeUcu3H5VRDqkvz5sfmDxJKo","path":"m/49'/1'/33'/1/0","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MtShtAJYb1afWduUTwF1SixJjan7urZKke","path":"m/49'/1'/33'/1/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N3cP668SeqyBEr9gnB4yQEmU3VyxeRYith","path":"m/49'/1'/33'/1/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8,"balance":"118641975500","totalReceived":"118641975500","totalSent":"0"},{"type":"XPUBAddress","name":"2NEzatauNhf9kPTwwj6ZfYKjUdy52j4hVUL","path":"m/49'/1'/33'/1/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4RjsDp4LBpkNqyF91aNjgpF9CwDwBkJZq","path":"m/49'/1'/33'/1/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8XygTmQc4NoBBPEy3yybnfCYhsxFtzPDY","path":"m/49'/1'/33'/1/6","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5BjBomZvb48sccK2vwLMiQ5ETKp1fdPVn","path":"m/49'/1'/33'/1/7","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MybMwbZRPCGU3SMWPwQCpDkbcQFw5Hbwen","path":"m/49'/1'/33'/1/8","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N7HexL4dyAQc7Th4iqcCW4hZuyiZsLWf74","path":"m/49'/1'/33'/1/9","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NF6X5FDGWrQj4nQrfP6hA77zB5WAc1DGup","path":"m/49'/1'/33'/1/10","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4ZRPdvc7BVioBTohy4F6QtxreqcjNj26b","path":"m/49'/1'/33'/1/11","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mtfho1rLmevh4qTnkYWxZEFCWteDMtTcUF","path":"m/49'/1'/33'/1/12","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NFUCphKYvmMcNZRZrF261mRX6iADVB9Qms","path":"m/49'/1'/33'/1/13","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5kBNMB8qgxE4Y4f8J19fScsE49J4aNvoJ","path":"m/49'/1'/33'/1/14","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NANWCaefhCKdXMcW8NbZnnrFRDvhJN2wPy","path":"m/49'/1'/33'/1/15","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NFHw7Yo2Bz8D2wGAYHW9qidbZFLpfJ72qB","path":"m/49'/1'/33'/1/16","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NBDSsBgy5PpFniLCb1eAFHcSxgxwPSDsZa","path":"m/49'/1'/33'/1/17","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NDWCSQHogc7sCuc2WoYt9PX2i2i6a5k6dX","path":"m/49'/1'/33'/1/18","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8vNyDP7iSDjm3BKpXrbDjAxyphqfvnJz8","path":"m/49'/1'/33'/1/19","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4tFKLurSbMusAyq1tv4tzymVjveAFV1Vb","path":"m/49'/1'/33'/1/20","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NBx5WwjAr2cH6Yqrp3Vsf957HtRKwDUVdX","path":"m/49'/1'/33'/1/21","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NBu1seHTaFhQxbcW5L5BkZzqFLGmZqpxsa","path":"m/49'/1'/33'/1/22","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NCDLoea22jGsXuarfT1n2QyCUh6RFhAPnT","path":"m/49'/1'/33'/1/23","transfers":0,"decimals":8}]}}`,
  1069  		},
  1070  		{
  1071  			name: "websocket getAccountInfo",
  1072  			req: websocketReq{
  1073  				Method: "getAccountInfo",
  1074  				Params: map[string]interface{}{
  1075  					"descriptor": dbtestdata.Addr4,
  1076  					"details":    "txids",
  1077  				},
  1078  			},
  1079  			want: `{"id":"3","data":{"page":1,"totalPages":1,"itemsOnPage":25,"address":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","balance":"0","totalReceived":"1","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"txids":["3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75"]}}`,
  1080  		},
  1081  		{
  1082  			name: "websocket getAccountInfo xpub",
  1083  			req: websocketReq{
  1084  				Method: "getAccountInfo",
  1085  				Params: map[string]interface{}{
  1086  					"descriptor": dbtestdata.Xpub,
  1087  					"details":    "tokens",
  1088  					"tokens":     "derived",
  1089  					"gap":        10,
  1090  				},
  1091  			},
  1092  			want: `{"id":"4","data":{"address":"upub5E1xjDmZ7Hhej6LPpS8duATdKXnRYui7bDYj6ehfFGzWDZtmCmQkZhc3Zb7kgRLtHWd16QFxyP86JKL3ShZEBFX88aciJ3xyocuyhZZ8g6q","balance":"118641975500","totalReceived":"118641975501","totalSent":"1","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":3,"usedTokens":2,"tokens":[{"type":"XPUBAddress","name":"2MzmAKayJmja784jyHvRUW1bXPget1csRRG","path":"m/49'/1'/33'/0/0","transfers":2,"decimals":8},{"type":"XPUBAddress","name":"2MsYfbi6ZdVXLDNrYAQ11ja9Sd3otMk4Pmj","path":"m/49'/1'/33'/0/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuAZNAjLSo6RLFad2fvHSfgqBD7BoEVy4T","path":"m/49'/1'/33'/0/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NEqKzw3BosGnBE9by5uaDy5QgwjHac4Zbg","path":"m/49'/1'/33'/0/3","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mw7vJNC8zUK6VNN4CEjtoTYmuNPLewxZzV","path":"m/49'/1'/33'/0/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N1kvo97NFASPXiwephZUxE9PRXunjTxEc4","path":"m/49'/1'/33'/0/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuWrWMzoBt8VDFNvPmpJf42M1GTUs85fPx","path":"m/49'/1'/33'/0/6","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MuVZ2Ca6Da9zmYynt49Rx7uikAgubGcymF","path":"m/49'/1'/33'/0/7","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzRGWDUmrPP9HwYu4B43QGCTLwoop5cExa","path":"m/49'/1'/33'/0/8","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5C9EEWJzyBXhpyPHqa3UNed73Amsi5b3L","path":"m/49'/1'/33'/0/9","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzNawz2zjwq1L85GDE3YydEJGJYfXxaWkk","path":"m/49'/1'/33'/0/10","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MzSBtRWHbBjeUcu3H5VRDqkvz5sfmDxJKo","path":"m/49'/1'/33'/1/0","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MtShtAJYb1afWduUTwF1SixJjan7urZKke","path":"m/49'/1'/33'/1/1","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N3cP668SeqyBEr9gnB4yQEmU3VyxeRYith","path":"m/49'/1'/33'/1/2","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu","path":"m/49'/1'/33'/1/3","transfers":1,"decimals":8},{"type":"XPUBAddress","name":"2NEzatauNhf9kPTwwj6ZfYKjUdy52j4hVUL","path":"m/49'/1'/33'/1/4","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4RjsDp4LBpkNqyF91aNjgpF9CwDwBkJZq","path":"m/49'/1'/33'/1/5","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N8XygTmQc4NoBBPEy3yybnfCYhsxFtzPDY","path":"m/49'/1'/33'/1/6","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N5BjBomZvb48sccK2vwLMiQ5ETKp1fdPVn","path":"m/49'/1'/33'/1/7","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2MybMwbZRPCGU3SMWPwQCpDkbcQFw5Hbwen","path":"m/49'/1'/33'/1/8","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N7HexL4dyAQc7Th4iqcCW4hZuyiZsLWf74","path":"m/49'/1'/33'/1/9","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NF6X5FDGWrQj4nQrfP6hA77zB5WAc1DGup","path":"m/49'/1'/33'/1/10","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2N4ZRPdvc7BVioBTohy4F6QtxreqcjNj26b","path":"m/49'/1'/33'/1/11","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2Mtfho1rLmevh4qTnkYWxZEFCWteDMtTcUF","path":"m/49'/1'/33'/1/12","transfers":0,"decimals":8},{"type":"XPUBAddress","name":"2NFUCphKYvmMcNZRZrF261mRX6iADVB9Qms","path":"m/49'/1'/33'/1/13","transfers":0,"decimals":8}]}}`,
  1093  		},
  1094  		{
  1095  			name: "websocket getAccountUtxo",
  1096  			req: websocketReq{
  1097  				Method: "getAccountUtxo",
  1098  				Params: map[string]interface{}{
  1099  					"descriptor": dbtestdata.Addr1,
  1100  				},
  1101  			},
  1102  			want: `{"id":"5","data":[{"txid":"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840","vout":0,"value":"100000000","height":225493,"confirmations":2}]}`,
  1103  		},
  1104  		{
  1105  			name: "websocket getAccountUtxo",
  1106  			req: websocketReq{
  1107  				Method: "getAccountUtxo",
  1108  				Params: map[string]interface{}{
  1109  					"descriptor": dbtestdata.Addr4,
  1110  				},
  1111  			},
  1112  			want: `{"id":"6","data":[]}`,
  1113  		},
  1114  		{
  1115  			name: "websocket getTransaction",
  1116  			req: websocketReq{
  1117  				Method: "getTransaction",
  1118  				Params: map[string]interface{}{
  1119  					"txid": dbtestdata.TxidB2T2,
  1120  				},
  1121  			},
  1122  			want: `{"id":"7","data":{"txid":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","vin":[{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","n":0,"addresses":["mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX"],"isAddress":true,"value":"317283951061"},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vout":1,"n":1,"addresses":["2MzmAKayJmja784jyHvRUW1bXPget1csRRG"],"isAddress":true,"value":"1"}],"vout":[{"value":"118641975500","n":0,"hex":"a91495e9fbe306449c991d314afe3c3567d5bf78efd287","addresses":["2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu"],"isAddress":true},{"value":"198641975500","n":1,"hex":"76a9143f8ba3fda3ba7b69f5818086e12223c6dd25e3c888ac","addresses":["mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP"],"isAddress":true}],"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockHeight":225494,"confirmations":1,"blockTime":1521595678,"value":"317283951000","valueIn":"317283951062","fees":"62"}}`,
  1123  		},
  1124  		{
  1125  			name: "websocket getTransaction",
  1126  			req: websocketReq{
  1127  				Method: "getTransaction",
  1128  				Params: map[string]interface{}{
  1129  					"txid": "not a tx",
  1130  				},
  1131  			},
  1132  			want: `{"id":"8","data":{"error":{"message":"Transaction 'not a tx' not found"}}}`,
  1133  		},
  1134  		{
  1135  			name: "websocket getTransactionSpecific",
  1136  			req: websocketReq{
  1137  				Method: "getTransactionSpecific",
  1138  				Params: map[string]interface{}{
  1139  					"txid": dbtestdata.TxidB2T2,
  1140  				},
  1141  			},
  1142  			want: `{"id":"9","data":{"hex":"","txid":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","version":0,"locktime":0,"vin":[{"coinbase":"","txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","vout":0,"scriptSig":{"hex":""},"sequence":0,"addresses":null},{"coinbase":"","txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vout":1,"scriptSig":{"hex":""},"sequence":0,"addresses":null}],"vout":[{"ValueSat":118641975500,"value":0,"n":0,"scriptPubKey":{"hex":"a91495e9fbe306449c991d314afe3c3567d5bf78efd287","addresses":null}},{"ValueSat":198641975500,"value":0,"n":1,"scriptPubKey":{"hex":"76a9143f8ba3fda3ba7b69f5818086e12223c6dd25e3c888ac","addresses":null}}],"confirmations":1,"time":1521595678,"blocktime":1521595678,"vsize":400}}`,
  1143  		},
  1144  		{
  1145  			name: "websocket estimateFee",
  1146  			req: websocketReq{
  1147  				Method: "estimateFee",
  1148  				Params: map[string]interface{}{
  1149  					"blocks": []int{2, 5, 10, 20},
  1150  					"specific": map[string]interface{}{
  1151  						"conservative": false,
  1152  						"txsize":       1234,
  1153  					},
  1154  				},
  1155  			},
  1156  			want: `{"id":"10","data":[{"feePerTx":"246","feePerUnit":"199"},{"feePerTx":"616","feePerUnit":"499"},{"feePerTx":"1233","feePerUnit":"999"},{"feePerTx":"2467","feePerUnit":"1999"}]}`,
  1157  		},
  1158  		{
  1159  			name: "websocket sendTransaction",
  1160  			req: websocketReq{
  1161  				Method: "sendTransaction",
  1162  				Params: map[string]interface{}{
  1163  					"hex": "123456",
  1164  				},
  1165  			},
  1166  			want: `{"id":"11","data":{"result":"9876"}}`,
  1167  		},
  1168  		{
  1169  			name: "websocket subscribeNewBlock",
  1170  			req: websocketReq{
  1171  				Method: "subscribeNewBlock",
  1172  			},
  1173  			want: `{"id":"12","data":{"subscribed":true}}`,
  1174  		},
  1175  		{
  1176  			name: "websocket unsubscribeNewBlock",
  1177  			req: websocketReq{
  1178  				Method: "unsubscribeNewBlock",
  1179  			},
  1180  			want: `{"id":"13","data":{"subscribed":false}}`,
  1181  		},
  1182  		{
  1183  			name: "websocket subscribeAddresses",
  1184  			req: websocketReq{
  1185  				Method: "subscribeAddresses",
  1186  				Params: map[string]interface{}{
  1187  					"addresses": []string{dbtestdata.Addr1, dbtestdata.Addr2},
  1188  				},
  1189  			},
  1190  			want: `{"id":"14","data":{"subscribed":true}}`,
  1191  		},
  1192  		{
  1193  			name: "websocket unsubscribeAddresses",
  1194  			req: websocketReq{
  1195  				Method: "unsubscribeAddresses",
  1196  			},
  1197  			want: `{"id":"15","data":{"subscribed":false}}`,
  1198  		},
  1199  		{
  1200  			name: "websocket ping",
  1201  			req: websocketReq{
  1202  				Method: "ping",
  1203  			},
  1204  			want: `{"id":"16","data":{}}`,
  1205  		},
  1206  		{
  1207  			name: "websocket getCurrentFiatRates all currencies",
  1208  			req: websocketReq{
  1209  				Method: "getCurrentFiatRates",
  1210  				Params: map[string]interface{}{
  1211  					"currencies": []string{},
  1212  				},
  1213  			},
  1214  			want: `{"id":"17","data":{"ts":1574346615,"rates":{"eur":7134.1,"usd":7914.5}}}`,
  1215  		},
  1216  		{
  1217  			name: "websocket getCurrentFiatRates usd",
  1218  			req: websocketReq{
  1219  				Method: "getCurrentFiatRates",
  1220  				Params: map[string]interface{}{
  1221  					"currencies": []string{"usd"},
  1222  				},
  1223  			},
  1224  			want: `{"id":"18","data":{"ts":1574346615,"rates":{"usd":7914.5}}}`,
  1225  		},
  1226  		{
  1227  			name: "websocket getCurrentFiatRates eur",
  1228  			req: websocketReq{
  1229  				Method: "getCurrentFiatRates",
  1230  				Params: map[string]interface{}{
  1231  					"currencies": []string{"eur"},
  1232  				},
  1233  			},
  1234  			want: `{"id":"19","data":{"ts":1574346615,"rates":{"eur":7134.1}}}`,
  1235  		},
  1236  		{
  1237  			name: "websocket getCurrentFiatRates incorrect currency",
  1238  			req: websocketReq{
  1239  				Method: "getCurrentFiatRates",
  1240  				Params: map[string]interface{}{
  1241  					"currencies": []string{"does-not-exist"},
  1242  				},
  1243  			},
  1244  			want: `{"id":"20","data":{"ts":1574346615,"rates":{"does-not-exist":-1}}}`,
  1245  		},
  1246  		{
  1247  			name: "websocket getFiatRatesForTimestamps missing date",
  1248  			req: websocketReq{
  1249  				Method: "getFiatRatesForTimestamps",
  1250  				Params: map[string]interface{}{
  1251  					"currencies": []string{"usd"},
  1252  				},
  1253  			},
  1254  			want: `{"id":"21","data":{"error":{"message":"No timestamps provided"}}}`,
  1255  		},
  1256  		{
  1257  			name: "websocket getFiatRatesForTimestamps incorrect date",
  1258  			req: websocketReq{
  1259  				Method: "getFiatRatesForTimestamps",
  1260  				Params: map[string]interface{}{
  1261  					"currencies": []string{"usd"},
  1262  					"timestamps": []string{"yesterday"},
  1263  				},
  1264  			},
  1265  			want: `{"id":"22","data":{"error":{"message":"json: cannot unmarshal string into Go struct field .timestamps of type int64"}}}`,
  1266  		},
  1267  		{
  1268  			name: "websocket getFiatRatesForTimestamps empty currency",
  1269  			req: websocketReq{
  1270  				Method: "getFiatRatesForTimestamps",
  1271  				Params: map[string]interface{}{
  1272  					"timestamps": []int64{7885693815},
  1273  					"currencies": []string{""},
  1274  				},
  1275  			},
  1276  			want: `{"id":"23","data":{"tickers":[{"ts":7885693815,"rates":{}}]}}`,
  1277  		},
  1278  		{
  1279  			name: "websocket getFiatRatesForTimestamps incorrect (future) date",
  1280  			req: websocketReq{
  1281  				Method: "getFiatRatesForTimestamps",
  1282  				Params: map[string]interface{}{
  1283  					"currencies": []string{"usd"},
  1284  					"timestamps": []int64{7885693815},
  1285  				},
  1286  			},
  1287  			want: `{"id":"24","data":{"tickers":[{"ts":7885693815,"rates":{"usd":-1}}]}}`,
  1288  		},
  1289  		{
  1290  			name: "websocket getFiatRatesForTimestamps exact date",
  1291  			req: websocketReq{
  1292  				Method: "getFiatRatesForTimestamps",
  1293  				Params: map[string]interface{}{
  1294  					"currencies": []string{"usd"},
  1295  					"timestamps": []int64{1574346615},
  1296  				},
  1297  			},
  1298  			want: `{"id":"25","data":{"tickers":[{"ts":1574346615,"rates":{"usd":7914.5}}]}}`,
  1299  		},
  1300  		{
  1301  			name: "websocket getFiatRatesForTimestamps closest date, eur",
  1302  			req: websocketReq{
  1303  				Method: "getFiatRatesForTimestamps",
  1304  				Params: map[string]interface{}{
  1305  					"currencies": []string{"eur"},
  1306  					"timestamps": []int64{1521507600},
  1307  				},
  1308  			},
  1309  			want: `{"id":"26","data":{"tickers":[{"ts":1521511200,"rates":{"eur":1300}}]}}`,
  1310  		},
  1311  		{
  1312  			name: "websocket getFiatRatesForTimestamps multiple timestamps usd",
  1313  			req: websocketReq{
  1314  				Method: "getFiatRatesForTimestamps",
  1315  				Params: map[string]interface{}{
  1316  					"currencies": []string{"usd"},
  1317  					"timestamps": []int64{1570346615, 1574346615},
  1318  				},
  1319  			},
  1320  			want: `{"id":"27","data":{"tickers":[{"ts":1574344800,"rates":{"usd":7814.5}},{"ts":1574346615,"rates":{"usd":7914.5}}]}}`,
  1321  		},
  1322  		{
  1323  			name: "websocket getFiatRatesForTimestamps multiple timestamps eur",
  1324  			req: websocketReq{
  1325  				Method: "getFiatRatesForTimestamps",
  1326  				Params: map[string]interface{}{
  1327  					"currencies": []string{"eur"},
  1328  					"timestamps": []int64{1570346615, 1574346615},
  1329  				},
  1330  			},
  1331  			want: `{"id":"28","data":{"tickers":[{"ts":1574344800,"rates":{"eur":7100}},{"ts":1574346615,"rates":{"eur":7134.1}}]}}`,
  1332  		},
  1333  		{
  1334  			name: "websocket getFiatRatesForTimestamps multiple timestamps with an error",
  1335  			req: websocketReq{
  1336  				Method: "getFiatRatesForTimestamps",
  1337  				Params: map[string]interface{}{
  1338  					"currencies": []string{"usd"},
  1339  					"timestamps": []int64{1570346615, 1574346615, 2000000000},
  1340  				},
  1341  			},
  1342  			want: `{"id":"29","data":{"tickers":[{"ts":1574344800,"rates":{"usd":7814.5}},{"ts":1574346615,"rates":{"usd":7914.5}},{"ts":2000000000,"rates":{"usd":-1}}]}}`,
  1343  		},
  1344  		{
  1345  			name: "websocket getFiatRatesForTimestamps multiple errors",
  1346  			req: websocketReq{
  1347  				Method: "getFiatRatesForTimestamps",
  1348  				Params: map[string]interface{}{
  1349  					"currencies": []string{"usd"},
  1350  					"timestamps": []int64{7832854800, 2000000000},
  1351  				},
  1352  			},
  1353  			want: `{"id":"30","data":{"tickers":[{"ts":7832854800,"rates":{"usd":-1}},{"ts":2000000000,"rates":{"usd":-1}}]}}`,
  1354  		},
  1355  		{
  1356  			name: "websocket getTickersList",
  1357  			req: websocketReq{
  1358  				Method: "getFiatRatesTickersList",
  1359  				Params: map[string]interface{}{
  1360  					"timestamp": 1570346615,
  1361  				},
  1362  			},
  1363  			want: `{"id":"31","data":{"ts":1574344800,"available_currencies":["eur","usd"]}}`,
  1364  		},
  1365  		{
  1366  			name: "websocket getBalanceHistory Addr2",
  1367  			req: websocketReq{
  1368  				Method: "getBalanceHistory",
  1369  				Params: map[string]interface{}{
  1370  					"descriptor": "mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz",
  1371  				},
  1372  			},
  1373  			want: `{"id":"32","data":[{"time":1521514800,"txs":1,"received":"24690","sent":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"0","sent":"12345","rates":{"eur":1303,"usd":2003}}]}`,
  1374  		},
  1375  		{
  1376  			name: "websocket getBalanceHistory xpub",
  1377  			req: websocketReq{
  1378  				Method: "getBalanceHistory",
  1379  				Params: map[string]interface{}{
  1380  					"descriptor": dbtestdata.Xpub,
  1381  				},
  1382  			},
  1383  			want: `{"id":"33","data":[{"time":1521514800,"txs":1,"received":"1","sent":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"118641975500","sent":"1","rates":{"eur":1303,"usd":2003}}]}`,
  1384  		},
  1385  		{
  1386  			name: "websocket getBalanceHistory xpub from=1521504000&to=1521590400 currencies=[usd]",
  1387  			req: websocketReq{
  1388  				Method: "getBalanceHistory",
  1389  				Params: map[string]interface{}{
  1390  					"descriptor": dbtestdata.Xpub,
  1391  					"from":       1521504000,
  1392  					"to":         1521590400,
  1393  					"currencies": []string{"usd"},
  1394  				},
  1395  			},
  1396  			want: `{"id":"34","data":[{"time":1521514800,"txs":1,"received":"1","sent":"0","rates":{"usd":2001}}]}`,
  1397  		},
  1398  		{
  1399  			name: "websocket getBalanceHistory xpub from=1521504000&to=1521590400 currencies=[usd, eur, incorrect]",
  1400  			req: websocketReq{
  1401  				Method: "getBalanceHistory",
  1402  				Params: map[string]interface{}{
  1403  					"descriptor": dbtestdata.Xpub,
  1404  					"from":       1521504000,
  1405  					"to":         1521590400,
  1406  					"currencies": []string{"usd", "eur", "incorrect"},
  1407  				},
  1408  			},
  1409  			want: `{"id":"35","data":[{"time":1521514800,"txs":1,"received":"1","sent":"0","rates":{"eur":1301,"incorrect":-1,"usd":2001}}]}`,
  1410  		},
  1411  		{
  1412  			name: "websocket getBalanceHistory xpub from=1521504000&to=1521590400 currencies=[]",
  1413  			req: websocketReq{
  1414  				Method: "getBalanceHistory",
  1415  				Params: map[string]interface{}{
  1416  					"descriptor": dbtestdata.Xpub,
  1417  					"from":       1521504000,
  1418  					"to":         1521590400,
  1419  					"currencies": []string{},
  1420  				},
  1421  			},
  1422  			want: `{"id":"36","data":[{"time":1521514800,"txs":1,"received":"1","sent":"0","rates":{"eur":1301,"usd":2001}}]}`,
  1423  		},
  1424  	}
  1425  
  1426  	// send all requests at once
  1427  	for i, tt := range tests {
  1428  		t.Run(tt.name, func(t *testing.T) {
  1429  			tt.req.ID = strconv.Itoa(i)
  1430  			err = s.WriteJSON(tt.req)
  1431  			if err != nil {
  1432  				t.Fatal(err)
  1433  			}
  1434  		})
  1435  	}
  1436  
  1437  	// wait for all responses
  1438  	done := make(chan struct{})
  1439  
  1440  	go func() {
  1441  		defer close(done)
  1442  		for i := 0; i < len(tests); i++ {
  1443  			_, message, err := s.ReadMessage()
  1444  			if err != nil {
  1445  				t.Fatal(err)
  1446  			}
  1447  			var resp websocketResp
  1448  			err = json.Unmarshal(message, &resp)
  1449  			if err != nil {
  1450  				t.Fatal(err)
  1451  			}
  1452  			id, err := strconv.Atoi(resp.ID)
  1453  			if err != nil {
  1454  				t.Fatal(err)
  1455  			}
  1456  			got := strings.TrimSpace(string(message))
  1457  			if got != tests[id].want {
  1458  				t.Errorf("%s: got %v, want %v", tests[id].name, got, tests[id].want)
  1459  			} else {
  1460  				tests[id].want = "already checked, should not check twice"
  1461  			}
  1462  		}
  1463  	}()
  1464  
  1465  	select {
  1466  	case <-done:
  1467  		break
  1468  	case <-time.After(time.Second * 10):
  1469  		t.Error("Timeout while waiting for websocket responses")
  1470  	}
  1471  }
  1472  
  1473  func Test_PublicServer_BitcoinType(t *testing.T) {
  1474  	s, dbpath := setupPublicHTTPServer(t)
  1475  	defer closeAndDestroyPublicServer(t, s, dbpath)
  1476  	s.ConnectFullPublicInterface()
  1477  	// take the handler of the public server and pass it to the test server
  1478  	ts := httptest.NewServer(s.https.Handler)
  1479  	defer ts.Close()
  1480  
  1481  	httpTestsBitcoinType(t, ts)
  1482  	socketioTestsBitcoinType(t, ts)
  1483  	websocketTestsBitcoinType(t, ts)
  1484  }