diff --git a/.circleci/config.yml b/.circleci/config.yml index fade56e7..023b98d1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,13 +21,13 @@ jobs: - run: name: Install snet-cli command: | - ./scripts/blockchain install - sudo pip3 install -e . + ./packages/snet_cli/scripts/blockchain install + sudo pip3 install -e ./packages/snet_cli - run: name: Install platform-contracts from master command: | # Install platform-contracts (from master) - # we will deploy contracts from test/utils/reset_enviroment.sh + # we will deploy contracts from packages/snet_cli/test/utils/reset_enviroment.sh cd .. git clone https://github.com/singnet/platform-contracts cd platform-contracts @@ -42,7 +42,7 @@ jobs: - run: name: Functional tests command: | - bash -ex test/utils/run_all_functional.sh + bash -ex packages/snet_cli/test/utils/run_all_functional.sh - run: name: Trigger platform-pipeline build command: | diff --git a/.gitignore b/.gitignore index 006bc0d4..101f0af2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,5 @@ 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 -snet_cli/resources/node_modules -build/ -dist/ -client_libraries/ +__pycache__ +blockchain/node_modules +snet.egg-info/ diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index c5e35bef..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,5 +0,0 @@ -include snet_cli/resources/proto/* -include snet_cli/resources/contracts/abi/* -include snet_cli/resources/contracts/networks/* -include snet_cli/resources/package.json -include snet_cli/_vendor/* diff --git a/README.md b/README.md index 4b025ec5..77976127 100644 --- a/README.md +++ b/README.md @@ -1,124 +1,18 @@ -# snet-cli +# snet-python-monorepo [![CircleCI](https://circleci.com/gh/singnet/snet-cli.svg?style=svg)](https://circleci.com/gh/singnet/snet-cli) -SingularityNET CLI +SingularityNET Python Monorepo -## Getting Started +## Packages +This repository is a monorepo that includes several packages that we publish to PyPI from a shared codebase, specifically: -These instructions are for the development and use of the SingularityNET CLI. -For further details, please check our full [Documentation](http://snet-cli-docs.singularitynet.io/). +|Package |Description | +|----------------------------------------------|---------------------------------------------------------------------| +|[snet-cli](https://pypi.org/project/snet-cli/)|Command line interface to interact with the SingularityNET platform | +|[snet-sdk](https://pypi.org/project/snet-sdk/)|Integrate SingularityNET services seamlessly into Python applications| -### Installing with pip - -#### Install prerequisites - -You should have python with version >= 3.6.5 and pip installed. - -Additionally you should install the following packages: - -* libudev -* libusb 1.0 - -If you use Ubuntu (or any Linux distribution with APT package support) you should do the following: - -```bash -sudo apt-get install libudev-dev libusb-1.0-0-dev -``` - -#### Install snet-cli using pip - -```bash -$ pip3 install snet-cli -``` - - -#### Enabling commands autocomplete -If you want to enable auto completion of commands, you should install the following package -* python-argcomplete - -On ubuntu (or any Linux distribution with APT package support), you should do the following - -```bash -sudo apt install python-argcomplete -``` -After the package is installed, activate autocomplete - -##### for all python commands (which includes snet commands as well) - -```bash -sudo activate-global-python-argcomplete -``` -Note: Changes will not take effect until shell is restarted. - -##### only for snet commands, then you should do the following -```bash -echo 'eval "$(register-python-argcomplete snet)"' >> ~/.bashrc -``` -then -```bash -source ~/.bashrc -``` - -## Commands - -Complete documentation is available [here](http://snet-cli-docs.singularitynet.io/) - - -## 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 git@github.com:singnet/snet-cli.git -$ cd snet-cli -``` - -* Install development/test blockchain dependencies -```bash -$ ./scripts/blockchain install -``` - -* Install the package in development/editable mode -```bash -$ pip3 install -e . -``` - -#### Building Docs - -* Install sphinx, sphinx-argparse and the rtd theme -```bash -$ pip install sphinx -$ pip install sphinx-argparse -$ pip install sphinx-rtd-theme -``` - -* Run the build-docs.sh in the docs directory -```bash -$ cd docs -$ sh build-docs.sh -``` - -The documentation is generated under the docs/build/html folder - -### Release - -This project is published to [PyPI](https://pypi.org/project/snet-cli/). - -### Versioning - -We use [SemVer](http://semver.org/) for versioning. For the versions available, see the -[tags on this repository](https://github.com/singnet/snet-cli/tags). - ## License This project is licensed under the MIT License - see the -[LICENSE](https://github.com/singnet/alpha-daemon/blob/master/LICENSE) file for details. +[LICENSE](https://github.com/singnet/snet-cli/blob/master/LICENSE) file for details. diff --git a/packages/sdk/.gitignore b/packages/sdk/.gitignore new file mode 100644 index 00000000..674694bb --- /dev/null +++ b/packages/sdk/.gitignore @@ -0,0 +1,4 @@ +venv/ +snet.sdk.egg-info/ +build/ +dist/ diff --git a/packages/sdk/LICENSE b/packages/sdk/LICENSE new file mode 100644 index 00000000..c674cd51 --- /dev/null +++ b/packages/sdk/LICENSE @@ -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. diff --git a/packages/sdk/README.md b/packages/sdk/README.md new file mode 100644 index 00000000..66cf47da --- /dev/null +++ b/packages/sdk/README.md @@ -0,0 +1,113 @@ +# snet-sdk-python + +SingularityNET SDK for Python + +## Getting Started + +These instructions are for the development and use of the SingularityNET SDK for Python. + +### Core concepts + +The SingularityNET SDK allows you to make calls to SingularityNET services programmatically from your application. +To communicate between clients and services, SingularityNET uses [gRPC](https://grpc.io/). +To handle payment of services, SingularityNET uses [Ethereum state channels](https://dev.singularitynet.io/docs/concepts/multi-party-escrow/). +The SingularityNET SDK abstracts and manages state channels with service providers on behalf of the user and handles authentication with the SingularityNET services. + +### Usage + +To call a SingularityNET service, the user must be able to deposit funds (AGI tokens) to the [Multi-Party Escrow](https://dev.singularitynet.io/docs/concepts/multi-party-escrow/) Smart Contract. +To deposit these tokens or do any other transaction on the Ethereum blockchain, the user must possess an Ethereum identity with available Ether. + + +To interact with SingularityNET services, you must compile the appropriate client libraries for that service. +To generate the client libraries to use in your application, 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/snet_cli#installing-with-pip) + +Once you have the CLI installed, run the following command: +```bash +snet sdk generate-client-library python +``` + +Optionally, you can specify an output path; otherwise it's going to be `./client_libraries/python///`. +You should move or copy these generated files to the root of your project. + +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 SnetSDK +from config import config +snet = SnetSDK(config) +``` + +The `config` parameter must be a Python dictionary. +See [config.py.sample](https://github.com/singnet/snet-code-examples/blob/master/python/client/config.py.sample) for a sample configuration file. + +Now, the instance of the sdk can be used to create service client instances. To create a service client instance, it needs to be supplied with the client libraries that you compiled before. +Specifically, it needs the `Stub` object of the service you want to use from the compiled `_pb2_grpc.py` file of the client library. +Continuing from the previous code this is an example using `example-service` from the `snet` organization: + +```python +import example_service_pb2_grpc + +org_id = "snet" +service_id = "example-service" + +service_client = sdk.create_service_client(org_id, service_id, example_service_pb2_grpc.CalculatorStub) +``` + +The generated `service_client` instance can be used to call the methods exposed by the service. +To call these methods, a request object must be provided. Specifically, you should pick the appropriate request message type that is referenced in the stub object. +Continuing from the previous code this is an example using `example-service` from the `snet` organization: + +```python +import example_service_pb2 + +request = example_service_pb2.Numbers(a=20, b=3) + +result = service_client.service.mul(request) +print("Performing 20 * 3: {}".format(result)) # Performing 20 * 3: value: 60.0 +``` + +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) + +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 git@github.com:singnet/snet-cli.git +$ cd snet-cli/snet_sdk +``` + +* 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-cli/tags). + +## License + +This project is licensed under the MIT License - see the +[LICENSE](https://github.com/singnet/snet-cli/blob/master/snet_sdk/LICENSE) file for details. diff --git a/packages/sdk/requirements.txt b/packages/sdk/requirements.txt new file mode 100644 index 00000000..63cab9eb --- /dev/null +++ b/packages/sdk/requirements.txt @@ -0,0 +1,3 @@ +--index-url https://pypi.python.org/simple +../snet_cli/ +-e . diff --git a/scripts/package-pip b/packages/sdk/scripts/package-pip similarity index 100% rename from scripts/package-pip rename to packages/sdk/scripts/package-pip diff --git a/packages/sdk/setup.py b/packages/sdk/setup.py new file mode 100644 index 00000000..cd28bfe0 --- /dev/null +++ b/packages/sdk/setup.py @@ -0,0 +1,43 @@ +import pkg_resources +from setuptools import setup, find_namespace_packages + + +PACKAGE_NAME = 'snet.sdk' + + +def is_package_installed(package_name): + installed_modules = [p.project_name for p in pkg_resources.working_set] + return package_name in installed_modules + + +dependencies = [] + + +if is_package_installed('snet-cli'): + # The default setup.py in the snet_cli package for local development installs the whole snet_cli package, + # not the standalone snet.snet_cli namespace package; if a strict dependency on snet.snet_cli was enforced, + # this setup.py would fetch it from PyPI. So, if snet_cli is installed and in your Python path, the + # dependency on snet.snet_cli will be skipped. + # If snet_cli is not available, snet.snet_cli will be fetched from PyPI. + print("Package 'snet_cli' is installed and in your PYTHONPATH: skipping snet.snet_cli dependency") +else: + dependencies.append('snet.snet_cli') + + +version_dict = {} +with open("./snet/sdk/version.py") as fp: + exec(fp.read(), version_dict) + +setup( + name=PACKAGE_NAME, + version=version_dict['__version__'], + packages=find_namespace_packages(include=['snet.*']), + namespace_packages=['snet'], + url='https://github.com/singnet/snet-cli/tree/master/snet_sdk', + license='MIT', + author='SingularityNET Foundation', + author_email='info@singularitynet.io', + description='SingularityNET Python SDK', + python_requires='>=3.6', + install_requires=dependencies +) diff --git a/packages/sdk/snet/sdk/__init__.py b/packages/sdk/snet/sdk/__init__.py new file mode 100644 index 00000000..5228005a --- /dev/null +++ b/packages/sdk/snet/sdk/__init__.py @@ -0,0 +1,64 @@ +import json +import base64 +from urllib.parse import urljoin + +import web3 +from web3.gas_strategies.time_based import medium_gas_price_strategy +from rfc3986 import urlparse +import ipfsapi +from web3.datastructures import AttributeDict + +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 + +from snet.snet_cli.utils import get_contract_object +from snet.snet_cli.utils_ipfs import bytesuri_to_hash, get_from_ipfs_and_checkhash +from snet.snet_cli.mpe_service_metadata import mpe_service_metadata_from_json + + +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(medium_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) + + + def create_service_client(self, org_id, service_id, service_stub, group_name=None, payment_channel_management_strategy=PaymentChannelManagementStrategy, options=None): + if options is None: + options = dict() + + service_metadata = self.get_service_metadata(org_id, service_id) + group = service_metadata.get_group(group_name) + strategy = payment_channel_management_strategy(self) + service_client = ServiceClient(self, service_metadata, group, service_stub, strategy, options) + return service_client + + + def get_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_hash = bytesuri_to_hash(metadata_uri) + metadata_json = get_from_ipfs_and_checkhash(self.ipfs_client, metadata_hash) + metadata = mpe_service_metadata_from_json(metadata_json) + return metadata diff --git a/packages/sdk/snet/sdk/account.py b/packages/sdk/snet/sdk/account.py new file mode 100644 index 00000000..b8971ea0 --- /dev/null +++ b/packages/sdk/snet/sdk/account.py @@ -0,0 +1,88 @@ +import json + +from snet.snet_cli.utils import normalize_private_key, get_address_from_private, get_contract_object + +DEFAULT_GAS = 210000 + + +class TransactionError(Exception): + """Raised when an Ethereum transaction receipt has a status of 0. Can provide a custom message. Optionally includes receipt""" + def __init__(self, message, receipt=None): + super().__init__(message) + self.message = message + self.receipt = receipt + def __str__(self): + return self.message + + +class Account: + def __init__(self, w3, config, mpe_contract): + self.config = config + self.web3 = w3 + self.mpe_contract = mpe_contract + self.token_contract = get_contract_object(self.web3, "SingularityNetToken.json") + private_key = config.get("private_key", None) + signer_private_key = config.get("signer_private_key", None) + if private_key is not None: + self.private_key = normalize_private_key(config["private_key"]) + if signer_private_key is not None: + self.signer_private_key = normalize_private_key(config["signer_private_key"]) + else: + self.signer_private_key = self.private_key + self.address = get_address_from_private(self.private_key) + self.signer_address = get_address_from_private(self.signer_private_key) + self.nonce = 0 + + + def _get_nonce(self): + nonce = self.web3.eth.getTransactionCount(self.address) + if self.nonce >= nonce: + nonce = self.nonce + 1 + self.nonce = nonce + return nonce + + + def _get_gas_price(self): + return self.web3.eth.generateGasPrice() + + + def _send_signed_transaction(self, contract_fn, *args): + transaction = contract_fn(*args).buildTransaction({ + "chainId": int(self.web3.version.network), + "gas": DEFAULT_GAS, + "gasPrice": self._get_gas_price(), + "nonce": self._get_nonce() + }) + signed_txn = self.web3.eth.account.signTransaction(transaction, private_key=self.private_key) + return self.web3.toHex(self.web3.eth.sendRawTransaction(signed_txn.rawTransaction)) + + + def send_transaction(self, contract_fn, *args): + txn_hash = self._send_signed_transaction(contract_fn, *args) + return self.web3.eth.waitForTransactionReceipt(txn_hash) + + + def _parse_receipt(self, receipt, event, encoder=json.JSONEncoder): + if receipt.status == 0: + raise TransactionError("Transaction failed", receipt) + else: + return json.dumps(dict(event().processReceipt(receipt)[0]["args"]), cls=encoder) + + + def escrow_balance(self): + return self.mpe_contract.balance(self.address) + + + def deposit_to_escrow_account(self, amount_in_cogs): + already_approved = self.allowance() + if amount_in_cogs > already_approved: + self.approve_transfer(amount_in_cogs) + return self.mpe_contract.deposit(self, amount_in_cogs) + + + def approve_transfer(self, amount_in_cogs): + return self.send_transaction(self.token_contract.functions.approve(self.mpe_contract.contract.address, amount_in_cogs)) + + + def allowance(self): + return self.token_contract.functions.allowance(self.address, self.mpe_contract.contract.address).call() diff --git a/packages/sdk/snet/sdk/generic_client_interceptor.py b/packages/sdk/snet/sdk/generic_client_interceptor.py new file mode 100644 index 00000000..d2bb7e56 --- /dev/null +++ b/packages/sdk/snet/sdk/generic_client_interceptor.py @@ -0,0 +1,55 @@ +# Copyright 2017 gRPC authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Base class for interceptors that operate on all RPC types.""" + +import grpc + + +class _GenericClientInterceptor( + grpc.UnaryUnaryClientInterceptor, grpc.UnaryStreamClientInterceptor, + grpc.StreamUnaryClientInterceptor, grpc.StreamStreamClientInterceptor): + + def __init__(self, interceptor_function): + self._fn = interceptor_function + + def intercept_unary_unary(self, continuation, client_call_details, request): + new_details, new_request_iterator, postprocess = self._fn( + client_call_details, iter((request,)), False, False) + response = continuation(new_details, next(new_request_iterator)) + return postprocess(response) if postprocess else response + + def intercept_unary_stream(self, continuation, client_call_details, + request): + new_details, new_request_iterator, postprocess = self._fn( + client_call_details, iter((request,)), False, True) + response_it = continuation(new_details, next(new_request_iterator)) + return postprocess(response_it) if postprocess else response_it + + def intercept_stream_unary(self, continuation, client_call_details, + request_iterator): + new_details, new_request_iterator, postprocess = self._fn( + client_call_details, request_iterator, True, False) + response = continuation(new_details, new_request_iterator) + return postprocess(response) if postprocess else response + + def intercept_stream_stream(self, continuation, client_call_details, + request_iterator): + new_details, new_request_iterator, postprocess = self._fn( + client_call_details, request_iterator, True, True) + response_it = continuation(new_details, new_request_iterator) + return postprocess(response_it) if postprocess else response_it + + +def create(intercept_call): + return _GenericClientInterceptor(intercept_call) diff --git a/packages/sdk/snet/sdk/mpe_contract.py b/packages/sdk/snet/sdk/mpe_contract.py new file mode 100644 index 00000000..8ef3dcaf --- /dev/null +++ b/packages/sdk/snet/sdk/mpe_contract.py @@ -0,0 +1,74 @@ +import base64 + +import web3 +from snet.sdk.payment_channel import PaymentChannel + +from snet.snet_cli.utils import get_contract_object, get_contract_deployment_block + + +BLOCKS_PER_BATCH = 20000 + + +class MPEContract: + def __init__(self, w3): + self.web3 = w3 + self.contract = get_contract_object(self.web3, "MultiPartyEscrow.json") + self.event_topics = [self.web3.sha3(text="ChannelOpen(uint256,uint256,address,address,address,bytes32,uint256,uint256)").hex()] + self.deployment_block = get_contract_deployment_block(self.web3, "MultiPartyEscrow.json") + + + def get_past_open_channels(self, account, service, starting_block_number=0, to_block_number=None): + if to_block_number is None: + to_block_number = self.web3.eth.getBlock("latest")["number"] + + if starting_block_number == 0: + starting_block_number = self.deployment_block + + logs = [] + from_block = starting_block_number + while from_block <= to_block_number: + to_block = min(from_block + BLOCKS_PER_BATCH, to_block_number) + logs = logs + self.web3.eth.getLogs({"fromBlock" : from_block, "toBlock": to_block, "address": self.contract.address, "topics": self.event_topics}) + from_block = to_block + 1 + + event_abi = self.contract._find_matching_event_abi(event_name="ChannelOpen") + channels_opened = list(filter(lambda channel: channel.sender == account.address and channel.signer == account.signer_address and channel.recipient == service.group["payment_address"], [web3.utils.events.get_event_data(event_abi, l)["args"] for l in logs])) + return list(map(lambda channel: PaymentChannel(channel["channelId"], self.web3, account, service, self), channels_opened)) + + + def balance(self, address): + return self.contract.functions.balances(address).call() + + + def deposit(self, account, amount_in_cogs): + return account.send_transaction(self.contract.functions.deposit, amount_in_cogs) + + + def open_channel(self, account, service, amount, expiration): + return account.send_transaction(self.contract.functions.openChannel, account.signer_address, service.group["payment_address"], base64.b64decode(str(service.group["group_id"])), amount, expiration) + + + def deposit_and_open_channel(self, account, service, amount, expiration): + already_approved_amount = account.allowance() + if amount > already_approved_amount: + account.approve_transfer(amount) + return account.send_transaction(self.contract.functions.depositAndOpenChannel, account.signer_address, service.group["payment_address"], base64.b64decode(str(service.group["group_id"])), amount, expiration) + + + def channel_add_funds(self, account, channel_id, amount): + self._fund_escrow_account(account, amount) + return account.send_transaction(self.contract.functions.channelAddFunds, channel_id, amount) + + + def channel_extend(self, account, channel_id, expiration): + return account.send_transaction(self.contract.functions.channelExtend, channel_id, expiration) + + + def channel_extend_and_add_funds(self, account, channel_id, expiration, amount): + self._fund_escrow_account(account, amount) + return account.send_transaction(self.contract.functions.channelExtendAndAddFunds, channel_id, expiration, amount) + + def _fund_escrow_account(self, account, amount): + current_escrow_balance = self.balance(account.address) + if amount > current_escrow_balance: + account.deposit_to_escrow_account(amount - current_escrow_balance) diff --git a/packages/sdk/snet/sdk/payment_channel.py b/packages/sdk/snet/sdk/payment_channel.py new file mode 100644 index 00000000..60538504 --- /dev/null +++ b/packages/sdk/snet/sdk/payment_channel.py @@ -0,0 +1,57 @@ +import web3 +import importlib +from eth_account.messages import defunct_hash_message + +from snet.snet_cli.utils import RESOURCES_PATH, add_to_path + +class PaymentChannel: + def __init__(self, channel_id, w3, account, service, mpe_contract): + self.channel_id = channel_id + self.web3 = w3 + self.account = account + self.mpe_contract = mpe_contract + self.payment_channel_state_service_client = service.payment_channel_state_service_client + self.state = { + "nonce": 0, + "last_signed_amount": 0 + } + + + def add_funds(self, amount): + return self.mpe_contract.channel_add_funds(self.account, self.channel_id, amount) + + + def extend_expiration(self, expiration): + return self.mpe_contract.channel_extend(self.account, self.channel_id, expiration) + + + def extend_and_add_funds(self, expiration, amount): + return self.mpe_contract.channel_extend_and_add_funds(self.account, self.channel_id, expiration, amount) + + + def sync_state(self): + channel_blockchain_data = self.mpe_contract.contract.functions.channels(self.channel_id).call() + (current_nonce, last_signed_amount) = self._get_current_channel_state() + nonce = channel_blockchain_data[0] + total_amount = channel_blockchain_data[5] + expiration = channel_blockchain_data[6] + available_amount = total_amount - last_signed_amount + self.state = { + "current_nonce": current_nonce, + "last_signed_amount": last_signed_amount, + "nonce": nonce, + "total_amount": total_amount, + "expiration": expiration, + "available_amount": available_amount + } + + + def _get_current_channel_state(self): + stub = self.payment_channel_state_service_client + message = web3.Web3.soliditySha3(["uint256"], [self.channel_id]) + signature = self.web3.eth.account.signHash(defunct_hash_message(message), self.account.signer_private_key).signature + with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): + state_service_pb2 = importlib.import_module("state_service_pb2") + request = state_service_pb2.ChannelStateRequest(channel_id=web3.Web3.toBytes(self.channel_id), signature=bytes(signature)) + response = stub.GetChannelState(request) + return int.from_bytes(response.current_nonce, byteorder="big"), int.from_bytes(response.current_signed_amount, byteorder="big") diff --git a/snet_cli/_vendor/__init__.py b/packages/sdk/snet/sdk/payment_channel_management_strategies/__init__.py similarity index 100% rename from snet_cli/_vendor/__init__.py rename to packages/sdk/snet/sdk/payment_channel_management_strategies/__init__.py diff --git a/packages/sdk/snet/sdk/payment_channel_management_strategies/default.py b/packages/sdk/snet/sdk/payment_channel_management_strategies/default.py new file mode 100644 index 00000000..3a8d1e45 --- /dev/null +++ b/packages/sdk/snet/sdk/payment_channel_management_strategies/default.py @@ -0,0 +1,35 @@ +class PaymentChannelManagementStrategy: + def __init__(self, sdk_context, block_offset=0, call_allowance=1): + self.sdk_context = sdk_context + self.block_offset = block_offset + self.call_allowance = call_allowance + + def select_channel(self, service_client): + account = self.sdk_context.account + service_client.load_open_channels() + service_client.update_channel_states() + payment_channels = service_client.payment_channels + service_call_price = service_client.metadata["pricing"]["price_in_cogs"] + mpe_balance = account.escrow_balance() + default_expiration = service_client.default_channel_expiration() + + payment_channel = payment_channels[0] + + if self._has_sufficient_funds(payment_channel, service_call_price) and not self._is_valid(payment_channel, default_expiration): + payment_channel.extend_expiration(default_expiration + self.block_offset) + elif not self._has_sufficient_funds(payment_channel, service_call_price) and self._is_valid(payment_channel, default_expiration): + payment_channel.add_funds(service_call_price*self.call_allowance) + elif not self._has_sufficient_funds(payment_channel, service_call_price) and not self._is_valid(payment_channel, default_expiration): + payment_channel.extend_and_add_funds(default_expiration + self.block_offset, service_call_price*self.call_allowance) + + return payment_channel + + + @staticmethod + def _has_sufficient_funds(channel, amount): + return channel.state["available_amount"] >= amount + + + @staticmethod + def _is_valid(channel, expiry): + return channel.state["expiration"] >= expiry diff --git a/packages/sdk/snet/sdk/service_client.py b/packages/sdk/snet/sdk/service_client.py new file mode 100644 index 00000000..912d0d83 --- /dev/null +++ b/packages/sdk/snet/sdk/service_client.py @@ -0,0 +1,132 @@ +import collections +import importlib + +import grpc +import web3 +from rfc3986 import urlparse +from eth_account.messages import defunct_hash_message + +import snet.sdk.generic_client_interceptor as generic_client_interceptor +from snet.snet_cli.utils import RESOURCES_PATH, add_to_path + + +class _ClientCallDetails( + collections.namedtuple( + '_ClientCallDetails', + ('method', 'timeout', 'metadata', 'credentials')), + grpc.ClientCallDetails): + pass + + +class ServiceClient: + def __init__(self, sdk, metadata, group, service_stub, payment_channel_management_strategy, options): + self.sdk = sdk + self.options = options + self.group = group + self.metadata = metadata + self.payment_channel_management_strategy = payment_channel_management_strategy + self.expiry_threshold = self.metadata["payment_expiration_threshold"] + self._base_grpc_channel = self._get_grpc_channel() + self.grpc_channel = grpc.intercept_channel(self._base_grpc_channel, generic_client_interceptor.create(self._intercept_call)) + self.payment_channel_state_service_client = self._generate_payment_channel_state_service_client() + self.service = self._generate_grpc_stub(service_stub) + self.payment_channels = [] + self.last_read_block = 0 + + + def _generate_grpc_stub(self, service_stub): + grpc_channel = self._base_grpc_channel + disable_blockchain_operations = self.options.get("disable_blockchain_operations", False) + if disable_blockchain_operations is False: + grpc_channel = self.grpc_channel + stub_instance = service_stub(grpc_channel) + return stub_instance + + + def _get_grpc_channel(self): + endpoint = self.options.get("endpoint", None) + if endpoint is None: + endpoint = self.metadata.get_endpoints_for_group(self.group["group_name"])[0] + endpoint_object = urlparse(endpoint) + if endpoint_object.port is not None: + channel_endpoint = endpoint_object.hostname + ":" + str(endpoint_object.port) + else: + channel_endpoint = endpoint_object.hostname + + if endpoint_object.scheme == "http": + return grpc.insecure_channel(channel_endpoint) + elif endpoint_object.scheme == "https": + return grpc.secure_channel(channel_endpoint, grpc.ssl_channel_credentials()) + else: + raise ValueError('Unsupported scheme in service metadata ("{}")'.format(endpoint_object.scheme)) + + + def _get_service_call_metadata(self): + channel = self.payment_channel_management_strategy.select_channel(self) + amount = channel.state["last_signed_amount"] + int(self.metadata["pricing"]["price_in_cogs"]) + message = web3.Web3.soliditySha3( + ["address", "uint256", "uint256", "uint256"], + [self.sdk.mpe_contract.contract.address, channel.channel_id, channel.state["nonce"], amount] + ) + signature = bytes(self.sdk.web3.eth.account.signHash(defunct_hash_message(message), self.sdk.account.signer_private_key).signature) + metadata = [ + ("snet-payment-type", "escrow"), + ("snet-payment-channel-id", str(channel.channel_id)), + ("snet-payment-channel-nonce", str(channel.state["nonce"])), + ("snet-payment-channel-amount", str(amount)), + ("snet-payment-channel-signature-bin", signature) + ] + return metadata + + + def _intercept_call(self, client_call_details, request_iterator, request_streaming, + response_streaming): + metadata = [] + if client_call_details.metadata is not None: + metadata = list(client_call_details.metadata) + metadata.extend(self._get_service_call_metadata()) + client_call_details = _ClientCallDetails( + client_call_details.method, client_call_details.timeout, metadata, + client_call_details.credentials) + return client_call_details, request_iterator, None + + + def load_open_channels(self): + current_block_number = self.sdk.web3.eth.getBlock("latest").number + new_payment_channels = self.sdk.mpe_contract.get_past_open_channels(self.sdk.account, self, self.last_read_block) + self.payment_channels = self.payment_channels + new_payment_channels + self.last_read_block = current_block_number + return self.payment_channels + + + def update_channel_states(self): + for channel in self.payment_channels: + channel.sync_state() + return self.payment_channels + + + def default_channel_expiration(self): + current_block_number = self.sdk.web3.eth.getBlock("latest").number + return current_block_number + self.expiry_threshold + + + def _generate_payment_channel_state_service_client(self): + grpc_channel = self._base_grpc_channel + with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): + state_service_pb2_grpc = importlib.import_module("state_service_pb2_grpc") + return state_service_pb2_grpc.PaymentChannelStateServiceStub(grpc_channel) + + + def open_channel(self, amount, expiration): + receipt = self.sdk.mpe_contract.open_channel(self.sdk.account, self, amount, expiration) + return self._get_newly_opened_channel(receipt) + + + def deposit_and_open_channel(self, amount, expiration): + receipt = self.sdk.mpe_contract.deposit_and_open_channel(self.sdk.account, self, amount, expiration) + return self._get_newly_opened_channel(receipt) + + + def _get_newly_opened_channel(self, receipt): + open_channels = self.sdk.mpe_contract.get_past_open_channels(self.sdk.account, self, receipt["blockNumber"], receipt["blockNumber"]) + return open_channels[0] diff --git a/packages/sdk/snet/sdk/version.py b/packages/sdk/snet/sdk/version.py new file mode 100644 index 00000000..bbab0242 --- /dev/null +++ b/packages/sdk/snet/sdk/version.py @@ -0,0 +1 @@ +__version__ = "0.1.4" diff --git a/packages/snet_cli/.gitignore b/packages/snet_cli/.gitignore new file mode 100644 index 00000000..0bf999a7 --- /dev/null +++ b/packages/snet_cli/.gitignore @@ -0,0 +1,9 @@ +venv/ +snet_cli.egg-info/ +snet.snet_cli.egg-info/ +snet/snet_cli/resources/contracts/abi +snet/snet_cli/resources/contracts/networks +snet/snet_cli/resources/node_modules +snet/snet_cli/resources/proto/*.py +build/ +dist/ diff --git a/packages/snet_cli/LICENSE b/packages/snet_cli/LICENSE new file mode 100644 index 00000000..c674cd51 --- /dev/null +++ b/packages/snet_cli/LICENSE @@ -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. diff --git a/packages/snet_cli/MANIFEST.in b/packages/snet_cli/MANIFEST.in new file mode 100644 index 00000000..e9df6bc0 --- /dev/null +++ b/packages/snet_cli/MANIFEST.in @@ -0,0 +1,11 @@ +include snet/snet_cli/resources/proto/* +include snet/snet_cli/resources/contracts/abi/* +include snet/snet_cli/resources/contracts/networks/* +include snet/snet_cli/resources/package.json + +recursive-include snet_cli/_vendor * + +recursive-exclude * .git +recursive-exclude * node_modules +recursive-exclude * __pycache__ +recursive-exclude * *.py[co] diff --git a/packages/snet_cli/README.md b/packages/snet_cli/README.md new file mode 100644 index 00000000..d3401870 --- /dev/null +++ b/packages/snet_cli/README.md @@ -0,0 +1,124 @@ +# snet-cli + +[![CircleCI](https://circleci.com/gh/singnet/snet-cli.svg?style=svg)](https://circleci.com/gh/singnet/snet-cli) + +SingularityNET CLI + +## Getting Started + +These instructions are for the development and use of the SingularityNET CLI. +For further details, please check our full [Documentation](http://snet-cli-docs.singularitynet.io/). + +### Installing with pip + +#### Install prerequisites + +You should have python with version >= 3.6.5 and pip installed. + +Additionally you should install the following packages: + +* libudev +* libusb 1.0 + +If you use Ubuntu (or any Linux distribution with APT package support) you should do the following: + +```bash +sudo apt-get install libudev-dev libusb-1.0-0-dev +``` + +#### Install snet-cli using pip + +```bash +$ pip3 install snet-cli +``` + + +#### Enabling commands autocomplete +If you want to enable auto completion of commands, you should install the following package +* python-argcomplete + +On ubuntu (or any Linux distribution with APT package support), you should do the following + +```bash +sudo apt install python-argcomplete +``` +After the package is installed, activate autocomplete + +##### for all python commands (which includes snet commands as well) + +```bash +sudo activate-global-python-argcomplete +``` +Note: Changes will not take effect until shell is restarted. + +##### only for snet commands, then you should do the following +```bash +echo 'eval "$(register-python-argcomplete snet)"' >> ~/.bashrc +``` +then +```bash +source ~/.bashrc +``` + +## Commands + +Complete documentation is available [here](http://snet-cli-docs.singularitynet.io/) + + +## 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 git@github.com:singnet/snet-cli.git +$ cd snet-cli/snet_cli +``` + +* Install development/test blockchain dependencies +```bash +$ ./scripts/blockchain install +``` + +* Install the package in development/editable mode +```bash +$ pip3 install -e . +``` + +#### Building Docs + +* Install sphinx, sphinx-argparse and the rtd theme +```bash +$ pip install sphinx +$ pip install sphinx-argparse +$ pip install sphinx-rtd-theme +``` + +* Run the build-docs.sh in the docs directory +```bash +$ cd docs +$ sh build-docs.sh +``` + +The documentation is generated under the docs/build/html folder + +### Release + +This project is published to [PyPI](https://pypi.org/project/snet-cli/). + +### Versioning + +We use [SemVer](http://semver.org/) for versioning. For the versions available, see the +[tags on this repository](https://github.com/singnet/snet-cli/tags). + +## License + +This project is licensed under the MIT License - see the +[LICENSE](https://github.com/singnet/snet-cli/blob/master/snet_cli/LICENSE) file for details. diff --git a/docs/Makefile b/packages/snet_cli/docs/Makefile similarity index 100% rename from docs/Makefile rename to packages/snet_cli/docs/Makefile diff --git a/docs/build-docs.sh b/packages/snet_cli/docs/build-docs.sh similarity index 100% rename from docs/build-docs.sh rename to packages/snet_cli/docs/build-docs.sh diff --git a/docs/source/conf.py b/packages/snet_cli/docs/source/conf.py similarity index 100% rename from docs/source/conf.py rename to packages/snet_cli/docs/source/conf.py diff --git a/docs/source/generate_rst.py b/packages/snet_cli/docs/source/generate_rst.py similarity index 100% rename from docs/source/generate_rst.py rename to packages/snet_cli/docs/source/generate_rst.py diff --git a/docs/source/img/singularityNET.png b/packages/snet_cli/docs/source/img/singularityNET.png similarity index 100% rename from docs/source/img/singularityNET.png rename to packages/snet_cli/docs/source/img/singularityNET.png diff --git a/docs/source/index_template.tpl b/packages/snet_cli/docs/source/index_template.tpl similarity index 100% rename from docs/source/index_template.tpl rename to packages/snet_cli/docs/source/index_template.tpl diff --git a/docs/source/snet-cli-static/theme.css b/packages/snet_cli/docs/source/snet-cli-static/theme.css similarity index 100% rename from docs/source/snet-cli-static/theme.css rename to packages/snet_cli/docs/source/snet-cli-static/theme.css diff --git a/docs/source/subcommand_template.tpl b/packages/snet_cli/docs/source/subcommand_template.tpl similarity index 100% rename from docs/source/subcommand_template.tpl rename to packages/snet_cli/docs/source/subcommand_template.tpl diff --git a/packages/snet_cli/requirements.txt b/packages/snet_cli/requirements.txt new file mode 100644 index 00000000..ea8d66ba --- /dev/null +++ b/packages/snet_cli/requirements.txt @@ -0,0 +1,2 @@ +--index-url https://pypi.python.org/simple +-e . diff --git a/scripts/blockchain b/packages/snet_cli/scripts/blockchain similarity index 90% rename from scripts/blockchain rename to packages/snet_cli/scripts/blockchain index e943b8cf..46cf9904 100755 --- a/scripts/blockchain +++ b/packages/snet_cli/scripts/blockchain @@ -10,12 +10,13 @@ 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.joinpath("blockchain") + cur_dir = pathlib.Path(__file__).absolute().parent + blockchain_dir = cur_dir.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_cli", "resources", "contracts") + contract_json_dest_dir = cur_dir.parent.joinpath("snet", "snet_cli", "resources", "contracts") abi_contract_names = ["Registry", "MultiPartyEscrow"] networks_contract_names = ["Registry", "MultiPartyEscrow"] diff --git a/packages/snet_cli/scripts/package-namespace-pip b/packages/snet_cli/scripts/package-namespace-pip new file mode 100755 index 00000000..086a88f2 --- /dev/null +++ b/packages/snet_cli/scripts/package-namespace-pip @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +python3.6 setup-namespace.py sdist bdist_wheel diff --git a/packages/snet_cli/scripts/package-pip b/packages/snet_cli/scripts/package-pip new file mode 100755 index 00000000..4dda00f6 --- /dev/null +++ b/packages/snet_cli/scripts/package-pip @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +python3.6 setup.py sdist bdist_wheel diff --git a/packages/snet_cli/setup-namespace.py b/packages/snet_cli/setup-namespace.py new file mode 100644 index 00000000..0fec53da --- /dev/null +++ b/packages/snet_cli/setup-namespace.py @@ -0,0 +1,68 @@ +from setuptools import setup, find_namespace_packages +from setuptools.command.develop import develop as _develop +from setuptools.command.install import install as _install + + +def install_and_compile_proto(): + import snet.snet_cli + from snet.snet_cli.utils import compile_proto as compile_proto + from pathlib import Path + proto_dir = Path(__file__).absolute().parent.joinpath("snet", "snet_cli", "resources", "proto") + dest_dir = Path(snet.snet_cli.__file__).absolute().parent.joinpath("resources", "proto") + print(proto_dir, "->", dest_dir) + for fn in proto_dir.glob('*.proto'): + print("Compiling protobuf", fn) + compile_proto(proto_dir, dest_dir, proto_file=fn) + + +class develop(_develop): + """Post-installation for development mode.""" + + def run(self): + _develop.run(self) + self.execute(install_and_compile_proto, (), msg="Compile protocol buffers") + + +class install(_install): + """Post-installation for installation mode.""" + + def run(self): + _install.run(self) + self.execute(install_and_compile_proto, (), msg="Compile protocol buffers") + + +version_dict = {} +with open("./snet/snet_cli/version.py") as fp: + exec(fp.read(), version_dict) +setup( + name='snet.snet-cli', + version=version_dict['__version__'], + packages=find_namespace_packages(include=['snet.*']), + namespace_packages=['snet'], + url='https://github.com/singnet/snet-cli', + license='MIT', + author='SingularityNET Foundation', + author_email='info@singularitynet.io', + description='SingularityNET CLI standalone namespace package', + python_requires='>=3.6', + install_requires=[ + 'grpcio-tools==1.19.0', + 'jsonrpcclient==2.5.2', + 'web3==4.8.3', + 'mnemonic==0.18', + 'pycoin>=0.80', + 'rlp==1.0.1', + 'pyyaml>=4.2b1', + 'ipfsapi==0.4.2.post1', + 'rfc3986==1.1.0', + 'pymultihash==0.8.2', + 'base58==1.0.2', + 'argcomplete>=1.9.4', + 'grpcio-health-checking==1.19.0' + ], + cmdclass={ + 'develop': develop, + 'install': install + }, + include_package_data=True +) diff --git a/packages/snet_cli/setup.py b/packages/snet_cli/setup.py new file mode 100644 index 00000000..faca0963 --- /dev/null +++ b/packages/snet_cli/setup.py @@ -0,0 +1,82 @@ +from setuptools import setup, find_packages, find_namespace_packages +from setuptools.command.develop import develop as _develop +from setuptools.command.install import install as _install + + +def install_and_compile_proto(): + import snet.snet_cli + from snet.snet_cli.utils import compile_proto as compile_proto + from pathlib import Path + proto_dir = Path(__file__).absolute().parent.joinpath("snet", "snet_cli", "resources", "proto") + dest_dir = Path(snet.snet_cli.__file__).absolute().parent.joinpath("resources", "proto") + print(proto_dir, "->", dest_dir) + for fn in proto_dir.glob('*.proto'): + print("Compiling protobuf", fn) + compile_proto(proto_dir, dest_dir, proto_file=fn) + + +class develop(_develop): + """Post-installation for development mode.""" + + def run(self): + _develop.run(self) + self.execute(install_and_compile_proto, (), msg="Compile protocol buffers") + + +class install(_install): + """Post-installation for installation mode.""" + + def run(self): + _install.run(self) + self.execute(install_and_compile_proto, (), msg="Compile protocol buffers") + + +version_dict = {} +with open("./snet/snet_cli/version.py") as fp: + exec(fp.read(), version_dict) +setup( + name='snet-cli', + version=version_dict['__version__'], + packages=find_namespace_packages(include=['snet.*'])+find_packages(), + namespace_packages=['snet'], + url='https://github.com/singnet/snet-cli', + license='MIT', + author='SingularityNET Foundation', + author_email='info@singularitynet.io', + description='SingularityNET CLI', + python_requires='>=3.6', + install_requires=[ + 'grpcio-tools==1.19.0', + 'jsonrpcclient==2.5.2', + 'web3==4.8.3', + 'mnemonic==0.18', + 'pycoin>=0.80', + 'trezor==0.9.1', + 'rlp==1.0.1', + 'pyyaml>=4.2b1', + 'ipfsapi==0.4.2.post1', + 'rfc3986==1.1.0', + 'hidapi>=0.7.99', # _vendor/ledgerblue + 'protobuf>=2.6.1', # _vendor/ledgerblue + 'pycryptodome>=3.6.6', # _vendor/ledgerblue + 'eth-hash>=0.2.0', # the latest eth-hash v0.2.0 requires pycryptodome>=3.6.6,<4 + 'future==0.16.0', # _vendor/ledgerblue + 'ecpy>=0.8.1', # _vendor/ledgerblue + 'pillow>=3.4.0', # _vendor/ledgerblue + 'python-u2flib-host>=3.0.2', # _vendor/ledgerblue + 'pymultihash==0.8.2', + 'base58==1.0.2', + 'argcomplete>=1.9.4', + 'grpcio-health-checking==1.19.0' + ], + cmdclass={ + 'develop': develop, + 'install': install, + }, + include_package_data=True, + entry_points={ + 'console_scripts': [ + 'snet = snet_cli:main', + ], + } +) diff --git a/snet_cli/resources/contracts/placeholder.txt b/packages/snet_cli/snet/snet_cli/__init__.py similarity index 100% rename from snet_cli/resources/contracts/placeholder.txt rename to packages/snet_cli/snet/snet_cli/__init__.py diff --git a/snet_cli/contract.py b/packages/snet_cli/snet/snet_cli/contract.py similarity index 100% rename from snet_cli/contract.py rename to packages/snet_cli/snet/snet_cli/contract.py diff --git a/snet_cli/mpe_channel_command.py b/packages/snet_cli/snet/snet_cli/mpe_channel_command.py similarity index 98% rename from snet_cli/mpe_channel_command.py rename to packages/snet_cli/snet/snet_cli/mpe_channel_command.py index faf114da..8f4e9865 100644 --- a/snet_cli/mpe_channel_command.py +++ b/packages/snet_cli/snet/snet_cli/mpe_channel_command.py @@ -1,19 +1,21 @@ - -from snet_cli.mpe_service_command import MPEServiceCommand -from snet_cli.utils import compile_proto import base64 from pathlib import Path import os -from snet_cli.utils import get_contract_def, abi_get_element_by_name, abi_decode_struct_to_dict -from snet_cli.mpe_service_metadata import load_mpe_service_metadata -from snet_cli.utils_ipfs import safe_extract_proto_from_ipfs import shutil import tempfile -from snet_cli.utils_agi2cogs import cogs2stragi import pickle +from collections import defaultdict + from web3.utils.encoding import pad_hex from web3.utils.events import get_event_data -from collections import defaultdict + +from snet_cli.mpe_service_command import MPEServiceCommand +from snet_cli.utils_agi2cogs import cogs2stragi + +from snet.snet_cli.utils_ipfs import safe_extract_proto_from_ipfs +from snet.snet_cli.mpe_service_metadata import load_mpe_service_metadata +from snet.snet_cli.utils import compile_proto, get_contract_def, abi_get_element_by_name, abi_decode_struct_to_dict + # we inherit MPEServiceCommand because we need _get_service_metadata_from_registry class MPEChannelCommand(MPEServiceCommand): diff --git a/snet_cli/mpe_service_metadata.py b/packages/snet_cli/snet/snet_cli/mpe_service_metadata.py similarity index 99% rename from snet_cli/mpe_service_metadata.py rename to packages/snet_cli/snet/snet_cli/mpe_service_metadata.py index 91c935b4..9ab8054c 100644 --- a/snet_cli/mpe_service_metadata.py +++ b/packages/snet_cli/snet/snet_cli/mpe_service_metadata.py @@ -39,9 +39,10 @@ import base64 import secrets -from snet_cli.utils import is_valid_endpoint from collections import defaultdict +from snet.snet_cli.utils import is_valid_endpoint + # TODO: we should use some standard solution here class MPEServiceMetadata: diff --git a/packages/snet_cli/snet/snet_cli/resources/contracts/placeholder.txt b/packages/snet_cli/snet/snet_cli/resources/contracts/placeholder.txt new file mode 100644 index 00000000..e69de29b diff --git a/snet_cli/resources/package.json b/packages/snet_cli/snet/snet_cli/resources/package.json similarity index 100% rename from snet_cli/resources/package.json rename to packages/snet_cli/snet/snet_cli/resources/package.json diff --git a/snet_cli/resources/proto/control_service.proto b/packages/snet_cli/snet/snet_cli/resources/proto/control_service.proto similarity index 100% rename from snet_cli/resources/proto/control_service.proto rename to packages/snet_cli/snet/snet_cli/resources/proto/control_service.proto diff --git a/snet_cli/resources/proto/merckledag.proto b/packages/snet_cli/snet/snet_cli/resources/proto/merckledag.proto similarity index 100% rename from snet_cli/resources/proto/merckledag.proto rename to packages/snet_cli/snet/snet_cli/resources/proto/merckledag.proto diff --git a/snet_cli/resources/proto/state_service.proto b/packages/snet_cli/snet/snet_cli/resources/proto/state_service.proto similarity index 100% rename from snet_cli/resources/proto/state_service.proto rename to packages/snet_cli/snet/snet_cli/resources/proto/state_service.proto diff --git a/snet_cli/resources/proto/unixfs.proto b/packages/snet_cli/snet/snet_cli/resources/proto/unixfs.proto similarity index 100% rename from snet_cli/resources/proto/unixfs.proto rename to packages/snet_cli/snet/snet_cli/resources/proto/unixfs.proto diff --git a/snet_cli/utils.py b/packages/snet_cli/snet/snet_cli/utils.py similarity index 80% rename from snet_cli/utils.py rename to packages/snet_cli/snet/snet_cli/utils.py index d85dbefb..7bf982ad 100644 --- a/snet_cli/utils.py +++ b/packages/snet_cli/snet/snet_cli/utils.py @@ -2,9 +2,10 @@ import os import subprocess import functools +import sys from urllib.parse import urlparse -from pathlib import Path +from pathlib import Path, PurePath import web3 import pkg_resources @@ -12,6 +13,9 @@ from grpc_tools.protoc import main as protoc +RESOURCES_PATH = PurePath(os.path.realpath(__file__)).parent.joinpath("resources") + + class DefaultAttributeObject(object): def __init__(self, **kwargs): for k, v in kwargs.items(): @@ -122,7 +126,7 @@ def walk_imports(entry_path): return seen_paths -def get_contract_def(contract_name, contract_artifacts_root=Path(__file__).absolute().parent.joinpath("resources", "contracts")): +def get_contract_def(contract_name, contract_artifacts_root=RESOURCES_PATH.joinpath("contracts")): contract_def = {} with open(Path(__file__).absolute().parent.joinpath(contract_artifacts_root, "abi", "{}.json".format(contract_name))) as f: contract_def["abi"] = json.load(f) @@ -159,12 +163,11 @@ def compile_proto(entry_path, codegen_dir, proto_file=None, target_language="pyt compiler_args.append("--grpc_python_out={}".format(codegen_dir)) compiler = protoc elif target_language == "nodejs": - resources_path = Path(os.path.dirname(os.path.realpath(__file__))).joinpath("resources") - protoc_node_compiler_path = resources_path.joinpath("node_modules").joinpath("grpc-tools").joinpath("bin").joinpath("protoc.js").absolute() - grpc_node_plugin_path = resources_path.joinpath("node_modules").joinpath("grpc-tools").joinpath("bin").joinpath("grpc_node_plugin").resolve() + protoc_node_compiler_path = Path(RESOURCES_PATH.joinpath("node_modules").joinpath("grpc-tools").joinpath("bin").joinpath("protoc.js")).absolute() + grpc_node_plugin_path = Path(RESOURCES_PATH.joinpath("node_modules").joinpath("grpc-tools").joinpath("bin").joinpath("grpc_node_plugin")).resolve() if not os.path.isfile(protoc_node_compiler_path) or not os.path.isfile(grpc_node_plugin_path): print("Missing required node.js protoc compiler. Retrieving from npm...") - subprocess.run(["npm", "install"], cwd=resources_path) + subprocess.run(["npm", "install"], cwd=RESOURCES_PATH) compiler_args.append("--js_out=import_style=commonjs,binary:{}".format(codegen_dir)) compiler_args.append("--grpc_out={}".format(codegen_dir)) compiler_args.append("--plugin=protoc-gen-grpc={}".format(grpc_node_plugin_path)) @@ -181,6 +184,7 @@ def compile_proto(entry_path, codegen_dir, proto_file=None, target_language="pyt return False except Exception as e: + print(e) return False def abi_get_element_by_name(abi, name): @@ -253,3 +257,43 @@ def rgetattr(obj, attr): 2 """ return functools.reduce(getattr, [obj] + attr.split('.')) + + +def get_contract_object(w3, contract_file): + with open(RESOURCES_PATH.joinpath("contracts", "abi", contract_file)) as f: + abi = json.load(f) + with open(RESOURCES_PATH.joinpath("contracts", "networks", contract_file)) as f: + networks = json.load(f) + address = w3.toChecksumAddress(networks[w3.version.network]["address"]) + return w3.eth.contract(abi=abi, address=address) + + +def get_contract_deployment_block(w3, contract_file): + with open(RESOURCES_PATH.joinpath("contracts", "networks", contract_file)) as f: + networks = json.load(f) + txn_hash = networks[w3.version.network]["transactionHash"] + return w3.eth.getTransactionReceipt(txn_hash).blockNumber + + +def normalize_private_key(private_key): + if private_key.startswith("0x"): + private_key = bytes(bytearray.fromhex(private_key[2:])) + else: + private_key = bytes(bytearray.fromhex(private_key)) + return private_key + + +def get_address_from_private(private_key): + return web3.eth.Account.privateKeyToAccount(private_key).address + + +class add_to_path(): + def __init__(self, path): + self.path = path + def __enter__(self): + sys.path.insert(0, self.path) + def __exit__(self, exc_type, exc_value, traceback): + try: + sys.path.remove(self.path) + except ValueError: + pass diff --git a/snet_cli/utils_ipfs.py b/packages/snet_cli/snet/snet_cli/utils_ipfs.py similarity index 96% rename from snet_cli/utils_ipfs.py rename to packages/snet_cli/snet/snet_cli/utils_ipfs.py index 28a2ba1c..c5f24a7a 100644 --- a/snet_cli/utils_ipfs.py +++ b/packages/snet_cli/snet/snet_cli/utils_ipfs.py @@ -38,8 +38,8 @@ def get_from_ipfs_and_checkhash(ipfs_client, ipfs_hash_base58, validate=True): We must check the hash becasue we cannot believe that ipfs_client wasn't been compromise """ if validate: - from snet_cli.resources.proto.unixfs_pb2 import Data - from snet_cli.resources.proto.merckledag_pb2 import MerkleNode + from snet.snet_cli.resources.proto.unixfs_pb2 import Data + from snet.snet_cli.resources.proto.merckledag_pb2 import MerkleNode # No nice Python library to parse ipfs blocks, so do it ourselves. block_data = ipfs_client.block_get(ipfs_hash_base58) diff --git a/snet_cli/utils_proto.py b/packages/snet_cli/snet/snet_cli/utils_proto.py similarity index 100% rename from snet_cli/utils_proto.py rename to packages/snet_cli/snet/snet_cli/utils_proto.py diff --git a/snet_cli/version.py b/packages/snet_cli/snet/snet_cli/version.py similarity index 100% rename from snet_cli/version.py rename to packages/snet_cli/snet/snet_cli/version.py diff --git a/snet_cli/__init__.py b/packages/snet_cli/snet_cli/__init__.py similarity index 99% rename from snet_cli/__init__.py rename to packages/snet_cli/snet_cli/__init__.py index 633e954f..6483d16d 100644 --- a/snet_cli/__init__.py +++ b/packages/snet_cli/snet_cli/__init__.py @@ -7,6 +7,7 @@ from snet_cli.config import Config import argcomplete + def main(): try: argv = sys.argv[1:] diff --git a/packages/snet_cli/snet_cli/_vendor/__init__.py b/packages/snet_cli/snet_cli/_vendor/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/snet_cli/_vendor/ledgerblue/BlueHSMServer_pb2.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/BlueHSMServer_pb2.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/BlueHSMServer_pb2.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/BlueHSMServer_pb2.py diff --git a/snet_cli/_vendor/ledgerblue/Dongle.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/Dongle.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/Dongle.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/Dongle.py diff --git a/snet_cli/_vendor/ledgerblue/__init__.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/__init__.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/__init__.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/__init__.py diff --git a/snet_cli/_vendor/ledgerblue/checkGenuine.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/checkGenuine.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/checkGenuine.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/checkGenuine.py diff --git a/snet_cli/_vendor/ledgerblue/comm.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/comm.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/comm.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/comm.py diff --git a/snet_cli/_vendor/ledgerblue/commException.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/commException.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/commException.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/commException.py diff --git a/snet_cli/_vendor/ledgerblue/commHTTP.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/commHTTP.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/commHTTP.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/commHTTP.py diff --git a/snet_cli/_vendor/ledgerblue/commU2F.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/commU2F.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/commU2F.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/commU2F.py diff --git a/snet_cli/_vendor/ledgerblue/deleteApp.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/deleteApp.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/deleteApp.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/deleteApp.py diff --git a/snet_cli/_vendor/ledgerblue/deployed.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/deployed.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/deployed.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/deployed.py diff --git a/snet_cli/_vendor/ledgerblue/derivePassphrase.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/derivePassphrase.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/derivePassphrase.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/derivePassphrase.py diff --git a/snet_cli/_vendor/ledgerblue/ecWrapper.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/ecWrapper.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/ecWrapper.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/ecWrapper.py diff --git a/snet_cli/_vendor/ledgerblue/endorsementSetup.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/endorsementSetup.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/endorsementSetup.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/endorsementSetup.py diff --git a/snet_cli/_vendor/ledgerblue/endorsementSetupLedger.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/endorsementSetupLedger.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/endorsementSetupLedger.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/endorsementSetupLedger.py diff --git a/snet_cli/_vendor/ledgerblue/genCAPair.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/genCAPair.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/genCAPair.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/genCAPair.py diff --git a/snet_cli/_vendor/ledgerblue/getMemInfo.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/getMemInfo.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/getMemInfo.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/getMemInfo.py diff --git a/snet_cli/_vendor/ledgerblue/hashApp.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/hashApp.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/hashApp.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/hashApp.py diff --git a/snet_cli/_vendor/ledgerblue/hexLoader.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/hexLoader.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/hexLoader.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/hexLoader.py diff --git a/snet_cli/_vendor/ledgerblue/hexParser.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/hexParser.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/hexParser.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/hexParser.py diff --git a/snet_cli/_vendor/ledgerblue/hostOnboard.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/hostOnboard.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/hostOnboard.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/hostOnboard.py diff --git a/snet_cli/_vendor/ledgerblue/ledgerWrapper.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/ledgerWrapper.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/ledgerWrapper.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/ledgerWrapper.py diff --git a/snet_cli/_vendor/ledgerblue/listApps.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/listApps.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/listApps.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/listApps.py diff --git a/snet_cli/_vendor/ledgerblue/loadApp.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/loadApp.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/loadApp.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/loadApp.py diff --git a/snet_cli/_vendor/ledgerblue/loadMCU.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/loadMCU.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/loadMCU.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/loadMCU.py diff --git a/snet_cli/_vendor/ledgerblue/mcuBootloader.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/mcuBootloader.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/mcuBootloader.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/mcuBootloader.py diff --git a/snet_cli/_vendor/ledgerblue/resetCustomCA.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/resetCustomCA.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/resetCustomCA.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/resetCustomCA.py diff --git a/snet_cli/_vendor/ledgerblue/runApp.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/runApp.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/runApp.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/runApp.py diff --git a/snet_cli/_vendor/ledgerblue/runScript.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/runScript.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/runScript.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/runScript.py diff --git a/snet_cli/_vendor/ledgerblue/setupCustomCA.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/setupCustomCA.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/setupCustomCA.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/setupCustomCA.py diff --git a/snet_cli/_vendor/ledgerblue/signApp.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/signApp.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/signApp.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/signApp.py diff --git a/snet_cli/_vendor/ledgerblue/updateFirmware.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/updateFirmware.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/updateFirmware.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/updateFirmware.py diff --git a/snet_cli/_vendor/ledgerblue/verifyApp.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/verifyApp.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/verifyApp.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/verifyApp.py diff --git a/snet_cli/_vendor/ledgerblue/verifyEndorsement1.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/verifyEndorsement1.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/verifyEndorsement1.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/verifyEndorsement1.py diff --git a/snet_cli/_vendor/ledgerblue/verifyEndorsement2.py b/packages/snet_cli/snet_cli/_vendor/ledgerblue/verifyEndorsement2.py similarity index 100% rename from snet_cli/_vendor/ledgerblue/verifyEndorsement2.py rename to packages/snet_cli/snet_cli/_vendor/ledgerblue/verifyEndorsement2.py diff --git a/snet_cli/arguments.py b/packages/snet_cli/snet_cli/arguments.py similarity index 99% rename from snet_cli/arguments.py rename to packages/snet_cli/snet_cli/arguments.py index 259de685..38b11daa 100644 --- a/snet_cli/arguments.py +++ b/packages/snet_cli/snet_cli/arguments.py @@ -7,16 +7,17 @@ from snet_cli.commands import IdentityCommand, SessionSetCommand, SessionShowCommand, NetworkCommand, ContractCommand, \ OrganizationCommand, VersionCommand from snet_cli.identity import get_identity_types -from snet_cli.utils import type_converter, get_contract_def from snet_cli.mpe_account_command import MPEAccountCommand from snet_cli.mpe_service_command import MPEServiceCommand -from snet_cli.mpe_channel_command import MPEChannelCommand from snet_cli.mpe_client_command import MPEClientCommand from snet_cli.mpe_treasurer_command import MPETreasurerCommand from snet_cli.sdk_command import SDKCommand from snet_cli.utils_agi2cogs import stragi2cogs from snet_cli.config import Config, get_session_keys, get_session_network_keys_removable +from snet.snet_cli.mpe_channel_command import MPEChannelCommand +from snet.snet_cli.utils import type_converter, get_contract_def, RESOURCES_PATH + class CustomParser(argparse.ArgumentParser): def __init__(self, default_choice=None, *args, **kwargs): @@ -234,7 +235,7 @@ def add_contract_options(parser): subparsers = parser.add_subparsers(title="contracts", metavar="CONTRACT") subparsers.required = True - for path in Path(__file__).absolute().parent.joinpath("resources", "contracts", "abi").glob("*json"): + for path in Path(RESOURCES_PATH.joinpath("contracts", "abi")).glob("*json"): contract_name = re.search(r"([^.]*)\.json", os.path.basename(path)).group(1) contract_p = subparsers.add_parser(contract_name, help="{} contract".format(contract_name)) add_contract_function_options(contract_p, contract_name) diff --git a/snet_cli/commands.py b/packages/snet_cli/snet_cli/commands.py similarity index 99% rename from snet_cli/commands.py rename to packages/snet_cli/snet_cli/commands.py index 16d27169..8f854d91 100644 --- a/snet_cli/commands.py +++ b/packages/snet_cli/snet_cli/commands.py @@ -3,23 +3,22 @@ import sys from textwrap import indent from urllib.parse import urljoin +import secrets +import string +from web3.eth import is_checksum_address +from web3.gas_strategies.time_based import fast_gas_price_strategy, medium_gas_price_strategy, slow_gas_price_strategy import ipfsapi import yaml from rfc3986 import urlparse -from snet_cli.contract import Contract -from snet_cli.identity import get_kws_for_identity_type -from snet_cli.utils import DefaultAttributeObject, get_web3, serializable, type_converter, get_contract_def, get_cli_version, bytes32_to_str - from snet_cli.utils_config import get_contract_address, get_field_from_args_or_session, read_default_contract_address from snet_cli.identity import RpcIdentityProvider, MnemonicIdentityProvider, TrezorIdentityProvider, \ LedgerIdentityProvider, KeyIdentityProvider, KeyStoreIdentityProvider -from web3.eth import is_checksum_address -import secrets -import string -from web3.gas_strategies.time_based import fast_gas_price_strategy, medium_gas_price_strategy, slow_gas_price_strategy +from snet_cli.identity import get_kws_for_identity_type +from snet.snet_cli.contract import Contract +from snet.snet_cli.utils import DefaultAttributeObject, get_web3, serializable, type_converter, get_contract_def, get_cli_version, bytes32_to_str class Command(object): diff --git a/snet_cli/config.py b/packages/snet_cli/snet_cli/config.py similarity index 100% rename from snet_cli/config.py rename to packages/snet_cli/snet_cli/config.py diff --git a/snet_cli/identity.py b/packages/snet_cli/snet_cli/identity.py similarity index 97% rename from snet_cli/identity.py rename to packages/snet_cli/snet_cli/identity.py index bad5b0d1..4b8a2f89 100644 --- a/snet_cli/identity.py +++ b/packages/snet_cli/snet_cli/identity.py @@ -16,7 +16,10 @@ from snet_cli._vendor.ledgerblue.comm import getDongle from snet_cli._vendor.ledgerblue.commException import CommException - + +from snet.snet_cli.utils import get_address_from_private, normalize_private_key + + BIP32_HARDEN = 0x80000000 @@ -37,12 +40,8 @@ def sign_message_after_soliditySha3(self, message): class KeyIdentityProvider(IdentityProvider): def __init__(self, w3, private_key): self.w3 = w3 - if private_key.startswith("0x"): - self.private_key = bytes(bytearray.fromhex(private_key[2:])) - else: - self.private_key = bytes(bytearray.fromhex(private_key)) - - self.address = web3.eth.Account.privateKeyToAccount(self.private_key).address + self.private_key = normalize_private_key(private_key) + self.address = get_address_from_private(self.private_key) def get_address(self): return self.address @@ -116,7 +115,7 @@ def __init__(self, w3, mnemonic, index): change_subtree = account_subtree.subkey(i=0) account = change_subtree.subkey(i=index) self.private_key = account.secret_exponent().to_bytes(32, 'big') - self.address = web3.eth.Account.privateKeyToAccount(self.private_key).address + self.address = get_address_from_private(self.private_key) def get_address(self): return self.address diff --git a/snet_cli/mpe_account_command.py b/packages/snet_cli/snet_cli/mpe_account_command.py similarity index 100% rename from snet_cli/mpe_account_command.py rename to packages/snet_cli/snet_cli/mpe_account_command.py diff --git a/snet_cli/mpe_client_command.py b/packages/snet_cli/snet_cli/mpe_client_command.py similarity index 94% rename from snet_cli/mpe_client_command.py rename to packages/snet_cli/snet_cli/mpe_client_command.py index 8e1e1e8c..47713759 100644 --- a/snet_cli/mpe_client_command.py +++ b/packages/snet_cli/snet_cli/mpe_client_command.py @@ -1,13 +1,16 @@ -from snet_cli.mpe_channel_command import MPEChannelCommand import base64 from pathlib import Path import json import sys + import grpc from eth_account.messages import defunct_hash_message -from snet_cli.utils_proto import import_protobuf_from_dir, switch_to_json_payload_encoding + from snet_cli.utils_agi2cogs import cogs2stragi -from snet_cli.utils import open_grpc_channel, rgetattr, compile_proto + +from snet.snet_cli.utils import open_grpc_channel, rgetattr, compile_proto, RESOURCES_PATH +from snet.snet_cli.mpe_channel_command import MPEChannelCommand +from snet.snet_cli.utils_proto import import_protobuf_from_dir, switch_to_json_payload_encoding # we inherit MPEChannelCommand because client needs channels @@ -149,14 +152,10 @@ def call_server_lowlevel(self): # III. Stateless client related functions def _get_channel_state_from_server(self, grpc_channel, channel_id): - # Compile protobuf if needed - codegen_dir = Path.home().joinpath(".snet", "mpe_client", "state_service") - proto_dir = Path(__file__).absolute().parent.joinpath("resources", "proto") - if (not codegen_dir.joinpath("state_service_pb2.py").is_file()): - compile_proto(proto_dir, codegen_dir, proto_file = "state_service.proto") + proto_dir = RESOURCES_PATH.joinpath("proto") # make PaymentChannelStateService.GetChannelState call to the daemon - stub_class, request_class, _ = import_protobuf_from_dir(codegen_dir, "GetChannelState") + stub_class, request_class, _ = import_protobuf_from_dir(proto_dir, "GetChannelState") message = self.w3.soliditySha3(["uint256"], [channel_id]) signature = self.ident.sign_message_after_soliditySha3(message) diff --git a/snet_cli/mpe_service_command.py b/packages/snet_cli/snet_cli/mpe_service_command.py similarity index 96% rename from snet_cli/mpe_service_command.py rename to packages/snet_cli/snet_cli/mpe_service_command.py index 22e035c3..32f45c2d 100644 --- a/snet_cli/mpe_service_command.py +++ b/packages/snet_cli/snet_cli/mpe_service_command.py @@ -1,14 +1,17 @@ -from snet_cli.commands import BlockchainCommand -import snet_cli.utils_ipfs as utils_ipfs -from snet_cli.mpe_service_metadata import MPEServiceMetadata, load_mpe_service_metadata, mpe_service_metadata_from_json -from snet_cli.utils import type_converter, bytes32_to_str, open_grpc_channel -from snet_cli.utils_ipfs import hash_to_bytesuri, bytesuri_to_hash, get_from_ipfs_and_checkhash, safe_extract_proto_from_ipfs import web3 import json import grpc +from collections import defaultdict + from grpc_health.v1 import health_pb2 as heartb_pb2 from grpc_health.v1 import health_pb2_grpc as heartb_pb2_grpc -from collections import defaultdict + +from snet_cli.commands import BlockchainCommand + +import snet.snet_cli.utils_ipfs as utils_ipfs +from snet.snet_cli.utils_ipfs import hash_to_bytesuri, bytesuri_to_hash, get_from_ipfs_and_checkhash, safe_extract_proto_from_ipfs +from snet.snet_cli.mpe_service_metadata import MPEServiceMetadata, load_mpe_service_metadata, mpe_service_metadata_from_json +from snet.snet_cli.utils import type_converter, bytes32_to_str, open_grpc_channel class MPEServiceCommand(BlockchainCommand): @@ -218,4 +221,4 @@ def extract_service_api_from_registry(self): def delete_service_registration(self): params = [type_converter("bytes32")(self.args.org_id), type_converter("bytes32")(self.args.service_id)] - self.transact_contract_command("Registry", "deleteServiceRegistration", params) \ No newline at end of file + self.transact_contract_command("Registry", "deleteServiceRegistration", params) diff --git a/snet_cli/mpe_treasurer_command.py b/packages/snet_cli/snet_cli/mpe_treasurer_command.py similarity index 97% rename from snet_cli/mpe_treasurer_command.py rename to packages/snet_cli/snet_cli/mpe_treasurer_command.py index f603cec1..5452cec3 100644 --- a/snet_cli/mpe_treasurer_command.py +++ b/packages/snet_cli/snet_cli/mpe_treasurer_command.py @@ -1,11 +1,13 @@ -from snet_cli.mpe_client_command import MPEClientCommand -from snet_cli.utils import compile_proto, open_grpc_channel from pathlib import Path + +import web3 import grpc -from snet_cli.utils_proto import import_protobuf_from_dir -from snet_cli.utils import int4bytes_big + +from snet_cli.mpe_client_command import MPEClientCommand from snet_cli.utils_agi2cogs import cogs2stragi -import web3 + +from snet.snet_cli.utils_proto import import_protobuf_from_dir +from snet.snet_cli.utils import compile_proto, open_grpc_channel, int4bytes_big, RESOURCES_PATH class MPETreasurerCommand(MPEClientCommand): @@ -32,7 +34,7 @@ def _get_stub_and_request_classes(self, service_name): """ import protobuf and return stub and request class """ # Compile protobuf if needed codegen_dir = Path.home().joinpath(".snet", "mpe_client", "control_service") - proto_dir = Path(__file__).absolute().parent.joinpath("resources", "proto") + proto_dir = RESOURCES_PATH.joinpath("proto") if (not codegen_dir.joinpath("control_service_pb2.py").is_file()): compile_proto(proto_dir, codegen_dir, proto_file = "control_service.proto") diff --git a/snet_cli/sdk_command.py b/packages/snet_cli/snet_cli/sdk_command.py similarity index 86% rename from snet_cli/sdk_command.py rename to packages/snet_cli/snet_cli/sdk_command.py index e31ba930..58c62e28 100644 --- a/snet_cli/sdk_command.py +++ b/packages/snet_cli/snet_cli/sdk_command.py @@ -1,11 +1,11 @@ -from snet_cli.mpe_service_command import MPEServiceCommand import os from pathlib import Path, PurePath from tempfile import TemporaryDirectory -from snet_cli.utils import type_converter, bytes32_to_str, compile_proto -from snet_cli.utils_ipfs import bytesuri_to_hash, get_from_ipfs_and_checkhash, safe_extract_proto_from_ipfs -from snet_cli.mpe_service_metadata import mpe_service_metadata_from_json +from snet_cli.mpe_service_command import MPEServiceCommand + +from snet.snet_cli.utils_ipfs import bytesuri_to_hash, get_from_ipfs_and_checkhash, safe_extract_proto_from_ipfs +from snet.snet_cli.utils import type_converter, bytes32_to_str, compile_proto class SDKCommand(MPEServiceCommand): def generate_client_library(self): diff --git a/snet_cli/utils_agi2cogs.py b/packages/snet_cli/snet_cli/utils_agi2cogs.py similarity index 100% rename from snet_cli/utils_agi2cogs.py rename to packages/snet_cli/snet_cli/utils_agi2cogs.py diff --git a/snet_cli/utils_config.py b/packages/snet_cli/snet_cli/utils_config.py similarity index 98% rename from snet_cli/utils_config.py rename to packages/snet_cli/snet_cli/utils_config.py index f1e4a66f..f07e1a38 100644 --- a/snet_cli/utils_config.py +++ b/packages/snet_cli/snet_cli/utils_config.py @@ -1,4 +1,4 @@ -from snet_cli.utils import get_contract_def +from snet.snet_cli.utils import get_contract_def def get_contract_address(cmd, contract_name, error_message = None): """ diff --git a/test/README.md b/packages/snet_cli/test/README.md similarity index 100% rename from test/README.md rename to packages/snet_cli/test/README.md diff --git a/test/functional_tests/script10_claim_timeout_all.sh b/packages/snet_cli/test/functional_tests/script10_claim_timeout_all.sh similarity index 100% rename from test/functional_tests/script10_claim_timeout_all.sh rename to packages/snet_cli/test/functional_tests/script10_claim_timeout_all.sh diff --git a/test/functional_tests/script11_update_metadata.sh b/packages/snet_cli/test/functional_tests/script11_update_metadata.sh similarity index 100% rename from test/functional_tests/script11_update_metadata.sh rename to packages/snet_cli/test/functional_tests/script11_update_metadata.sh diff --git a/test/functional_tests/script12_extend_add_for_service.sh b/packages/snet_cli/test/functional_tests/script12_extend_add_for_service.sh similarity index 100% rename from test/functional_tests/script12_extend_add_for_service.sh rename to packages/snet_cli/test/functional_tests/script12_extend_add_for_service.sh diff --git a/test/functional_tests/script13_call_reinitialize.sh b/packages/snet_cli/test/functional_tests/script13_call_reinitialize.sh similarity index 100% rename from test/functional_tests/script13_call_reinitialize.sh rename to packages/snet_cli/test/functional_tests/script13_call_reinitialize.sh diff --git a/test/functional_tests/script15_sdk_generate_client_library.sh b/packages/snet_cli/test/functional_tests/script15_sdk_generate_client_library.sh similarity index 100% rename from test/functional_tests/script15_sdk_generate_client_library.sh rename to packages/snet_cli/test/functional_tests/script15_sdk_generate_client_library.sh diff --git a/test/functional_tests/script1_twogroups.sh b/packages/snet_cli/test/functional_tests/script1_twogroups.sh similarity index 100% rename from test/functional_tests/script1_twogroups.sh rename to packages/snet_cli/test/functional_tests/script1_twogroups.sh diff --git a/test/functional_tests/script2_deposit_transfer.sh b/packages/snet_cli/test/functional_tests/script2_deposit_transfer.sh similarity index 100% rename from test/functional_tests/script2_deposit_transfer.sh rename to packages/snet_cli/test/functional_tests/script2_deposit_transfer.sh diff --git a/test/functional_tests/script3_without_addresses.sh b/packages/snet_cli/test/functional_tests/script3_without_addresses.sh similarity index 98% rename from test/functional_tests/script3_without_addresses.sh rename to packages/snet_cli/test/functional_tests/script3_without_addresses.sh index 85120223..57f9d014 100755 --- a/test/functional_tests/script3_without_addresses.sh +++ b/packages/snet_cli/test/functional_tests/script3_without_addresses.sh @@ -2,7 +2,7 @@ # check how snet-cli works if we pass contract address via command line interface # remove networks -rm -rf ../../snet_cli/resources/contracts/networks/*.json +rm -rf ../../snet/snet_cli/resources/contracts/networks/*.json #unset addresses snet unset current_singularitynettoken_at || echo "could fail if hasn't been set (it is ok)" diff --git a/test/functional_tests/script4_only_with_networks.sh b/packages/snet_cli/test/functional_tests/script4_only_with_networks.sh similarity index 90% rename from test/functional_tests/script4_only_with_networks.sh rename to packages/snet_cli/test/functional_tests/script4_only_with_networks.sh index a2050646..9bbd80da 100755 --- a/test/functional_tests/script4_only_with_networks.sh +++ b/packages/snet_cli/test/functional_tests/script4_only_with_networks.sh @@ -7,7 +7,7 @@ # In this test we check this priority -rm -rf ../../snet_cli/resources/contracts/networks/*.json +rm -rf ../../snet/snet_cli/resources/contracts/networks/*.json #unset addresses snet unset current_singularitynettoken_at || echo "could fail if hasn't been set (it is ok)" @@ -20,9 +20,9 @@ snet organization create testo --org-id testo -y -q && exit 1 || echo "fail as # set networks -echo '{"829257324":{"events":{},"links":{},"address":"0x5c7a4290f6f8ff64c69eeffdfafc8644a4ec3a4e","transactionHash":""}}' > ../../snet_cli/resources/contracts/networks/MultiPartyEscrow.json -echo '{"829257324":{"events":{},"links":{},"address":"0x4e74fefa82e83e0964f0d9f53c68e03f7298a8b2","transactionHash":""}}' > ../../snet_cli/resources/contracts/networks/Registry.json -echo '{"829257324":{"events":{},"links":{},"address":"0x6e5f20669177f5bdf3703ec5ea9c4d4fe3aabd14","transactionHash":""}}' > ../../snet_cli/resources/contracts/networks/SingularityNetToken.json +echo '{"829257324":{"events":{},"links":{},"address":"0x5c7a4290f6f8ff64c69eeffdfafc8644a4ec3a4e","transactionHash":""}}' > ../../snet/snet_cli/resources/contracts/networks/MultiPartyEscrow.json +echo '{"829257324":{"events":{},"links":{},"address":"0x4e74fefa82e83e0964f0d9f53c68e03f7298a8b2","transactionHash":""}}' > ../../snet/snet_cli/resources/contracts/networks/Registry.json +echo '{"829257324":{"events":{},"links":{},"address":"0x6e5f20669177f5bdf3703ec5ea9c4d4fe3aabd14","transactionHash":""}}' > ../../snet/snet_cli/resources/contracts/networks/SingularityNetToken.json # this should work @@ -50,9 +50,9 @@ snet organization delete testo -y -q --registry-at 0x4e74fefa82e8 # set INVALID networks -echo '{"829257324":{"events":{},"links":{},"address":"0x1c7a4290f6f8ff64c69eeffdfafc8644a4ec3a4e","transactionHash":""}}' > ../../snet_cli/resources/contracts/networks/MultiPartyEscrow.json -echo '{"829257324":{"events":{},"links":{},"address":"0x1e74fefa82e83e0964f0d9f53c68e03f7298a8b2","transactionHash":""}}' > ../../snet_cli/resources/contracts/networks/Registry.json -echo '{"829257324":{"events":{},"links":{},"address":"0x1e5f20669177f5bdf3703ec5ea9c4d4fe3aabd14","transactionHash":""}}' > ../../snet_cli/resources/contracts/networks/SingularityNetToken.json +echo '{"829257324":{"events":{},"links":{},"address":"0x1c7a4290f6f8ff64c69eeffdfafc8644a4ec3a4e","transactionHash":""}}' > ../../snet/snet_cli/resources/contracts/networks/MultiPartyEscrow.json +echo '{"829257324":{"events":{},"links":{},"address":"0x1e74fefa82e83e0964f0d9f53c68e03f7298a8b2","transactionHash":""}}' > ../../snet/snet_cli/resources/contracts/networks/Registry.json +echo '{"829257324":{"events":{},"links":{},"address":"0x1e5f20669177f5bdf3703ec5ea9c4d4fe3aabd14","transactionHash":""}}' > ../../snet/snet_cli/resources/contracts/networks/SingularityNetToken.json # this should fail (because addresses in networks are invalid ) snet account balance && exit 1 || echo "fail as expected" diff --git a/test/functional_tests/script5_identity1_rpc_mnemonic.sh b/packages/snet_cli/test/functional_tests/script5_identity1_rpc_mnemonic.sh similarity index 100% rename from test/functional_tests/script5_identity1_rpc_mnemonic.sh rename to packages/snet_cli/test/functional_tests/script5_identity1_rpc_mnemonic.sh diff --git a/test/functional_tests/script6_organization.sh b/packages/snet_cli/test/functional_tests/script6_organization.sh similarity index 100% rename from test/functional_tests/script6_organization.sh rename to packages/snet_cli/test/functional_tests/script6_organization.sh diff --git a/test/functional_tests/script7_contracts.sh b/packages/snet_cli/test/functional_tests/script7_contracts.sh similarity index 100% rename from test/functional_tests/script7_contracts.sh rename to packages/snet_cli/test/functional_tests/script7_contracts.sh diff --git a/test/functional_tests/script8_networks.sh b/packages/snet_cli/test/functional_tests/script8_networks.sh similarity index 100% rename from test/functional_tests/script8_networks.sh rename to packages/snet_cli/test/functional_tests/script8_networks.sh diff --git a/test/functional_tests/script9_treasurer.sh b/packages/snet_cli/test/functional_tests/script9_treasurer.sh similarity index 100% rename from test/functional_tests/script9_treasurer.sh rename to packages/snet_cli/test/functional_tests/script9_treasurer.sh diff --git a/test/functional_tests/service_spec1/ExampleService.proto b/packages/snet_cli/test/functional_tests/service_spec1/ExampleService.proto similarity index 100% rename from test/functional_tests/service_spec1/ExampleService.proto rename to packages/snet_cli/test/functional_tests/service_spec1/ExampleService.proto diff --git a/test/functional_tests/simple_daemon/test_simple_daemon.py b/packages/snet_cli/test/functional_tests/simple_daemon/test_simple_daemon.py similarity index 94% rename from test/functional_tests/simple_daemon/test_simple_daemon.py rename to packages/snet_cli/test/functional_tests/simple_daemon/test_simple_daemon.py index a43aaa7e..b965bc5a 100644 --- a/test/functional_tests/simple_daemon/test_simple_daemon.py +++ b/packages/snet_cli/test/functional_tests/simple_daemon/test_simple_daemon.py @@ -1,14 +1,16 @@ -from snet_cli.utils import compile_proto, DefaultAttributeObject from concurrent import futures import time import web3 -from snet_cli.mpe_channel_command import MPEChannelCommand + from snet_cli.config import Config +from snet.snet_cli.mpe_channel_command import MPEChannelCommand +from snet.snet_cli.utils import compile_proto, DefaultAttributeObject + compile_proto("../service_spec1", ".", proto_file = "ExampleService.proto") -compile_proto("../../../snet_cli/resources/proto/", ".", proto_file = "state_service.proto") -compile_proto("../../../snet_cli/resources/proto/", ".", proto_file = "control_service.proto") +compile_proto("../../../snet/snet_cli/resources/proto/", ".", proto_file = "state_service.proto") +compile_proto("../../../snet/snet_cli/resources/proto/", ".", proto_file = "control_service.proto") PRICE = 10000 import grpc diff --git a/test/unit_tests/test_mpe_service_metadata_1.py b/packages/snet_cli/test/unit_tests/test_mpe_service_metadata_1.py similarity index 95% rename from test/unit_tests/test_mpe_service_metadata_1.py rename to packages/snet_cli/test/unit_tests/test_mpe_service_metadata_1.py index a95ae516..fa9cad91 100644 --- a/test/unit_tests/test_mpe_service_metadata_1.py +++ b/packages/snet_cli/test/unit_tests/test_mpe_service_metadata_1.py @@ -1,5 +1,6 @@ import unittest -from snet_cli.mpe_service_metadata import MPEServiceMetadata + +from snet.snet_cli.mpe_service_metadata import MPEServiceMetadata class TestStringMethods(unittest.TestCase): diff --git a/test/utils/reset_environment.sh b/packages/snet_cli/test/utils/reset_environment.sh similarity index 98% rename from test/utils/reset_environment.sh rename to packages/snet_cli/test/utils/reset_environment.sh index d35f6d49..ed2b534a 100755 --- a/test/utils/reset_environment.sh +++ b/packages/snet_cli/test/utils/reset_environment.sh @@ -16,7 +16,7 @@ fi # I. restart ipfs killall ipfs || echo "supress an error" -sudo rm -rf ~/.ipfs/ +sudo rm -rf ~/.ipfs ipfs init ipfs bootstrap rm --all ipfs config Addresses.API /ip4/127.0.0.1/tcp/5002 diff --git a/packages/snet_cli/test/utils/run_all_functional.sh b/packages/snet_cli/test/utils/run_all_functional.sh new file mode 100755 index 00000000..63c69ece --- /dev/null +++ b/packages/snet_cli/test/utils/run_all_functional.sh @@ -0,0 +1,5 @@ +for f in packages/snet_cli/test/functional_tests/*.sh +do + bash -ex packages/snet_cli/test/utils/reset_environment.sh --i-no-what-i-am-doing + bash -ex -c "cd packages/snet_cli/test/functional_tests; bash -ex `basename $f`" +done diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..89b31b4f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +--index-url https://pypi.python.org/simple +packages/snet_cli/ +packages/sdk/ +-e . diff --git a/setup.py b/setup.py index e234abc8..3f40806e 100644 --- a/setup.py +++ b/setup.py @@ -1,81 +1,53 @@ -from setuptools import setup, find_packages -from setuptools.command.develop import develop as _develop -from setuptools.command.install import install as _install - - -def install_and_compile_proto(): - import snet_cli - from snet_cli.utils import compile_proto as compile_proto - from pathlib import Path - proto_dir = Path(__file__).absolute().parent.joinpath("snet_cli", "resources", "proto") - dest_dir = Path(snet_cli.__file__).absolute().parent.joinpath("resources", "proto") - print(proto_dir, "->", dest_dir) - for fn in proto_dir.glob('*.proto'): - print("Compiling protobuf", fn) - compile_proto(proto_dir, dest_dir, proto_file=fn) - - -class develop(_develop): - """Post-installation for development mode.""" - +import sys +import os +import subprocess +from setuptools import setup +from setuptools.command.develop import develop +from setuptools.command.install import install + +PACKAGE_NAME = 'snet' +SOURCES = { + 'snet_cli': 'packages/snet_cli', + 'sdk': 'packages/sdk', +} + +def install_packages(sources, develop=False): + print("installing all packages in {} mode".format( + "development" if develop else "normal")) + wd = os.getcwd() + for k, v in sources.items(): + try: + os.chdir(os.path.join(wd, v)) + if develop: + subprocess.check_call([sys.executable, '-m', 'pip', 'install', '-e', '.']) + else: + subprocess.check_call([sys.executable, '-m', 'pip', 'install', '.']) + except Exception as e: + print("Oops, something went wrong installing", k) + print(e) + finally: + os.chdir(wd) + +class DevelopCmd(develop): + """ Add custom steps for the develop command """ def run(self): - _develop.run(self) - self.execute(install_and_compile_proto, (), msg="Compile protocol buffers") - - -class install(_install): - """Post-installation for installation mode.""" + install_packages(SOURCES, develop=True) + develop.run(self) +class InstallCmd(install): + """ Add custom steps for the install command """ def run(self): - _install.run(self) - self.execute(install_and_compile_proto, (), msg="Compile protocol buffers") - - -version_dict = {} -with open("./snet_cli/version.py") as fp: - exec(fp.read(), version_dict) + install_packages(SOURCES, develop=False) + install.run(self) setup( - name='snet-cli', - version=version_dict['__version__'], - packages=find_packages(), - url='https://github.com/singnet/snet-cli', - license='MIT', - author='SingularityNET Foundation', - author_email='info@singularitynet.io', - description='SingularityNET CLI', - python_requires='>=3.6', - install_requires=[ - 'grpcio-tools==1.19.0', - 'jsonrpcclient==2.5.2', - 'web3==4.8.3', - 'mnemonic==0.18', - 'pycoin>=0.80', - 'trezor==0.9.1', - 'rlp==1.0.1', - 'pyyaml>=4.2b1', - 'ipfsapi==0.4.2.post1', - 'rfc3986==1.1.0', - 'hidapi>=0.7.99', # _vendor/ledgerblue - 'protobuf>=2.6.1', # _vendor/ledgerblue - 'pycryptodome>=3.6.6', # _vendor/ledgerblue - 'eth-hash>=0.2.0', # the latest eth-hash v0.2.0 requires pycryptodome>=3.6.6,<4 - 'future==0.16.0', # _vendor/ledgerblue - 'ecpy>=0.8.1', # _vendor/ledgerblue - 'pillow>=3.4.0', # _vendor/ledgerblue - 'python-u2flib-host>=3.0.2', # _vendor/ledgerblue - 'pymultihash==0.8.2', - 'base58==1.0.2', - 'argcomplete>=1.9.4', - 'grpcio-health-checking==1.19.0' - ], + name=PACKAGE_NAME, + version="0.0.1", + author="SingularityNET Foundation", + author_email="info@singularitynet.io", + description="SingularityNET Monorepo", + license="MIT", cmdclass={ - 'develop': develop, - 'install': install, - }, - include_package_data=True, - entry_points={ - 'console_scripts': [ - 'snet = snet_cli:main', - ], + 'install': InstallCmd, + 'develop': DevelopCmd, } ) diff --git a/test/utils/run_all_functional.sh b/test/utils/run_all_functional.sh deleted file mode 100755 index 72fe98b6..00000000 --- a/test/utils/run_all_functional.sh +++ /dev/null @@ -1,5 +0,0 @@ -for f in test/functional_tests/*.sh -do - bash -ex test/utils/reset_environment.sh --i-no-what-i-am-doing - bash -ex -c "cd test/functional_tests; bash -ex `basename $f`" -done