From 757b3e39d6f0e4e51c4b60aae17c0668e3b20d62 Mon Sep 17 00:00:00 2001 From: Jakub Matys Date: Wed, 20 Jun 2018 19:09:47 +0200 Subject: [PATCH] Added mempool RPC/resync tests --- bchain/coins/btc/bitcoinrpc_test.go | 13 +++ bchain/tests/rpc/rpc.go | 162 +++++++++++++++++++++++++++- 2 files changed, 174 insertions(+), 1 deletion(-) diff --git a/bchain/coins/btc/bitcoinrpc_test.go b/bchain/coins/btc/bitcoinrpc_test.go index 52b09898..9da65080 100644 --- a/bchain/coins/btc/bitcoinrpc_test.go +++ b/bchain/coins/btc/bitcoinrpc_test.go @@ -19,6 +19,7 @@ func getRPCClient(cfg json.RawMessage) (bchain.BlockChain, error) { if err != nil { return nil, err } + cli.Mempool = bchain.NewUTXOMempool(cli, cli.ChainConfig.MempoolWorkers, cli.ChainConfig.MempoolSubWorkers) return cli, nil } @@ -43,3 +44,15 @@ func TestBitcoinRPC_GetBlock(t *testing.T) { func TestBitcoinRPC_GetTransaction(t *testing.T) { rpcTest.TestGetTransaction(t) } + +func TestBitcoinRPC_TestGetTransactionForMempool(t *testing.T) { + rpcTest.TestGetTransactionForMempool(t) +} + +func TestBitcoinRPC_TestMempoolSync(t *testing.T) { + rpcTest.TestMempoolSync(t) +} + +func TestBitcoinRPC_GetMempoolEntry(t *testing.T) { + rpcTest.TestGetMempoolEntry(t) +} diff --git a/bchain/tests/rpc/rpc.go b/bchain/tests/rpc/rpc.go index 0a486963..15f799f3 100644 --- a/bchain/tests/rpc/rpc.go +++ b/bchain/tests/rpc/rpc.go @@ -5,8 +5,10 @@ package rpc import ( "blockbook/bchain" "encoding/json" + "math/rand" "reflect" "testing" + "time" ) type TestConfig struct { @@ -103,6 +105,7 @@ func (rt *Test) TestGetBlock(t *testing.T) { } } + func (rt *Test) TestGetTransaction(t *testing.T) { for txid, want := range rt.TestData.TxDetails { got, err := rt.Client.GetTransaction(txid) @@ -110,7 +113,6 @@ func (rt *Test) TestGetTransaction(t *testing.T) { t.Error(err) return } - // Confirmations is variable field, we just check if is set and reset it if got.Confirmations > 0 { got.Confirmations = 0 @@ -124,3 +126,161 @@ func (rt *Test) TestGetTransaction(t *testing.T) { } } } + +func (rt *Test) getMempool(t *testing.T) []string { + var ( + txs []string + err error + ) + // attempts to get transactions for 2 min + for i := 0; i < 8; i++ { + txs, err = rt.Client.GetMempool() + if err != nil { + t.Fatal(err) + } + if len(txs) == 0 { + time.Sleep(15 * time.Second) + continue + } + + // done + break + } + if len(txs) == 0 { + t.Skipf("Skipping test, all attempts to get mempool failed") + } + + return txs +} + +func (rt *Test) getMempoolTransaction(t *testing.T, txid string) *bchain.Tx { + tx, err := rt.Client.GetTransactionForMempool(txid) + if err != nil { + t.Fatal(err) + } + if tx.Confirmations > 0 { + t.Skip("Skipping test, transaction moved away from mepool") + } + + return tx +} + +func (rt *Test) TestGetTransactionForMempool(t *testing.T) { + txs := rt.getMempool(t) + txid := txs[rand.Intn(len(txs))] + got := rt.getMempoolTransaction(t, txid) + + want, err := rt.Client.GetTransaction(txid) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("GetTransactionForMempool() got %v, want %v", got, want) + } +} + +func (rt *Test) getMempoolAddresses(t *testing.T, txs []string) map[string][]string { + txid2addrs := map[string][]string{} + for i := 0; i < len(txs); i++ { + tx := rt.getMempoolTransaction(t, txs[i]) + addrs := []string{} + for _, vin := range tx.Vin { + for _, a := range vin.Addresses { + addrs = append(addrs, a) + } + } + for _, vout := range tx.Vout { + for _, a := range vout.ScriptPubKey.Addresses { + addrs = append(addrs, a) + } + } + if len(addrs) > 0 { + txid2addrs[tx.Txid] = addrs + } + } + return txid2addrs +} + +func (rt *Test) TestMempoolSync(t *testing.T) { + for i := 0; i < 3; i++ { + txs := rt.getMempool(t) + txid2addrs := rt.getMempoolAddresses(t, txs) + if len(txid2addrs) == 0 { + t.Fatal("No transaction in mempool has any address") + } + + n, err := rt.Client.ResyncMempool(nil) + if err != nil { + t.Fatal(err) + } + + if tmp := rt.getMempool(t); len(txs) != len(tmp) { + // mempool reset + continue + } + + if len(txs) != n { + t.Fatalf("ResyncMempool() returned different number of transactions than backend call") + } + + for txid, addrs := range txid2addrs { + for _, a := range addrs { + txs, err := rt.Client.GetMempoolTransactions(a) + if err != nil { + t.Fatal(err) + } + if !containsString(txs, txid) { + t.Errorf("ResyncMempool() - for address %s, transaction %s wasn't found in mempool", a, txid) + return + } + } + } + + // done + return + } + t.Skip("Skipping test, all attempts to sync mempool failed due to network state changes") +} + +func containsString(slice []string, s string) bool { + for i := 0; i < len(slice); i++ { + if slice[i] == s { + return true + } + } + return false +} + +func (rt *Test) TestGetMempoolEntry(t *testing.T) { + for i := 0; i < 3; i++ { + txs := rt.getMempool(t) + h, err := rt.Client.GetBestBlockHeight() + if err != nil { + t.Fatal(err) + } + + tx := rt.getMempoolTransaction(t, txs[rand.Intn(len(txs))]) + e, err := rt.Client.GetMempoolEntry(tx.Txid) + if err != nil { + if err, ok := err.(*bchain.RPCError); ok && err.Code == -5 { + // mempool reset + continue + } + } + + if e.Height != h { + t.Errorf("GetMempoolEntry() got height %d, want %d", e.Height, h) + } + if e.Size <= 0 { + t.Errorf("GetMempoolEntry() got zero or negative size %d", e.Size) + } + if e.Fee <= 0 { + t.Errorf("GetMempoolEntry() got zero or negative fee %f", e.Fee) + } + + // done + return + } + t.Skip("Skipping test, all attempts to get mempool entry failed due to network state changes") +}