Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
lAmeR1 committed Jun 27, 2022
1 parent 0ee3013 commit a7b9f46
Show file tree
Hide file tree
Showing 19 changed files with 3,369 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.idea
Pipfile.lock
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
15 changes: 15 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
grpcio = "*"
grpcio-tools = "*"
fastapi = "*"

[dev-packages]
uvicorn = "*"

[requires]
python_version = "3.10"
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: uvicorn app.main:app --host=0.0.0.0 --port=${PORT:-5000}
33 changes: 33 additions & 0 deletions helper/Event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class Event(object):
def __init__(self):
self.callbacks = set()
self._lock = False

def __call__(self, *args, **kwargs):
if not self._lock:
for callback in self.callbacks:
evt_args = self.__get_evt_args(*args, **kwargs)

callback(evt_args)

def __add__(self, listener):
self.callbacks.add(listener)
return self

def __sub__(self, listener):
self.callbacks.remove(listener)
return self

def __get_evt_args(self, *args, **kwargs):
evt_args = {u"Args": args}
evt_args.update(kwargs)
evt_args[u"EventObject"] = self
return evt_args


class LockableEvent(Event):
def lock(self):
self._lock = True

def unlock(self):
self._lock = False
Empty file added helper/__init__.py
Empty file.
20 changes: 20 additions & 0 deletions kaspad/KaspadClient.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# encoding: utf-8

from kaspad.KaspadThread import KaspadThread


# pipenv run python -m grpc_tools.protoc -I./protos --python_out=. --grpc_python_out=. ./protos/rpc.proto ./protos/messages.proto ./protos/p2p.proto

class KaspadClient(object):
def __init__(self, kaspad_host, kaspad_port):
self.kaspad_host = kaspad_host
self.kaspad_port = kaspad_port

def request(self, command, params=None, timeout=5):
with KaspadThread(self.kaspad_host, self.kaspad_port) as t:
return t.request(command, params, wait_for_response=True, timeout=timeout)

def notify(self, command, params, callback):
t = KaspadThread(self.kaspad_host, self.kaspad_port)
t.on_new_response += callback
t.request(command, params, wait_for_response=False)
40 changes: 40 additions & 0 deletions kaspad/KaspadMultiClient.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# encoding: utf-8
from contextlib import suppress

from kaspad.KaspadClient import KaspadClient

# pipenv run python -m grpc_tools.protoc -I./protos --python_out=. --grpc_python_out=. ./protos/rpc.proto ./protos/messages.proto ./protos/p2p.proto
from kaspad.KaspadThread import KaspadCommunicationError


class KaspadMultiClient(object):
def __init__(self, hosts: list[str]):
self.kaspads = [KaspadClient(*h.split(":")) for h in hosts]
self.__default_kaspad = None
self.__new_default_kaspad()

def __new_default_kaspad(self):
print('looking for new kaspad server')
for k in self.kaspads:
with suppress(KaspadCommunicationError):
resp = k.request("getInfoRequest", timeout=1)
if resp["getInfoResponse"]["p2pId"]:
self.__default_kaspad = k
break
else:
raise KaspadCommunicationError('Kaspads not working.')

def request(self, command, params=None):
try:
return self.__default_kaspad.request(command, params)
except KaspadCommunicationError:
self.__new_default_kaspad()
return self.__default_kaspad.request(command, params)


def notify(self, command, params, callback):
try:
return self.__default_kaspad.notify(command, params, callback)
except KaspadCommunicationError:
self.__new_default_kaspad()
return self.__default_kaspad.notify(command, params, callback)
82 changes: 82 additions & 0 deletions kaspad/KaspadThread.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# encoding: utf-8
import queue
import threading
from queue import Queue, SimpleQueue

import grpc
from google.protobuf import json_format

from helper.Event import Event
from . import messages_pb2_grpc
from .messages_pb2 import KaspadMessage


class KaspadCommunicationError(Exception): pass

# pipenv run python -m grpc_tools.protoc -I./protos --python_out=. --grpc_python_out=. ./protos/rpc.proto ./protos/messages.proto ./protos/p2p.proto

class KaspadThread(object):
def __init__(self, kaspad_host, kaspad_port):
# This is a sample Python script.

self.channel = grpc.insecure_channel(f'{kaspad_host}:{kaspad_port}')
self.stub = messages_pb2_grpc.RPCStub(self.channel)
self.on_new_response = Event()
self.on_new_error = Event()

self.__queue = SimpleQueue()

self.__thread = threading.Thread(target=self.__loop, daemon=True).start()
self.__closing = False

def __enter__(self, *args):
return self

def __exit__(self, *args):
# return
self.__closing = True
self.channel.close()

def request(self, command, params=None, wait_for_response=True, timeout=5):
if wait_for_response:
q = Queue()

def listener(e):
q.put(e["resp"])

self.on_new_response += listener
self.__queue.put((command, params))
try:
resp = q.get(timeout=timeout)
self.on_new_response -= listener
return resp
except queue.Empty as e:
raise KaspadCommunicationError(str(e))

else:
self.__queue.put((command, params))

def __loop(self):
try:
for resp in self.stub.MessageStream(self.__inbox()):
resp = json_format.MessageToDict(resp)
self.on_new_response(resp=resp)
except Exception as e:
if not (self.__closing is True and '"grpc_message":"Channel closed!"' in str(e)):
self.on_new_error(error=e)

def __inbox(self):
while True:
msg = KaspadMessage()
cmd, params = self.__queue.get()
msg2 = getattr(msg, cmd)
payload = params

if payload:
if isinstance(payload, dict):
json_format.ParseDict(payload, msg2)
if isinstance(payload, str):
json_format.Parse(payload, msg2)

msg2.SetInParent()
yield msg
Empty file added kaspad/__init__.py
Empty file.
Loading

0 comments on commit a7b9f46

Please sign in to comment.