-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reorganized sdk and cli to be two different packages in the same repository Moved sdk to cli repo Rewrote SDK to match JS SDK implementation CircleCI fix
- Loading branch information
1 parent
f749b34
commit 92335ac
Showing
113 changed files
with
1,026 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,19 @@ | ||
venv/ | ||
.idea/ | ||
snet_cli.egg-info/ | ||
blockchain/node_modules/ | ||
__pycache__/ | ||
snet_cli/resources/contracts/abi | ||
snet_cli/resources/contracts/networks | ||
snet_cli/resources/proto/*.py | ||
build/ | ||
dist/ | ||
client_libraries/ | ||
|
||
snet_cli/venv/ | ||
.idea/ | ||
snet_cli/snet_cli.egg-info/ | ||
snet_cli/snet_cli/resources/contracts/abi | ||
snet_cli/snet_cli/resources/contracts/networks | ||
snet_cli/snet_cli/resources/proto/*.py | ||
snet_cli/build/ | ||
snet_cli/dist/ | ||
|
||
snet_sdk/venv/ | ||
snet_sdk/snet_sdk.egg-info/ | ||
__pycache__ | ||
snet_sdk/snet_sdk/resources/contracts/abi | ||
snet_sdk/snet_sdk/resources/contracts/networks | ||
snet_sdk/build/ | ||
snet_sdk/dist/ |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
for f in snet_cli/test/functional_tests/*.sh | ||
do | ||
bash -ex snet_cli/test/utils/reset_environment.sh --i-no-what-i-am-doing | ||
bash -ex -c "cd snet_cli/test/functional_tests; bash -ex `basename $f`" | ||
done |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2018 SingularityNET | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
include snet_sdk/resources/proto/* | ||
include snet_sdk/resources/contracts/abi/* | ||
include snet_sdk/resources/contracts/networks/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
# snet-sdk-python | ||
|
||
SingularityNET SDK for Python | ||
|
||
## Getting Started | ||
|
||
These instructions are for the development and use of the SingularityNET SDK for Python. | ||
|
||
### Usage | ||
|
||
The SingularityNET SDK allows you to import compiled client libraries for your service or services or choice and make calls to those services programmatically from your application by setting up state channels with the providers of those services and making gRPC calls to the SingularityNET daemons for those services by selecting a channel with sufficient funding and supplying the appropriate metadata for authentication. | ||
|
||
Once you have installed the snet-sdk in your current environment and it's in your PYTHONPATH, you should import it and create an instance of the base sdk class: | ||
|
||
```python | ||
from snet_sdk import Snet | ||
import config | ||
snet = Snet(private_key=config.private_key) | ||
``` | ||
|
||
Now, the instance of the sdk can be used to instantiate clients for SingularityNET services. To interact with those services, the sdk needs to be supplied the compiled client libraries and a reference to their path on your file system. | ||
|
||
To generate the client libraries, you need the SingularityNET Command Line Interface, or CLI, which you can download from PyPi, see [https://github.com/singnet/snet-cli#installing-with-pip](https://github.com/singnet/snet-cli#installing-with-pip) | ||
|
||
Once you have the CLI installed, run the following command: | ||
```bash | ||
snet sdk generate-client-library python <org_id> <service_id> | ||
``` | ||
|
||
Optionally, you can specify an output path; otherwise it's going to be `./client_libraries/python/<org_id>/<service_id>` | ||
|
||
Now, by default the sdk is going to look for those client libraries in the `./grpc/<org_id>/<service_id>` in the directory of the main process of the module it's being imported by. | ||
|
||
Once you have the generated client libraries in place, you can create an instance of a SingularityNET service client: | ||
```python | ||
client = snet.client("<org_id>", "<service_id>") | ||
``` | ||
|
||
The client exposes the following properties and methods: | ||
- All of the modules from the generated client library as `client.grpc.<module_name`. These are temporarily added to your PYTHONPATH and imported at runtime | ||
- Functions to open, fund and extend state channels | ||
- Functions to retrieve the list of state channels between you and the service provider from the blockchain | ||
- Functions to get the updated state for a specific channel from the service provider, signed by yourself | ||
- Functions to generate and sign the required metadata to make gRPC calls to the service daemon | ||
|
||
This is an example of how to make a call to a SingularityNET service in the `snet` organization with the `example-service` service_id using the base SDK and client instances created as shown before: | ||
```python | ||
stub = client.grpc.example_service_pb2_grpc.CalculatorStub(client.grpc_channel) | ||
request = calculator.grpc.example_service_pb2.Numbers(a=10, b=12) | ||
result = stub.add(request) | ||
print(result) | ||
``` | ||
If you have no open state channels with the service provider, you can create one by calling the following function: | ||
```python | ||
client.open_channel() | ||
``` | ||
By default, this will create a channel with the shortest possible expiration date and the necessary amount to make 100 service calls. | ||
Once an open channel is created and funded, the sdk will automatically find and use a funded, non-expired channel. | ||
|
||
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) | ||
- [gRPC Python’s documentation](https://grpc.io/grpc/python/) | ||
|
||
--- | ||
|
||
## Development | ||
|
||
### Installing | ||
|
||
#### Prerequisites | ||
|
||
* [Python 3.6.5](https://www.python.org/downloads/release/python-365/) | ||
* [Node 8+ w/npm](https://nodejs.org/en/download/) | ||
|
||
--- | ||
|
||
* Clone the git repository | ||
```bash | ||
$ git clone [email protected]:singnet/snet-sdk-python.git | ||
$ cd snet-sdk-python | ||
``` | ||
|
||
* Install development/test blockchain dependencies | ||
```bash | ||
$ ./scripts/blockchain install | ||
``` | ||
|
||
* Install the package in development/editable mode | ||
```bash | ||
$ pip install -e . | ||
``` | ||
|
||
### Versioning | ||
|
||
We use [SemVer](http://semver.org/) for versioning. For the versions available, see the | ||
[tags on this repository](https://github.com/singnet/snet-sdk-python/tags). | ||
|
||
## License | ||
|
||
This project is licensed under the MIT License - see the | ||
[LICENSE](https://github.com/singnet/snet-sdk-python/blob/master/LICENSE) file for details. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"dependencies": { | ||
"singularitynet-platform-contracts": "0.2.6", | ||
"singularitynet-token-contracts": "2.0.1" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#!/usr/bin/env python3 | ||
|
||
import os | ||
import pathlib | ||
import shutil | ||
import subprocess | ||
import sys | ||
|
||
|
||
def main(): | ||
assert len(sys.argv) > 1, "please select a target from 'install', 'uninstall'" | ||
target = sys.argv[1] | ||
blockchain_dir = pathlib.Path(__file__).absolute().parent.parent.parent.joinpath("blockchain") | ||
node_modules_dir = blockchain_dir.joinpath("node_modules") | ||
platform_json_src_dir = node_modules_dir.joinpath("singularitynet-platform-contracts") | ||
token_json_src_dir = node_modules_dir.joinpath("singularitynet-token-contracts") | ||
token_contract_name = "SingularityNetToken" | ||
contract_json_dest_dir = pathlib.Path(__file__).absolute().parent.parent.joinpath("snet_sdk", "resources", "contracts") | ||
abi_contract_names = ["Registry", "MultiPartyEscrow"] | ||
networks_contract_names = ["Registry", "MultiPartyEscrow"] | ||
|
||
npm_location = shutil.which('npm') | ||
if not npm_location: | ||
raise Exception("This script requires 'npm' to be installed and in your PATH") | ||
|
||
if target == "install": | ||
shutil.rmtree(contract_json_dest_dir) | ||
|
||
subprocess.call([npm_location, "install"], cwd=blockchain_dir) | ||
|
||
os.makedirs(contract_json_dest_dir.joinpath("abi"), exist_ok=True) | ||
os.makedirs(contract_json_dest_dir.joinpath("networks"), exist_ok=True) | ||
|
||
for contract_name in abi_contract_names: | ||
shutil.copy(platform_json_src_dir.joinpath("abi", "{}.json".format(contract_name)), | ||
contract_json_dest_dir.joinpath("abi", "{}.json".format(contract_name))) | ||
for contract_name in networks_contract_names: | ||
shutil.copy(platform_json_src_dir.joinpath("networks", "{}.json".format(contract_name)), | ||
contract_json_dest_dir.joinpath("networks", "{}.json".format(contract_name))) | ||
|
||
shutil.copy(token_json_src_dir.joinpath("abi", "{}.json".format(token_contract_name)), | ||
contract_json_dest_dir.joinpath("abi", "{}.json".format(token_contract_name))) | ||
shutil.copy(token_json_src_dir.joinpath("networks", "{}.json".format(token_contract_name)), | ||
contract_json_dest_dir.joinpath("networks", "{}.json".format(token_contract_name))) | ||
elif target == "uninstall": | ||
try: | ||
shutil.rmtree(node_modules_dir) | ||
shutil.rmtree(contract_json_dest_dir.joinpath("abi")) | ||
shutil.rmtree(contract_json_dest_dir.joinpath("networks")) | ||
except FileNotFoundError: | ||
pass | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#!/usr/bin/env bash | ||
|
||
python3.6 setup.py sdist bdist_wheel |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
from setuptools import setup, find_packages | ||
import re | ||
|
||
with open('snet_sdk/__init__.py', 'rt', encoding='utf8') as f: | ||
version = re.search(r'__version__ = "(.*?)"', f.read()).group(1) | ||
|
||
setup( | ||
name='snet_sdk', | ||
version=version, | ||
packages=find_packages(), | ||
url='https://github.com/singnet/snet-sdk-python', | ||
license='MIT', | ||
author='SingularityNET Foundation', | ||
author_email='[email protected]', | ||
description='SingularityNET Python SDK', | ||
install_requires=[ | ||
'grpcio-tools==1.17.1', | ||
'ecdsa==0.13', | ||
'web3==4.2.1', | ||
'ipfsapi==0.4.2.post1', | ||
'rfc3986==1.1.0' | ||
] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import operator | ||
from functools import reduce | ||
import sys | ||
import os | ||
import importlib | ||
import json | ||
import base64 | ||
from pathlib import PurePath, Path | ||
from urllib.parse import urljoin | ||
|
||
import hashlib | ||
import collections | ||
import web3 | ||
from web3.gas_strategies.rpc import rpc_gas_price_strategy | ||
from eth_account.messages import defunct_hash_message | ||
from rfc3986 import urlparse | ||
import ipfsapi | ||
from web3.utils.datastructures import AttributeDict, MutableAttributeDict | ||
|
||
import snet_sdk.generic_client_interceptor as generic_client_interceptor | ||
|
||
from snet_sdk.utils import get_contract_object | ||
from snet_sdk.service_client import ServiceClient | ||
from snet_sdk.account import Account | ||
from snet_sdk.mpe_contract import MPEContract | ||
from snet_sdk.payment_channel_management_strategies.default import PaymentChannelManagementStrategy | ||
|
||
__version__ = "0.0.1" | ||
|
||
|
||
class SnetSDK: | ||
"""Base Snet SDK""" | ||
def __init__( | ||
self, | ||
config | ||
): | ||
self._config = config | ||
|
||
# Instantiate Ethereum client | ||
eth_rpc_endpoint = self._config.get("eth_rpc_endpoint", "https://mainnet.infura.io") | ||
provider = web3.HTTPProvider(eth_rpc_endpoint) | ||
self.web3 = web3.Web3(provider) | ||
self.web3.eth.setGasPriceStrategy(rpc_gas_price_strategy) | ||
|
||
self.mpe_contract = MPEContract(self.web3) | ||
|
||
# Instantiate IPFS client | ||
ipfs_rpc_endpoint = self._config.get("ipfs_rpc_endpoint", "https://ipfs.singularitynet.io:80") | ||
ipfs_rpc_endpoint = urlparse(ipfs_rpc_endpoint) | ||
ipfs_scheme = ipfs_rpc_endpoint.scheme if ipfs_rpc_endpoint.scheme else "http" | ||
ipfs_port = ipfs_rpc_endpoint.port if ipfs_rpc_endpoint.port else 5001 | ||
self.ipfs_client = ipfsapi.connect(urljoin(ipfs_scheme, ipfs_rpc_endpoint.hostname), ipfs_port) | ||
|
||
self.registry_contract = get_contract_object(self.web3, "Registry.json") | ||
self.account = Account(self.web3, config, self.mpe_contract) | ||
|
||
self.block_offset = self._config.get("block_offset", 604800) | ||
|
||
def create_service_client(self, org_id, service_id, service_stub, group_name="default_group", payment_channel_management_strategy=PaymentChannelManagementStrategy, options={}): | ||
service_metadata = self.service_metadata(org_id, service_id) | ||
try: | ||
group = next(filter(lambda group: group["group_name"] == group_name, service_metadata.groups)) | ||
except StopIteration: | ||
raise ValueError("Group[name: {}] not found for orgId: {} and serviceId: {}".format(group_name, org_id, service_id)) | ||
service_client = ServiceClient(self, service_metadata, group, service_stub, payment_channel_management_strategy(self), options) | ||
return service_client | ||
|
||
def service_metadata(self, org_id, service_id): | ||
(found, registration_id, metadata_uri, tags) = self.registry_contract.functions.getServiceRegistrationById(bytes(org_id, "utf-8"), bytes(service_id, "utf-8")).call() | ||
metadata = AttributeDict(json.loads(self.ipfs_client.cat(metadata_uri.rstrip(b"\0").decode('ascii')[7:]))) | ||
return metadata |
Oops, something went wrong.