Skip to content

Commit

Permalink
Add /peers route to get the validator peers
Browse files Browse the repository at this point in the history
Signed-off-by: Andrea Gunderson <[email protected]>
  • Loading branch information
agunde406 committed Nov 10, 2017
1 parent 25e640f commit 48fe6b7
Show file tree
Hide file tree
Showing 10 changed files with 206 additions and 2 deletions.
33 changes: 33 additions & 0 deletions protos/client_peers.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2017 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -----------------------------------------------------------------------------

syntax = "proto3";

option java_multiple_files = true;
option java_package = "sawtooth.sdk.protobuf";
option go_package = "client_peer";

message ClientPeersGetRequest{
}

message ClientPeersGetResponse {
enum Status {
STATUS_UNSET = 0;
OK = 1;
ERROR = 2;
}
Status status = 1;
repeated string peers = 2;
}
8 changes: 6 additions & 2 deletions protos/validator.proto
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,14 @@ message Message {
// A response with the batch statuses
CLIENT_BATCH_STATUS_RESPONSE = 121;
// A request for one or more transaction receipts
CLIENT_RECEIPT_GET_REQUEST= 122;
CLIENT_RECEIPT_GET_REQUEST = 122;
// A response with the receipts
CLIENT_RECEIPT_GET_RESPONSE= 123;
CLIENT_RECEIPT_GET_RESPONSE = 123;
CLIENT_BLOCK_GET_BY_NUM_REQUEST = 124;
// A request for a validator's peers
CLIENT_PEERS_GET_REQUEST = 125;
// A response with the validator's peers
CLIENT_PEERS_GET_RESPONSE = 126;

// Message types for events
CLIENT_EVENTS_SUBSCRIBE_REQUEST = 500;
Expand Down
16 changes: 16 additions & 0 deletions rest_api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,22 @@ paths:
503:
$ref: "#/responses/503ServiceUnavailable"

/peers:
get:
summary: Fetches the endpoints of the authorized peers of the validator
responses:
200:
description: Successfully retrieved peers
schema:
data: List of peers endpoints
link:
$ref: "#/definitions/Link"
400:
$ref: "#/responses/400BadRequest"
500:
$ref: "#/responses/500ServerError"
503:
$ref: "#/responses/503ServiceUnavailable"

responses:
400BadRequest:
Expand Down
2 changes: 2 additions & 0 deletions rest_api/sawtooth_rest_api/rest_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ def start_rest_api(host, port, connection, timeout, registry):
app.router.add_get('/receipts', handler.list_receipts)
app.router.add_post('/receipts', handler.list_receipts)

app.router.add_get('/peers', handler.fetch_peers)

subscriber_handler = StateDeltaSubscriberHandler(connection)
app.router.add_get('/subscriptions', subscriber_handler.subscriptions)
app.on_shutdown.append(lambda app: subscriber_handler.on_shutdown())
Expand Down
21 changes: 21 additions & 0 deletions rest_api/sawtooth_rest_api/route_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,13 @@
from sawtooth_rest_api.protobuf import client_block_pb2
from sawtooth_rest_api.protobuf import client_batch_pb2
from sawtooth_rest_api.protobuf import client_receipt_pb2
from sawtooth_rest_api.protobuf import client_peers_pb2
from sawtooth_rest_api.protobuf.block_pb2 import BlockHeader
from sawtooth_rest_api.protobuf.batch_pb2 import BatchList
from sawtooth_rest_api.protobuf.batch_pb2 import BatchHeader
from sawtooth_rest_api.protobuf.transaction_pb2 import TransactionHeader

# pylint: disable=too-many-lines

DEFAULT_TIMEOUT = 300
LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -541,6 +543,25 @@ async def list_receipts(self, request):

return self._wrap_response(request, data=data, metadata=metadata)

async def fetch_peers(self, request):
"""Fetches the peers from the validator.
Request:
Response:
data: JSON array of peer endpoints
link: The link to this exact query
"""

response = await self._query_validator(
Message.CLIENT_PEERS_GET_REQUEST,
client_peers_pb2.ClientPeersGetResponse,
client_peers_pb2.ClientPeersGetRequest())

return self._wrap_response(
request,
data=response['peers'],
metadata=self._get_metadata(request, response))

async def _query_validator(self, request_type, response_proto,
payload, error_traps=None):
"""Sends a request to the validator and parses the response.
Expand Down
56 changes: 56 additions & 0 deletions rest_api/tests/unit/test_peers_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Copyright 2017 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ------------------------------------------------------------------------------

import json
from aiohttp.test_utils import unittest_run_loop
from components import Mocks, BaseApiTest
from sawtooth_rest_api.protobuf.validator_pb2 import Message
from sawtooth_rest_api.protobuf import client_peers_pb2


class PeersGetRequestTests(BaseApiTest):

async def get_application(self, loop):
self.set_status_and_connection(
Message.CLIENT_PEERS_GET_REQUEST,
client_peers_pb2.ClientPeersGetRequest,
client_peers_pb2.ClientPeersGetResponse)

handlers = self.build_handlers(loop, self.connection)
return self.build_app(loop, '/peers', handlers.fetch_peers)

@unittest_run_loop
async def test_peers_request(self):
"""Verifies a GET /peers works proberly.
It will receive a Protobuf response with:
- list of peer endoints
It should send an empty Protobuf request.
It should send back a JSON response with:
- a response status of 200
- a link property that ends in '/peers'
- a data property matching the peers
"""
self.connection.preset_response(
peers=["Peer1", "Peer2"],
status=self.status.OK)

response = await self.get_assert_200('/peers')
self.connection.assert_valid_request_sent()

self.assert_has_valid_link(response, '/peers')
self.assertEquals(["Peer1", "Peer2"], response['data'])
5 changes: 5 additions & 0 deletions validator/sawtooth_validator/server/component_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,8 @@ def add(
validator_pb2.Message.CLIENT_EVENTS_GET_REQUEST,
ClientEventsGetRequestHandler(event_broadcaster),
thread_pool)

dispatcher.add_handler(
validator_pb2.Message.CLIENT_PEERS_GET_REQUEST,
client_handlers.PeersGetRequest(gossip),
thread_pool)
16 changes: 16 additions & 0 deletions validator/sawtooth_validator/state/client_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from sawtooth_validator.protobuf import client_transaction_pb2
from sawtooth_validator.protobuf import client_batch_submit_pb2
from sawtooth_validator.protobuf import client_list_control_pb2
from sawtooth_validator.protobuf import client_peers_pb2
from sawtooth_validator.protobuf.block_pb2 import BlockHeader
from sawtooth_validator.protobuf.batch_pb2 import BatchHeader
from sawtooth_validator.protobuf.transaction_pb2 import TransactionHeader
Expand Down Expand Up @@ -934,3 +935,18 @@ def _respond(self, request):
block_id = ""

return self._wrap_response(transaction=txn, block_id=block_id)


class PeersGetRequest(_ClientRequestHandler):
def __init__(self, gossip):
super().__init__(
client_peers_pb2.ClientPeersGetRequest,
client_peers_pb2.ClientPeersGetResponse,
validator_pb2.Message.CLIENT_PEERS_GET_RESPONSE
)
self._gossip = gossip

def _respond(self, request):
peers = self._gossip.get_peers()
endpoints = [peers[connection_id] for connection_id in peers]
return self._wrap_response(peers=endpoints)
4 changes: 4 additions & 0 deletions validator/tests/test_client_request_handlers/mocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,7 @@ def make_store_and_tracker(size=3):
tracker.notify_txn_invalid('t-invalid', 'error message', b'error data')

return store, tracker

class MockGossip:
def get_peers(self):
return {"connection_id": "Peer1"}
47 changes: 47 additions & 0 deletions validator/tests/test_client_request_handlers/test_peer_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright 2017 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ------------------------------------------------------------------------------

import sawtooth_validator.state.client_handlers as handlers
from sawtooth_validator.protobuf import client_peers_pb2
from test_client_request_handlers.base_case import ClientHandlerTestCase
from test_client_request_handlers.mocks import MockGossip


class TestPeerRequests(ClientHandlerTestCase):
def setUp(self):
gossip = MockGossip()
self.initialize(
handlers.PeersGetRequest(gossip),
client_peers_pb2.ClientPeersGetRequest,
client_peers_pb2.ClientPeersGetResponse,
)

def test_peer_request(self):
"""Verifies requests for peers work properly.
Queries the default mock gossip for peers. Expecting "Peer1"
Expects to find:
- a status of OK
- a head_id of 'B-2' (the latest)
- the default paging response, showing all 3 resources returned
- a list of blocks with 3 items
- the items are instances of Block
- The first item has a header_signature of 'B-2'
"""
response = self.make_request()

self.assertEqual(self.status.OK, response.status)
self.assertEqual(["Peer1"], response.peers)

0 comments on commit 48fe6b7

Please sign in to comment.