diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go new file mode 100644 index 00000000..4c9c4878 --- /dev/null +++ b/server/public_ethereumtype_test.go @@ -0,0 +1,60 @@ +//go:build unittest +// +build unittest + +package server + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/golang/glog" + "github.com/trezor/blockbook/bchain/coins/eth" + "github.com/trezor/blockbook/tests/dbtestdata" +) + +func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { + tests := []httpTests{ + { + name: "apiIndex", + r: newGetRequest(ts.URL + "/api"), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"blockbook":{"coin":"Fakecoin"`, + `"bestHeight":4321001`, + `"decimals":18`, + `"backend":{"chain":"fakecoin","blocks":2,"headers":2,"bestBlockHash":"0x2b57e15e93a0ed197417a34c2498b7187df79099572c04a6b6e6ff418f74e6ee"`, + `"version":"001001","subversion":"/Fakecoin:0.0.1/"`, + }, + }, + { + name: "apiAddress EthAddr4b", + r: newGetRequest(ts.URL + "/api/v2/address/" + dbtestdata.EthAddr4b), + status: http.StatusOK, + contentType: "application/json; charset=utf-8", + body: []string{ + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":18}],"erc20Contract":{"contract":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","name":"Contract 75","symbol":"S75","decimals":18}}`, + }, + }, + } + + performHttpTests(tests, t, ts) +} + +func Test_PublicServer_EthereumType(t *testing.T) { + parser := eth.NewEthereumParser(1) + chain, err := dbtestdata.NewFakeBlockChainEthereumType(parser) + if err != nil { + glog.Fatal("fakechain: ", err) + } + + s, dbpath := setupPublicHTTPServer(parser, chain, t) + defer closeAndDestroyPublicServer(t, s, dbpath) + s.ConnectFullPublicInterface() + // take the handler of the public server and pass it to the test server + ts := httptest.NewServer(s.https.Handler) + defer ts.Close() + + httpTestsEthereumType(t, ts) +} diff --git a/server/public_test.go b/server/public_test.go index 3de7c4ff..f06d55d2 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -36,7 +36,7 @@ func TestMain(m *testing.M) { os.Exit(c) } -func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *common.InternalState, string) { +func setupRocksDB(parser bchain.BlockChainParser, chain bchain.BlockChain, t *testing.T) (*db.RocksDB, *common.InternalState, string) { tmp, err := ioutil.TempDir("", "testdb") if err != nil { t.Fatal(err) @@ -50,7 +50,15 @@ func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *c t.Fatal(err) } d.SetInternalState(is) - block1 := dbtestdata.GetTestBitcoinTypeBlock1(parser) + // there are 2 simulated block, of height bestBlockHeight-1 and bestBlockHeight + bestHeight, err := chain.GetBestBlockHeight() + if err != nil { + t.Fatal(err) + } + block1, err := chain.GetBlock("", bestHeight-1) + if err != nil { + t.Fatal(err) + } // setup internal state BlockTimes for i := uint32(0); i < block1.Height; i++ { is.BlockTimes = append(is.BlockTimes, 0) @@ -59,7 +67,10 @@ func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *c if err := d.ConnectBlock(block1); err != nil { t.Fatal(err) } - block2 := dbtestdata.GetTestBitcoinTypeBlock2(parser) + block2, err := chain.GetBlock("", bestHeight) + if err != nil { + t.Fatal(err) + } if err := d.ConnectBlock(block2); err != nil { t.Fatal(err) } @@ -70,31 +81,22 @@ func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *c return d, is, tmp } -func setupPublicHTTPServer(t *testing.T) (*PublicServer, string) { - parser := btc.NewBitcoinParser( - btc.GetChainParams("test"), - &btc.Configuration{ - BlockAddressesToKeep: 1, - XPubMagic: 70617039, - XPubMagicSegwitP2sh: 71979618, - XPubMagicSegwitNative: 73342198, - Slip44: 1, - }) +var metrics *common.Metrics - d, is, path := setupRocksDB(t, parser) +func setupPublicHTTPServer(parser bchain.BlockChainParser, chain bchain.BlockChain, t *testing.T) (*PublicServer, string) { + d, is, path := setupRocksDB(parser, chain, t) // setup internal state and match BestHeight to test data is.Coin = "Fakecoin" is.CoinLabel = "Fake Coin" is.CoinShortcut = "FAKE" - metrics, err := common.GetMetrics("Fakecoin") - if err != nil { - glog.Fatal("metrics: ", err) - } - - chain, err := dbtestdata.NewFakeBlockChain(parser) - if err != nil { - glog.Fatal("fakechain: ", err) + var err error + // metrics can be setup only once + if metrics == nil { + metrics, err = common.GetMetrics("Fakecoin") + if err != nil { + glog.Fatal("metrics: ", err) + } } mempool, err := chain.CreateMempool(chain) @@ -204,14 +206,45 @@ func InitTestFiatRates(d *db.RocksDB) error { }, d) } +type httpTests struct { + name string + r *http.Request + status int + contentType string + body []string +} + +func performHttpTests(tests []httpTests, t *testing.T, ts *httptest.Server) { + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + resp, err := http.DefaultClient.Do(tt.r) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() + if resp.StatusCode != tt.status { + t.Errorf("StatusCode = %v, want %v", resp.StatusCode, tt.status) + } + if resp.Header["Content-Type"][0] != tt.contentType { + t.Errorf("Content-Type = %v, want %v", resp.Header["Content-Type"][0], tt.contentType) + } + bb, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatal(err) + } + b := string(bb) + for _, c := range tt.body { + if !strings.Contains(b, c) { + t.Errorf("got %v, want to contain %v", b, c) + break + } + } + }) + } +} + func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { - tests := []struct { - name string - r *http.Request - status int - contentType string - body []string - }{ + tests := []httpTests{ { name: "explorerTx", r: newGetRequest(ts.URL + "/tx/fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db"), @@ -947,33 +980,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { }, }, } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - resp, err := http.DefaultClient.Do(tt.r) - if err != nil { - t.Fatal(err) - } - defer resp.Body.Close() - if resp.StatusCode != tt.status { - t.Errorf("StatusCode = %v, want %v", resp.StatusCode, tt.status) - } - if resp.Header["Content-Type"][0] != tt.contentType { - t.Errorf("Content-Type = %v, want %v", resp.Header["Content-Type"][0], tt.contentType) - } - bb, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatal(err) - } - b := string(bb) - for _, c := range tt.body { - if !strings.Contains(b, c) { - t.Errorf("got %v, want to contain %v", b, c) - break - } - } - }) - } + performHttpTests(tests, t, ts) } func socketioTestsBitcoinType(t *testing.T, ts *httptest.Server) { @@ -1558,7 +1565,22 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) { } func Test_PublicServer_BitcoinType(t *testing.T) { - s, dbpath := setupPublicHTTPServer(t) + parser := btc.NewBitcoinParser( + btc.GetChainParams("test"), + &btc.Configuration{ + BlockAddressesToKeep: 1, + XPubMagic: 70617039, + XPubMagicSegwitP2sh: 71979618, + XPubMagicSegwitNative: 73342198, + Slip44: 1, + }) + + chain, err := dbtestdata.NewFakeBlockChain(parser) + if err != nil { + glog.Fatal("fakechain: ", err) + } + + s, dbpath := setupPublicHTTPServer(parser, chain, t) defer closeAndDestroyPublicServer(t, s, dbpath) s.ConnectFullPublicInterface() // take the handler of the public server and pass it to the test server diff --git a/tests/dbtestdata/fakechain_ethereumtype.go b/tests/dbtestdata/fakechain_ethereumtype.go new file mode 100644 index 00000000..800ff03f --- /dev/null +++ b/tests/dbtestdata/fakechain_ethereumtype.go @@ -0,0 +1,128 @@ +package dbtestdata + +import ( + "encoding/json" + "math/big" + "strconv" + + "github.com/trezor/blockbook/bchain" +) + +type fakeBlockChainEthereumType struct { + *fakeBlockChain +} + +// NewFakeBlockChainEthereumType returns mocked blockchain RPC interface used for tests +func NewFakeBlockChainEthereumType(parser bchain.BlockChainParser) (bchain.BlockChain, error) { + return &fakeBlockChainEthereumType{&fakeBlockChain{&bchain.BaseChain{Parser: parser}}}, nil +} + +func (c *fakeBlockChainEthereumType) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) { + return bchain.NewMempoolEthereumType(chain, 1, false), nil +} + +func (c *fakeBlockChainEthereumType) GetChainInfo() (v *bchain.ChainInfo, err error) { + return &bchain.ChainInfo{ + Chain: c.GetNetworkName(), + Blocks: 2, + Headers: 2, + Bestblockhash: GetTestEthereumTypeBlock2(c.Parser).BlockHeader.Hash, + Version: "001001", + Subversion: c.GetSubversion(), + }, nil +} + +func (c *fakeBlockChainEthereumType) GetBestBlockHash() (v string, err error) { + return GetTestEthereumTypeBlock2(c.Parser).BlockHeader.Hash, nil +} + +func (c *fakeBlockChainEthereumType) GetBestBlockHeight() (v uint32, err error) { + return GetTestEthereumTypeBlock2(c.Parser).BlockHeader.Height, nil +} + +func (c *fakeBlockChainEthereumType) GetBlockHash(height uint32) (v string, err error) { + b1 := GetTestEthereumTypeBlock1(c.Parser) + if height == b1.BlockHeader.Height { + return b1.BlockHeader.Hash, nil + } + b2 := GetTestEthereumTypeBlock2(c.Parser) + if height == b2.BlockHeader.Height { + return b2.BlockHeader.Hash, nil + } + return "", bchain.ErrBlockNotFound +} + +func (c *fakeBlockChainEthereumType) GetBlockHeader(hash string) (v *bchain.BlockHeader, err error) { + b1 := GetTestEthereumTypeBlock1(c.Parser) + if hash == b1.BlockHeader.Hash { + return &b1.BlockHeader, nil + } + b2 := GetTestEthereumTypeBlock2(c.Parser) + if hash == b2.BlockHeader.Hash { + return &b2.BlockHeader, nil + } + return nil, bchain.ErrBlockNotFound +} + +func (c *fakeBlockChainEthereumType) GetBlock(hash string, height uint32) (v *bchain.Block, err error) { + b1 := GetTestEthereumTypeBlock1(c.Parser) + if hash == b1.BlockHeader.Hash || height == b1.BlockHeader.Height { + return b1, nil + } + b2 := GetTestEthereumTypeBlock2(c.Parser) + if hash == b2.BlockHeader.Hash || height == b2.BlockHeader.Height { + return b2, nil + } + return nil, bchain.ErrBlockNotFound +} + +func (c *fakeBlockChainEthereumType) GetBlockInfo(hash string) (v *bchain.BlockInfo, err error) { + b1 := GetTestEthereumTypeBlock1(c.Parser) + if hash == b1.BlockHeader.Hash { + return getBlockInfo(b1), nil + } + b2 := GetTestEthereumTypeBlock2(c.Parser) + if hash == b2.BlockHeader.Hash { + return getBlockInfo(b2), nil + } + return nil, bchain.ErrBlockNotFound +} + +func (c *fakeBlockChainEthereumType) GetTransaction(txid string) (v *bchain.Tx, err error) { + v = getTxInBlock(GetTestEthereumTypeBlock1(c.Parser), txid) + if v == nil { + v = getTxInBlock(GetTestEthereumTypeBlock2(c.Parser), txid) + } + if v != nil { + return v, nil + } + return nil, bchain.ErrTxNotFound +} + +func (c *fakeBlockChainEthereumType) GetTransactionSpecific(tx *bchain.Tx) (v json.RawMessage, err error) { + txS, _ := tx.CoinSpecificData.(bchain.EthereumSpecificData) + + rm, err := json.Marshal(txS) + if err != nil { + return nil, err + } + return json.RawMessage(rm), nil +} + +func (c *fakeBlockChainEthereumType) EthereumTypeGetBalance(addrDesc bchain.AddressDescriptor) (*big.Int, error) { + return big.NewInt(123450000 + int64(addrDesc[0])), nil +} + +func (c *fakeBlockChainEthereumType) EthereumTypeGetNonce(addrDesc bchain.AddressDescriptor) (uint64, error) { + return uint64(addrDesc[0]), nil +} + +func (c *fakeBlockChainEthereumType) EthereumTypeGetErc20ContractInfo(contractDesc bchain.AddressDescriptor) (*bchain.Erc20Contract, error) { + addresses, _, _ := c.Parser.GetAddressesFromAddrDesc(contractDesc) + return &bchain.Erc20Contract{ + Contract: addresses[0], + Name: "Contract " + strconv.Itoa(int(contractDesc[0])), + Symbol: "S" + strconv.Itoa(int(contractDesc[0])), + Decimals: 18, + }, nil +}