Repository: crypto-org-chain/chainlibpy (language: python)

Issue:
Problem: predefined network configuration
Add mainnet and testnet network configuration like `grpc_endpoint`, `chain_id`, `address_prefix` and etc\

This helps to prevent typo errors


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6c04784..0143ced 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,7 @@ This log documents all public API breaking backwards incompatible changes.
 
 ## 2.2.0
 
-*Jan 5, 2022*
+[#25](https://github.com/crypto-org-chain/chainlibpy/issues/25) Add mainnet and testnet-croeseid-4 network configurations
 [#24](https://github.com/crypto-org-chain/chainlibpy/pull/24) Fix unable to use secure gRPC channel to interact with chain
 
 *Dec 7, 2021*
diff --git a/chainlibpy/__init__.py b/chainlibpy/__init__.py
index e69de29..1cdbce6 100644
--- a/chainlibpy/__init__.py
+++ b/chainlibpy/__init__.py
@@ -0,0 +1,9 @@
+from .grpc_client import CRO_NETWORK, GrpcClient, NetworkConfig
+from .wallet import Wallet
+
+__all__ = [
+    "Wallet",
+    "GrpcClient",
+    "CRO_NETWORK",
+    "NetworkConfig",
+]
diff --git a/chainlibpy/grpc_client.py b/chainlibpy/grpc_client.py
index 70bdf7c..2703377 100644
--- a/chainlibpy/grpc_client.py
+++ b/chainlibpy/grpc_client.py
@@ -2,10 +2,11 @@
 # -*- coding: utf-8 -*-
 
 import time
+from dataclasses import dataclass
 from typing import List, Optional
 
 from google.protobuf.any_pb2 import Any as ProtoAny
-from grpc import ChannelCredentials, insecure_channel, secure_channel
+from grpc import ChannelCredentials, RpcError, insecure_channel, secure_channel
 
 from chainlibpy.generated.cosmos.auth.v1beta1.auth_pb2 import BaseAccount
 from chainlibpy.generated.cosmos.auth.v1beta1.query_pb2 import QueryAccountRequest
@@ -15,6 +16,8 @@
 from chainlibpy.generated.cosmos.bank.v1beta1.query_pb2 import (
     QueryBalanceRequest,
     QueryBalanceResponse,
+    QueryDenomMetadataRequest,
+    QueryDenomMetadataResponse,
 )
 from chainlibpy.generated.cosmos.bank.v1beta1.query_pb2_grpc import (
     QueryStub as BankGrpcClient,
@@ -44,28 +47,67 @@
 from chainlibpy.wallet import Wallet
 
 
+@dataclass
+class NetworkConfig:
+    grpc_endpoint: str
+    chain_id: str
+    address_prefix: str
+    coin_denom: str
+    coin_base_denom: str
+    derivation_path: str
+
+
+CRO_NETWORK = {
+    "mainnet": NetworkConfig(
+        grpc_endpoint="mainnet.crypto.org:9090",
+        chain_id="crypto-org-chain-mainnet-1",
+        address_prefix="cro",
+        coin_denom="cro",
+        coin_base_denom="basecro",
+        derivation_path="m/44'/394'/0'/0/0",
+    ),
+    "testnet_croeseid": NetworkConfig(
+        grpc_endpoint="testnet-croeseid-4.crypto.org:9090",
+        chain_id="testnet-croeseid-4",
+        address_prefix="tcro",
+        coin_denom="tcro",
+        coin_base_denom="basetcro",
+        derivation_path="m/44'/1'/0'/0/0",
+    ),
+}
+
+
 class GrpcClient:
     DEFAULT_GAS_LIMIT = 200000
 
     def __init__(
         self,
         wallet: Wallet,
-        chain_id: str,
-        grpc_endpoint: str,
+        network: NetworkConfig,
         credentials: ChannelCredentials = None,
     ) -> None:
         if credentials is None:
-            channel = insecure_channel(grpc_endpoint)
+            channel = insecure_channel(network.grpc_endpoint)
         else:
-            channel = secure_channel(grpc_endpoint, credentials)
+            channel = secure_channel(network.grpc_endpoint, credentials)
 
         self.bank_client = BankGrpcClient(channel)
         self.tx_client = TxGrpcClient(channel)
         self.auth_client = AuthGrpcClient(channel)
         self.wallet = wallet
-        self.chain_id = chain_id
-        account = self.query_account_data(self.wallet.address)
-        self.account_number = account.account_number
+        self.chain_id = network.chain_id
+
+        # TODO to remove when removing wallet parameter from this class
+        try:
+            account = self.query_account_data(self.wallet.address)
+            self.account_number = account.account_number
+        except RpcError:
+            # TODO dummy code, to remove when removing wallet parameter from this class
+            self.account_number = 0
+
+    def query_bank_denom_metadata(self, denom: str) -> QueryDenomMetadataResponse:
+        res = self.bank_client.DenomMetadata(QueryDenomMetadataRequest(denom=denom))
+        return res
 
     def get_balance(self, address: str, denom: str) -> QueryBalanceResponse:
         res = self.bank_client.Balance(QueryBalanceRequest(address=address, denom=denom))


Test patch:
diff --git a/tests/test_grpc_client.py b/tests/test_grpc_client.py
index 2e92667..3cc2ed1 100644
--- a/tests/test_grpc_client.py
+++ b/tests/test_grpc_client.py
@@ -1,73 +1,26 @@
-import pytest
-from google.protobuf.any_pb2 import Any
-
-from chainlibpy.generated.cosmos.auth.v1beta1.auth_pb2 import BaseAccount
-from chainlibpy.generated.cosmos.auth.v1beta1.query_pb2 import (
-    QueryAccountRequest,
-    QueryAccountResponse,
-)
-from chainlibpy.generated.cosmos.auth.v1beta1.query_pb2_grpc import (
-    QueryServicer as AuthQueryServicer,
-)
-from chainlibpy.generated.cosmos.auth.v1beta1.query_pb2_grpc import (
-    add_QueryServicer_to_server as add_auth,
-)
-from chainlibpy.generated.cosmos.bank.v1beta1.query_pb2 import (
-    QueryBalanceRequest,
-    QueryBalanceResponse,
-)
-from chainlibpy.generated.cosmos.bank.v1beta1.query_pb2_grpc import (
-    QueryServicer as BankQueryServicer,
-)
-from chainlibpy.generated.cosmos.bank.v1beta1.query_pb2_grpc import (
-    add_QueryServicer_to_server as add_bank,
-)
-from chainlibpy.generated.cosmos.base.v1beta1.coin_pb2 import Coin
-from chainlibpy.generated.cosmos.tx.v1beta1.service_pb2_grpc import (
-    ServiceServicer as TxServiceServicer,
-)
-from chainlibpy.generated.cosmos.tx.v1beta1.service_pb2_grpc import (
-    add_ServiceServicer_to_server as add_tx,
-)
-from chainlibpy.grpc_client import GrpcClient
-from chainlibpy.wallet import Wallet
-
-
-class MockBankQueryServicer(BankQueryServicer):
-    def Balance(self, request: QueryBalanceRequest, context) -> QueryBalanceResponse:
-        if request.address == "cro1yj3fd8gxrqd2662p8ywp26t4hfws9p5n75xjum":
-            return QueryBalanceResponse(balance=Coin(amount="100000000", denom="basecro"))
+import ssl
 
+import grpc
+import pytest
 
-class MockAuthQueryServicer(AuthQueryServicer):
-    def Account(self, request: QueryAccountRequest, context) -> QueryAccountResponse:
-        if request.address == "cro1yj3fd8gxrqd2662p8ywp26t4hfws9p5n75xjum":
-            acc = BaseAccount(account_number=1)
-            any = Any()
-            any.Pack(acc)
-            return QueryAccountResponse(account=any)
-
-
-class MockTxServiceServicer(TxServiceServicer):
-    pass
-
+from chainlibpy import CRO_NETWORK, GrpcClient, NetworkConfig, Wallet
 
-@pytest.fixture(scope="module")
-def mock_grpc_client(_grpc_server, grpc_addr):
-    seed = "burst negative solar evoke traffic yard lizard next series foster seminar enter wrist captain bulb trap giggle country sword season shoot boy bargain deal"  # noqa 501
-    wallet = Wallet(seed)
 
-    add_bank(MockBankQueryServicer(), _grpc_server)
-    add_auth(MockAuthQueryServicer(), _grpc_server)
-    add_tx(MockTxServiceServicer(), _grpc_server)
+@pytest.mark.parametrize("network_config", CRO_NETWORK.values())
+def test_network_config(network_config: "NetworkConfig"):
+    wallet = Wallet.new(path=network_config.derivation_path, hrp=network_config.address_prefix)
 
-    _grpc_server.add_insecure_port(grpc_addr)
-    _grpc_server.start()
+    (server_host, server_port) = network_config.grpc_endpoint.split(":")
 
-    yield GrpcClient(wallet, "chainmaind", grpc_addr)
-    _grpc_server.stop(grace=None)
+    conn = ssl.create_connection((server_host, server_port))
+    context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+    sock = context.wrap_socket(conn, server_hostname=server_host)
+    certificate = ssl.DER_cert_to_PEM_cert(sock.getpeercert(True))
+    creds = grpc.ssl_channel_credentials(str.encode(certificate))
 
+    client = GrpcClient(wallet, network_config, creds)
 
-def test_get_balance(mock_grpc_client):
-    balance = mock_grpc_client.get_balance("cro1yj3fd8gxrqd2662p8ywp26t4hfws9p5n75xjum", "basecro")
-    assert balance == QueryBalanceResponse(balance=Coin(amount="100000000", denom="basecro"))
+    assert (
+        client.query_bank_denom_metadata(network_config.coin_base_denom).metadata.base
+        == network_config.coin_base_denom
+    )