From 4a492c243f1966427970753c34420069049b42db Mon Sep 17 00:00:00 2001 From: "pritam.ghanghas@gmail.com" Date: Wed, 23 Mar 2022 19:32:09 +0530 Subject: [PATCH 1/9] feat: Light miner: Diagnostics page should NOT display blockchain details (#321) grpc client for communication with gatewayrs --- .github/workflows/linting.yml | 2 +- README.md | 2 +- dev-requirements.txt | 1 + hm_pyhelper/exceptions.py | 4 + hm_pyhelper/gateway_grpc/__init__.py | 0 hm_pyhelper/gateway_grpc/client.py | 236 ++++++++++++++++++++ hm_pyhelper/gateway_grpc/exceptions.py | 2 + hm_pyhelper/miner_json_rpc/__init__.py | 1 - hm_pyhelper/miner_json_rpc/client.py | 169 --------------- hm_pyhelper/miner_json_rpc/exceptions.py | 26 --- hm_pyhelper/miner_param.py | 11 +- hm_pyhelper/protos/local_pb2.py | 195 +++++++++++++++++ hm_pyhelper/protos/local_pb2_grpc.py | 264 +++++++++++++++++++++++ hm_pyhelper/protos/region_pb2.py | 41 ++++ hm_pyhelper/tests/test_gateway_grpc.py | 141 ++++++++++++ hm_pyhelper/tests/test_miner_json_rpc.py | 244 --------------------- protos/README.md | 2 + protos/local.proto | 59 +++++ protos/region.proto | 18 ++ protos/update_protos.sh | 22 ++ requirements.txt | 3 +- setup.py | 2 +- 22 files changed, 992 insertions(+), 453 deletions(-) create mode 100644 dev-requirements.txt create mode 100644 hm_pyhelper/gateway_grpc/__init__.py create mode 100644 hm_pyhelper/gateway_grpc/client.py create mode 100644 hm_pyhelper/gateway_grpc/exceptions.py delete mode 100644 hm_pyhelper/miner_json_rpc/__init__.py delete mode 100644 hm_pyhelper/miner_json_rpc/client.py delete mode 100644 hm_pyhelper/miner_json_rpc/exceptions.py create mode 100644 hm_pyhelper/protos/local_pb2.py create mode 100644 hm_pyhelper/protos/local_pb2_grpc.py create mode 100644 hm_pyhelper/protos/region_pb2.py create mode 100644 hm_pyhelper/tests/test_gateway_grpc.py delete mode 100644 hm_pyhelper/tests/test_miner_json_rpc.py create mode 100644 protos/local.proto create mode 100644 protos/region.proto create mode 100644 protos/update_protos.sh diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index e8b29ef..16e4d42 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -19,4 +19,4 @@ jobs: - name: Lint with flake8 run: | pip install flake8 - flake8 . --count --max-complexity=10 --statistics + flake8 . --count --max-complexity=10 --statistics --exclude protos diff --git a/README.md b/README.md index f786db5..9ffbc1b 100644 --- a/README.md +++ b/README.md @@ -179,7 +179,7 @@ and formatted the same way as the [smartphone app expects](https://docs.helium.c Usage: ``` -client = MinerClient() +client = GatewayClient() result = client.create_add_gateway_txn('owner_address', 'payer_address', 'gateway_address') ``` diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 0000000..deb728a --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1 @@ +grpcio-tools==1.44.0 diff --git a/hm_pyhelper/exceptions.py b/hm_pyhelper/exceptions.py index c15aa87..7f05c5e 100644 --- a/hm_pyhelper/exceptions.py +++ b/hm_pyhelper/exceptions.py @@ -18,6 +18,10 @@ class MinerFailedToFetchMacAddress(Exception): pass +class MinerFailedToFetchEthernetAddress(Exception): + pass + + class UnknownVariantException(Exception): pass diff --git a/hm_pyhelper/gateway_grpc/__init__.py b/hm_pyhelper/gateway_grpc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hm_pyhelper/gateway_grpc/client.py b/hm_pyhelper/gateway_grpc/client.py new file mode 100644 index 0000000..b04a5c5 --- /dev/null +++ b/hm_pyhelper/gateway_grpc/client.py @@ -0,0 +1,236 @@ +import base58 +import grpc +import subprocess +import json +from typing import Union + +from hm_pyhelper.protos import blockchain_txn_add_gateway_v1_pb2, \ + local_pb2_grpc, local_pb2, region_pb2 +from hm_pyhelper.gateway_grpc.exceptions import MinerMalformedAddGatewayTxn + +from hm_pyhelper.logger import get_logger + +LOGGER = get_logger(__name__) + + +def decode_pub_key(encoded_key: bytes) -> str: + # Addresses returned by the RPC response are missing a leading + # byte for the version. The version is currently always 0. + # https://github.com/helium/helium-js/blob/8d5cb76e156fb80de6fc80f239b43e3872c7b7d7/packages/crypto/src/Address.ts#L64 + version_byte = b'\x00' + + # Convert binary address to base58 + complete_key = version_byte + encoded_key + decoded_key = base58.b58encode_check(complete_key).decode() + return decoded_key + + +class GatewayClient(object): + ''' + GatewayClient wraps grpc api provided by helium gateway-rs + It provides some convenience methods to support the old api + to limit breaking changes. + Direct interaction with the grpc api can be achieved by + using GatewayClient.stub. + + All methods might return grpc pass through exceptions. + ''' + + def __init__(self, url='helium-miner:4467'): + self._url = url + self._channel = grpc.insecure_channel(url) + self._channel.subscribe(self._connect_state_handler) + self.stub = local_pb2_grpc.apiStub(self._channel) + + def _connect_state_handler(self, state): + if state == grpc.ChannelConnectivity.SHUTDOWN: + LOGGER.error('GRPC Channel shutdown : irrecoverable error') + + def __enter__(self): + return self + + def __exit__(self, _, _2, _3): + self._channel.close() + + def get_validator_info(self) -> local_pb2.height_res: + return self.stub.height(local_pb2.height_req()) + + def get_height(self) -> int: + return self.get_validator_info().height + + def get_region_enum(self) -> int: + ''' + Returns the current configured region of the gateway. + If not asserted or set in settings, defaults to 0 (US915) + ref: https://github.com/helium/proto/blob/master/src/region.proto + ''' + return self.stub.region(local_pb2.region_req()).region + + def get_region(self) -> str: + ''' + Returns the current configured region of the gateway. + If not asserted or set in settings, defaults to 0 (US915) + ''' + region_id = self.get_region_enum() + return region_pb2.region.Name(region_id) + + def sign(self, data: bytes) -> bytes: + ''' + Sign a message with the gateway private key + ''' + return self.stub.sign(local_pb2.sign_req(data=data)).signature + + def ecdh(self, address: bytes) -> bytes: + ''' + Return shared secret using ECDH + ''' + return self.stub.ecdh(local_pb2.ecdh_req(address=address)).secret + + def get_pubkey(self) -> str: + ''' + Returns decoded public key of the gateway + ''' + encoded_key = self.stub.pubkey(local_pb2.pubkey_req()).address + return decode_pub_key(encoded_key) + + def get_summary(self) -> dict: + ''' + Returns a dict with following information + { + "region": str + configured region eg. "US915", + "key": str + gateway/device public key, + "validator": { + "height": int + blockchain height, + "block_age": int + age of the last block in seconds, + "address": str + public key/address of the validator, + "uri": http url + http endpoint of the validator + } + } + ''' + validator_info = self.get_validator_info() + return { + 'region': self.get_region(), + 'key': self.get_pubkey(), + 'gateway_version': self.get_gateway_version(), + 'validator': { + 'height': validator_info.height, + 'block_age': validator_info.block_age, + 'address': decode_pub_key(validator_info.gateway.address), + 'uri': validator_info.gateway.uri + } + } + + def get_blockchain_config_variables(self, keys: list) -> local_pb2.config_res: + ''' + Allows one to query blockchain variables. For a complete list of chain variables ref + https://helium.plus/chain-vars + + Returns config_res which is a list of config_value for the given list + of blockchain variables. + ''' + return self.stub.config(local_pb2.config_req(keys=keys)) + + def get_blockchain_config_variable(self, key: str) -> local_pb2.config_value: + ''' + Convenience method to get a single variable from the blockchain + + Raises ValueError if the key is not found + ''' + values = self.get_blockchain_config_variables(keys=[key]).values + if not values[0].value: + raise ValueError(f'{key} not found on chain') + return values[0] + + def get_gateway_version(self) -> Union[str, None]: + ''' + Returns the current version of the gateway package installed + ''' + # NOTE:: there is a command line argument to helium-gateway + # but it is not exposed in the rpc, falling back to dpkg + try: + output = subprocess.check_output(['dpkg', '-s', 'helium_gateway']) + for line in output.decode().splitlines(): + if line.strip().startswith('Version'): + # dpkg has version without v but github tags begin with v + return "v" + line.split(':')[1].strip() + return None + except subprocess.CalledProcessError: + return None + + def create_add_gateway_txn(self, owner_address: str, payer_address: str, + staking_mode: local_pb2.gateway_staking_mode = local_pb2.light, + gateway_address: str = "") -> dict: + """ + Invokes the txn_add_gateway RPC endpoint on the gateway and returns + the same payload that the smartphone app traditionally expects. + https://docs.helium.com/mine-hnt/full-hotspots/become-a-maker/hotspot-integration-testing/#generate-an-add-hotspot-transaction + + Parameters: + - owner_address: The address of the account that owns the gateway. + - payer_address: The address of the account that will pay for the + transaction. This will typically be the + maker/Nebra's account. + - staking_mode: The staking mode of the gateway. + ref: + https://github.com/helium/proto/blob/master/src/service/local.proto#L38 + - gateway_address: The address of the miner itself. This is + an optional parameter because the miner + will always return it in the payload during + transaction generation. If the param is + provided, it will only be used as extra + validation. + """ + # NOTE:: this is unimplemented as of alpha23 release of the gateway + response = self.stub.add_gateway(local_pb2.add_gateway_req( + owner=owner_address.encode('utf-8'), + payer=payer_address.encode('utf-8'), + staking_mode=staking_mode + )) + result = json.loads(response.decode()) + if result["address"] != gateway_address: + raise MinerMalformedAddGatewayTxn + return result + + +def get_address_from_add_gateway_txn(add_gateway_txn: + blockchain_txn_add_gateway_v1_pb2, + address_type: str, + expected_address: str = None): + """ + Deserializes specified field in the blockchain_txn_add_gateway_v1_pb2 + protobuf to a base58 Helium address. + + Pararms: + - add_gateway_txn: The blockchain_txn_add_gateway_v1_pb2 to + inspect. + - address_type: 'owner', 'gateway', or 'payer'. + - expected_address (optional): Value we expect to be returned. + + Raises: + MinerMalformedAddGatewayTxn if expected_address supplied and + does not match the return value. + """ + + # Addresses returned by the RPC response are missing a leading + # byte for the version. The version is currently always 0. + # https://github.com/helium/helium-js/blob/8d5cb76e156fb80de6fc80f239b43e3872c7b7d7/packages/crypto/src/Address.ts#L64 + version_byte = b'\x00' + + # Convert binary address to base58 + address_bytes = version_byte + getattr(add_gateway_txn, address_type) + address = str(base58.b58encode_check(address_bytes), 'utf-8') + + # Ensure resulting address matches expectation + is_expected_address_defined = expected_address is not None + if is_expected_address_defined and address != expected_address: + msg = f"Expected {address_type} address to be {expected_address}," + \ + f"but is {address}" + raise MinerMalformedAddGatewayTxn(msg) + + return address diff --git a/hm_pyhelper/gateway_grpc/exceptions.py b/hm_pyhelper/gateway_grpc/exceptions.py new file mode 100644 index 0000000..8ad7b16 --- /dev/null +++ b/hm_pyhelper/gateway_grpc/exceptions.py @@ -0,0 +1,2 @@ +class MinerMalformedAddGatewayTxn(Exception): + pass diff --git a/hm_pyhelper/miner_json_rpc/__init__.py b/hm_pyhelper/miner_json_rpc/__init__.py deleted file mode 100644 index ad2fd40..0000000 --- a/hm_pyhelper/miner_json_rpc/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from hm_pyhelper.miner_json_rpc.client import Client as MinerClient # noqa diff --git a/hm_pyhelper/miner_json_rpc/client.py b/hm_pyhelper/miner_json_rpc/client.py deleted file mode 100644 index ca707d7..0000000 --- a/hm_pyhelper/miner_json_rpc/client.py +++ /dev/null @@ -1,169 +0,0 @@ -import requests -import base64 -import base58 - -from hm_pyhelper.protos import blockchain_txn_pb2, \ - blockchain_txn_add_gateway_v1_pb2 -from hm_pyhelper.miner_json_rpc.exceptions import MinerConnectionError, \ - MinerMalformedURL, \ - MinerRegionUnset, \ - MinerMalformedAddGatewayTxn - - -class Client(object): - - def __init__(self, url='http://helium-miner:4467'): - self.url = url - - def __fetch_data(self, method, **kwargs): - req_body = { - "jsonrpc": "2.0", - "id": 1, - "method": method, - } - if kwargs: - req_body["params"] = kwargs - try: - response = requests.post(self.url, json=req_body) - except requests.exceptions.ConnectionError: - raise MinerConnectionError( - "Unable to connect to miner %s" % self.url - ) - except requests.exceptions.MissingSchema: - raise MinerMalformedURL( - "Miner JSONRPC URL '%s' is not a valid URL" - % self.url - ) - - if not response.ok: - response.raise_for_status() - - return response.json().get('result') - - def get_height(self): - return self.__fetch_data('info_height') - - def get_region(self): - region = self.__fetch_data('info_region') - if not region.get('region'): - raise MinerRegionUnset( - "Miner at %s does not have an asserted region" - % self.url - ) - return region - - def get_summary(self): - return self.__fetch_data('info_summary') - - def get_peer_addr(self): - return self.__fetch_data('peer_addr') - - def get_peer_book(self): - return self.__fetch_data('peer_book', addr='self') - - def get_firmware_version(self): - summary = self.get_summary() - return summary.get('firmware_version') - - def create_add_gateway_txn(self, owner_address: str, payer_address: str, - gateway_address: str = None) -> dict: - """ - Invokes the txn_add_gateway RPC endpoint on the miner and returns - the same payload that the smartphone app traditionally expects. - https://docs.helium.com/mine-hnt/full-hotspots/become-a-maker/hotspot-integration-testing/#generate-an-add-hotspot-transaction - - Alternatively, the same thing can be accomplished with dbus, - like in the below, but RPC is generally easier to use. - https://github.com/NebraLtd/hm-config/blob/900aeed353fb9729b49bca97d7da8a9abf0a2029/gatewayconfig/bluetooth/characteristics/add_gateway_characteristic.py#L71 - - Parameters: - - owner_address: The address of the account that owns the gateway. - - payer_address: The address of the account that will pay for the - transaction. This will typically be the - maker/Nebra's account. - - gateway_address: The address of the miner itself. This is - an optional parameter because the miner - will always return it in the payload during - transaction generation. If the param is - provided, it will only be used as extra - validation. - - Raises: - - MinerMalformedAddGatewayTxn if returned transaction does - not correspond to the supplied parameters. - """ - # Invoke add_gateway_txn on miner - # https://github.com/helium/miner/blob/b9d2cd108cdcc864b641ccf4209f790b1461926d/src/jsonrpc/miner_jsonrpc_txn.erl#L31 - rpc_response = self.__fetch_data('txn_add_gateway', - owner=owner_address, - payer=payer_address) - encoded_wrapped_txn = rpc_response['result'] - - # Base64 decode - # https://github.com/helium/miner/blob/b9d2cd108cdcc864b641ccf4209f790b1461926d/src/jsonrpc/miner_jsonrpc_txn.erl#L38 - decoded_wrapped_txn = base64.b64decode(encoded_wrapped_txn) - - # Deserialize wrapped protobuf - # https://github.com/helium/blockchain-core/blob/3cd6bca6c5595a1363a9bbd625ef254383a4141b/src/transactions/blockchain_txn.erl#L160 - wrapped_txn = blockchain_txn_pb2.blockchain_txn() - wrapped_txn.ParseFromString(decoded_wrapped_txn) - - # Unwrap to get blockchain_txn_add_gateway_v1 - # https://github.com/helium/proto/blob/6dc60a9933628c3baf9d2f5386481f20a5d79bb8/src/blockchain_txn_add_gateway_v1.proto#L1 - add_gateway_txn = wrapped_txn.add_gateway - - txn_gateway_address = get_address_from_add_gateway_txn( - add_gateway_txn, 'gateway', gateway_address) - - txn_owner_address = get_address_from_add_gateway_txn( - add_gateway_txn, 'owner', owner_address) - - txn_payer_address = get_address_from_add_gateway_txn( - add_gateway_txn, 'payer', payer_address) - - return { - 'gateway_address': txn_gateway_address, - 'owner_address': txn_owner_address, - 'payer_address': txn_payer_address, - 'fee': add_gateway_txn.fee, - 'staking_fee': add_gateway_txn.staking_fee, - 'txn': encoded_wrapped_txn - } - - -def get_address_from_add_gateway_txn(add_gateway_txn: - blockchain_txn_add_gateway_v1_pb2, - address_type: str, - expected_address: str = None): - """ - Deserializes specified field in the blockchain_txn_add_gateway_v1_pb2 - protobuf to a base58 Helium address. - - Pararms: - - add_gateway_txn: The blockchain_txn_add_gateway_v1_pb2 to - inspect. - - address_type: 'owner', 'gateway', or 'payer'. - - expected_address (optional): Value we expect to be returned. - - Raises: - MinerMalformedAddGatewayTxn if expected_address supplied and - does not match the return value. - """ - - # Addresses returned by the RPC response are missing a leading - # byte for the version. The version is currently always 0. - # https://github.com/helium/helium-js/blob/8d5cb76e156fb80de6fc80f239b43e3872c7b7d7/packages/crypto/src/Address.ts#L64 - version_byte = b'\x00' - - # Convert binary address to base58 - address_bytes = version_byte + getattr(add_gateway_txn, address_type) - address = str(base58.b58encode_check(address_bytes), 'utf-8') - - # Ensure resulting address matches expectation - is_expected_address_defined = expected_address is not None - if is_expected_address_defined and address != expected_address: - msg = f"Expected {address_type} address to be {expected_address}," + \ - f"but is {address}" - raise MinerMalformedAddGatewayTxn(msg) - - return address diff --git a/hm_pyhelper/miner_json_rpc/exceptions.py b/hm_pyhelper/miner_json_rpc/exceptions.py deleted file mode 100644 index cceb09c..0000000 --- a/hm_pyhelper/miner_json_rpc/exceptions.py +++ /dev/null @@ -1,26 +0,0 @@ -class MinerJSONRPCException(Exception): - pass - - -class MinerConnectionError(MinerJSONRPCException): - pass - - -class MinerMalformedURL(MinerJSONRPCException): - pass - - -class MinerRegionUnset(MinerJSONRPCException): - pass - - -class MinerFailedFetchData(MinerJSONRPCException): - pass - - -class MinerFailedToFetchEthernetAddress(MinerJSONRPCException): - pass - - -class MinerMalformedAddGatewayTxn(MinerJSONRPCException): - pass diff --git a/hm_pyhelper/miner_param.py b/hm_pyhelper/miner_param.py index 27c3ab8..22ffb6a 100644 --- a/hm_pyhelper/miner_param.py +++ b/hm_pyhelper/miner_param.py @@ -8,8 +8,6 @@ SPIUnavailableException, ECCMalfunctionException, \ GatewayMFRFileNotFoundException, \ MinerFailedToFetchMacAddress -from hm_pyhelper.miner_json_rpc.exceptions import \ - MinerFailedToFetchEthernetAddress from hm_pyhelper.hardware_definitions import get_variant_attribute, \ UnknownVariantException, UnknownVariantAttributeException @@ -170,7 +168,7 @@ def is_miner_key_and_passed(test_result): def get_ethernet_addresses(diagnostics): - # Get ethernet MAC and WIFI address + # Get ethernet and wlan MAC address # The order of the values in the lists is important! # It determines which value will be available for which key @@ -182,13 +180,10 @@ def get_ethernet_addresses(diagnostics): for (path, key) in zip(path_to_files, keys): try: diagnostics[key] = get_mac_address(path) - except MinerFailedToFetchMacAddress as e: - diagnostics[key] = False - LOGGER.error(e) except Exception as e: diagnostics[key] = False LOGGER.error(e) - raise MinerFailedToFetchEthernetAddress(str(e)) + raise MinerFailedToFetchMacAddress(str(e)) def get_mac_address(path): @@ -206,8 +201,6 @@ def get_mac_address(path): The path must be a string value") try: file = open(path) - except MinerFailedToFetchMacAddress as e: - LOGGER.exception(str(e)) except FileNotFoundError as e: LOGGER.exception("Failed to find Miner" "Mac Address file at path %s" % path) diff --git a/hm_pyhelper/protos/local_pb2.py b/hm_pyhelper/protos/local_pb2.py new file mode 100644 index 0000000..f6b6130 --- /dev/null +++ b/hm_pyhelper/protos/local_pb2.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: local.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0blocal.proto\x12\x0chelium.local\"\x1d\n\npubkey_res\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\"\x0c\n\npubkey_req\"\x18\n\x08sign_req\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\x1d\n\x08sign_res\x12\x11\n\tsignature\x18\x01 \x01(\x0c\"\x1b\n\x08\x65\x63\x64h_req\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\"\x1a\n\x08\x65\x63\x64h_res\x12\x0e\n\x06secret\x18\x01 \x01(\x0c\"\x1a\n\nconfig_req\x12\x0c\n\x04keys\x18\x01 \x03(\t\"8\n\nconfig_res\x12*\n\x06values\x18\x01 \x03(\x0b\x32\x1a.helium.local.config_value\"9\n\x0c\x63onfig_value\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\x0c\")\n\tkeyed_uri\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12\x0b\n\x03uri\x18\x02 \x01(\t\"\x0c\n\nheight_req\"Y\n\nheight_res\x12\x0e\n\x06height\x18\x01 \x01(\x04\x12\x11\n\tblock_age\x18\x02 \x01(\x04\x12(\n\x07gateway\x18\x03 \x01(\x0b\x32\x17.helium.local.keyed_uri\"\x0c\n\nregion_req\"\x1c\n\nregion_res\x12\x0e\n\x06region\x18\x01 \x01(\x05\"i\n\x0f\x61\x64\x64_gateway_req\x12\r\n\x05owner\x18\x01 \x01(\x0c\x12\r\n\x05payer\x18\x02 \x01(\x0c\x12\x38\n\x0cstaking_mode\x18\x03 \x01(\x0e\x32\".helium.local.gateway_staking_mode\"*\n\x0f\x61\x64\x64_gateway_res\x12\x17\n\x0f\x61\x64\x64_gateway_txn\x18\x01 \x01(\x0c*9\n\x14gateway_staking_mode\x12\x0c\n\x08\x64\x61taonly\x10\x00\x12\x08\n\x04\x66ull\x10\x01\x12\t\n\x05light\x10\x02\x32\xba\x03\n\x03\x61pi\x12<\n\x06pubkey\x12\x18.helium.local.pubkey_req\x1a\x18.helium.local.pubkey_res\x12\x36\n\x04sign\x12\x16.helium.local.sign_req\x1a\x16.helium.local.sign_res\x12\x36\n\x04\x65\x63\x64h\x12\x16.helium.local.ecdh_req\x1a\x16.helium.local.ecdh_res\x12<\n\x06\x63onfig\x12\x18.helium.local.config_req\x1a\x18.helium.local.config_res\x12<\n\x06height\x12\x18.helium.local.height_req\x1a\x18.helium.local.height_res\x12<\n\x06region\x12\x18.helium.local.region_req\x1a\x18.helium.local.region_res\x12K\n\x0b\x61\x64\x64_gateway\x12\x1d.helium.local.add_gateway_req\x1a\x1d.helium.local.add_gateway_resb\x06proto3') + +_GATEWAY_STAKING_MODE = DESCRIPTOR.enum_types_by_name['gateway_staking_mode'] +gateway_staking_mode = enum_type_wrapper.EnumTypeWrapper(_GATEWAY_STAKING_MODE) +dataonly = 0 +full = 1 +light = 2 + + +_PUBKEY_RES = DESCRIPTOR.message_types_by_name['pubkey_res'] +_PUBKEY_REQ = DESCRIPTOR.message_types_by_name['pubkey_req'] +_SIGN_REQ = DESCRIPTOR.message_types_by_name['sign_req'] +_SIGN_RES = DESCRIPTOR.message_types_by_name['sign_res'] +_ECDH_REQ = DESCRIPTOR.message_types_by_name['ecdh_req'] +_ECDH_RES = DESCRIPTOR.message_types_by_name['ecdh_res'] +_CONFIG_REQ = DESCRIPTOR.message_types_by_name['config_req'] +_CONFIG_RES = DESCRIPTOR.message_types_by_name['config_res'] +_CONFIG_VALUE = DESCRIPTOR.message_types_by_name['config_value'] +_KEYED_URI = DESCRIPTOR.message_types_by_name['keyed_uri'] +_HEIGHT_REQ = DESCRIPTOR.message_types_by_name['height_req'] +_HEIGHT_RES = DESCRIPTOR.message_types_by_name['height_res'] +_REGION_REQ = DESCRIPTOR.message_types_by_name['region_req'] +_REGION_RES = DESCRIPTOR.message_types_by_name['region_res'] +_ADD_GATEWAY_REQ = DESCRIPTOR.message_types_by_name['add_gateway_req'] +_ADD_GATEWAY_RES = DESCRIPTOR.message_types_by_name['add_gateway_res'] +pubkey_res = _reflection.GeneratedProtocolMessageType('pubkey_res', (_message.Message,), { + 'DESCRIPTOR' : _PUBKEY_RES, + '__module__' : 'local_pb2' + # @@protoc_insertion_point(class_scope:helium.local.pubkey_res) + }) +_sym_db.RegisterMessage(pubkey_res) + +pubkey_req = _reflection.GeneratedProtocolMessageType('pubkey_req', (_message.Message,), { + 'DESCRIPTOR' : _PUBKEY_REQ, + '__module__' : 'local_pb2' + # @@protoc_insertion_point(class_scope:helium.local.pubkey_req) + }) +_sym_db.RegisterMessage(pubkey_req) + +sign_req = _reflection.GeneratedProtocolMessageType('sign_req', (_message.Message,), { + 'DESCRIPTOR' : _SIGN_REQ, + '__module__' : 'local_pb2' + # @@protoc_insertion_point(class_scope:helium.local.sign_req) + }) +_sym_db.RegisterMessage(sign_req) + +sign_res = _reflection.GeneratedProtocolMessageType('sign_res', (_message.Message,), { + 'DESCRIPTOR' : _SIGN_RES, + '__module__' : 'local_pb2' + # @@protoc_insertion_point(class_scope:helium.local.sign_res) + }) +_sym_db.RegisterMessage(sign_res) + +ecdh_req = _reflection.GeneratedProtocolMessageType('ecdh_req', (_message.Message,), { + 'DESCRIPTOR' : _ECDH_REQ, + '__module__' : 'local_pb2' + # @@protoc_insertion_point(class_scope:helium.local.ecdh_req) + }) +_sym_db.RegisterMessage(ecdh_req) + +ecdh_res = _reflection.GeneratedProtocolMessageType('ecdh_res', (_message.Message,), { + 'DESCRIPTOR' : _ECDH_RES, + '__module__' : 'local_pb2' + # @@protoc_insertion_point(class_scope:helium.local.ecdh_res) + }) +_sym_db.RegisterMessage(ecdh_res) + +config_req = _reflection.GeneratedProtocolMessageType('config_req', (_message.Message,), { + 'DESCRIPTOR' : _CONFIG_REQ, + '__module__' : 'local_pb2' + # @@protoc_insertion_point(class_scope:helium.local.config_req) + }) +_sym_db.RegisterMessage(config_req) + +config_res = _reflection.GeneratedProtocolMessageType('config_res', (_message.Message,), { + 'DESCRIPTOR' : _CONFIG_RES, + '__module__' : 'local_pb2' + # @@protoc_insertion_point(class_scope:helium.local.config_res) + }) +_sym_db.RegisterMessage(config_res) + +config_value = _reflection.GeneratedProtocolMessageType('config_value', (_message.Message,), { + 'DESCRIPTOR' : _CONFIG_VALUE, + '__module__' : 'local_pb2' + # @@protoc_insertion_point(class_scope:helium.local.config_value) + }) +_sym_db.RegisterMessage(config_value) + +keyed_uri = _reflection.GeneratedProtocolMessageType('keyed_uri', (_message.Message,), { + 'DESCRIPTOR' : _KEYED_URI, + '__module__' : 'local_pb2' + # @@protoc_insertion_point(class_scope:helium.local.keyed_uri) + }) +_sym_db.RegisterMessage(keyed_uri) + +height_req = _reflection.GeneratedProtocolMessageType('height_req', (_message.Message,), { + 'DESCRIPTOR' : _HEIGHT_REQ, + '__module__' : 'local_pb2' + # @@protoc_insertion_point(class_scope:helium.local.height_req) + }) +_sym_db.RegisterMessage(height_req) + +height_res = _reflection.GeneratedProtocolMessageType('height_res', (_message.Message,), { + 'DESCRIPTOR' : _HEIGHT_RES, + '__module__' : 'local_pb2' + # @@protoc_insertion_point(class_scope:helium.local.height_res) + }) +_sym_db.RegisterMessage(height_res) + +region_req = _reflection.GeneratedProtocolMessageType('region_req', (_message.Message,), { + 'DESCRIPTOR' : _REGION_REQ, + '__module__' : 'local_pb2' + # @@protoc_insertion_point(class_scope:helium.local.region_req) + }) +_sym_db.RegisterMessage(region_req) + +region_res = _reflection.GeneratedProtocolMessageType('region_res', (_message.Message,), { + 'DESCRIPTOR' : _REGION_RES, + '__module__' : 'local_pb2' + # @@protoc_insertion_point(class_scope:helium.local.region_res) + }) +_sym_db.RegisterMessage(region_res) + +add_gateway_req = _reflection.GeneratedProtocolMessageType('add_gateway_req', (_message.Message,), { + 'DESCRIPTOR' : _ADD_GATEWAY_REQ, + '__module__' : 'local_pb2' + # @@protoc_insertion_point(class_scope:helium.local.add_gateway_req) + }) +_sym_db.RegisterMessage(add_gateway_req) + +add_gateway_res = _reflection.GeneratedProtocolMessageType('add_gateway_res', (_message.Message,), { + 'DESCRIPTOR' : _ADD_GATEWAY_RES, + '__module__' : 'local_pb2' + # @@protoc_insertion_point(class_scope:helium.local.add_gateway_res) + }) +_sym_db.RegisterMessage(add_gateway_res) + +_API = DESCRIPTOR.services_by_name['api'] +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _GATEWAY_STAKING_MODE._serialized_start=676 + _GATEWAY_STAKING_MODE._serialized_end=733 + _PUBKEY_RES._serialized_start=29 + _PUBKEY_RES._serialized_end=58 + _PUBKEY_REQ._serialized_start=60 + _PUBKEY_REQ._serialized_end=72 + _SIGN_REQ._serialized_start=74 + _SIGN_REQ._serialized_end=98 + _SIGN_RES._serialized_start=100 + _SIGN_RES._serialized_end=129 + _ECDH_REQ._serialized_start=131 + _ECDH_REQ._serialized_end=158 + _ECDH_RES._serialized_start=160 + _ECDH_RES._serialized_end=186 + _CONFIG_REQ._serialized_start=188 + _CONFIG_REQ._serialized_end=214 + _CONFIG_RES._serialized_start=216 + _CONFIG_RES._serialized_end=272 + _CONFIG_VALUE._serialized_start=274 + _CONFIG_VALUE._serialized_end=331 + _KEYED_URI._serialized_start=333 + _KEYED_URI._serialized_end=374 + _HEIGHT_REQ._serialized_start=376 + _HEIGHT_REQ._serialized_end=388 + _HEIGHT_RES._serialized_start=390 + _HEIGHT_RES._serialized_end=479 + _REGION_REQ._serialized_start=481 + _REGION_REQ._serialized_end=493 + _REGION_RES._serialized_start=495 + _REGION_RES._serialized_end=523 + _ADD_GATEWAY_REQ._serialized_start=525 + _ADD_GATEWAY_REQ._serialized_end=630 + _ADD_GATEWAY_RES._serialized_start=632 + _ADD_GATEWAY_RES._serialized_end=674 + _API._serialized_start=736 + _API._serialized_end=1178 +# @@protoc_insertion_point(module_scope) diff --git a/hm_pyhelper/protos/local_pb2_grpc.py b/hm_pyhelper/protos/local_pb2_grpc.py new file mode 100644 index 0000000..b9f55d7 --- /dev/null +++ b/hm_pyhelper/protos/local_pb2_grpc.py @@ -0,0 +1,264 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +import hm_pyhelper.protos.local_pb2 as local__pb2 + + +class apiStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.pubkey = channel.unary_unary( + '/helium.local.api/pubkey', + request_serializer=local__pb2.pubkey_req.SerializeToString, + response_deserializer=local__pb2.pubkey_res.FromString, + ) + self.sign = channel.unary_unary( + '/helium.local.api/sign', + request_serializer=local__pb2.sign_req.SerializeToString, + response_deserializer=local__pb2.sign_res.FromString, + ) + self.ecdh = channel.unary_unary( + '/helium.local.api/ecdh', + request_serializer=local__pb2.ecdh_req.SerializeToString, + response_deserializer=local__pb2.ecdh_res.FromString, + ) + self.config = channel.unary_unary( + '/helium.local.api/config', + request_serializer=local__pb2.config_req.SerializeToString, + response_deserializer=local__pb2.config_res.FromString, + ) + self.height = channel.unary_unary( + '/helium.local.api/height', + request_serializer=local__pb2.height_req.SerializeToString, + response_deserializer=local__pb2.height_res.FromString, + ) + self.region = channel.unary_unary( + '/helium.local.api/region', + request_serializer=local__pb2.region_req.SerializeToString, + response_deserializer=local__pb2.region_res.FromString, + ) + self.add_gateway = channel.unary_unary( + '/helium.local.api/add_gateway', + request_serializer=local__pb2.add_gateway_req.SerializeToString, + response_deserializer=local__pb2.add_gateway_res.FromString, + ) + + +class apiServicer(object): + """Missing associated documentation comment in .proto file.""" + + def pubkey(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def sign(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ecdh(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def config(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def height(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def region(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def add_gateway(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_apiServicer_to_server(servicer, server): + rpc_method_handlers = { + 'pubkey': grpc.unary_unary_rpc_method_handler( + servicer.pubkey, + request_deserializer=local__pb2.pubkey_req.FromString, + response_serializer=local__pb2.pubkey_res.SerializeToString, + ), + 'sign': grpc.unary_unary_rpc_method_handler( + servicer.sign, + request_deserializer=local__pb2.sign_req.FromString, + response_serializer=local__pb2.sign_res.SerializeToString, + ), + 'ecdh': grpc.unary_unary_rpc_method_handler( + servicer.ecdh, + request_deserializer=local__pb2.ecdh_req.FromString, + response_serializer=local__pb2.ecdh_res.SerializeToString, + ), + 'config': grpc.unary_unary_rpc_method_handler( + servicer.config, + request_deserializer=local__pb2.config_req.FromString, + response_serializer=local__pb2.config_res.SerializeToString, + ), + 'height': grpc.unary_unary_rpc_method_handler( + servicer.height, + request_deserializer=local__pb2.height_req.FromString, + response_serializer=local__pb2.height_res.SerializeToString, + ), + 'region': grpc.unary_unary_rpc_method_handler( + servicer.region, + request_deserializer=local__pb2.region_req.FromString, + response_serializer=local__pb2.region_res.SerializeToString, + ), + 'add_gateway': grpc.unary_unary_rpc_method_handler( + servicer.add_gateway, + request_deserializer=local__pb2.add_gateway_req.FromString, + response_serializer=local__pb2.add_gateway_res.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'helium.local.api', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class api(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def pubkey(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/helium.local.api/pubkey', + local__pb2.pubkey_req.SerializeToString, + local__pb2.pubkey_res.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def sign(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/helium.local.api/sign', + local__pb2.sign_req.SerializeToString, + local__pb2.sign_res.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ecdh(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/helium.local.api/ecdh', + local__pb2.ecdh_req.SerializeToString, + local__pb2.ecdh_res.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def config(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/helium.local.api/config', + local__pb2.config_req.SerializeToString, + local__pb2.config_res.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def height(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/helium.local.api/height', + local__pb2.height_req.SerializeToString, + local__pb2.height_res.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def region(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/helium.local.api/region', + local__pb2.region_req.SerializeToString, + local__pb2.region_res.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def add_gateway(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/helium.local.api/add_gateway', + local__pb2.add_gateway_req.SerializeToString, + local__pb2.add_gateway_res.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/hm_pyhelper/protos/region_pb2.py b/hm_pyhelper/protos/region_pb2.py new file mode 100644 index 0000000..3227a35 --- /dev/null +++ b/hm_pyhelper/protos/region_pb2.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: region.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cregion.proto\x12\x06helium*\x94\x01\n\x06region\x12\t\n\x05US915\x10\x00\x12\t\n\x05\x45U868\x10\x01\x12\t\n\x05\x45U433\x10\x02\x12\t\n\x05\x43N470\x10\x03\x12\t\n\x05\x43N779\x10\x04\x12\t\n\x05\x41U915\x10\x05\x12\x0b\n\x07\x41S923_1\x10\x06\x12\t\n\x05KR920\x10\x07\x12\t\n\x05IN865\x10\x08\x12\x0b\n\x07\x41S923_2\x10\t\x12\x0b\n\x07\x41S923_3\x10\n\x12\x0b\n\x07\x41S923_4\x10\x0b\x62\x06proto3') + +_REGION = DESCRIPTOR.enum_types_by_name['region'] +region = enum_type_wrapper.EnumTypeWrapper(_REGION) +US915 = 0 +EU868 = 1 +EU433 = 2 +CN470 = 3 +CN779 = 4 +AU915 = 5 +AS923_1 = 6 +KR920 = 7 +IN865 = 8 +AS923_2 = 9 +AS923_3 = 10 +AS923_4 = 11 + + +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _REGION._serialized_start=25 + _REGION._serialized_end=173 +# @@protoc_insertion_point(module_scope) diff --git a/hm_pyhelper/tests/test_gateway_grpc.py b/hm_pyhelper/tests/test_gateway_grpc.py new file mode 100644 index 0000000..cdd0a37 --- /dev/null +++ b/hm_pyhelper/tests/test_gateway_grpc.py @@ -0,0 +1,141 @@ +import unittest +from unittest.mock import patch +import grpc +from concurrent import futures +from hm_pyhelper.gateway_grpc.client import GatewayClient + +from hm_pyhelper.protos import local_pb2 +from hm_pyhelper.protos import local_pb2_grpc + + +class TestData: + server_port = 4468 + height_res = local_pb2.height_res( + height=43, + block_age=42, + gateway=local_pb2.keyed_uri( + address=b"\000\177\327E-\223\222e\002e[*\250\260\361p\271\267/" + b"\220\026\010\360\213t\304\313\022\316>\254\347?", + uri="http://32.23.54.23:8080" # NOSONAR + ) + ) + validator_address_decoded = "11yJXQPG9deHqvw2ac6VWtNP7gZj8X3t3Qb3Gqm9j729p4AsdaA" + pubkey_encoded = b"\x01\xc3\x06\x7f\xb9\x19}\xd1n2\xe2M\xeb\xb5\x11\x7f" \ + b"\xbc\x12\xebT\xb9\x84R\xc7\xca\xf8o\xdddx\xea~\xab" + pubkey_decoded = "14RdqcZC2rbdTBwNaTsj5EVWYaM7BKGJ44ycq6wWJy9Hg7RKCii" + chain_vars = { + "block_size_limit": local_pb2.config_value(name="block_size_limit", + value=b"5242880", + type="int"), + "min_assert_h3_res": local_pb2.config_value(name="min_assert_h3_res", + value=b"12", + type="int") + } + region_enum = 0 + region_name = "US915" + dpkg_output = b"""Package: helium_gateway\n + Status: install ok installed\n + Priority: optional\n + Section: utility\n + Installed-Size: 3729\n + Maintainer: Marc Nijdam \n + Architecture: amd64\n + Version: 1.0.0~alpha.23\n + Depends: curl\n + Conffiles:\n + /etc/helium_gateway/settings.toml 4d6fb434f97a50066b8163a371d5c208\n + Description: Helium Gateway for LoRa packet forwarders\n + The Helium Gateway to attach your LoRa gateway to the Helium Blockchain.\n""" + expected_summary = { + 'region': region_name, + 'key': pubkey_decoded, + 'gateway_version': "v1.0.0~alpha.23", + 'validator': { + 'height': height_res.height, + 'block_age': height_res.block_age, + 'address': validator_address_decoded, + 'uri': height_res.gateway.uri + } + } + + +class MockServicer(local_pb2_grpc.apiServicer): + def height(self, request, context): + return TestData.height_res + + def region(self, request, context): + return local_pb2.region_res(region=0) + + def pubkey(self, request, context): + return local_pb2.pubkey_res(address=TestData.pubkey_encoded) + + def config(self, request, context): + result = local_pb2.config_res() + for key in request.keys: + if key in TestData.chain_vars.keys(): + result.values.append(TestData.chain_vars.get(key)) + else: + result.values.append(local_pb2.config_value(name=key)) + return result + + +class TestGatewayGRPCClient(unittest.TestCase): + + # we can start the real service hear by installing dpkg. But AFAIK + # our testing methods, real service exposes us to random failures + def setUp(self): + self.mock_server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) + local_pb2_grpc.add_apiServicer_to_server(MockServicer(), self.mock_server) + self.mock_server.add_insecure_port(f'[::]:{TestData.server_port}') + self.mock_server.start() + + def tearDown(self): + self.mock_server.stop(None) + + def test_get_pubkey(self): + with GatewayClient(f'localhost:{TestData.server_port}') as client: + self.assertEqual(client.get_pubkey(), TestData.pubkey_decoded) + + def test_get_validator_info(self): + with GatewayClient(f'localhost:{TestData.server_port}') as client: + self.assertEqual(client.get_validator_info(), TestData.height_res) + + def test_get_height(self): + with GatewayClient(f'localhost:{TestData.server_port}') as client: + self.assertEqual(client.get_height(), client.get_validator_info().height) + + def test_get_region(self): + with GatewayClient(f'localhost:{TestData.server_port}') as client: + self.assertEqual(client.get_region_enum(), TestData.region_enum) + self.assertEqual(client.get_region(), TestData.region_name) + + def test_get_blockchain_variable(self): + with GatewayClient(f'localhost:{TestData.server_port}') as client: + for key in TestData.chain_vars: + self.assertEqual(client.get_blockchain_config_variable(key), + TestData.chain_vars.get(key)) + + def test_get_summary(self): + with GatewayClient(f'localhost:{TestData.server_port}') as client: + # summary when helium_gateway is not installed + test_summary_copy = TestData.expected_summary.copy() + test_summary_copy['gateway_version'] = None + self.assertIn(client.get_summary(), + [TestData.expected_summary, test_summary_copy]) + + @patch('subprocess.check_output', return_value=TestData.dpkg_output) + def test_get_gateway_version(self, mock_check_output): + mock_check_output.return_value = TestData.dpkg_output + with GatewayClient(f'localhost:{TestData.server_port}') as client: + self.assertIn(client.get_gateway_version(), + [TestData.expected_summary['gateway_version'], None]) + + def test_connection_failure(self): + with self.assertRaises(grpc.RpcError): + with GatewayClient('localhost:1234') as client: + client.get_pubkey() + + def test_invalid_chain_var(self): + with GatewayClient(f'localhost:{TestData.server_port}') as client: + with self.assertRaises(ValueError): + client.get_blockchain_config_variable('not_a_key') diff --git a/hm_pyhelper/tests/test_miner_json_rpc.py b/hm_pyhelper/tests/test_miner_json_rpc.py deleted file mode 100644 index b740e48..0000000 --- a/hm_pyhelper/tests/test_miner_json_rpc.py +++ /dev/null @@ -1,244 +0,0 @@ -import unittest -import mock -import responses -import requests -from hm_pyhelper.miner_json_rpc import MinerClient -from hm_pyhelper.miner_json_rpc.exceptions import MinerRegionUnset -from hm_pyhelper.miner_json_rpc.exceptions import MinerMalformedURL -from hm_pyhelper.miner_json_rpc.exceptions import MinerConnectionError - -BASE_URL = 'http://helium-miner:4467' - - -@responses.activate -def response_result(data, status): - url = "https://fake_url" - responses.add(responses.POST, url, json=data, status=status) - resp = requests.post(url) - print(resp.json()) - return resp - - -def return_payload_with_method(method): - return {'jsonrpc': '2.0', 'id': 1, 'method': method} - - -class Result(object): - def __init__(self, result={'my': 'data'}): - self.result = result - - -class Response(object): - def __init__(self, data=Result()): - self.data = data - - -class TestMinerJSONRPC(unittest.TestCase): - def test_instantiation(self): - client = MinerClient() - self.assertIsInstance(client, MinerClient) - self.assertEqual(client.url, BASE_URL) - - def test_malformed_url(self): - client = MinerClient(url='fakeurl') - - exception_raised = False - exception_type = None - try: - client.get_height() - except Exception as exc: - exception_raised = True - exception_type = exc - - self.assertTrue(exception_raised) - self.assertIsInstance(exception_type, MinerMalformedURL) - - def test_connection_error(self): - client = MinerClient(url='http://notarealminer:9999') - - exception_raised = False - exception_type = None - try: - client.get_height() - except Exception as exc: - exception_raised = True - exception_type = exc - - self.assertTrue(exception_raised) - self.assertIsInstance(exception_type, MinerConnectionError) - - @mock.patch('hm_pyhelper.miner_json_rpc.client.requests.post', - return_value=response_result( - {"result": {'epoch': 25612, 'height': 993640}, "id": 1}, - 200)) - def test_get_height(self, mock_json_rpc_client): - client = MinerClient() - result = client.get_height() - mock_json_rpc_client.assert_called_with( - BASE_URL, json=return_payload_with_method('info_height')) - - self.assertEqual(result, {'epoch': 25612, 'height': 993640}) - - @mock.patch('hm_pyhelper.miner_json_rpc.client.requests.post', - return_value=response_result( - {"result": {'region': None}, "id": 1}, 200)) - def test_get_region_not_asserted(self, mock_json_rpc_client): - client = MinerClient() - exception_raised = False - exception_type = None - - try: - client.get_region() - except Exception as exc: - exception_raised = True - exception_type = exc - - self.assertTrue(exception_raised) - self.assertIsInstance(exception_type, MinerRegionUnset) - - @mock.patch('hm_pyhelper.miner_json_rpc.client.requests.post', - return_value=response_result( - {"result": {'region': "EU868"}, "id": 1}, 200)) - def test_get_region(self, mock_json_rpc_client): - client = MinerClient() - result = client.get_region() - mock_json_rpc_client.assert_called_with( - BASE_URL, json=return_payload_with_method('info_region') - ) - self.assertEqual(result, {'region': 'EU868'}) - - summary = { - 'block_age': 1136610, - 'epoch': 25612, - 'firmware_version': "0.1", - 'gateway_details': 'undefined', - 'height': 993640, - 'mac_addresses': [ - {'eth0': '0242AC110002'}, - {'ip6tnl0': '00000000000000000000000000000000'}, - {'tunl0': '00000000'}, - {'lo': '000000000000'} - ], - 'name': 'scruffy-chocolate-shell', - 'peer_book_entry_count': 3, - 'sync_height': 993640, - 'uptime': 144, - 'version': 10010005 - } - - result_json = {"result": summary, "id": 1} - - @mock.patch('hm_pyhelper.miner_json_rpc.client.requests.post', - return_value=response_result(result_json, 200)) - def test_get_summary(self, mock_json_rpc_client): - client = MinerClient() - result = client.get_summary() - mock_json_rpc_client.assert_called_with( - BASE_URL, json=return_payload_with_method('info_summary') - ) - self.assertEqual(result, self.summary) - - peer_addr = '/p2p/11jr2kMp1bZvSC6pd3XkNvs9Q43qCgEzxRwV6vpuqXanC5UcLEs' - - @mock.patch('hm_pyhelper.miner_json_rpc.client.requests.post', - return_value=response_result( - {"result": {'peer_addr': peer_addr}, "id": 1}, 200)) - def test_get_peer_addr(self, mock_json_rpc_client): - - client = MinerClient() - result = client.get_peer_addr() - mock_json_rpc_client.assert_called_with( - BASE_URL, json=return_payload_with_method('peer_addr') - ) - self.assertEqual(result, {'peer_addr': self.peer_addr}) - - @mock.patch('hm_pyhelper.miner_json_rpc.client.requests.post', - return_value=response_result( - {"result": [], "id": 1}, - 200)) - def test_get_peer_book(self, mock_json_rpc_client): - - client = MinerClient() - result = client.get_peer_book() - payload = return_payload_with_method('peer_book') - payload["params"] = {'addr': 'self'} - mock_json_rpc_client.assert_called_with( - BASE_URL, json=payload - ) - self.assertEqual(result, []) - - firmware_version = '2021.10.18.0' - data_response = { - 'block_age': 1136610, - 'epoch': 25612, - 'firmware_version': firmware_version, - 'gateway_details': 'undefined', - 'height': 993640, - 'mac_addresses': [ - {'eth0': '0242AC110002'}, - {'ip6tnl0': '00000000000000000000000000000000'}, - {'tunl0': '00000000'}, - {'lo': '000000000000'} - ], - 'name': 'scruffy-chocolate-shell', - 'peer_book_entry_count': 3, - 'sync_height': 993640, - 'uptime': 144, - 'version': 10010005 - } - - result_response = {"result": data_response, "id": 1} - - @mock.patch('hm_pyhelper.miner_json_rpc.client.requests.post', - return_value=response_result( - result_response, 200)) - def test_get_firmware_version(self, mock_json_rpc_client): - client = MinerClient() - result = client.get_firmware_version() - mock_json_rpc_client.assert_called_with( - BASE_URL, json=return_payload_with_method('info_summary') - ) - self.assertEqual(result, self.firmware_version) - - add_gateway_response = { - "jsonrpc": "2.0", - "result": { - "result": "CroBCiEBwPbb63LQD8x/m/ZDLLyOLgtxypQIjh+xPPS+d8g/i24SIQB" - "8XdzWqrIF201DNKHpXKFtsMtgvZeqBBc1wOk9sV2j4SJGMEQCIGE82g" - "Hbn0z/AOyaDXsuQDptC/I15fHCF//QEgzoxodrAiBsoRUiw8zMVttkP" - "hEOoMfM0smmdCZPKX6tVOgK0s/0KiohAVHXaw1kNly2VOt47MlzTfkC" - "IUTOgW34Orw1LSJt+9mCOICS9AFA6PsD" - }, - "id": "1" - } - - @mock.patch('hm_pyhelper.miner_json_rpc.client.requests.post', - return_value=response_result( - add_gateway_response, 200)) - def test_create_add_gateway_txn(self, mock_json_rpc_client): - self.maxDiff = None - client = MinerClient() - actual_result = client.create_add_gateway_txn( - '14QjC3A5DEH2uFwhDxyBHdFir7YWGG23Fic1wGvkCr6qrWC7Q47', - '13Zni1he7KY9pUmkXMhEhTwfUpL9AcEV1m2UbbvFsrU9QPTMgE3') - - expected_result = { - 'gateway_address': - '11wmnCAfvFkdx3Az1hesTsSv9YeBUhs8JZKjCqcs2vmRwRazpBa', - - 'owner_address': - '14QjC3A5DEH2uFwhDxyBHdFir7YWGG23Fic1wGvkCr6qrWC7Q47', - - 'payer_address': - '13Zni1he7KY9pUmkXMhEhTwfUpL9AcEV1m2UbbvFsrU9QPTMgE3', - - 'fee': 65000, - 'staking_fee': 4000000, - 'txn': "CroBCiEBwPbb63LQD8x/m/ZDLLyOLgtxypQIjh+xPPS+d8g/i24SIQB8Xd" - "zWqrIF201DNKHpXKFtsMtgvZeqBBc1wOk9sV2j4SJGMEQCIGE82gHbn0z/" - "AOyaDXsuQDptC/I15fHCF//QEgzoxodrAiBsoRUiw8zMVttkPhEOoMfM0s" - "mmdCZPKX6tVOgK0s/0KiohAVHXaw1kNly2VOt47MlzTfkCIUTOgW34Orw1" - "LSJt+9mCOICS9AFA6PsD" - } - - self.assertDictEqual(actual_result, expected_result) diff --git a/protos/README.md b/protos/README.md index fd14e46..4479460 100644 --- a/protos/README.md +++ b/protos/README.md @@ -16,3 +16,5 @@ DST_DIR=/PATH/TO/hm-pyhelper/hm_pyhelper/protos protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/blockchain_txn.proto protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/blockchain_txn_add_gateway_v1.proto ``` + +For frequently changing proto files, we use [this] (https://github.com/NebraLtd/hm-pyhelper/blob/master/protos/update_protos.sh) script to download and generate the python code. \ No newline at end of file diff --git a/protos/local.proto b/protos/local.proto new file mode 100644 index 0000000..d2a640d --- /dev/null +++ b/protos/local.proto @@ -0,0 +1,59 @@ +syntax = "proto3"; + +package helium.local; + +message pubkey_res { bytes address = 1; } +message pubkey_req {} + +message sign_req { bytes data = 1; } +message sign_res { bytes signature = 1; } + +message ecdh_req { bytes address = 1; } +message ecdh_res { bytes secret = 1; } + +message config_req { repeated string keys = 1; } +message config_res { repeated config_value values = 1; } + +message config_value { + string name = 1; + string type = 2; + bytes value = 3; +} + +message keyed_uri { + bytes address = 1; + string uri = 2; +} + +message height_req {} +message height_res { + uint64 height = 1; + uint64 block_age = 2; + keyed_uri gateway = 3; +} + +message region_req {} +message region_res { int32 region = 1; } + +enum gateway_staking_mode { + dataonly = 0; + full = 1; + light = 2; +} +message add_gateway_req { + bytes owner = 1; + bytes payer = 2; + gateway_staking_mode staking_mode = 3; +} + +message add_gateway_res { bytes add_gateway_txn = 1; } + +service api { + rpc pubkey(pubkey_req) returns (pubkey_res); + rpc sign(sign_req) returns (sign_res); + rpc ecdh(ecdh_req) returns (ecdh_res); + rpc config(config_req) returns (config_res); + rpc height(height_req) returns (height_res); + rpc region(region_req) returns (region_res); + rpc add_gateway(add_gateway_req) returns (add_gateway_res); +} diff --git a/protos/region.proto b/protos/region.proto new file mode 100644 index 0000000..85a8656 --- /dev/null +++ b/protos/region.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package helium; + +enum region { + US915 = 0; + EU868 = 1; + EU433 = 2; + CN470 = 3; + CN779 = 4; + AU915 = 5; + AS923_1 = 6; + KR920 = 7; + IN865 = 8; + AS923_2 = 9; + AS923_3 = 10; + AS923_4 = 11; +} diff --git a/protos/update_protos.sh b/protos/update_protos.sh new file mode 100644 index 0000000..1c37e6b --- /dev/null +++ b/protos/update_protos.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +SRC_DIR=. +DST_DIR=../hm_pyhelper/protos + +function update_proto() { + echo "Updating $1" + wget $1 + + # replace old if succcessful + if [[ $? -eq 0 ]]; then + mv $2.1 $2 + fi +} + +update_proto https://raw.githubusercontent.com/helium/proto/master/src/service/local.proto local.proto +update_proto https://raw.githubusercontent.com/helium/proto/master/src/region.proto region.proto + +python -m grpc_tools.protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/region.proto +python -m grpc_tools.protoc -I=$SRC_DIR --python_out=$DST_DIR --grpc_python_out=$DST_DIR $SRC_DIR/local.proto + +sed -i -e 's/import *local_pb2/import hm_pyhelper.protos.local_pb2/' $DST_DIR/local_pb2_grpc.py diff --git a/requirements.txt b/requirements.txt index 2947bde..97666b9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ requests==2.26.0 retry==0.9.2 base58==2.1.1 -protobuf==3.19.3 \ No newline at end of file +protobuf==3.19.3 +grpcio==1.44.0 diff --git a/setup.py b/setup.py index 3bcf082..c166a2b 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='hm_pyhelper', - version='0.13.16', + version='0.13.17', author="Nebra Ltd", author_email="support@nebra.com", description="Helium Python Helper", From bbf9ebe2e500ff2a6c0738658076f50f3e9fa77d Mon Sep 17 00:00:00 2001 From: "pritam.ghanghas@gmail.com" Date: Wed, 27 Apr 2022 15:52:07 +0530 Subject: [PATCH 2/9] feat: Light miner: Diagnostics page should NOT display blockchain details * add contants for gateway diagnostics --- hm_pyhelper/constants/diagnostics.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hm_pyhelper/constants/diagnostics.py b/hm_pyhelper/constants/diagnostics.py index d8ccf9f..a44b9ad 100644 --- a/hm_pyhelper/constants/diagnostics.py +++ b/hm_pyhelper/constants/diagnostics.py @@ -13,3 +13,13 @@ LTE_KEY = 'lte' PF_KEY = 'legacy_pass_fail' LORA_KEY = 'lora' + +# gatewayrs diagnostic keys +VALIDATOR_ADDRESS_KEY = 'validator_address' +VALIDATOR_URI_KEY = 'validator_uri' +VALIDATOR_BLOCK_HEIGHT_KEY = 'validator_height' +VALIDATOR_BLOCK_HEIGHT_SHORT_KEY = 'MH' +VALIDATOR_BLOCK_AGE = 'validator_block_age' +GATEWAY_PUBKEY_KEY = 'gateway_pubkey' +GATEWAY_REGION_KEY = 'gateway_region' +GATEWAY_REGION_SHORT_KEY = 'RE' From 19255b0d3f06c1af84c08d6d515ec455dd4bd372 Mon Sep 17 00:00:00 2001 From: Pritam Ghanghas Date: Fri, 26 Aug 2022 17:55:51 +0530 Subject: [PATCH 3/9] feat: Merge light-hotspot branch into production #163 (#185) * bring back json rpc client so that pyhelper remains compatible with both miner and gateway client API during transition. --- README.md | 3 + hm_pyhelper/miner_json_rpc/__init__.py | 1 + hm_pyhelper/miner_json_rpc/client.py | 169 ++++++++++++++++ hm_pyhelper/miner_json_rpc/exceptions.py | 26 +++ hm_pyhelper/tests/test_miner_json_rpc.py | 243 +++++++++++++++++++++++ 5 files changed, 442 insertions(+) create mode 100644 hm_pyhelper/miner_json_rpc/__init__.py create mode 100644 hm_pyhelper/miner_json_rpc/client.py create mode 100644 hm_pyhelper/miner_json_rpc/exceptions.py create mode 100644 hm_pyhelper/tests/test_miner_json_rpc.py diff --git a/README.md b/README.md index 9ffbc1b..767bab5 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,9 @@ and formatted the same way as the [smartphone app expects](https://docs.helium.c Usage: ``` +While using miner container, use json rpc client +client = MinerClient() +While using gateway-rs container, use grpc client client = GatewayClient() result = client.create_add_gateway_txn('owner_address', 'payer_address', 'gateway_address') ``` diff --git a/hm_pyhelper/miner_json_rpc/__init__.py b/hm_pyhelper/miner_json_rpc/__init__.py new file mode 100644 index 0000000..ad2fd40 --- /dev/null +++ b/hm_pyhelper/miner_json_rpc/__init__.py @@ -0,0 +1 @@ +from hm_pyhelper.miner_json_rpc.client import Client as MinerClient # noqa diff --git a/hm_pyhelper/miner_json_rpc/client.py b/hm_pyhelper/miner_json_rpc/client.py new file mode 100644 index 0000000..ca707d7 --- /dev/null +++ b/hm_pyhelper/miner_json_rpc/client.py @@ -0,0 +1,169 @@ +import requests +import base64 +import base58 + +from hm_pyhelper.protos import blockchain_txn_pb2, \ + blockchain_txn_add_gateway_v1_pb2 +from hm_pyhelper.miner_json_rpc.exceptions import MinerConnectionError, \ + MinerMalformedURL, \ + MinerRegionUnset, \ + MinerMalformedAddGatewayTxn + + +class Client(object): + + def __init__(self, url='http://helium-miner:4467'): + self.url = url + + def __fetch_data(self, method, **kwargs): + req_body = { + "jsonrpc": "2.0", + "id": 1, + "method": method, + } + if kwargs: + req_body["params"] = kwargs + try: + response = requests.post(self.url, json=req_body) + except requests.exceptions.ConnectionError: + raise MinerConnectionError( + "Unable to connect to miner %s" % self.url + ) + except requests.exceptions.MissingSchema: + raise MinerMalformedURL( + "Miner JSONRPC URL '%s' is not a valid URL" + % self.url + ) + + if not response.ok: + response.raise_for_status() + + return response.json().get('result') + + def get_height(self): + return self.__fetch_data('info_height') + + def get_region(self): + region = self.__fetch_data('info_region') + if not region.get('region'): + raise MinerRegionUnset( + "Miner at %s does not have an asserted region" + % self.url + ) + return region + + def get_summary(self): + return self.__fetch_data('info_summary') + + def get_peer_addr(self): + return self.__fetch_data('peer_addr') + + def get_peer_book(self): + return self.__fetch_data('peer_book', addr='self') + + def get_firmware_version(self): + summary = self.get_summary() + return summary.get('firmware_version') + + def create_add_gateway_txn(self, owner_address: str, payer_address: str, + gateway_address: str = None) -> dict: + """ + Invokes the txn_add_gateway RPC endpoint on the miner and returns + the same payload that the smartphone app traditionally expects. + https://docs.helium.com/mine-hnt/full-hotspots/become-a-maker/hotspot-integration-testing/#generate-an-add-hotspot-transaction + + Alternatively, the same thing can be accomplished with dbus, + like in the below, but RPC is generally easier to use. + https://github.com/NebraLtd/hm-config/blob/900aeed353fb9729b49bca97d7da8a9abf0a2029/gatewayconfig/bluetooth/characteristics/add_gateway_characteristic.py#L71 + + Parameters: + - owner_address: The address of the account that owns the gateway. + - payer_address: The address of the account that will pay for the + transaction. This will typically be the + maker/Nebra's account. + - gateway_address: The address of the miner itself. This is + an optional parameter because the miner + will always return it in the payload during + transaction generation. If the param is + provided, it will only be used as extra + validation. + + Raises: + - MinerMalformedAddGatewayTxn if returned transaction does + not correspond to the supplied parameters. + """ + # Invoke add_gateway_txn on miner + # https://github.com/helium/miner/blob/b9d2cd108cdcc864b641ccf4209f790b1461926d/src/jsonrpc/miner_jsonrpc_txn.erl#L31 + rpc_response = self.__fetch_data('txn_add_gateway', + owner=owner_address, + payer=payer_address) + encoded_wrapped_txn = rpc_response['result'] + + # Base64 decode + # https://github.com/helium/miner/blob/b9d2cd108cdcc864b641ccf4209f790b1461926d/src/jsonrpc/miner_jsonrpc_txn.erl#L38 + decoded_wrapped_txn = base64.b64decode(encoded_wrapped_txn) + + # Deserialize wrapped protobuf + # https://github.com/helium/blockchain-core/blob/3cd6bca6c5595a1363a9bbd625ef254383a4141b/src/transactions/blockchain_txn.erl#L160 + wrapped_txn = blockchain_txn_pb2.blockchain_txn() + wrapped_txn.ParseFromString(decoded_wrapped_txn) + + # Unwrap to get blockchain_txn_add_gateway_v1 + # https://github.com/helium/proto/blob/6dc60a9933628c3baf9d2f5386481f20a5d79bb8/src/blockchain_txn_add_gateway_v1.proto#L1 + add_gateway_txn = wrapped_txn.add_gateway + + txn_gateway_address = get_address_from_add_gateway_txn( + add_gateway_txn, 'gateway', gateway_address) + + txn_owner_address = get_address_from_add_gateway_txn( + add_gateway_txn, 'owner', owner_address) + + txn_payer_address = get_address_from_add_gateway_txn( + add_gateway_txn, 'payer', payer_address) + + return { + 'gateway_address': txn_gateway_address, + 'owner_address': txn_owner_address, + 'payer_address': txn_payer_address, + 'fee': add_gateway_txn.fee, + 'staking_fee': add_gateway_txn.staking_fee, + 'txn': encoded_wrapped_txn + } + + +def get_address_from_add_gateway_txn(add_gateway_txn: + blockchain_txn_add_gateway_v1_pb2, + address_type: str, + expected_address: str = None): + """ + Deserializes specified field in the blockchain_txn_add_gateway_v1_pb2 + protobuf to a base58 Helium address. + + Pararms: + - add_gateway_txn: The blockchain_txn_add_gateway_v1_pb2 to + inspect. + - address_type: 'owner', 'gateway', or 'payer'. + - expected_address (optional): Value we expect to be returned. + + Raises: + MinerMalformedAddGatewayTxn if expected_address supplied and + does not match the return value. + """ + + # Addresses returned by the RPC response are missing a leading + # byte for the version. The version is currently always 0. + # https://github.com/helium/helium-js/blob/8d5cb76e156fb80de6fc80f239b43e3872c7b7d7/packages/crypto/src/Address.ts#L64 + version_byte = b'\x00' + + # Convert binary address to base58 + address_bytes = version_byte + getattr(add_gateway_txn, address_type) + address = str(base58.b58encode_check(address_bytes), 'utf-8') + + # Ensure resulting address matches expectation + is_expected_address_defined = expected_address is not None + if is_expected_address_defined and address != expected_address: + msg = f"Expected {address_type} address to be {expected_address}," + \ + f"but is {address}" + raise MinerMalformedAddGatewayTxn(msg) + + return address diff --git a/hm_pyhelper/miner_json_rpc/exceptions.py b/hm_pyhelper/miner_json_rpc/exceptions.py new file mode 100644 index 0000000..cceb09c --- /dev/null +++ b/hm_pyhelper/miner_json_rpc/exceptions.py @@ -0,0 +1,26 @@ +class MinerJSONRPCException(Exception): + pass + + +class MinerConnectionError(MinerJSONRPCException): + pass + + +class MinerMalformedURL(MinerJSONRPCException): + pass + + +class MinerRegionUnset(MinerJSONRPCException): + pass + + +class MinerFailedFetchData(MinerJSONRPCException): + pass + + +class MinerFailedToFetchEthernetAddress(MinerJSONRPCException): + pass + + +class MinerMalformedAddGatewayTxn(MinerJSONRPCException): + pass diff --git a/hm_pyhelper/tests/test_miner_json_rpc.py b/hm_pyhelper/tests/test_miner_json_rpc.py new file mode 100644 index 0000000..783b5bd --- /dev/null +++ b/hm_pyhelper/tests/test_miner_json_rpc.py @@ -0,0 +1,243 @@ +import unittest +import mock +import responses +import requests +from hm_pyhelper.miner_json_rpc import MinerClient +from hm_pyhelper.miner_json_rpc.exceptions import MinerRegionUnset +from hm_pyhelper.miner_json_rpc.exceptions import MinerMalformedURL +from hm_pyhelper.miner_json_rpc.exceptions import MinerConnectionError + +BASE_URL = 'http://helium-miner:4467' + + +@responses.activate +def response_result(data, status): + url = "https://fake_url" + responses.add(responses.POST, url, json=data, status=status) + resp = requests.post(url) + return resp + + +def return_payload_with_method(method): + return {'jsonrpc': '2.0', 'id': 1, 'method': method} + + +class Result(object): + def __init__(self, result={'my': 'data'}): + self.result = result + + +class Response(object): + def __init__(self, data=Result()): + self.data = data + + +class TestMinerJSONRPC(unittest.TestCase): + def test_instantiation(self): + client = MinerClient() + self.assertIsInstance(client, MinerClient) + self.assertEqual(client.url, BASE_URL) + + def test_malformed_url(self): + client = MinerClient(url='fakeurl') + + exception_raised = False + exception_type = None + try: + client.get_height() + except Exception as exc: + exception_raised = True + exception_type = exc + + self.assertTrue(exception_raised) + self.assertIsInstance(exception_type, MinerMalformedURL) + + def test_connection_error(self): + client = MinerClient(url='http://notarealminer:9999') + + exception_raised = False + exception_type = None + try: + client.get_height() + except Exception as exc: + exception_raised = True + exception_type = exc + + self.assertTrue(exception_raised) + self.assertIsInstance(exception_type, MinerConnectionError) + + @mock.patch('hm_pyhelper.miner_json_rpc.client.requests.post', + return_value=response_result( + {"result": {'epoch': 25612, 'height': 993640}, "id": 1}, + 200)) + def test_get_height(self, mock_json_rpc_client): + client = MinerClient() + result = client.get_height() + mock_json_rpc_client.assert_called_with( + BASE_URL, json=return_payload_with_method('info_height')) + + self.assertEqual(result, {'epoch': 25612, 'height': 993640}) + + @mock.patch('hm_pyhelper.miner_json_rpc.client.requests.post', + return_value=response_result( + {"result": {'region': None}, "id": 1}, 200)) + def test_get_region_not_asserted(self, mock_json_rpc_client): + client = MinerClient() + exception_raised = False + exception_type = None + + try: + client.get_region() + except Exception as exc: + exception_raised = True + exception_type = exc + + self.assertTrue(exception_raised) + self.assertIsInstance(exception_type, MinerRegionUnset) + + @mock.patch('hm_pyhelper.miner_json_rpc.client.requests.post', + return_value=response_result( + {"result": {'region': "EU868"}, "id": 1}, 200)) + def test_get_region(self, mock_json_rpc_client): + client = MinerClient() + result = client.get_region() + mock_json_rpc_client.assert_called_with( + BASE_URL, json=return_payload_with_method('info_region') + ) + self.assertEqual(result, {'region': 'EU868'}) + + summary = { + 'block_age': 1136610, + 'epoch': 25612, + 'firmware_version': "0.1", + 'gateway_details': 'undefined', + 'height': 993640, + 'mac_addresses': [ + {'eth0': '0242AC110002'}, + {'ip6tnl0': '00000000000000000000000000000000'}, + {'tunl0': '00000000'}, + {'lo': '000000000000'} + ], + 'name': 'scruffy-chocolate-shell', + 'peer_book_entry_count': 3, + 'sync_height': 993640, + 'uptime': 144, + 'version': 10010005 + } + + result_json = {"result": summary, "id": 1} + + @mock.patch('hm_pyhelper.miner_json_rpc.client.requests.post', + return_value=response_result(result_json, 200)) + def test_get_summary(self, mock_json_rpc_client): + client = MinerClient() + result = client.get_summary() + mock_json_rpc_client.assert_called_with( + BASE_URL, json=return_payload_with_method('info_summary') + ) + self.assertEqual(result, self.summary) + + peer_addr = '/p2p/11jr2kMp1bZvSC6pd3XkNvs9Q43qCgEzxRwV6vpuqXanC5UcLEs' + + @mock.patch('hm_pyhelper.miner_json_rpc.client.requests.post', + return_value=response_result( + {"result": {'peer_addr': peer_addr}, "id": 1}, 200)) + def test_get_peer_addr(self, mock_json_rpc_client): + + client = MinerClient() + result = client.get_peer_addr() + mock_json_rpc_client.assert_called_with( + BASE_URL, json=return_payload_with_method('peer_addr') + ) + self.assertEqual(result, {'peer_addr': self.peer_addr}) + + @mock.patch('hm_pyhelper.miner_json_rpc.client.requests.post', + return_value=response_result( + {"result": [], "id": 1}, + 200)) + def test_get_peer_book(self, mock_json_rpc_client): + + client = MinerClient() + result = client.get_peer_book() + payload = return_payload_with_method('peer_book') + payload["params"] = {'addr': 'self'} + mock_json_rpc_client.assert_called_with( + BASE_URL, json=payload + ) + self.assertEqual(result, []) + + firmware_version = '2021.10.18.0' + data_response = { + 'block_age': 1136610, + 'epoch': 25612, + 'firmware_version': firmware_version, + 'gateway_details': 'undefined', + 'height': 993640, + 'mac_addresses': [ + {'eth0': '0242AC110002'}, + {'ip6tnl0': '00000000000000000000000000000000'}, + {'tunl0': '00000000'}, + {'lo': '000000000000'} + ], + 'name': 'scruffy-chocolate-shell', + 'peer_book_entry_count': 3, + 'sync_height': 993640, + 'uptime': 144, + 'version': 10010005 + } + + result_response = {"result": data_response, "id": 1} + + @mock.patch('hm_pyhelper.miner_json_rpc.client.requests.post', + return_value=response_result( + result_response, 200)) + def test_get_firmware_version(self, mock_json_rpc_client): + client = MinerClient() + result = client.get_firmware_version() + mock_json_rpc_client.assert_called_with( + BASE_URL, json=return_payload_with_method('info_summary') + ) + self.assertEqual(result, self.firmware_version) + + add_gateway_response = { + "jsonrpc": "2.0", + "result": { + "result": "CroBCiEBwPbb63LQD8x/m/ZDLLyOLgtxypQIjh+xPPS+d8g/i24SIQB" + "8XdzWqrIF201DNKHpXKFtsMtgvZeqBBc1wOk9sV2j4SJGMEQCIGE82g" + "Hbn0z/AOyaDXsuQDptC/I15fHCF//QEgzoxodrAiBsoRUiw8zMVttkP" + "hEOoMfM0smmdCZPKX6tVOgK0s/0KiohAVHXaw1kNly2VOt47MlzTfkC" + "IUTOgW34Orw1LSJt+9mCOICS9AFA6PsD" + }, + "id": "1" + } + + @mock.patch('hm_pyhelper.miner_json_rpc.client.requests.post', + return_value=response_result( + add_gateway_response, 200)) + def test_create_add_gateway_txn(self, mock_json_rpc_client): + self.maxDiff = None + client = MinerClient() + actual_result = client.create_add_gateway_txn( + '14QjC3A5DEH2uFwhDxyBHdFir7YWGG23Fic1wGvkCr6qrWC7Q47', + '13Zni1he7KY9pUmkXMhEhTwfUpL9AcEV1m2UbbvFsrU9QPTMgE3') + + expected_result = { + 'gateway_address': + '11wmnCAfvFkdx3Az1hesTsSv9YeBUhs8JZKjCqcs2vmRwRazpBa', + + 'owner_address': + '14QjC3A5DEH2uFwhDxyBHdFir7YWGG23Fic1wGvkCr6qrWC7Q47', + + 'payer_address': + '13Zni1he7KY9pUmkXMhEhTwfUpL9AcEV1m2UbbvFsrU9QPTMgE3', + + 'fee': 65000, + 'staking_fee': 4000000, + 'txn': "CroBCiEBwPbb63LQD8x/m/ZDLLyOLgtxypQIjh+xPPS+d8g/i24SIQB8Xd" + "zWqrIF201DNKHpXKFtsMtgvZeqBBc1wOk9sV2j4SJGMEQCIGE82gHbn0z/" + "AOyaDXsuQDptC/I15fHCF//QEgzoxodrAiBsoRUiw8zMVttkPhEOoMfM0s" + "mmdCZPKX6tVOgK0s/0KiohAVHXaw1kNly2VOt47MlzTfkCIUTOgW34Orw1" + "LSJt+9mCOICS9AFA6PsD" + } + + self.assertDictEqual(actual_result, expected_result) From b1b780dc8be5185106d43dc4886ddd21a86191a2 Mon Sep 17 00:00:00 2001 From: Murat Ursavas Date: Wed, 29 Mar 2023 20:27:55 +0300 Subject: [PATCH 4/9] Merged master and updated grpc --- requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 3762b86..83c3bda 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,5 +2,5 @@ requests==2.28.1 retry==0.9.2 base58==2.1.1 protobuf==4.21.12 -grpcio==1.51.1 +grpcio==1.53.0 packaging>=22.0 diff --git a/setup.py b/setup.py index f12d917..f91eaab 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='hm_pyhelper', - version='0.13.59', + version='0.14.0', author="Nebra Ltd", author_email="support@nebra.com", description="Helium Python Helper", From da47af211c26335261321826550b54c234ded7da Mon Sep 17 00:00:00 2001 From: Murat Ursavas Date: Thu, 30 Mar 2023 11:46:54 +0300 Subject: [PATCH 5/9] Fixed dependency issues Dependencies are now only required to be in requirements.txt --- requirements.txt | 2 +- setup.py | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/requirements.txt b/requirements.txt index 83c3bda..4e11c17 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -requests==2.28.1 +requests>=2.28.1 retry==0.9.2 base58==2.1.1 protobuf==4.21.12 diff --git a/setup.py b/setup.py index f91eaab..c606bd8 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,14 @@ from setuptools import setup, find_packages -from os.path import join, dirname +import os + +# allow setup.py to be run from any path +here = os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)) +os.chdir(here) + +requires = [ + line.strip() + for line in open(os.path.join(here, "requirements.txt"), "r").readlines() +] setup( name='hm_pyhelper', @@ -7,16 +16,10 @@ author="Nebra Ltd", author_email="support@nebra.com", description="Helium Python Helper", - long_description=open(join(dirname(__file__), 'README.md')).read(), + long_description=open(os.path.join(os.path.dirname(__file__), 'README.md')).read(), long_description_content_type="text/markdown", url="https://github.com/NebraLtd/hm-pyhelper", - install_requires=[ - 'requests>=2.28.1', - 'retry==0.9.2', - 'base58==2.1.1', - 'protobuf==4.21.12', - 'packaging>=22.0' - ], + install_requires=requires, project_urls={ "Bug Tracker": "https://github.com/NebraLtd/hm-pyhelper/issues", }, From af38d2d738de384eb2944b109148ad74e0e4f28c Mon Sep 17 00:00:00 2001 From: Murat Ursavas Date: Fri, 31 Mar 2023 09:49:58 +0300 Subject: [PATCH 6/9] Fixed unit test coverage --- hm_pyhelper/tests/test_diagnostic_report.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hm_pyhelper/tests/test_diagnostic_report.py b/hm_pyhelper/tests/test_diagnostic_report.py index 68ad315..5b99ae1 100644 --- a/hm_pyhelper/tests/test_diagnostic_report.py +++ b/hm_pyhelper/tests/test_diagnostic_report.py @@ -1,5 +1,6 @@ import unittest import json +from hm_pyhelper.constants.diagnostics import ERRORS_KEY from hm_pyhelper.diagnostics import Diagnostic, DiagnosticsReport from hm_pyhelper.diagnostics.diagnostics_report import \ @@ -78,7 +79,7 @@ def test_passed_false_on_errors(self): response = { 'diagnostics_passed': False, - 'errors': ['ECC', 'BN', 'OK', 'PK', 'PF'], + ERRORS_KEY: ['ECC', 'BN', 'OK', 'PK', 'PF'], 'serial_number': '0000000021aabbcc', 'ECC': 'gateway_mfr test finished with error', 'E0': 'F0:4C:D5:58:E0:E1', From 16199312b4c6e344c89f49315177a94046566244 Mon Sep 17 00:00:00 2001 From: Murat Ursavas Date: Fri, 31 Mar 2023 10:14:05 +0300 Subject: [PATCH 7/9] Made styling of var strings consistent --- hm_pyhelper/miner_param.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/hm_pyhelper/miner_param.py b/hm_pyhelper/miner_param.py index 3718144..2d77a9e 100644 --- a/hm_pyhelper/miner_param.py +++ b/hm_pyhelper/miner_param.py @@ -35,10 +35,8 @@ def run_gateway_mfr(sub_command: str, slot: int = False) -> dict: capture_output=True, check=True ) - LOGGER.info( - 'gateway_mfr response stdout: %s' % run_gateway_mfr_result.stdout) - LOGGER.info( - 'gateway_mfr response stderr: %s' % run_gateway_mfr_result.stderr) + LOGGER.info(f"gateway_mfr response stdout: {run_gateway_mfr_result.stdout}") + LOGGER.info(f"gateway_mfr response stderr: {run_gateway_mfr_result.stderr}") except subprocess.CalledProcessError as e: err_str = "gateway_mfr exited with a non-zero status" LOGGER.exception(err_str) @@ -221,7 +219,7 @@ def provision_key(slot: int, force: bool = False): try: gateway_mfr_result = run_gateway_mfr("provision", slot=slot) - LOGGER.info("[ECC Provisioning] %s", gateway_mfr_result) + LOGGER.info(f"[ECC Provisioning] {gateway_mfr_result}") provisioning_successful = True response = gateway_mfr_result @@ -231,9 +229,9 @@ def provision_key(slot: int, force: bool = False): response = str(exp) except Exception as exp: - LOGGER.error("[ECC Provisioning] Error during provisioning. %s" % str(exp)) - provisioning_successful = False response = str(exp) + LOGGER.error(f"[ECC Provisioning] Error during provisioning. {response}") + provisioning_successful = False # Try key generation. if provisioning_successful is False and force is True: @@ -243,8 +241,8 @@ def provision_key(slot: int, force: bool = False): response = gateway_mfr_result except Exception as exp: - LOGGER.error("[ECC Provisioning] key --generate failed: %s" % str(exp)) response = str(exp) + LOGGER.error(f"[ECC Provisioning] key --generate failed: {response}") return provisioning_successful, response @@ -304,14 +302,14 @@ def get_mac_address(path): # logging as warning because some people remove wifi from their outdoor units. # We can't do anything about these errors even if they were failing wifi units. LOGGER.warning("Failed to find Miner" - "Mac Address file at path %s" % path) + f"Mac Address file at path {path}") raise MinerFailedToFetchMacAddress("Failed to find file" "containing miner mac address. " "Exception: %s" % str(e)) \ .with_traceback(e.__traceback__) except PermissionError as e: LOGGER.exception("Permissions invalid for Miner" - "Mac Address file at path %s" % path) + f"Mac Address file at path {path}") raise MinerFailedToFetchMacAddress("Failed to fetch" "miner mac address. " "Invalid permissions to access " @@ -342,16 +340,16 @@ def retry_get_region(region_override, region_filepath): return region_override LOGGER.debug( - "No region override set (value = %s), will retrieve from miner." % region_override) # noqa: E501 + f"No region override set (value = {region_override}), will retrieve from miner.") # noqa: E501 with open(region_filepath) as region_file: region = region_file.read().rstrip('\n') - LOGGER.debug("Region {} parsed from {} ".format(region, region_filepath)) + LOGGER.debug(f"Region {region} parsed from {region_filepath}") is_region_valid = len(region) > 3 if is_region_valid: return region - raise MalformedRegionException("Region %s is invalid" % region) + raise MalformedRegionException(f"Region {region} is invalid") @retry(SPIUnavailableException, delay=SPI_UNAVAILABLE_SLEEP_SECONDS, @@ -360,11 +358,11 @@ def await_spi_available(spi_bus): """ Check that the SPI bus path exists, assuming it is in /dev/{spi_bus} """ - if os.path.exists('/dev/{}'.format(spi_bus)): - LOGGER.debug("SPI bus %s Configured Correctly" % spi_bus) + if os.path.exists(f"/dev/{spi_bus}"): + LOGGER.debug(f"SPI bus {spi_bus} Configured Correctly") return True else: - raise SPIUnavailableException("SPI bus %s not found!" % spi_bus) + raise SPIUnavailableException(f"SPI bus {spi_bus} not found!") def config_search_param(command, param): From 8f24a05148d935dff44c4017128c04ed1d1c29c9 Mon Sep 17 00:00:00 2001 From: Murat Ursavas Date: Fri, 31 Mar 2023 11:21:37 +0300 Subject: [PATCH 8/9] Updated protos --- .../protos/gateway_staking_mode_pb2.py | 25 +++ hm_pyhelper/protos/local_pb2.py | 201 +++--------------- hm_pyhelper/protos/local_pb2_grpc.py | 139 ++---------- hm_pyhelper/protos/region_pb2.py | 28 +-- hm_pyhelper/tests/test_gateway_grpc.py | 2 +- protos/gateway_staking_mode.proto | 9 + protos/local.proto | 41 +--- protos/region.proto | 18 +- protos/update_protos.sh | 14 +- 9 files changed, 125 insertions(+), 352 deletions(-) create mode 100644 hm_pyhelper/protos/gateway_staking_mode_pb2.py create mode 100644 protos/gateway_staking_mode.proto mode change 100644 => 100755 protos/update_protos.sh diff --git a/hm_pyhelper/protos/gateway_staking_mode_pb2.py b/hm_pyhelper/protos/gateway_staking_mode_pb2.py new file mode 100644 index 0000000..d151bb3 --- /dev/null +++ b/hm_pyhelper/protos/gateway_staking_mode_pb2.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: gateway_staking_mode.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1agateway_staking_mode.proto\x12\x06helium*9\n\x14gateway_staking_mode\x12\x0c\n\x08\x64\x61taonly\x10\x00\x12\x08\n\x04\x66ull\x10\x01\x12\t\n\x05light\x10\x02\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'gateway_staking_mode_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _GATEWAY_STAKING_MODE._serialized_start=38 + _GATEWAY_STAKING_MODE._serialized_end=95 +# @@protoc_insertion_point(module_scope) diff --git a/hm_pyhelper/protos/local_pb2.py b/hm_pyhelper/protos/local_pb2.py index f6b6130..c98dd39 100644 --- a/hm_pyhelper/protos/local_pb2.py +++ b/hm_pyhelper/protos/local_pb2.py @@ -2,194 +2,43 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: local.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper +from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() +import hm_pyhelper.protos.gateway_staking_mode_pb2 as gateway__staking__mode__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0blocal.proto\x12\x0chelium.local\"\x1d\n\npubkey_res\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\"\x0c\n\npubkey_req\"\x18\n\x08sign_req\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\x1d\n\x08sign_res\x12\x11\n\tsignature\x18\x01 \x01(\x0c\"\x1b\n\x08\x65\x63\x64h_req\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\"\x1a\n\x08\x65\x63\x64h_res\x12\x0e\n\x06secret\x18\x01 \x01(\x0c\"\x1a\n\nconfig_req\x12\x0c\n\x04keys\x18\x01 \x03(\t\"8\n\nconfig_res\x12*\n\x06values\x18\x01 \x03(\x0b\x32\x1a.helium.local.config_value\"9\n\x0c\x63onfig_value\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\x0c\")\n\tkeyed_uri\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12\x0b\n\x03uri\x18\x02 \x01(\t\"\x0c\n\nheight_req\"Y\n\nheight_res\x12\x0e\n\x06height\x18\x01 \x01(\x04\x12\x11\n\tblock_age\x18\x02 \x01(\x04\x12(\n\x07gateway\x18\x03 \x01(\x0b\x32\x17.helium.local.keyed_uri\"\x0c\n\nregion_req\"\x1c\n\nregion_res\x12\x0e\n\x06region\x18\x01 \x01(\x05\"i\n\x0f\x61\x64\x64_gateway_req\x12\r\n\x05owner\x18\x01 \x01(\x0c\x12\r\n\x05payer\x18\x02 \x01(\x0c\x12\x38\n\x0cstaking_mode\x18\x03 \x01(\x0e\x32\".helium.local.gateway_staking_mode\"*\n\x0f\x61\x64\x64_gateway_res\x12\x17\n\x0f\x61\x64\x64_gateway_txn\x18\x01 \x01(\x0c*9\n\x14gateway_staking_mode\x12\x0c\n\x08\x64\x61taonly\x10\x00\x12\x08\n\x04\x66ull\x10\x01\x12\t\n\x05light\x10\x02\x32\xba\x03\n\x03\x61pi\x12<\n\x06pubkey\x12\x18.helium.local.pubkey_req\x1a\x18.helium.local.pubkey_res\x12\x36\n\x04sign\x12\x16.helium.local.sign_req\x1a\x16.helium.local.sign_res\x12\x36\n\x04\x65\x63\x64h\x12\x16.helium.local.ecdh_req\x1a\x16.helium.local.ecdh_res\x12<\n\x06\x63onfig\x12\x18.helium.local.config_req\x1a\x18.helium.local.config_res\x12<\n\x06height\x12\x18.helium.local.height_req\x1a\x18.helium.local.height_res\x12<\n\x06region\x12\x18.helium.local.region_req\x1a\x18.helium.local.region_res\x12K\n\x0b\x61\x64\x64_gateway\x12\x1d.helium.local.add_gateway_req\x1a\x1d.helium.local.add_gateway_resb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0blocal.proto\x12\x0chelium.local\x1a\x1agateway_staking_mode.proto\"9\n\npubkey_res\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12\x1a\n\x12onboarding_address\x18\x02 \x01(\x0c\"\x0c\n\npubkey_req\")\n\tkeyed_uri\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0c\x12\x0b\n\x03uri\x18\x02 \x01(\t\"\x0c\n\nregion_req\"\x1c\n\nregion_res\x12\x0e\n\x06region\x18\x01 \x01(\x05\"\x0c\n\nrouter_req\",\n\nrouter_res\x12\x0b\n\x03uri\x18\x01 \x01(\t\x12\x11\n\tconnected\x18\x02 \x01(\x08\"c\n\x0f\x61\x64\x64_gateway_req\x12\r\n\x05owner\x18\x01 \x01(\x0c\x12\r\n\x05payer\x18\x02 \x01(\x0c\x12\x32\n\x0cstaking_mode\x18\x03 \x01(\x0e\x32\x1c.helium.gateway_staking_mode\"*\n\x0f\x61\x64\x64_gateway_res\x12\x17\n\x0f\x61\x64\x64_gateway_txn\x18\x01 \x01(\x0c\x32\x8c\x02\n\x03\x61pi\x12<\n\x06pubkey\x12\x18.helium.local.pubkey_req\x1a\x18.helium.local.pubkey_res\x12<\n\x06region\x12\x18.helium.local.region_req\x1a\x18.helium.local.region_res\x12<\n\x06router\x12\x18.helium.local.router_req\x1a\x18.helium.local.router_res\x12K\n\x0b\x61\x64\x64_gateway\x12\x1d.helium.local.add_gateway_req\x1a\x1d.helium.local.add_gateway_resb\x06proto3') -_GATEWAY_STAKING_MODE = DESCRIPTOR.enum_types_by_name['gateway_staking_mode'] -gateway_staking_mode = enum_type_wrapper.EnumTypeWrapper(_GATEWAY_STAKING_MODE) -dataonly = 0 -full = 1 -light = 2 - - -_PUBKEY_RES = DESCRIPTOR.message_types_by_name['pubkey_res'] -_PUBKEY_REQ = DESCRIPTOR.message_types_by_name['pubkey_req'] -_SIGN_REQ = DESCRIPTOR.message_types_by_name['sign_req'] -_SIGN_RES = DESCRIPTOR.message_types_by_name['sign_res'] -_ECDH_REQ = DESCRIPTOR.message_types_by_name['ecdh_req'] -_ECDH_RES = DESCRIPTOR.message_types_by_name['ecdh_res'] -_CONFIG_REQ = DESCRIPTOR.message_types_by_name['config_req'] -_CONFIG_RES = DESCRIPTOR.message_types_by_name['config_res'] -_CONFIG_VALUE = DESCRIPTOR.message_types_by_name['config_value'] -_KEYED_URI = DESCRIPTOR.message_types_by_name['keyed_uri'] -_HEIGHT_REQ = DESCRIPTOR.message_types_by_name['height_req'] -_HEIGHT_RES = DESCRIPTOR.message_types_by_name['height_res'] -_REGION_REQ = DESCRIPTOR.message_types_by_name['region_req'] -_REGION_RES = DESCRIPTOR.message_types_by_name['region_res'] -_ADD_GATEWAY_REQ = DESCRIPTOR.message_types_by_name['add_gateway_req'] -_ADD_GATEWAY_RES = DESCRIPTOR.message_types_by_name['add_gateway_res'] -pubkey_res = _reflection.GeneratedProtocolMessageType('pubkey_res', (_message.Message,), { - 'DESCRIPTOR' : _PUBKEY_RES, - '__module__' : 'local_pb2' - # @@protoc_insertion_point(class_scope:helium.local.pubkey_res) - }) -_sym_db.RegisterMessage(pubkey_res) - -pubkey_req = _reflection.GeneratedProtocolMessageType('pubkey_req', (_message.Message,), { - 'DESCRIPTOR' : _PUBKEY_REQ, - '__module__' : 'local_pb2' - # @@protoc_insertion_point(class_scope:helium.local.pubkey_req) - }) -_sym_db.RegisterMessage(pubkey_req) - -sign_req = _reflection.GeneratedProtocolMessageType('sign_req', (_message.Message,), { - 'DESCRIPTOR' : _SIGN_REQ, - '__module__' : 'local_pb2' - # @@protoc_insertion_point(class_scope:helium.local.sign_req) - }) -_sym_db.RegisterMessage(sign_req) - -sign_res = _reflection.GeneratedProtocolMessageType('sign_res', (_message.Message,), { - 'DESCRIPTOR' : _SIGN_RES, - '__module__' : 'local_pb2' - # @@protoc_insertion_point(class_scope:helium.local.sign_res) - }) -_sym_db.RegisterMessage(sign_res) - -ecdh_req = _reflection.GeneratedProtocolMessageType('ecdh_req', (_message.Message,), { - 'DESCRIPTOR' : _ECDH_REQ, - '__module__' : 'local_pb2' - # @@protoc_insertion_point(class_scope:helium.local.ecdh_req) - }) -_sym_db.RegisterMessage(ecdh_req) - -ecdh_res = _reflection.GeneratedProtocolMessageType('ecdh_res', (_message.Message,), { - 'DESCRIPTOR' : _ECDH_RES, - '__module__' : 'local_pb2' - # @@protoc_insertion_point(class_scope:helium.local.ecdh_res) - }) -_sym_db.RegisterMessage(ecdh_res) - -config_req = _reflection.GeneratedProtocolMessageType('config_req', (_message.Message,), { - 'DESCRIPTOR' : _CONFIG_REQ, - '__module__' : 'local_pb2' - # @@protoc_insertion_point(class_scope:helium.local.config_req) - }) -_sym_db.RegisterMessage(config_req) - -config_res = _reflection.GeneratedProtocolMessageType('config_res', (_message.Message,), { - 'DESCRIPTOR' : _CONFIG_RES, - '__module__' : 'local_pb2' - # @@protoc_insertion_point(class_scope:helium.local.config_res) - }) -_sym_db.RegisterMessage(config_res) - -config_value = _reflection.GeneratedProtocolMessageType('config_value', (_message.Message,), { - 'DESCRIPTOR' : _CONFIG_VALUE, - '__module__' : 'local_pb2' - # @@protoc_insertion_point(class_scope:helium.local.config_value) - }) -_sym_db.RegisterMessage(config_value) - -keyed_uri = _reflection.GeneratedProtocolMessageType('keyed_uri', (_message.Message,), { - 'DESCRIPTOR' : _KEYED_URI, - '__module__' : 'local_pb2' - # @@protoc_insertion_point(class_scope:helium.local.keyed_uri) - }) -_sym_db.RegisterMessage(keyed_uri) - -height_req = _reflection.GeneratedProtocolMessageType('height_req', (_message.Message,), { - 'DESCRIPTOR' : _HEIGHT_REQ, - '__module__' : 'local_pb2' - # @@protoc_insertion_point(class_scope:helium.local.height_req) - }) -_sym_db.RegisterMessage(height_req) - -height_res = _reflection.GeneratedProtocolMessageType('height_res', (_message.Message,), { - 'DESCRIPTOR' : _HEIGHT_RES, - '__module__' : 'local_pb2' - # @@protoc_insertion_point(class_scope:helium.local.height_res) - }) -_sym_db.RegisterMessage(height_res) - -region_req = _reflection.GeneratedProtocolMessageType('region_req', (_message.Message,), { - 'DESCRIPTOR' : _REGION_REQ, - '__module__' : 'local_pb2' - # @@protoc_insertion_point(class_scope:helium.local.region_req) - }) -_sym_db.RegisterMessage(region_req) - -region_res = _reflection.GeneratedProtocolMessageType('region_res', (_message.Message,), { - 'DESCRIPTOR' : _REGION_RES, - '__module__' : 'local_pb2' - # @@protoc_insertion_point(class_scope:helium.local.region_res) - }) -_sym_db.RegisterMessage(region_res) - -add_gateway_req = _reflection.GeneratedProtocolMessageType('add_gateway_req', (_message.Message,), { - 'DESCRIPTOR' : _ADD_GATEWAY_REQ, - '__module__' : 'local_pb2' - # @@protoc_insertion_point(class_scope:helium.local.add_gateway_req) - }) -_sym_db.RegisterMessage(add_gateway_req) - -add_gateway_res = _reflection.GeneratedProtocolMessageType('add_gateway_res', (_message.Message,), { - 'DESCRIPTOR' : _ADD_GATEWAY_RES, - '__module__' : 'local_pb2' - # @@protoc_insertion_point(class_scope:helium.local.add_gateway_res) - }) -_sym_db.RegisterMessage(add_gateway_res) - -_API = DESCRIPTOR.services_by_name['api'] +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'local_pb2', globals()) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - _GATEWAY_STAKING_MODE._serialized_start=676 - _GATEWAY_STAKING_MODE._serialized_end=733 - _PUBKEY_RES._serialized_start=29 - _PUBKEY_RES._serialized_end=58 - _PUBKEY_REQ._serialized_start=60 - _PUBKEY_REQ._serialized_end=72 - _SIGN_REQ._serialized_start=74 - _SIGN_REQ._serialized_end=98 - _SIGN_RES._serialized_start=100 - _SIGN_RES._serialized_end=129 - _ECDH_REQ._serialized_start=131 - _ECDH_REQ._serialized_end=158 - _ECDH_RES._serialized_start=160 - _ECDH_RES._serialized_end=186 - _CONFIG_REQ._serialized_start=188 - _CONFIG_REQ._serialized_end=214 - _CONFIG_RES._serialized_start=216 - _CONFIG_RES._serialized_end=272 - _CONFIG_VALUE._serialized_start=274 - _CONFIG_VALUE._serialized_end=331 - _KEYED_URI._serialized_start=333 - _KEYED_URI._serialized_end=374 - _HEIGHT_REQ._serialized_start=376 - _HEIGHT_REQ._serialized_end=388 - _HEIGHT_RES._serialized_start=390 - _HEIGHT_RES._serialized_end=479 - _REGION_REQ._serialized_start=481 - _REGION_REQ._serialized_end=493 - _REGION_RES._serialized_start=495 - _REGION_RES._serialized_end=523 - _ADD_GATEWAY_REQ._serialized_start=525 - _ADD_GATEWAY_REQ._serialized_end=630 - _ADD_GATEWAY_RES._serialized_start=632 - _ADD_GATEWAY_RES._serialized_end=674 - _API._serialized_start=736 - _API._serialized_end=1178 + _PUBKEY_RES._serialized_start=57 + _PUBKEY_RES._serialized_end=114 + _PUBKEY_REQ._serialized_start=116 + _PUBKEY_REQ._serialized_end=128 + _KEYED_URI._serialized_start=130 + _KEYED_URI._serialized_end=171 + _REGION_REQ._serialized_start=173 + _REGION_REQ._serialized_end=185 + _REGION_RES._serialized_start=187 + _REGION_RES._serialized_end=215 + _ROUTER_REQ._serialized_start=217 + _ROUTER_REQ._serialized_end=229 + _ROUTER_RES._serialized_start=231 + _ROUTER_RES._serialized_end=275 + _ADD_GATEWAY_REQ._serialized_start=277 + _ADD_GATEWAY_REQ._serialized_end=376 + _ADD_GATEWAY_RES._serialized_start=378 + _ADD_GATEWAY_RES._serialized_end=420 + _API._serialized_start=423 + _API._serialized_end=691 # @@protoc_insertion_point(module_scope) diff --git a/hm_pyhelper/protos/local_pb2_grpc.py b/hm_pyhelper/protos/local_pb2_grpc.py index b9f55d7..34b79f3 100644 --- a/hm_pyhelper/protos/local_pb2_grpc.py +++ b/hm_pyhelper/protos/local_pb2_grpc.py @@ -19,31 +19,16 @@ def __init__(self, channel): request_serializer=local__pb2.pubkey_req.SerializeToString, response_deserializer=local__pb2.pubkey_res.FromString, ) - self.sign = channel.unary_unary( - '/helium.local.api/sign', - request_serializer=local__pb2.sign_req.SerializeToString, - response_deserializer=local__pb2.sign_res.FromString, - ) - self.ecdh = channel.unary_unary( - '/helium.local.api/ecdh', - request_serializer=local__pb2.ecdh_req.SerializeToString, - response_deserializer=local__pb2.ecdh_res.FromString, - ) - self.config = channel.unary_unary( - '/helium.local.api/config', - request_serializer=local__pb2.config_req.SerializeToString, - response_deserializer=local__pb2.config_res.FromString, - ) - self.height = channel.unary_unary( - '/helium.local.api/height', - request_serializer=local__pb2.height_req.SerializeToString, - response_deserializer=local__pb2.height_res.FromString, - ) self.region = channel.unary_unary( '/helium.local.api/region', request_serializer=local__pb2.region_req.SerializeToString, response_deserializer=local__pb2.region_res.FromString, ) + self.router = channel.unary_unary( + '/helium.local.api/router', + request_serializer=local__pb2.router_req.SerializeToString, + response_deserializer=local__pb2.router_res.FromString, + ) self.add_gateway = channel.unary_unary( '/helium.local.api/add_gateway', request_serializer=local__pb2.add_gateway_req.SerializeToString, @@ -60,31 +45,13 @@ def pubkey(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') - def sign(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def ecdh(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def config(self, request, context): - """Missing associated documentation comment in .proto file.""" - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def height(self, request, context): + def region(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') - def region(self, request, context): + def router(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') @@ -104,31 +71,16 @@ def add_apiServicer_to_server(servicer, server): request_deserializer=local__pb2.pubkey_req.FromString, response_serializer=local__pb2.pubkey_res.SerializeToString, ), - 'sign': grpc.unary_unary_rpc_method_handler( - servicer.sign, - request_deserializer=local__pb2.sign_req.FromString, - response_serializer=local__pb2.sign_res.SerializeToString, - ), - 'ecdh': grpc.unary_unary_rpc_method_handler( - servicer.ecdh, - request_deserializer=local__pb2.ecdh_req.FromString, - response_serializer=local__pb2.ecdh_res.SerializeToString, - ), - 'config': grpc.unary_unary_rpc_method_handler( - servicer.config, - request_deserializer=local__pb2.config_req.FromString, - response_serializer=local__pb2.config_res.SerializeToString, - ), - 'height': grpc.unary_unary_rpc_method_handler( - servicer.height, - request_deserializer=local__pb2.height_req.FromString, - response_serializer=local__pb2.height_res.SerializeToString, - ), 'region': grpc.unary_unary_rpc_method_handler( servicer.region, request_deserializer=local__pb2.region_req.FromString, response_serializer=local__pb2.region_res.SerializeToString, ), + 'router': grpc.unary_unary_rpc_method_handler( + servicer.router, + request_deserializer=local__pb2.router_req.FromString, + response_serializer=local__pb2.router_res.SerializeToString, + ), 'add_gateway': grpc.unary_unary_rpc_method_handler( servicer.add_gateway, request_deserializer=local__pb2.add_gateway_req.FromString, @@ -162,58 +114,7 @@ def pubkey(request, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod - def sign(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary(request, target, '/helium.local.api/sign', - local__pb2.sign_req.SerializeToString, - local__pb2.sign_res.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) - - @staticmethod - def ecdh(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary(request, target, '/helium.local.api/ecdh', - local__pb2.ecdh_req.SerializeToString, - local__pb2.ecdh_res.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) - - @staticmethod - def config(request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - insecure=False, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None): - return grpc.experimental.unary_unary(request, target, '/helium.local.api/config', - local__pb2.config_req.SerializeToString, - local__pb2.config_res.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) - - @staticmethod - def height(request, + def region(request, target, options=(), channel_credentials=None, @@ -223,14 +124,14 @@ def height(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/helium.local.api/height', - local__pb2.height_req.SerializeToString, - local__pb2.height_res.FromString, + return grpc.experimental.unary_unary(request, target, '/helium.local.api/region', + local__pb2.region_req.SerializeToString, + local__pb2.region_res.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod - def region(request, + def router(request, target, options=(), channel_credentials=None, @@ -240,9 +141,9 @@ def region(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/helium.local.api/region', - local__pb2.region_req.SerializeToString, - local__pb2.region_res.FromString, + return grpc.experimental.unary_unary(request, target, '/helium.local.api/router', + local__pb2.router_req.SerializeToString, + local__pb2.router_res.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/hm_pyhelper/protos/region_pb2.py b/hm_pyhelper/protos/region_pb2.py index 3227a35..0635563 100644 --- a/hm_pyhelper/protos/region_pb2.py +++ b/hm_pyhelper/protos/region_pb2.py @@ -2,11 +2,9 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: region.proto """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper +from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database # @@protoc_insertion_point(imports) @@ -15,27 +13,15 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cregion.proto\x12\x06helium*\x94\x01\n\x06region\x12\t\n\x05US915\x10\x00\x12\t\n\x05\x45U868\x10\x01\x12\t\n\x05\x45U433\x10\x02\x12\t\n\x05\x43N470\x10\x03\x12\t\n\x05\x43N779\x10\x04\x12\t\n\x05\x41U915\x10\x05\x12\x0b\n\x07\x41S923_1\x10\x06\x12\t\n\x05KR920\x10\x07\x12\t\n\x05IN865\x10\x08\x12\x0b\n\x07\x41S923_2\x10\t\x12\x0b\n\x07\x41S923_3\x10\n\x12\x0b\n\x07\x41S923_4\x10\x0b\x62\x06proto3') - -_REGION = DESCRIPTOR.enum_types_by_name['region'] -region = enum_type_wrapper.EnumTypeWrapper(_REGION) -US915 = 0 -EU868 = 1 -EU433 = 2 -CN470 = 3 -CN779 = 4 -AU915 = 5 -AS923_1 = 6 -KR920 = 7 -IN865 = 8 -AS923_2 = 9 -AS923_3 = 10 -AS923_4 = 11 - +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cregion.proto\x12\x06helium*\xf1\x02\n\x06region\x12\t\n\x05US915\x10\x00\x12\t\n\x05\x45U868\x10\x01\x12\t\n\x05\x45U433\x10\x02\x12\t\n\x05\x43N470\x10\x03\x12\r\n\x05\x43N779\x10\x04\x1a\x02\x08\x01\x12\t\n\x05\x41U915\x10\x05\x12\x0b\n\x07\x41S923_1\x10\x06\x12\t\n\x05KR920\x10\x07\x12\t\n\x05IN865\x10\x08\x12\x0b\n\x07\x41S923_2\x10\t\x12\x0b\n\x07\x41S923_3\x10\n\x12\x0b\n\x07\x41S923_4\x10\x0b\x12\x0c\n\x08\x41S923_1B\x10\x0c\x12\x0c\n\x08\x43\x44\x39\x30\x30_1A\x10\r\x12\t\n\x05RU864\x10\x0e\x12\x0b\n\x07\x45U868_A\x10\x0f\x12\x0b\n\x07\x45U868_B\x10\x10\x12\x0b\n\x07\x45U868_C\x10\x11\x12\x0b\n\x07\x45U868_D\x10\x12\x12\x0b\n\x07\x45U868_E\x10\x13\x12\x0b\n\x07\x45U868_F\x10\x14\x12\r\n\tAU915_SB1\x10\x15\x12\r\n\tAU915_SB2\x10\x16\x12\x0c\n\x08\x41S923_1A\x10\x17\x12\x0c\n\x08\x41S923_1C\x10\x18\x12\x0c\n\x08\x41S923_1D\x10\x19\x12\x0c\n\x08\x41S923_1E\x10\x1a\x12\x0c\n\x08\x41S923_1F\x10\x1b\x62\x06proto3') +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'region_pb2', globals()) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None + _REGION.values_by_name["CN779"]._options = None + _REGION.values_by_name["CN779"]._serialized_options = b'\010\001' _REGION._serialized_start=25 - _REGION._serialized_end=173 + _REGION._serialized_end=394 # @@protoc_insertion_point(module_scope) diff --git a/hm_pyhelper/tests/test_gateway_grpc.py b/hm_pyhelper/tests/test_gateway_grpc.py index cdd0a37..5928e78 100644 --- a/hm_pyhelper/tests/test_gateway_grpc.py +++ b/hm_pyhelper/tests/test_gateway_grpc.py @@ -49,7 +49,7 @@ class TestData: expected_summary = { 'region': region_name, 'key': pubkey_decoded, - 'gateway_version': "v1.0.0~alpha.23", + 'gateway_version': "v1.0.0", 'validator': { 'height': height_res.height, 'block_age': height_res.block_age, diff --git a/protos/gateway_staking_mode.proto b/protos/gateway_staking_mode.proto new file mode 100644 index 0000000..795e4dc --- /dev/null +++ b/protos/gateway_staking_mode.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package helium; + +enum gateway_staking_mode { + dataonly = 0; + full = 1; + light = 2; +} diff --git a/protos/local.proto b/protos/local.proto index d2a640d..2c7dfc3 100644 --- a/protos/local.proto +++ b/protos/local.proto @@ -2,44 +2,28 @@ syntax = "proto3"; package helium.local; -message pubkey_res { bytes address = 1; } -message pubkey_req {} - -message sign_req { bytes data = 1; } -message sign_res { bytes signature = 1; } - -message ecdh_req { bytes address = 1; } -message ecdh_res { bytes secret = 1; } +import "gateway_staking_mode.proto"; -message config_req { repeated string keys = 1; } -message config_res { repeated config_value values = 1; } - -message config_value { - string name = 1; - string type = 2; - bytes value = 3; +message pubkey_res { + bytes address = 1; + bytes onboarding_address = 2; } +message pubkey_req {} message keyed_uri { bytes address = 1; string uri = 2; } -message height_req {} -message height_res { - uint64 height = 1; - uint64 block_age = 2; - keyed_uri gateway = 3; -} - message region_req {} message region_res { int32 region = 1; } -enum gateway_staking_mode { - dataonly = 0; - full = 1; - light = 2; +message router_req {} +message router_res { + string uri = 1; + bool connected = 2; } + message add_gateway_req { bytes owner = 1; bytes payer = 2; @@ -50,10 +34,7 @@ message add_gateway_res { bytes add_gateway_txn = 1; } service api { rpc pubkey(pubkey_req) returns (pubkey_res); - rpc sign(sign_req) returns (sign_res); - rpc ecdh(ecdh_req) returns (ecdh_res); - rpc config(config_req) returns (config_res); - rpc height(height_req) returns (height_res); rpc region(region_req) returns (region_res); + rpc router(router_req) returns (router_res); rpc add_gateway(add_gateway_req) returns (add_gateway_res); } diff --git a/protos/region.proto b/protos/region.proto index 85a8656..facb13e 100644 --- a/protos/region.proto +++ b/protos/region.proto @@ -7,7 +7,7 @@ enum region { EU868 = 1; EU433 = 2; CN470 = 3; - CN779 = 4; + CN779 = 4 [ deprecated = true ]; AU915 = 5; AS923_1 = 6; KR920 = 7; @@ -15,4 +15,20 @@ enum region { AS923_2 = 9; AS923_3 = 10; AS923_4 = 11; + AS923_1B = 12; + CD900_1A = 13; + RU864 = 14; + EU868_A = 15; + EU868_B = 16; + EU868_C = 17; + EU868_D = 18; + EU868_E = 19; + EU868_F = 20; + AU915_SB1 = 21; + AU915_SB2 = 22; + AS923_1A = 23; + AS923_1C = 24; + AS923_1D = 25; + AS923_1E = 26; + AS923_1F = 27; } diff --git a/protos/update_protos.sh b/protos/update_protos.sh old mode 100644 new mode 100755 index 1c37e6b..08305a1 --- a/protos/update_protos.sh +++ b/protos/update_protos.sh @@ -1,22 +1,28 @@ #!/bin/bash +# Making sure it is installed and up to date +pip install grpcio-tools + SRC_DIR=. DST_DIR=../hm_pyhelper/protos function update_proto() { - echo "Updating $1" - wget $1 + echo "Updating $2" # replace old if succcessful - if [[ $? -eq 0 ]]; then - mv $2.1 $2 + if [[ $(wget "$1" -O "$2.1") -eq 0 ]]; then + echo -e "Overwriting $2..\n" + mv "$2.1" "$2" fi } update_proto https://raw.githubusercontent.com/helium/proto/master/src/service/local.proto local.proto update_proto https://raw.githubusercontent.com/helium/proto/master/src/region.proto region.proto +update_proto https://raw.githubusercontent.com/helium/proto/master/src/gateway_staking_mode.proto gateway_staking_mode.proto +python -m grpc_tools.protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/gateway_staking_mode.proto python -m grpc_tools.protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/region.proto python -m grpc_tools.protoc -I=$SRC_DIR --python_out=$DST_DIR --grpc_python_out=$DST_DIR $SRC_DIR/local.proto sed -i -e 's/import *local_pb2/import hm_pyhelper.protos.local_pb2/' $DST_DIR/local_pb2_grpc.py +sed -i -e 's/import *gateway_staking_mode_pb2/import hm_pyhelper.protos.gateway_staking_mode_pb2/' $DST_DIR/local_pb2.py From fe6ff948bb0dbb5ea541f1c223168e2eec6cfcf9 Mon Sep 17 00:00:00 2001 From: Murat Ursavas Date: Fri, 31 Mar 2023 13:08:46 +0300 Subject: [PATCH 9/9] Removed chain vars and config, fixed unit tests --- .github/workflows/publish-to-pypi-test.yml | 34 +++++++-------- hm_pyhelper/gateway_grpc/client.py | 51 ++-------------------- hm_pyhelper/tests/test_gateway_grpc.py | 46 +------------------ 3 files changed, 23 insertions(+), 108 deletions(-) diff --git a/.github/workflows/publish-to-pypi-test.yml b/.github/workflows/publish-to-pypi-test.yml index 249ddef..648ff75 100644 --- a/.github/workflows/publish-to-pypi-test.yml +++ b/.github/workflows/publish-to-pypi-test.yml @@ -17,30 +17,30 @@ jobs: - name: Fetch gateway-mfr-rs env: - GATEWAY_MFR_RS_RELEASE: v0.3.2 + GATEWAY_MFR_RS_RELEASE: "0.4.1" run: | - wget "https://github.com/helium/gateway-mfr-rs/releases/download/${GATEWAY_MFR_RS_RELEASE}/gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-arm-unknown-linux-gnueabihf-ecc608.tar.gz" - wget "https://github.com/helium/gateway-mfr-rs/releases/download/${GATEWAY_MFR_RS_RELEASE}/gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-arm-unknown-linux-gnueabihf-ecc608.checksum" - wget "https://github.com/helium/gateway-mfr-rs/releases/download/${GATEWAY_MFR_RS_RELEASE}/gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-aarch64-unknown-linux-gnu-ecc608.tar.gz" - wget "https://github.com/helium/gateway-mfr-rs/releases/download/${GATEWAY_MFR_RS_RELEASE}/gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-aarch64-unknown-linux-gnu-ecc608.checksum" - wget "https://github.com/helium/gateway-mfr-rs/releases/download/${GATEWAY_MFR_RS_RELEASE}/gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-x86_64-unknown-linux-gnu-ecc608.tar.gz" - wget "https://github.com/helium/gateway-mfr-rs/releases/download/${GATEWAY_MFR_RS_RELEASE}/gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-x86_64-unknown-linux-gnu-ecc608.checksum" - SHA256_ARM=$( shasum -a 256 "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-arm-unknown-linux-gnueabihf-ecc608.tar.gz" | awk '{print $1}') - SHA256_AARCH64=$( shasum -a 256 "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-aarch64-unknown-linux-gnu-ecc608.tar.gz" | awk '{print $1}') - SHA256_X86_64=$( shasum -a 256 "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-x86_64-unknown-linux-gnu-ecc608.tar.gz" | awk '{print $1}') + wget "https://github.com/helium/gateway-mfr-rs/releases/download/v${GATEWAY_MFR_RS_RELEASE}/gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-arm-unknown-linux-gnueabihf.tar.gz" + wget "https://github.com/helium/gateway-mfr-rs/releases/download/v${GATEWAY_MFR_RS_RELEASE}/gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-arm-unknown-linux-gnueabihf.checksum" + wget "https://github.com/helium/gateway-mfr-rs/releases/download/v${GATEWAY_MFR_RS_RELEASE}/gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-aarch64-unknown-linux-gnu.tar.gz" + wget "https://github.com/helium/gateway-mfr-rs/releases/download/v${GATEWAY_MFR_RS_RELEASE}/gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-aarch64-unknown-linux-gnu.checksum" + wget "https://github.com/helium/gateway-mfr-rs/releases/download/v${GATEWAY_MFR_RS_RELEASE}/gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-x86_64-tpm-debian-gnu.tar.gz" + wget "https://github.com/helium/gateway-mfr-rs/releases/download/v${GATEWAY_MFR_RS_RELEASE}/gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-x86_64-tpm-debian-gnu.checksum" + SHA256_ARM=$( shasum -a 256 "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-arm-unknown-linux-gnueabihf.tar.gz" | awk '{print $1}') + SHA256_AARCH64=$( shasum -a 256 "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-aarch64-unknown-linux-gnu.tar.gz" | awk '{print $1}') + SHA256_X86_64=$( shasum -a 256 "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-x86_64-tpm-debian-gnu.tar.gz" | awk '{print $1}') echo "Generated checksum ARM: ${SHA256_ARM}" echo "Generated checksum AARCH64: ${SHA256_AARCH64}" echo "Generated checksum X86_64: ${SHA256_X86_64}" # Verify the checksums - if grep -q "${SHA256_ARM}" "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-arm-unknown-linux-gnueabihf-ecc608.checksum" && grep -q "${SHA256_AARCH64}" "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-aarch64-unknown-linux-gnu-ecc608.checksum" && grep -q "${SHA256_X86_64}" "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-x86_64-unknown-linux-gnu-ecc608.checksum"; then + if grep -q "${SHA256_ARM}" "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-arm-unknown-linux-gnueabihf.checksum" && grep -q "${SHA256_AARCH64}" "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-aarch64-unknown-linux-gnu.checksum" && grep -q "${SHA256_X86_64}" "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-x86_64-tpm-debian-gnu.checksum"; then echo "Checksum verified for gateway_mfr. Unpacking tarball..." # Unpack the tarballs - tar -xvf "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-arm-unknown-linux-gnueabihf-ecc608.tar.gz" + tar -xvf "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-arm-unknown-linux-gnueabihf.tar.gz" mv gateway_mfr gateway_mfr_arm - tar -xvf "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-aarch64-unknown-linux-gnu-ecc608.tar.gz" + tar -xvf "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-aarch64-unknown-linux-gnu.tar.gz" mv gateway_mfr gateway_mfr_aarch64 - tar -xvf "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-x86_64-unknown-linux-gnu-ecc608.tar.gz" + tar -xvf "gateway-mfr-${GATEWAY_MFR_RS_RELEASE}-x86_64-tpm-debian-gnu.tar.gz" mv gateway_mfr gateway_mfr_x86_64 exit 0 else @@ -53,13 +53,13 @@ jobs: chmod +x gateway_mfr_arm chmod +x gateway_mfr_aarch64 chmod +x gateway_mfr_x86_64 - + - name: Move gateway_mfr in place run: | mv gateway_mfr_arm hm_pyhelper/gateway_mfr mv gateway_mfr_aarch64 hm_pyhelper/gateway_mfr_aarch64 mv gateway_mfr_x86_64 hm_pyhelper/gateway_mfr_x86_64 - + - name: Install pypa/build run: | python -m pip install build --user @@ -78,4 +78,4 @@ jobs: with: password: ${{ secrets.TEST_PYPI_API_TOKEN }} repository_url: https://test.pypi.org/legacy/ - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/light-hotspot' diff --git a/hm_pyhelper/gateway_grpc/client.py b/hm_pyhelper/gateway_grpc/client.py index b04a5c5..522c95b 100644 --- a/hm_pyhelper/gateway_grpc/client.py +++ b/hm_pyhelper/gateway_grpc/client.py @@ -5,7 +5,7 @@ from typing import Union from hm_pyhelper.protos import blockchain_txn_add_gateway_v1_pb2, \ - local_pb2_grpc, local_pb2, region_pb2 + local_pb2_grpc, local_pb2, region_pb2, gateway_staking_mode_pb2 from hm_pyhelper.gateway_grpc.exceptions import MinerMalformedAddGatewayTxn from hm_pyhelper.logger import get_logger @@ -52,12 +52,6 @@ def __enter__(self): def __exit__(self, _, _2, _3): self._channel.close() - def get_validator_info(self) -> local_pb2.height_res: - return self.stub.height(local_pb2.height_req()) - - def get_height(self) -> int: - return self.get_validator_info().height - def get_region_enum(self) -> int: ''' Returns the current configured region of the gateway. @@ -100,53 +94,15 @@ def get_summary(self) -> dict: "region": str configured region eg. "US915", "key": str - gateway/device public key, - "validator": { - "height": int - blockchain height, - "block_age": int - age of the last block in seconds, - "address": str - public key/address of the validator, - "uri": http url - http endpoint of the validator - } + gateway/device public key } ''' - validator_info = self.get_validator_info() return { 'region': self.get_region(), 'key': self.get_pubkey(), 'gateway_version': self.get_gateway_version(), - 'validator': { - 'height': validator_info.height, - 'block_age': validator_info.block_age, - 'address': decode_pub_key(validator_info.gateway.address), - 'uri': validator_info.gateway.uri - } } - def get_blockchain_config_variables(self, keys: list) -> local_pb2.config_res: - ''' - Allows one to query blockchain variables. For a complete list of chain variables ref - https://helium.plus/chain-vars - - Returns config_res which is a list of config_value for the given list - of blockchain variables. - ''' - return self.stub.config(local_pb2.config_req(keys=keys)) - - def get_blockchain_config_variable(self, key: str) -> local_pb2.config_value: - ''' - Convenience method to get a single variable from the blockchain - - Raises ValueError if the key is not found - ''' - values = self.get_blockchain_config_variables(keys=[key]).values - if not values[0].value: - raise ValueError(f'{key} not found on chain') - return values[0] - def get_gateway_version(self) -> Union[str, None]: ''' Returns the current version of the gateway package installed @@ -164,7 +120,8 @@ def get_gateway_version(self) -> Union[str, None]: return None def create_add_gateway_txn(self, owner_address: str, payer_address: str, - staking_mode: local_pb2.gateway_staking_mode = local_pb2.light, + staking_mode: gateway_staking_mode_pb2.gateway_staking_mode + = gateway_staking_mode_pb2.gateway_staking_mode.light, gateway_address: str = "") -> dict: """ Invokes the txn_add_gateway RPC endpoint on the gateway and returns diff --git a/hm_pyhelper/tests/test_gateway_grpc.py b/hm_pyhelper/tests/test_gateway_grpc.py index 5928e78..d372bda 100644 --- a/hm_pyhelper/tests/test_gateway_grpc.py +++ b/hm_pyhelper/tests/test_gateway_grpc.py @@ -10,27 +10,10 @@ class TestData: server_port = 4468 - height_res = local_pb2.height_res( - height=43, - block_age=42, - gateway=local_pb2.keyed_uri( - address=b"\000\177\327E-\223\222e\002e[*\250\260\361p\271\267/" - b"\220\026\010\360\213t\304\313\022\316>\254\347?", - uri="http://32.23.54.23:8080" # NOSONAR - ) - ) validator_address_decoded = "11yJXQPG9deHqvw2ac6VWtNP7gZj8X3t3Qb3Gqm9j729p4AsdaA" pubkey_encoded = b"\x01\xc3\x06\x7f\xb9\x19}\xd1n2\xe2M\xeb\xb5\x11\x7f" \ b"\xbc\x12\xebT\xb9\x84R\xc7\xca\xf8o\xdddx\xea~\xab" pubkey_decoded = "14RdqcZC2rbdTBwNaTsj5EVWYaM7BKGJ44ycq6wWJy9Hg7RKCii" - chain_vars = { - "block_size_limit": local_pb2.config_value(name="block_size_limit", - value=b"5242880", - type="int"), - "min_assert_h3_res": local_pb2.config_value(name="min_assert_h3_res", - value=b"12", - type="int") - } region_enum = 0 region_name = "US915" dpkg_output = b"""Package: helium_gateway\n @@ -40,7 +23,7 @@ class TestData: Installed-Size: 3729\n Maintainer: Marc Nijdam \n Architecture: amd64\n - Version: 1.0.0~alpha.23\n + Version: 1.0.0\n Depends: curl\n Conffiles:\n /etc/helium_gateway/settings.toml 4d6fb434f97a50066b8163a371d5c208\n @@ -49,13 +32,7 @@ class TestData: expected_summary = { 'region': region_name, 'key': pubkey_decoded, - 'gateway_version': "v1.0.0", - 'validator': { - 'height': height_res.height, - 'block_age': height_res.block_age, - 'address': validator_address_decoded, - 'uri': height_res.gateway.uri - } + 'gateway_version': "v1.0.0" } @@ -96,25 +73,11 @@ def test_get_pubkey(self): with GatewayClient(f'localhost:{TestData.server_port}') as client: self.assertEqual(client.get_pubkey(), TestData.pubkey_decoded) - def test_get_validator_info(self): - with GatewayClient(f'localhost:{TestData.server_port}') as client: - self.assertEqual(client.get_validator_info(), TestData.height_res) - - def test_get_height(self): - with GatewayClient(f'localhost:{TestData.server_port}') as client: - self.assertEqual(client.get_height(), client.get_validator_info().height) - def test_get_region(self): with GatewayClient(f'localhost:{TestData.server_port}') as client: self.assertEqual(client.get_region_enum(), TestData.region_enum) self.assertEqual(client.get_region(), TestData.region_name) - def test_get_blockchain_variable(self): - with GatewayClient(f'localhost:{TestData.server_port}') as client: - for key in TestData.chain_vars: - self.assertEqual(client.get_blockchain_config_variable(key), - TestData.chain_vars.get(key)) - def test_get_summary(self): with GatewayClient(f'localhost:{TestData.server_port}') as client: # summary when helium_gateway is not installed @@ -134,8 +97,3 @@ def test_connection_failure(self): with self.assertRaises(grpc.RpcError): with GatewayClient('localhost:1234') as client: client.get_pubkey() - - def test_invalid_chain_var(self): - with GatewayClient(f'localhost:{TestData.server_port}') as client: - with self.assertRaises(ValueError): - client.get_blockchain_config_variable('not_a_key')