Skip to content

Commit

Permalink
topic queries start
Browse files Browse the repository at this point in the history
  • Loading branch information
nadineloepfe committed Dec 27, 2024
1 parent d172e1a commit 5050105
Show file tree
Hide file tree
Showing 20 changed files with 1,030 additions and 51 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ ADMIN_KEY=<ADMIN_KEY>
TOKEN_ID=<TOKEN_ID>
TOKEN_NAME=<TOKEN_NAME>
TOKEN_SYMBOL=<TOKEN_SYMBOL>
TOPIC_ID=<TOPIC_ID>
NETWORK=<NETWORK>
3 changes: 0 additions & 3 deletions examples/account_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
import sys
from dotenv import load_dotenv

project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.insert(0, project_root)

from hedera_sdk_python.client.client import Client
from hedera_sdk_python.account.account_id import AccountId
from hedera_sdk_python.crypto.private_key import PrivateKey
Expand Down
3 changes: 0 additions & 3 deletions examples/query_balance.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
import time
from dotenv import load_dotenv

project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.insert(0, project_root)

from hedera_sdk_python.account.account_id import AccountId
from hedera_sdk_python.crypto.private_key import PrivateKey
from hedera_sdk_python.client.network import Network
Expand Down
3 changes: 0 additions & 3 deletions examples/query_receipt.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
import sys
from dotenv import load_dotenv

project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.insert(0, project_root)

from hedera_sdk_python.account.account_id import AccountId
from hedera_sdk_python.crypto.private_key import PrivateKey
from hedera_sdk_python.client.network import Network
Expand Down
30 changes: 30 additions & 0 deletions examples/query_topic_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import os
from dotenv import load_dotenv

from hedera_sdk_python.client.network import Network
from hedera_sdk_python.client.client import Client
from hedera_sdk_python.consensus.topic_id import TopicId
from hedera_sdk_python.query.topic_info_query import TopicInfoQuery
from hedera_sdk_python.account.account_id import AccountId
from hedera_sdk_python.crypto.private_key import PrivateKey

load_dotenv()

def query_topic_info():
operator_id = AccountId.from_string(os.getenv('OPERATOR_ID'))
operator_key = PrivateKey.from_string(os.getenv('OPERATOR_KEY'))
topic_id = TopicId.from_string(os.getenv('TOPIC_ID'))

network = Network(network='testnet')
client = Client(network)
client.set_operator(operator_id, operator_key)

topic_info_query = TopicInfoQuery().set_topic_id(topic_id)
try:
topic_info = topic_info_query.execute(client)
print(topic_info)
except Exception as e:
print(f"Failed to retrieve topic info: {e}")

if __name__ == "__main__":
query_topic_info()
42 changes: 42 additions & 0 deletions examples/query_topic_message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import os
from dotenv import load_dotenv

from hedera_sdk_python.client.network import Network
from hedera_sdk_python.client.client import Client
from hedera_sdk_python.consensus.topic_id import TopicId
from hedera_sdk_python.query.topic_message_query import TopicMessageQuery
from datetime import datetime
import time

load_dotenv()

def query_topic_messages():

network = Network(network='testnet')
client = Client(network)

def on_message_handler(msg):
print("Received topic message:", msg)

def on_error_handler(e):
print("Subscription error:", e)

query = (
TopicMessageQuery()
.set_topic_id("0.0.12345")
.set_start_time(datetime.utcnow())
.set_chunking_enabled(True)
)

query.subscribe(
client,
on_message=on_message_handler,
on_error=on_error_handler
)

time.sleep(10)
print("Done waiting. Exiting.")


if __name__ == "__main__":
query_topic_messages()
3 changes: 0 additions & 3 deletions examples/token_associate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
import sys
from dotenv import load_dotenv

project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.insert(0, project_root)

from hedera_sdk_python.client.client import Client
from hedera_sdk_python.account.account_id import AccountId
from hedera_sdk_python.crypto.private_key import PrivateKey
Expand Down
3 changes: 0 additions & 3 deletions examples/token_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
import sys
from dotenv import load_dotenv

project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.insert(0, project_root)

from hedera_sdk_python.client.client import Client
from hedera_sdk_python.account.account_id import AccountId
from hedera_sdk_python.crypto.private_key import PrivateKey
Expand Down
3 changes: 0 additions & 3 deletions examples/transfer_hbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
import sys
from dotenv import load_dotenv

project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.insert(0, project_root)

from hedera_sdk_python.client.client import Client
from hedera_sdk_python.account.account_id import AccountId
from hedera_sdk_python.crypto.private_key import PrivateKey
Expand Down
3 changes: 0 additions & 3 deletions examples/transfer_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
import sys
from dotenv import load_dotenv

project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
sys.path.insert(0, project_root)

from hedera_sdk_python.client.client import Client
from hedera_sdk_python.account.account_id import AccountId
from hedera_sdk_python.crypto.private_key import PrivateKey
Expand Down
67 changes: 42 additions & 25 deletions generate_proto.sh
Original file line number Diff line number Diff line change
@@ -1,41 +1,58 @@
#!/bin/bash
#!/usr/bin/env bash

# Source the activate script to set up the PATH for this shell session
source ./.venv/bin/activate
set -e

hapi_version=v0.57.3
protos_dir=.protos

mkdir -p $protos_dir
rm -rf $protos_dir/*

mkdir -p "$protos_dir"
rm -rf "$protos_dir"/*

# Download the tarball of a specific tag and immediately extract the subdirectory
curl -sL "https://github.com/hashgraph/hedera-protobufs/archive/refs/tags/${hapi_version}.tar.gz" | tar -xz -C $protos_dir --strip-components=1
# Keep 'platform' and 'services', remove everything else in the current directory
find "$protos_dir" -mindepth 1 -maxdepth 1 ! -name platform ! -name services -exec rm -r {} +
curl -sL "https://github.com/hashgraph/hedera-protobufs/archive/refs/tags/${hapi_version}.tar.gz" \
| tar -xz -C "$protos_dir" --strip-components=1

find "$protos_dir" -mindepth 1 -maxdepth 1 \
! -name platform \
! -name mirror \
! -name services \
-exec rm -r {} +

rm -rf src/hedera_sdk_python/hapi/*
mkdir -p src/hedera_sdk_python/hapi/auxiliary/tss
mkdir -p src/hedera_sdk_python/hapi/event
mkdir -p src/hedera_sdk_python/hapi/mirror
touch src/hedera_sdk_python/hapi/__init__.py

python -m grpc_tools.protoc \
--proto_path="$protos_dir/platform" \
--proto_path="$protos_dir/services" \
--pyi_out=./src/hedera_sdk_python/hapi \
--python_out=./src/hedera_sdk_python/hapi \
--grpc_python_out=./src/hedera_sdk_python/hapi \
"$protos_dir"/services/*.proto \
"$protos_dir"/services/auxiliary/tss/*.proto \
"$protos_dir"/platform/event/*.proto

python -m grpc_tools.protoc \
--proto_path=$protos_dir/platform \
--proto_path=$protos_dir/services \
--pyi_out=./src/hedera_sdk_python/hapi \
--python_out=./src/hedera_sdk_python/hapi \
--grpc_python_out=./src/hedera_sdk_python/hapi \
$protos_dir/services/*.proto $protos_dir/services/auxiliary/tss/*.proto $protos_dir/platform/event/*.proto

# Modify the script for the specific import changes
--proto_path="$protos_dir/mirror" \
--proto_path="$protos_dir/services" \
--proto_path="$protos_dir/platform" \
--pyi_out=./src/hedera_sdk_python/hapi/mirror \
--python_out=./src/hedera_sdk_python/hapi/mirror \
--grpc_python_out=./src/hedera_sdk_python/hapi/mirror \
$(find "$protos_dir/mirror" -name '*.proto')

if [[ "$OSTYPE" == "darwin"* ]]; then
find ./src/hedera_sdk_python/hapi -type f -name "*.py" -exec sed -i '' \
-e '/^import .*_pb2 as .*__pb2/s/^/from . /' \
-e 's/^from auxiliary\.tss/from .auxiliary.tss/' \
-e 's/^from event/from .event/' {} +
find ./src/hedera_sdk_python/hapi -type f -name "*.py" -exec sed -i '' \
-e '/^import .*_pb2 as .*__pb2/s/^/from . /' \
-e 's/^from auxiliary\.tss/from .auxiliary.tss/' \
-e 's/^from event/from .event/' {} +
else
find ./src/hedera_sdk_python/hapi -type f -name "*.py" -exec sed -i \
-e '/^import .*_pb2 as .*__pb2/s/^/from . /' \
-e 's/^from auxiliary\.tss/from .auxiliary.tss/' \
-e 's/^from event/from .event/' {} +
find ./src/hedera_sdk_python/hapi -type f -name "*.py" -exec sed -i \
-e '/^import .*_pb2 as .*__pb2/s/^/from . /' \
-e 's/^from auxiliary\.tss/from .auxiliary.tss/' \
-e 's/^from event/from .event/' {} +
fi

echo "All done generating protos."
Binary file modified src/.DS_Store
Binary file not shown.
12 changes: 9 additions & 3 deletions src/hedera_sdk_python/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,22 @@ def send_query(self, query, node_account_id, timeout=60):
None. Exceptions are caught and printed, returning None on failure.
"""
self._switch_node(node_account_id)
stub = self.crypto_stub

try:
request = query._make_request()

if hasattr(request, 'cryptogetAccountBalance'):
response = stub.cryptoGetBalance(request, timeout=timeout)
response = self.crypto_stub.cryptoGetBalance(request, timeout=timeout)

elif hasattr(request, 'transactionGetReceipt'):
response = stub.getTransactionReceipts(request, timeout=timeout)
response = self.crypto_stub.getTransactionReceipts(request, timeout=timeout)

elif hasattr(request, 'consensusGetTopicInfo'):
response = self.topic_stub.getTopicInfo(request, timeout=timeout)

else:
raise Exception("Unsupported query type.")

return response
except grpc.RpcError as e:
print(f"gRPC error during query execution: {e}")
Expand Down
66 changes: 66 additions & 0 deletions src/hedera_sdk_python/consensus/topic_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from hedera_sdk_python.hapi.basic_types_pb2 import Key, AccountID
from hedera_sdk_python.hapi.timestamp_pb2 import Timestamp
from hedera_sdk_python.hapi.duration_pb2 import Duration

class TopicInfo:
"""
Represents the information retrieved from ConsensusService.getTopicInfo() about a topic.
"""

def __init__(
self,
memo: str,
running_hash: bytes,
sequence_number: int,
expiration_time: Timestamp,
admin_key: Key,
submit_key: Key,
auto_renew_period: Duration,
auto_renew_account: AccountID,
ledger_id: bytes,
):
self.memo = memo
self.running_hash = running_hash
self.sequence_number = sequence_number
self.expiration_time = expiration_time
self.admin_key = admin_key
self.submit_key = submit_key
self.auto_renew_period = auto_renew_period
self.auto_renew_account = auto_renew_account
self.ledger_id = ledger_id

@classmethod
def from_proto(cls, topic_info_proto):
"""
Constructs a TopicInfo object from a protobuf ConsensusTopicInfo message.
Args:
topic_info_proto (ConsensusTopicInfo): The protobuf message with topic info.
Returns:
TopicInfo: A new instance populated from the protobuf message.
"""
return cls(
memo=topic_info_proto.memo,
running_hash=topic_info_proto.runningHash,
sequence_number=topic_info_proto.sequenceNumber,
expiration_time=topic_info_proto.expirationTime,
admin_key=topic_info_proto.adminKey if topic_info_proto.HasField("adminKey") else None,
submit_key=topic_info_proto.submitKey if topic_info_proto.HasField("submitKey") else None,
auto_renew_period=topic_info_proto.autoRenewPeriod
if topic_info_proto.HasField("autoRenewPeriod")
else None,
auto_renew_account=topic_info_proto.autoRenewAccount
if topic_info_proto.HasField("autoRenewAccount")
else None,
ledger_id=topic_info_proto.ledger_id,
)

def __repr__(self):
return (
f"TopicInfo(memo={self.memo!r}, running_hash={self.running_hash!r}, "
f"sequence_number={self.sequence_number}, expiration_time={self.expiration_time}, "
f"admin_key={self.admin_key}, submit_key={self.submit_key}, "
f"auto_renew_period={self.auto_renew_period}, auto_renew_account={self.auto_renew_account}, "
f"ledger_id={self.ledger_id!r})"
)
Empty file removed src/hedera_sdk_python/py.typed
Empty file.
48 changes: 48 additions & 0 deletions src/hedera_sdk_python/query/topic_info_query.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from hedera_sdk_python.query.query import Query
from hedera_sdk_python.hapi import query_pb2, consensus_get_topic_info_pb2
from hedera_sdk_python.consensus.topic_id import TopicId
from hedera_sdk_python.consensus.topic_info import TopicInfo

class TopicInfoQuery(Query):
"""
A query to retrieve information about a specific Hedera topic.
"""

def __init__(self):
super().__init__()
self.topic_id = None

def set_topic_id(self, topic_id: TopicId):
self.topic_id = topic_id
return self

def _make_request(self):
if not self.topic_id:
raise ValueError("Topic ID must be set before making the request.")

query_header = self._make_request_header()

topic_info_query = consensus_get_topic_info_pb2.ConsensusGetTopicInfoQuery()
topic_info_query.header.CopyFrom(query_header)
topic_info_query.topicID.CopyFrom(self.topic_id.to_proto())

query = query_pb2.Query()
query.consensusGetTopicInfo.CopyFrom(topic_info_query)

return query

def _get_status_from_response(self, response):
"""
Must read nodeTransactionPrecheckCode from response.consensusGetTopicInfo.header
"""
return response.consensusGetTopicInfo.header.nodeTransactionPrecheckCode

def _map_response(self, response):
"""
Return a TopicInfo instance built from the protobuf
"""
if not response.consensusGetTopicInfo.topicInfo:
raise Exception("No topicInfo returned in the response.")

proto_topic_info = response.consensusGetTopicInfo.topicInfo
return TopicInfo.from_proto(proto_topic_info)
Loading

0 comments on commit 5050105

Please sign in to comment.