Skip to content

Commit

Permalink
examples: added python example (rpcpool#333)
Browse files Browse the repository at this point in the history
  • Loading branch information
lvboudre authored May 6, 2024
1 parent db75e96 commit d0bbfbb
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ dump.rdb
*.sw?
.idea/
.vscode
venv
__pycache__/
72 changes: 72 additions & 0 deletions examples/python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Python example

## Instruction

This Python example uses [grpc.io](https://grpc.io/) library.
It assumes your CA trust store on your machine allows trust the CA from your RPC endpoint.

## Installation

Create a virtual environment and install its dependencies:
```bash
$ python -m venv venv
$ . venv/bin/activate
(venv) $ python -m pip install -U pip
(venv) $ python -m pip install -r requirements.txt
```

## Launch the helloworld_geyser

Print the usage:

```bash
(venv) $ python helloworld_geyser.py --help
Usage: helloworld_geyser.py [OPTIONS]

Simple program to get the latest solana slot number

Options:
--rpc-fqdn TEXT Fully Qualified domain name of your RPC endpoint
--x-token TEXT x-token to authenticate each gRPC call
--help Show this message and exit.
```

- `rpc-fqdn`: is the fully qualified domain name without the `https://`, such as `index.rpcpool.com`.
- `x-token` : is the x-token to authenticate yourself to the RPC node.

Here is a full example:

```bash
(venv) $ python helloworld_geyser.py --rpc-fqdn 'index.rpcpool.com' --x-token '2625ae71-0823-41b3-b3bc-4ff89d762d52'
slot: 264236514

```

**NOTE**: `2625ae71-0823-41b3-b3bc-4ff89d762d52` is a fake x-token, you need to provide your own token.

## Generate gRPC service and request signatures

The library `grpcio` generates the stubs for you.

From the directory of `helloword_geyser.py` you can generate all the stubs and data types using the following command:

```bash
(venv) $ python -m grpc_tools.protoc -I../../yellowstone-grpc-proto/proto/ --python_out=. --pyi_out=. --grpc_python_out=. ../../yellowstone-grpc-proto/proto/*
```

This will generate:
- geyser_pb2.py
- geyser_pb2.pyi
- geyser_pb2_grpc.py
- solana_storage_pb2.py
- solana_storage_pb2.pyi
- solana_storage_pb2_grpc.py

Which you can then import into your code.


## Useful documentation for grpcio authentication process

- [secure_channel](https://grpc.github.io/grpc/python/grpc.html#create-client-credentials)

- [extend auth method via call credentials](https://grpc.io/docs/guides/auth/#extending-grpc-to-support-other-authentication-mechanisms)
57 changes: 57 additions & 0 deletions examples/python/helloworld_geyser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from typing import Optional
import grpc
import geyser_pb2
import geyser_pb2_grpc
import logging
import click



def _triton_sign_request(
callback: grpc.AuthMetadataPluginCallback,
x_token: Optional[str],
error: Optional[Exception],
):
# WARNING: metadata is a 1d-tuple (<value>,), the last comma is necessary
metadata = (("x-token", x_token),)
return callback(metadata, error)


class TritonAuthMetadataPlugin(grpc.AuthMetadataPlugin):
"""Metadata wrapper for raw access token credentials."""

def __init__(self, x_token: str):
self.x_token = x_token

def __call__(
self,
context: grpc.AuthMetadataContext,
callback: grpc.AuthMetadataPluginCallback,
):
return _triton_sign_request(callback, self.x_token, None)


@click.command()
@click.option('--rpc-fqdn', help='Fully Qualified domain name of your RPC endpoint')
@click.option('--x-token', help='x-token to authenticate each gRPC call')
def helloworld_geyser(rpc_fqdn, x_token):
"""Simple program to get the latest solana slot number"""
auth = TritonAuthMetadataPlugin(x_token)
# ssl_creds allow you to use our https endpoint
# grpc.ssl_channel_credentials with no arguments will look through your CA trust store.
ssl_creds = grpc.ssl_channel_credentials()

# call credentials will be sent on each request if setup with composite_channel_credentials.
call_creds: grpc.CallCredentials = grpc.metadata_call_credentials(auth)

# Combined creds will store the channel creds aswell as the call credentials
combined_creds = grpc.composite_channel_credentials(ssl_creds, call_creds)

with grpc.secure_channel(rpc_fqdn, credentials=combined_creds) as channel:
stub = geyser_pb2_grpc.GeyserStub(channel)
response = stub.GetSlot(geyser_pb2.GetSlotRequest())
print(response)

if __name__ == '__main__':
logging.basicConfig()
helloworld_geyser()
4 changes: 4 additions & 0 deletions examples/python/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
click==8.1.7
grpcio==1.63.0
grpcio-tools==1.63.0
protobuf==5.26.1

0 comments on commit d0bbfbb

Please sign in to comment.