Skip to content
This repository has been archived by the owner on Mar 6, 2023. It is now read-only.

Problem: Missing secure gRPC channel #24

Merged
merged 1 commit into from
Jan 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

This log documents all public API breaking backwards incompatible changes.

## 2.2.0

*Jan 5, 2022*
[#24](https://github.com/crypto-org-chain/chainlibpy/pull/24) Fix unable to use secure gRPC channel to interact with chain

*Dec 7, 2021*

## 2.1.0
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
- [Usage](#usage)
- [Generating a wallet](#generating-a-wallet)
- [Signing and broadcasting a transaction](#signing-and-broadcasting-a-transaction)
- [Acknowledgement](#acknowledgement)
- [Using secure gRPC channel](#using-secure-grpc-channel)
- [Acknowledgement](#acknowledgement)
- [Development](#development)
- [Set up development environment](#set-up-development-environment)
- [Generate gRPC code](#generate-grpc-code)
Expand Down Expand Up @@ -77,7 +78,11 @@ client.broadcast_tx(tx)

You may also refer to `example/transaction.py` on how to use a high level function `bank_send()` to sign and broadcast a transaction

### Acknowledgement<a name="acknowledgement"></a>
### Using secure gRPC channel<a name="using-secure-grpc-channel"></a>

Please refer to `example/secure_channel_example.py` on how to use secure gRPC channel with server certificate

## Acknowledgement<a name="acknowledgement"></a>

Thanks [cosmospy](https://github.com/hukkinj1/cosmospy) for the following:

Expand Down
15 changes: 12 additions & 3 deletions chainlibpy/grpc_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from typing import List, Optional

from google.protobuf.any_pb2 import Any as ProtoAny
from grpc import insecure_channel
from grpc import ChannelCredentials, insecure_channel, secure_channel

from chainlibpy.generated.cosmos.auth.v1beta1.auth_pb2 import BaseAccount
from chainlibpy.generated.cosmos.auth.v1beta1.query_pb2 import QueryAccountRequest
Expand Down Expand Up @@ -47,8 +47,17 @@
class GrpcClient:
DEFAULT_GAS_LIMIT = 200000

def __init__(self, wallet: Wallet, chain_id: str, grpc_endpoint: str) -> None:
channel = insecure_channel(grpc_endpoint)
def __init__(
self,
wallet: Wallet,
chain_id: str,
grpc_endpoint: str,
credentials: ChannelCredentials = None,
) -> None:
if credentials is None:
channel = insecure_channel(grpc_endpoint)
else:
channel = secure_channel(grpc_endpoint, credentials)
Comment on lines +57 to +60
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this may be better:
channel = insecure_channel(grpc_endpoint, credentials) if credentials else insecure_channel(grpc_endpoint)

or

if credentials:
     channel = secure_channel(grpc_endpoint, credentials)
else:
    channel = insecure_channel(grpc_endpoint)

or using if not credentials:

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to explicitly check for None to avoid confusion with False.
In this case, it does not matter that much though, as the variable is not a Boolean type.


self.bank_client = BankGrpcClient(channel)
self.tx_client = TxGrpcClient(channel)
Expand Down
60 changes: 60 additions & 0 deletions example/secure_channel_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import ssl

import grpc

from chainlibpy.grpc_client import GrpcClient
from chainlibpy.wallet import Wallet

DENOM = "basetcro"
MNEMONIC_PHRASE = "first ... last"
CHAIN_ID = "testnet-croeseid-4"

SERVER_HOST = "testnet-croeseid-4.crypto.org"
SERVER_PORT = "9090"
GRPC_ENDPOINT = f"{SERVER_HOST}:{SERVER_PORT}"

DEFAULT_DERIVATION_PATH = "m/44'/1'/0'/0/0"
DEFAULT_BECH32_HRP = "tcro"


def example_with_certificate_file():
wallet = Wallet(MNEMONIC_PHRASE, DEFAULT_DERIVATION_PATH, DEFAULT_BECH32_HRP)

# 1. .cer certificate file could be obtained from the browser
# more details could be found here https://stackoverflow.com/questions/25940396/how-to-export-certificate-from-chrome-on-a-mac/59466184#59466184 # noqa501
# 2. convert .cer file to .crt file
# `openssl x509 -inform DER -in cert.cer -out cert.crt``
with open("./cert.crt", "rb") as f:
creds = grpc.ssl_channel_credentials(f.read())

client = GrpcClient(wallet, CHAIN_ID, GRPC_ENDPOINT, creds)

from_address = wallet.address
res = client.get_balance(from_address, DENOM)
print(f"address {from_address} initial balance: {res.balance.amount}")


def example_with_certificate_request():
wallet = Wallet(MNEMONIC_PHRASE, DEFAULT_DERIVATION_PATH, DEFAULT_BECH32_HRP)

# if server does not use Server Name Indication (SNI), commented code below is enough:
# creds = ssl.get_server_certificate((SERVER_HOST, SERVER_PORT))
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, CHAIN_ID, GRPC_ENDPOINT, creds)

from_address = wallet.address
res = client.get_balance(from_address, DENOM)
print(f"address {from_address} initial balance: {res.balance.amount}")


if __name__ == "__main__":
example_with_certificate_file()
example_with_certificate_request()
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "chainlibpy"
version = "2.1.0"
version = "2.2.0"
description = "Tools for Crypto.org Chain wallet management and offline transaction signing"
authors = ["chain-dev-team <chain-dev@crypto.com>"]
license = "Apache-2.0"
Expand Down