From b72461dbeeb025abc906896105298b8bf940aa2d Mon Sep 17 00:00:00 2001 From: Patrick O'Grady Date: Mon, 28 Sep 2020 07:49:37 -0700 Subject: [PATCH 1/5] Get current block from indexer instead of bitcoind --- bitcoin/client.go | 26 -------------------------- mocks/services/client.go | 10 +++++----- services/network_service.go | 11 +++++++---- services/types.go | 2 +- 4 files changed, 13 insertions(+), 36 deletions(-) diff --git a/bitcoin/client.go b/bitcoin/client.go index bdb1efd..ff21196 100644 --- a/bitcoin/client.go +++ b/bitcoin/client.go @@ -161,32 +161,6 @@ func newHTTPClient(timeout time.Duration) *http.Client { return httpClient } -// NetworkStatus returns the *types.NetworkStatusResponse for -// bitcoind. -func (b *Client) NetworkStatus(ctx context.Context) (*types.NetworkStatusResponse, error) { - rawBlock, err := b.getBlock(ctx, nil) - if err != nil { - return nil, fmt.Errorf("%w: unable to get current block", err) - } - - currentBlock, err := b.parseBlockData(rawBlock) - if err != nil { - return nil, fmt.Errorf("%w: unable to parse current block", err) - } - - peers, err := b.GetPeers(ctx) - if err != nil { - return nil, err - } - - return &types.NetworkStatusResponse{ - CurrentBlockIdentifier: currentBlock.BlockIdentifier, - CurrentBlockTimestamp: currentBlock.Timestamp, - GenesisBlockIdentifier: b.genesisBlockIdentifier, - Peers: peers, - }, nil -} - // GetPeers fetches the list of peer nodes func (b *Client) GetPeers(ctx context.Context) ([]*types.Peer, error) { info, err := b.getPeerInfo(ctx) diff --git a/mocks/services/client.go b/mocks/services/client.go index 838124f..a47044e 100644 --- a/mocks/services/client.go +++ b/mocks/services/client.go @@ -15,16 +15,16 @@ type Client struct { mock.Mock } -// NetworkStatus provides a mock function with given fields: _a0 -func (_m *Client) NetworkStatus(_a0 context.Context) (*types.NetworkStatusResponse, error) { +// GetPeers provides a mock function with given fields: _a0 +func (_m *Client) GetPeers(_a0 context.Context) ([]*types.Peer, error) { ret := _m.Called(_a0) - var r0 *types.NetworkStatusResponse - if rf, ok := ret.Get(0).(func(context.Context) *types.NetworkStatusResponse); ok { + var r0 []*types.Peer + if rf, ok := ret.Get(0).(func(context.Context) []*types.Peer); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.NetworkStatusResponse) + r0 = ret.Get(0).([]*types.Peer) } } diff --git a/services/network_service.go b/services/network_service.go index 0d1932b..92046cc 100644 --- a/services/network_service.go +++ b/services/network_service.go @@ -65,7 +65,7 @@ func (s *NetworkAPIService) NetworkStatus( return nil, wrapErr(ErrUnavailableOffline, nil) } - rawStatus, err := s.client.NetworkStatus(ctx) + peers, err := s.client.GetPeers(ctx) if err != nil { return nil, wrapErr(ErrBitcoind, err) } @@ -75,9 +75,12 @@ func (s *NetworkAPIService) NetworkStatus( return nil, wrapErr(ErrNotReady, nil) } - rawStatus.CurrentBlockIdentifier = cachedBlockResponse.Block.BlockIdentifier - - return rawStatus, nil + return &types.NetworkStatusResponse{ + CurrentBlockIdentifier: cachedBlockResponse.Block.BlockIdentifier, + CurrentBlockTimestamp: cachedBlockResponse.Block.Timestamp, + GenesisBlockIdentifier: s.config.GenesisBlockIdentifier, + Peers: peers, + }, nil } // NetworkOptions implements the /network/options endpoint. diff --git a/services/types.go b/services/types.go index 158c0b3..feebdf5 100644 --- a/services/types.go +++ b/services/types.go @@ -44,7 +44,7 @@ var ( // Client is used by the servicers to get Peer information // and to submit transactions. type Client interface { - NetworkStatus(context.Context) (*types.NetworkStatusResponse, error) + GetPeers(context.Context) ([]*types.Peer, error) SendRawTransaction(context.Context, string) (string, error) SuggestedFeeRate(context.Context, int64) (float64, error) RawMempool(context.Context) ([]string, error) From 7a1cea08c1245f36459e71875af299084ac93ebc Mon Sep 17 00:00:00 2001 From: Patrick O'Grady Date: Mon, 28 Sep 2020 07:51:43 -0700 Subject: [PATCH 2/5] Update version --- services/types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/types.go b/services/types.go index feebdf5..5d83625 100644 --- a/services/types.go +++ b/services/types.go @@ -38,7 +38,7 @@ var ( // variable instead of a constant because // we typically need the pointer of this // value. - MiddlewareVersion = "0.0.2" + MiddlewareVersion = "0.0.3" ) // Client is used by the servicers to get Peer information From eee610e20a9713a4dda550624b0fad9c796ee1b5 Mon Sep 17 00:00:00 2001 From: Patrick O'Grady Date: Mon, 28 Sep 2020 07:59:43 -0700 Subject: [PATCH 3/5] Add back NetworkStatus --- bitcoin/client.go | 26 ++++++++++ bitcoin/client_test.go | 107 ++++++++++++++--------------------------- 2 files changed, 62 insertions(+), 71 deletions(-) diff --git a/bitcoin/client.go b/bitcoin/client.go index ff21196..bdb1efd 100644 --- a/bitcoin/client.go +++ b/bitcoin/client.go @@ -161,6 +161,32 @@ func newHTTPClient(timeout time.Duration) *http.Client { return httpClient } +// NetworkStatus returns the *types.NetworkStatusResponse for +// bitcoind. +func (b *Client) NetworkStatus(ctx context.Context) (*types.NetworkStatusResponse, error) { + rawBlock, err := b.getBlock(ctx, nil) + if err != nil { + return nil, fmt.Errorf("%w: unable to get current block", err) + } + + currentBlock, err := b.parseBlockData(rawBlock) + if err != nil { + return nil, fmt.Errorf("%w: unable to parse current block", err) + } + + peers, err := b.GetPeers(ctx) + if err != nil { + return nil, err + } + + return &types.NetworkStatusResponse{ + CurrentBlockIdentifier: currentBlock.BlockIdentifier, + CurrentBlockTimestamp: currentBlock.Timestamp, + GenesisBlockIdentifier: b.genesisBlockIdentifier, + Peers: peers, + }, nil +} + // GetPeers fetches the list of peer nodes func (b *Client) GetPeers(ctx context.Context) ([]*types.Peer, error) { info, err := b.getPeerInfo(ctx) diff --git a/bitcoin/client_test.go b/bitcoin/client_test.go index 554e3dc..216de85 100644 --- a/bitcoin/client_test.go +++ b/bitcoin/client_test.go @@ -293,66 +293,51 @@ var ( } ) -func TestNetworkStatus(t *testing.T) { +func TestGetPeers(t *testing.T) { tests := map[string]struct { responses []responseFixture - expectedStatus *types.NetworkStatusResponse - expectedError error + expectedPeers []*types.Peer + expectedError error }{ "successful": { responses: []responseFixture{ - { - status: http.StatusOK, - body: loadFixture("get_blockchain_info_response.json"), - url: url, - }, - { - status: http.StatusOK, - body: loadFixture("get_block_response.json"), - url: url, - }, { status: http.StatusOK, body: loadFixture("get_peer_info_response.json"), url: url, }, }, - expectedStatus: &types.NetworkStatusResponse{ - CurrentBlockIdentifier: blockIdentifier1000, - CurrentBlockTimestamp: block1000.Time * 1000, - GenesisBlockIdentifier: MainnetGenesisBlockIdentifier, - Peers: []*types.Peer{ - { - PeerID: "77.93.223.9:8333", - Metadata: forceMarshalMap(t, &PeerInfo{ - Addr: "77.93.223.9:8333", - Version: 70015, - SubVer: "/Satoshi:0.14.2/", - StartingHeight: 643579, - RelayTxes: true, - LastSend: 1597606676, - LastRecv: 1597606677, - BanScore: 0, - SyncedHeaders: 644046, - SyncedBlocks: 644046, - }), - }, - { - PeerID: "172.105.93.179:8333", - Metadata: forceMarshalMap(t, &PeerInfo{ - Addr: "172.105.93.179:8333", - RelayTxes: true, - LastSend: 1597606678, - LastRecv: 1597606676, - Version: 70015, - SubVer: "/Satoshi:0.18.1/", - StartingHeight: 643579, - BanScore: 0, - SyncedHeaders: 644046, - SyncedBlocks: 644046, - }), - }, + expectedPeers: []*types.Peer{ + { + PeerID: "77.93.223.9:8333", + Metadata: forceMarshalMap(t, &PeerInfo{ + Addr: "77.93.223.9:8333", + Version: 70015, + SubVer: "/Satoshi:0.14.2/", + StartingHeight: 643579, + RelayTxes: true, + LastSend: 1597606676, + LastRecv: 1597606677, + BanScore: 0, + SyncedHeaders: 644046, + SyncedBlocks: 644046, + }), + }, + { + PeerID: "172.105.93.179:8333", + Metadata: forceMarshalMap(t, &PeerInfo{ + Addr: "172.105.93.179:8333", + RelayTxes: true, + LastSend: 1597606678, + LastRecv: 1597606676, + Version: 70015, + SubVer: "/Satoshi:0.18.1/", + StartingHeight: 643579, + BanScore: 0, + SyncedHeaders: 644046, + SyncedBlocks: 644046, + }), }, }, }, @@ -366,7 +351,7 @@ func TestNetworkStatus(t *testing.T) { }, expectedError: errors.New("rpc in warmup"), }, - "blockchain info error": { + "peer info error": { responses: []responseFixture{ { status: http.StatusInternalServerError, @@ -376,26 +361,6 @@ func TestNetworkStatus(t *testing.T) { }, expectedError: errors.New("invalid response: 500 Internal Server Error"), }, - "peer info not accessible": { - responses: []responseFixture{ - { - status: http.StatusOK, - body: loadFixture("get_blockchain_info_response.json"), - url: url, - }, - { - status: http.StatusOK, - body: loadFixture("get_block_response.json"), - url: url, - }, - { - status: http.StatusInternalServerError, - body: "{}", - url: url, - }, - }, - expectedError: errors.New("invalid response: 500 Internal Server Error"), - }, } for name, test := range tests { @@ -420,12 +385,12 @@ func TestNetworkStatus(t *testing.T) { })) client := NewClient(ts.URL, MainnetGenesisBlockIdentifier, MainnetCurrency) - status, err := client.NetworkStatus(context.Background()) + peers, err := client.GetPeers(context.Background()) if test.expectedError != nil { assert.Contains(err.Error(), test.expectedError.Error()) } else { assert.NoError(err) - assert.Equal(test.expectedStatus, status) + assert.Equal(test.expectedPeers, peers) } }) } From f37487e905559c90d3b281ad5c618a5d20db0307 Mon Sep 17 00:00:00 2001 From: Patrick O'Grady Date: Mon, 28 Sep 2020 08:01:13 -0700 Subject: [PATCH 4/5] Add back network status test --- bitcoin/client_test.go | 138 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/bitcoin/client_test.go b/bitcoin/client_test.go index 216de85..4e1ab94 100644 --- a/bitcoin/client_test.go +++ b/bitcoin/client_test.go @@ -293,6 +293,144 @@ var ( } ) +func TestNetworkStatus(t *testing.T) { + tests := map[string]struct { + responses []responseFixture + + expectedStatus *types.NetworkStatusResponse + expectedError error + }{ + "successful": { + responses: []responseFixture{ + { + status: http.StatusOK, + body: loadFixture("get_blockchain_info_response.json"), + url: url, + }, + { + status: http.StatusOK, + body: loadFixture("get_block_response.json"), + url: url, + }, + { + status: http.StatusOK, + body: loadFixture("get_peer_info_response.json"), + url: url, + }, + }, + expectedStatus: &types.NetworkStatusResponse{ + CurrentBlockIdentifier: blockIdentifier1000, + CurrentBlockTimestamp: block1000.Time * 1000, + GenesisBlockIdentifier: MainnetGenesisBlockIdentifier, + Peers: []*types.Peer{ + { + PeerID: "77.93.223.9:8333", + Metadata: forceMarshalMap(t, &PeerInfo{ + Addr: "77.93.223.9:8333", + Version: 70015, + SubVer: "/Satoshi:0.14.2/", + StartingHeight: 643579, + RelayTxes: true, + LastSend: 1597606676, + LastRecv: 1597606677, + BanScore: 0, + SyncedHeaders: 644046, + SyncedBlocks: 644046, + }), + }, + { + PeerID: "172.105.93.179:8333", + Metadata: forceMarshalMap(t, &PeerInfo{ + Addr: "172.105.93.179:8333", + RelayTxes: true, + LastSend: 1597606678, + LastRecv: 1597606676, + Version: 70015, + SubVer: "/Satoshi:0.18.1/", + StartingHeight: 643579, + BanScore: 0, + SyncedHeaders: 644046, + SyncedBlocks: 644046, + }), + }, + }, + }, + }, + "blockchain warming up error": { + responses: []responseFixture{ + { + status: http.StatusOK, + body: loadFixture("rpc_in_warmup_response.json"), + url: url, + }, + }, + expectedError: errors.New("rpc in warmup"), + }, + "blockchain info error": { + responses: []responseFixture{ + { + status: http.StatusInternalServerError, + body: "{}", + url: url, + }, + }, + expectedError: errors.New("invalid response: 500 Internal Server Error"), + }, + "peer info not accessible": { + responses: []responseFixture{ + { + status: http.StatusOK, + body: loadFixture("get_blockchain_info_response.json"), + url: url, + }, + { + status: http.StatusOK, + body: loadFixture("get_block_response.json"), + url: url, + }, + { + status: http.StatusInternalServerError, + body: "{}", + url: url, + }, + }, + expectedError: errors.New("invalid response: 500 Internal Server Error"), + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + var ( + assert = assert.New(t) + ) + + responses := make(chan responseFixture, len(test.responses)) + for _, response := range test.responses { + responses <- response + } + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + response := <-responses + assert.Equal("application/json", r.Header.Get("Content-Type")) + assert.Equal("POST", r.Method) + assert.Equal(response.url, r.URL.RequestURI()) + + w.WriteHeader(response.status) + fmt.Fprintln(w, response.body) + })) + + client := NewClient(ts.URL, MainnetGenesisBlockIdentifier, MainnetCurrency) + status, err := client.NetworkStatus(context.Background()) + if test.expectedError != nil { + assert.Contains(err.Error(), test.expectedError.Error()) + } else { + assert.NoError(err) + assert.Equal(test.expectedStatus, status) + } + }) + } +} + func TestGetPeers(t *testing.T) { tests := map[string]struct { responses []responseFixture From 283cdf433dc3bb14668bf03feb546d27fd1600a1 Mon Sep 17 00:00:00 2001 From: Patrick O'Grady Date: Mon, 28 Sep 2020 08:09:07 -0700 Subject: [PATCH 5/5] Fix tests --- services/network_service_test.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/services/network_service_test.go b/services/network_service_test.go index 8d8dbb2..d60d6ed 100644 --- a/services/network_service_test.go +++ b/services/network_service_test.go @@ -27,7 +27,7 @@ import ( ) var ( - middlewareVersion = "0.0.2" + middlewareVersion = "0.0.3" defaultNetworkOptions = &types.NetworkOptionsResponse{ Version: &types.Version{ RosettaVersion: "1.4.4", @@ -78,8 +78,9 @@ func TestNetworkEndpoints_Offline(t *testing.T) { func TestNetworkEndpoints_Online(t *testing.T) { cfg := &configuration.Configuration{ - Mode: configuration.Online, - Network: networkIdentifier, + Mode: configuration.Online, + Network: networkIdentifier, + GenesisBlockIdentifier: bitcoin.MainnetGenesisBlockIdentifier, } mockIndexer := &mocks.Indexer{} mockClient := &mocks.Client{} @@ -92,9 +93,6 @@ func TestNetworkEndpoints_Online(t *testing.T) { networkIdentifier, }, networkList.NetworkIdentifiers) - rawStatus := &types.NetworkStatusResponse{ - GenesisBlockIdentifier: bitcoin.MainnetGenesisBlockIdentifier, - } blockResponse := &types.BlockResponse{ Block: &types.Block{ BlockIdentifier: &types.BlockIdentifier{ @@ -103,7 +101,11 @@ func TestNetworkEndpoints_Online(t *testing.T) { }, }, } - mockClient.On("NetworkStatus", ctx).Return(rawStatus, nil) + mockClient.On("GetPeers", ctx).Return([]*types.Peer{ + { + PeerID: "77.93.223.9:8333", + }, + }, nil) mockIndexer.On( "GetBlockLazy", ctx, @@ -117,6 +119,11 @@ func TestNetworkEndpoints_Online(t *testing.T) { assert.Equal(t, &types.NetworkStatusResponse{ GenesisBlockIdentifier: bitcoin.MainnetGenesisBlockIdentifier, CurrentBlockIdentifier: blockResponse.Block.BlockIdentifier, + Peers: []*types.Peer{ + { + PeerID: "77.93.223.9:8333", + }, + }, }, networkStatus) networkOptions, err := servicer.NetworkOptions(ctx, nil)