Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added TCK support #53

Closed
wants to merge 2 commits into from
Closed
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
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ dependencies = [
"grpcio==1.68.1",
"cryptography==44.0.0",
"python-dotenv==1.0.1",
"requests==2.32.3"
"requests==2.32.3",
"jsonrpcserver>=5.0.9",
]
classifiers = [
"Development Status :: 2 - Pre-Alpha",
Expand Down
65 changes: 65 additions & 0 deletions tck/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Testing Compatibility Toolkit (TCK) Guide

This document describes how to run Hiero TCK tests for the Python SDK.
## Setup
1. **Set up the Python SDK**

Follow the steps in the [Hiero TCK README](https://github.com/hiero-ledger/hiero-sdk-tck) to clone the repository and set it up on your local machine.


2. **Environment Variables**

Ensure that the environment variables in the `.env` file for both the TCK and the Python SDK are identical, to make sure that they run in the same configuration.

## Running the TCK Tests

1. **Navigate to the TCK Directory in the Python SDK**

Change directory to the location of the TCK within the Python SDK. For example:

```shell script
cd hiero-python-sdk/tck
```

2. **Start the TCK Server**

Launch the TCK server by running:

```shell script
python3 server.py
```

3. **Execute TCK Tests**

With the TCK server running, you can now execute tests either from the command line or directly via your IDE. Refer to the TCK's README for more information.


## Troubleshooting

- **Environment Variables Not Set Correctly:**
Double-check your `.env` files for typos or mismatched variables between the TCK and Python SDK.

- **Server Issues:**
Ensure no other application is using the port required by the TCK server. Check server logs for further details if the server does not start as expected.

- **Test Failures:**
Review the output for any error messages. They can give insights into configuration issues or missing dependencies.

- **Enable Logging:**
In the server.py file, uncomment `logging.basicConfig(level=logging.DEBUG)` to enable logging, and better debug what is happening.


## Contributing

Contributions are welcome! Please follow these steps:

1. Fork this repository.
2. Create a new branch with your feature or fix.
3. Make your changes and ensure the tests pass.
3. Submit a pull request.

Please ensure all new code is covered by unit tests.

## License

This project is licensed under the MIT License.
Empty file added tck/__init__.py
Empty file.
51 changes: 51 additions & 0 deletions tck/account_create.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import utils

from jsonrpcserver import method, Success
from hiero_sdk_python.account.account_create_transaction import AccountCreateTransaction
from key_identifier import KeyIdentifier

@method
def createAccount(key: str = None, initialBalance: str = None, receiverSignatureRequired: bool = None, autoRenewPeriod: str = None,
memo: str = None, maxAutoTokenAssociations: int = None, stakedAccountId: str = None, stakedNodeId: str = None,
declineStakingReward: bool = None, alias: str = None, commonTransactionParams: dict = None):
"""
:param key: string, optional, DER-encoded hex string representation for private or public keys. Keylists and threshold keys are the hex of the serialized protobuf bytes.
:param initialBalance: Units of tinybars, optional
:param receiverSignatureRequired: bool, optional
:param autoRenewPeriod: string, Units of seconds, optional
:param memo: string, optional
:param maxAutoTokenAssociations: int32, optional
:param stakedAccountId: string, optional
:param stakedNodeId: string, optional
:param declineStakingReward: bool, optional
:param alias: string, optional, Hex string representation of the keccak-256 hash of an ECDSAsecp256k1 public key type.
:param commonTransactionParams: JSON object, optional

:return accountId: string, The ID of the created account.
:return status: string, The status of the submitted AccountCreateTransaction (from a TransactionReceipt).
"""
key_and_public = KeyIdentifier.identify(key)

if key_and_public[1] is False:
public_key = key_and_public[0].public_key()
else:
public_key = key_and_public[0]

# TODO: add all of the other transaction parameters
transaction = (
AccountCreateTransaction()
.set_key(public_key)
# .set_initial_balance(int(initialBalance))
.set_receiver_signature_required(receiverSignatureRequired)
# .set_auto_renew_period(0, int(autoRenewPeriod))
.set_account_memo(memo)
.freeze_with(utils.__client)
)

transaction.sign(utils.__operatorPrivateKey)
receipt = transaction.execute(utils.__client)

return Success({
"accountId": str(receipt.accountId),
"status": str(receipt.status),
})
17 changes: 17 additions & 0 deletions tck/key_identifier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from hiero_sdk_python import PublicKey, PrivateKey

class KeyIdentifier:

@classmethod
def identify(cls, key: str) -> tuple[None | PublicKey | PrivateKey, bool | None]:
"""
This function will either return a cryptography ...Key object, or it will return None, should not be used
for anything outside of the TCK code.
"""
try:
return PublicKey.from_string(key), True
except ValueError:
try:
return PrivateKey.from_string(key), False
except ValueError:
return None, None
10 changes: 10 additions & 0 deletions tck/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import logging
import utils
import account_create
from jsonrpcserver import serve, method

# NOTE: enable if you want to see the logs
# logging.basicConfig(level=logging.DEBUG)

if __name__ == '__main__':
serve(port=8545) # Specify your desired port here
59 changes: 59 additions & 0 deletions tck/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from hiero_sdk_python import Network
from hiero_sdk_python.account.account_id import AccountId
from hiero_sdk_python.client.client import Client
from jsonrpcserver import Success, method, InvalidParams
from hiero_sdk_python.crypto.private_key import PrivateKey

__operatorAccountId: AccountId
__operatorPrivateKey: PrivateKey
__client: Client = Client(Network(network='testnet'))

@method
def setup(operatorAccountId: str=None, operatorPrivateKey: str=None, nodeIp: str=None, nodeAccountId: str=None, mirrorNetworkIp: str=None):
global __client, __operatorAccountId, __operatorPrivateKey
__operatorAccountId = AccountId.from_string(operatorAccountId)
__operatorPrivateKey = PrivateKey.from_string(operatorPrivateKey)
__client.set_operator(__operatorAccountId, __operatorPrivateKey)

return Success({
"message":"Successfully setup custom client.",
"status":"SUCCESS"
})

@method
def reset():
global __operatorAccountId, __operatorPrivateKey, __client
__operatorAccountId = None
__operatorPrivateKey = None
__client = Client(Network(network='testnet'))

return Success({
"message":"Successfully reset client.",
"status":"SUCCESS"
})

@method
def generateKey(type: str=None, fromKey: str=None, threshold: int=None, keys: list=None):
if type == "ed25519PublicKey":
if fromKey is None:
return Success({"key": PrivateKey.generate_ed25519().public_key().to_string_raw()})
else:
new_account_private_key = PrivateKey.from_string(fromKey)
new_account_public_key = new_account_private_key.public_key()
return Success({"key": new_account_public_key.to_string_raw()})

elif type == "ecdsaSecp256k1PublicKey":
if fromKey is None:
return Success({"key": PrivateKey.generate_ecdsa().public_key().to_string_raw()})
else:
new_account_private_key = PrivateKey.from_string(fromKey)
new_account_public_key = new_account_private_key.public_key()
return Success({"key": new_account_public_key.to_string_raw()})

elif type == "ed25519PrivateKey":
new_account_private_key = PrivateKey.generate()
return Success({"key": new_account_private_key.to_string_raw()})

else:
return InvalidParams("Unknown type")

Loading
Loading