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

SDK release v3.4.0 #58

Merged
merged 37 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
7af6406
Rewritten the test_sdk_client.py using unittest library, commented ou…
May 31, 2024
24212b0
Added force_update key to config
Jun 3, 2024
277b8be
Commented out the tests that are currently duplicate
Jun 3, 2024
7256a02
Added automatic proto and pb2 files removal after running the test, u…
Jun 10, 2024
eeee36d
Commented out tests that are under development
Jun 17, 2024
3ae10b9
Add network change and identity functionality
mabredin Jun 17, 2024
7411e10
Moved private keys to environment variables
Jun 17, 2024
f870285
Merge pull request #41 from singnet/development_optional_proto_genera…
mabredin Jun 17, 2024
664ae2e
Merge branch 'development' into passing_additional_parameters
mabredin Jun 17, 2024
818b9ef
Merge pull request #42 from singnet/passing_additional_parameters
mabredin Jun 17, 2024
29788fe
Merge pull request #40 from singnet/development_test_sdk_client
mabredin Jun 17, 2024
3f4b03f
Github actions implementation_dev (#47)
Deralden Jun 20, 2024
1107aa7
Change CLI version in dependencies (#48)
mabredin Jun 20, 2024
2f376c7
Removed the test code that is currently not relevant, updated the rea…
PAY2109 Jun 20, 2024
5ca939a
Upgrade version
kiruxaspb Jun 20, 2024
8000369
Fix conflicts
kiruxaspb Jun 20, 2024
8f9b5dd
Suppressed the warnings related to two networks (eth-typing package)
Jun 21, 2024
129fe54
Changed snet-cli to snet.cli in the requirements.txt
Jun 24, 2024
c74822b
Merge pull request #52 from singnet/development_requirements_small_fix
mabredin Jun 24, 2024
283289f
Added the get_service_and_messages_info() and the print_service_info(…
Jun 25, 2024
0628d6e
Changed the method names to plural form
Jun 25, 2024
4fe5658
Merge pull request #53 from singnet/development_service_info_method
PAY2109 Jun 26, 2024
1847623
Added urllib3 version to the requirements.txt file to avoid possible …
Jun 28, 2024
6b42e4b
An SDK instance now is being created with "base" config info, and ser…
Jul 1, 2024
a0cc2ec
get_services_and_messages_info_as_pretty_string now actually returns …
Jul 1, 2024
4731b8c
create_service_client method now takes org_id, service_id and group_n…
Jul 1, 2024
9cf54c4
test_sdk_client.py updated according to the new code logic
Jul 1, 2024
6e906b7
Merge pull request #54 from singnet/development_reorganize_sdk_config
kiruxaspb Jul 23, 2024
7ddf6b6
Merge branch 'development' into development_suppress_warnings
kiruxaspb Jul 23, 2024
ddca08b
Fixed import
kiruxaspb Jul 23, 2024
aa55b12
README.md update according to the latest config logic changes
Jul 23, 2024
61b55f0
README.md: removed the dead link
Jul 23, 2024
16ef58b
Merge pull request #51 from singnet/development_suppress_warnings
kiruxaspb Jul 24, 2024
5711a39
Update version.py
kiruxaspb Jul 29, 2024
53daf30
Merge pull request #59 from singnet/update-sdk-version
kiruxaspb Jul 29, 2024
01a7a75
Merge branch 'master' into development
kiruxaspb Jul 29, 2024
bc6c272
Upgrade cli version in requirements
kiruxaspb Jul 30, 2024
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
57 changes: 36 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,7 @@ config = {
"private_key": 'YOUR_PRIVATE_WALLET_KEY',
"eth_rpc_endpoint": f"https://sepolia.infura.io/v3/YOUR_INFURA_KEY",
"email": "[email protected]",
"free_call_auth_token-bin":"f5533eb0f01f0d45239c11b411bdfd4221fd3b125e4250db1f7bc044466108bc10ce95ab62ae224b6578b68d0ce337b4ec36e4b9dfbe6653e04973107813cbc01c",
"free-call-token-expiry-block":19690819,
"concurrency": False,
"org_id": "organization_id",
"service_id": "id_of_the_service",
"group_name": "default_group",
"identity_name": "local_name_for_that_identity",
"identity_type": "key",
"network": "sepolia",
Expand All @@ -55,46 +50,66 @@ See [test_sdk_client.py](https://github.com/singnet/snet-sdk-python/blob/master/
private_key: Your wallet's private key that will be used to pay for calls. Is **required** to make a call;
eth_rpc_endpoint: RPC endpoint that is used to access the Ethereum network. Is **required** to make a call;
email: Your email;
"free_call_auth_token-bin" and "free-call-token-expiry-block": Are used to make free calls. See more on that below;
org_id: ID of the organization that owns the service you want to call. Is **required** to make a call;
service_id: ID of the service you want to call. Is **required** to make a call;
identity_name: Name that will be used locally to save your wallet settings. You can check your identities in the `~/.snet/config` file;
identity_type: Type of your wallet authentication. Note that snet-sdk currently supports only "key" identity_type;
network: You can set the Ethereum network that will be used to make a call;
force_update: If set to False, will reuse the existing gRPC stubs (if any) instead of downloading proto and regenerating them every time.

##### List organizations and their services

After executing this code, you should have client libraries created for this service. They are located at the following path: `~/.snet/org_id/service_id/python/`
You can use the sdk client instance`s methods get_organization_list() to list all organizations and get_services_list("org_id") to list all services of a given organization.

Note: Currently you can only save files to `~/.snet/`. We will fix this in the future.
```python
print(snet_sdk.get_organization_list())
print(snet_sdk.get_services_list("26072b8b6a0e448180f8c0e702ab6d2f"))
```

##### Free call configuration

If you want to use a free call you need to add these attributes to the config dictionary:
If you want to use the free calls you will need to pass these arguments to the create_service_client() method:

```
"free_call_auth_token-bin":"f2548d27ffd319b9c05918eeac15ebab934e5cfcd68e1ec3db2b92765",
"free-call-token-expiry-block":172800,
"email":"[email protected]"
```
You can receive these for a given service from the [Dapp](https://beta.singularitynet.io/)
#### Calling the service
Now, the instance of the sdk can be used to create the service client instances.
Continuing from the previous code this is an example using `Exampleservice` from the `26072b8b6a0e448180f8c0e702ab6d2f` organization:
Continuing from the previous code here is an example using `Exampleservice` from the `26072b8b6a0e448180f8c0e702ab6d2f` organization:

```python
service_client = snet_sdk.create_service_client(org_id="26072b8b6a0e448180f8c0e702ab6d2f",
service_id="Exampleservice",
group_name="default_group")
```
Creating a service client with free calls included would look like this:
```python
service_client = snet_sdk.create_service_client(org_id="26072b8b6a0e448180f8c0e702ab6d2f",
service_id="Exampleservice",
group_name="default_group",
free_call_auth_token_bin="f2548d27ffd319b9c05918eeac15ebab934e5cfcd68e1ec3db2b92765",
free_call_token_expiry_block=172800)
```

After executing this code, you should have client libraries created for this service. They are located at the following path: `~/.snet/org_id/service_id/python/`

Note: Currently you can only save files to `~/.snet/`. We will fix this in the future.
```python
service_client = snet_sdk.create_service_client()
service_client.deposit_and_open_channel(123456, 33333)
```
`deposit_and_open_channel()` function deposits the specified amount of AGIX tokens in cogs into an MPE smart contract and opens a payment channel.
The instance of service_client that has been generated can be utilized to invoke the methods that the service offers.
To do this, use the the call_rpc method. This method needs the names of the method and data object, along with the data itself, to be passed into it.
The specific data that needs to be passed can be found in the .proto file. Building upon the previously written code, here’s an example that uses the *Exampleservice* from the *26072b8b6a0e448180f8c0e702ab6d2f* organization:
`deposit_and_open_channel(amount, expiration)` function deposits the specified amount of AGIX tokens in cogs into an MPE smart contract and opens a payment channel. Expiration is payment channel's TTL in blocks.
The instance of service_client that has been generated can be utilized to invoke the methods that the service offers. You can list these using the get_services_and_messages_info_as_pretty_string() method:
```python
result = service_client.call_rpc("mul", "Numbers", a=20, b=3)
print(f"Performing 20 * 3: {result}") # Performing 20 * 3: value: 60.0
print(service_client.get_services_and_messages_info_as_pretty_string())
```

You can get this code example at [https://github.com/singnet/snet-code-examples/tree/python_client/python/client](https://github.com/singnet/snet-code-examples/tree/python_client/python/client)
To invoke the service`s methods, you can use the the call_rpc() method. This method requires the names of the method and data object, along with the data itself, to be passed into it.
To continue with our example, here’s a call to the *mul* method of the *Exampleservice* from the *26072b8b6a0e448180f8c0e702ab6d2f* organization:

```python
result = service_client.call_rpc("mul", "Numbers", a=20, b=3)
print(f"Calculating 20 * 3: {result}") # Calculating 20 * 3: 60.0
```

For more information about gRPC and how to use it with Python, please see:
- [gRPC Basics - Python](https://grpc.io/docs/tutorials/basic/python.html)
Expand Down
5 changes: 3 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ argcomplete==3.1.2
grpcio-health-checking==1.59.0
jsonschema==4.0.0
eth-account==0.9.0
snet-cli==2.1.3
snet.contracts==0.1.1
snet.cli==2.1.3
snet.contracts==0.1.1
urllib3>=2.2.2
110 changes: 75 additions & 35 deletions snet/sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,22 @@
from pathlib import Path
import sys
from typing import Any, NewType
import warnings
import copy

import google.protobuf.internal.api_implementation

with warnings.catch_warnings():
# Suppress the eth-typing package`s warnings related to some new networks
warnings.filterwarnings("ignore", "Network .* does not have a valid ChainId. eth-typing should be "
"updated with the latest networks.", UserWarning)

from snet.sdk.metadata_provider.ipfs_metadata_provider import IPFSMetadataProvider
from snet.sdk.payment_strategies.default_payment_strategy import DefaultPaymentStrategy
from snet.cli.commands.sdk_command import SDKCommand
from snet.cli.commands.commands import BlockchainCommand
from snet.cli.config import Config
from snet.cli.utils.utils import bytes32_to_str, type_converter

google.protobuf.internal.api_implementation.Type = lambda: 'python'

Expand Down Expand Up @@ -40,7 +50,7 @@


class Arguments:
def __init__(self, org_id, service_id):
def __init__(self, org_id=None, service_id=None):
self.org_id = org_id
self.service_id = service_id
self.language = "python"
Expand All @@ -50,56 +60,46 @@ def __init__(self, org_id, service_id):
class SnetSDK:
"""Base Snet SDK"""

def __init__(self, config, metadata_provider=None):
self._config = config
def __init__(self, sdk_config, metadata_provider=None):
self._sdk_config = sdk_config
self._metadata_provider = metadata_provider

# Instantiate Ethereum client
eth_rpc_endpoint = self._config.get("eth_rpc_endpoint", "https://mainnet.infura.io/v3/e7732e1f679e461b9bb4da5653ac3fc2")
eth_rpc_request_kwargs = self._config.get("eth_rpc_request_kwargs")
eth_rpc_endpoint = self._sdk_config.get("eth_rpc_endpoint",
"https://mainnet.infura.io/v3/e7732e1f679e461b9bb4da5653ac3fc2")
eth_rpc_request_kwargs = self._sdk_config.get("eth_rpc_request_kwargs")

provider = web3.HTTPProvider(endpoint_uri=eth_rpc_endpoint, request_kwargs=eth_rpc_request_kwargs)

self.web3 = web3.Web3(provider)

# Get MPE contract address from config if specified; mostly for local testing
_mpe_contract_address = self._config.get("mpe_contract_address", None)
_mpe_contract_address = self._sdk_config.get("mpe_contract_address", None)
if _mpe_contract_address is None:
self.mpe_contract = MPEContract(self.web3)
else:
self.mpe_contract = MPEContract(self.web3, _mpe_contract_address)

# Instantiate IPFS client
ipfs_endpoint = self._config.get("default_ipfs_endpoint", "/dns/ipfs.singularitynet.io/tcp/80/")
ipfs_endpoint = self._sdk_config.get("default_ipfs_endpoint", "/dns/ipfs.singularitynet.io/tcp/80/")
self.ipfs_client = ipfshttpclient.connect(ipfs_endpoint)

# Get Registry contract address from config if specified; mostly for local testing
_registry_contract_address = self._config.get("registry_contract_address", None)
_registry_contract_address = self._sdk_config.get("registry_contract_address", None)
if _registry_contract_address is None:
self.registry_contract = get_contract_object(self.web3, "Registry")
else:
self.registry_contract = get_contract_object(self.web3, "Registry", _registry_contract_address)

self.account = Account(self.web3, config, self.mpe_contract)
self.account = Account(self.web3, sdk_config, self.mpe_contract)

global_config = Config(sdk_config=config)
global_config = Config(sdk_config=sdk_config)
self.setup_config(global_config)
sdk = SDKCommand(global_config, args=Arguments(config['org_id'], config['service_id']))
force_update = config.get('force_update', False)

if force_update:
sdk.generate_client_library()
else:
path_to_pb_files = self.get_path_to_pb_files(config['org_id'], config['service_id'])
pb_2_file_name = find_file_by_keyword(path_to_pb_files, keyword="pb2.py")
pb_2_grpc_file_name = find_file_by_keyword(path_to_pb_files, keyword="pb2_grpc.py")
if not pb_2_file_name or not pb_2_grpc_file_name:
sdk.generate_client_library()

def setup_config(self, config: Config) -> None:
out_f = sys.stdout
network = self._config.get("network", None)
identity_name = self._config.get("identity_name", None)
network = self._sdk_config.get("network", None)
identity_name = self._sdk_config.get("identity_name", None)
# Checking for an empty network
if network and config["session"]["network"] != network:
config.set_session_network(network, out_f)
Expand All @@ -117,21 +117,39 @@ def set_session_identity(self, identity_name: str, config: Config, out_f):
elif config["session"]["identity"] != identity_name:
config.set_session_identity(identity_name, out_f)

def create_service_client(self, payment_channel_management_strategy=None,
options=None, concurrent_calls=1):
org_id = self._config.get("org_id")
service_id = self._config.get("service_id")
group_name = self._config.get("group_name", "default_group")
def create_service_client(self, org_id: str, service_id: str, group_name=None,
payment_channel_management_strategy=None,
free_call_auth_token_bin=None,
free_call_token_expiry_block=None,
options=None,
concurrent_calls=1):

# Create and instance of the Config object, so we can create an instance of SDKCommand
sdk_config_object = Config(sdk_config=self._sdk_config)
sdk = SDKCommand(sdk_config_object, args=Arguments(org_id, service_id))

# Download the proto file and generate stubs if needed
force_update = self._sdk_config.get('force_update', False)
if force_update:
sdk.generate_client_library()
else:
path_to_pb_files = self.get_path_to_pb_files(org_id, service_id)
pb_2_file_name = find_file_by_keyword(path_to_pb_files, keyword="pb2.py")
pb_2_grpc_file_name = find_file_by_keyword(path_to_pb_files, keyword="pb2_grpc.py")
if not pb_2_file_name or not pb_2_grpc_file_name:
sdk.generate_client_library()

if payment_channel_management_strategy is None:
payment_channel_management_strategy = DefaultPaymentStrategy(concurrent_calls)

if options is None:
options = dict()

options['free_call_auth_token-bin'] = bytes.fromhex(self._config.get("free_call_auth_token-bin", ""))
options['free-call-token-expiry-block'] = self._config.get("free-call-token-expiry-block", 0)
options['email'] = self._config.get("email", "")
options['concurrency'] = self._config.get("concurrency", True)
options['free_call_auth_token-bin'] = bytes.fromhex(free_call_auth_token_bin) if\
free_call_token_expiry_block else ""
options['free-call-token-expiry-block'] = free_call_token_expiry_block if\
free_call_token_expiry_block else 0
options['email'] = self._sdk_config.get("email", "")
options['concurrency'] = self._sdk_config.get("concurrency", True)

if self._metadata_provider is None:
self._metadata_provider = IPFSMetadataProvider(self.ipfs_client, self.registry_contract)
Expand All @@ -144,8 +162,8 @@ def create_service_client(self, payment_channel_management_strategy=None,

pb2_module = self.get_module_by_keyword(org_id, service_id, keyword="pb2.py")

service_client = ServiceClient(org_id, service_id, service_metadata, group, service_stub, strategy, options,
self.mpe_contract, self.account, self.web3, pb2_module)
service_client = ServiceClient(org_id, service_id, service_metadata, group, service_stub, strategy,
options, self.mpe_contract, self.account, self.web3, pb2_module)
return service_client

def get_service_stub(self, org_id: str, service_id: str) -> ServiceStub:
Expand Down Expand Up @@ -204,3 +222,25 @@ def _get_service_group_details(self, service_metadata, group_name):
return self._get_first_group(service_metadata)

return self._get_group_by_group_name(service_metadata, group_name)

def get_organization_list(self) -> list:
global_config = Config(sdk_config=self._sdk_config)
blockchain_command = BlockchainCommand(config=global_config, args=Arguments())
org_list = blockchain_command.call_contract_command(
"Registry", "listOrganizations", [])
organization_list = []
for idx, org_id in enumerate(org_list):
organization_list.append(bytes32_to_str(org_id))
return organization_list

def get_services_list(self, org_id: str) -> list:
global_config = Config(sdk_config=self._sdk_config)
blockchain_command = BlockchainCommand(config=global_config, args=Arguments())
(found, org_service_list) = blockchain_command.call_contract_command("Registry",
"listServicesForOrganization",
[type_converter("bytes32")(org_id)])
if not found:
raise Exception(f"Organization with id={org_id} doesn't exist!")
org_service_list = list(map(bytes32_to_str, org_service_list))
return org_service_list

Loading
Loading