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'/1'/33'/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 '1234'</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×tamp=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×tamp=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×tamp=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¤cy=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¤cy=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¤cy=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¤cy=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¤cy=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 }