diff --git a/README.md b/README.md index 80eb3de..34902de 100644 --- a/README.md +++ b/README.md @@ -147,29 +147,53 @@ visit the [Payment](#payment) section. ## Payment -### Free call +When creating a service client, you can select a payment strategy using the `payment_strategy_type` parameter: -If you want to use the free calls you will need to pass these arguments to the `create_service_client()` method: +```python +from snet.sdk import PaymentStrategyType -```python -free_call_auth_token_bin = "f2548d27ffd319b9c05918eeac15ebab934e5cfcd68e1ec3db2b92765", -free_call_token_expiry_block = 172800, -email = "test@test.com" # which using in AI marketplace account +payment_strategy_type = PaymentStrategyType. ``` -You can receive these for a given service from the [Dapp](https://beta.singularitynet.io/) +These are four payment strategies: + +- `PaymentStrategyType.DEFAULT` +- `PaymentStrategyType.FREE_CALL` +- `PaymentStrategyType.PAID_CALL` +- `PaymentStrategyType.PREPAID_CALL` + +The default payment strategy selects one of the other three each time the service is called, depending on the +availability of free calls, as well as the presence of parameters required for concurrent calls. While choosing +a specific payment strategy will not allow you to switch to another. This is especially convenient when you want +to use free calls without accidentally spending money. +> Note: If you don't specify ะตั€ัƒ `payment_strategy_type` parameter, the default payment strategy will be used. + +### Free call + +If you want to use the free calls you will need to choose `PaymentStrategyType.FREE_CALL` as the payment strategy type. Creating a service client with free calls included would look like this: + ```python service_client = snet_sdk.create_service_client(org_id="26072b8b6a0e448180f8c0e702ab6d2f", - service_id="Exampleservice" - free_call_auth_token_bin="f2548d27ffd319b9c05918eeac15ebab934e5cfcd68e1ec3db2b92765", - free_call_token_expiry_block=172800, - email="test@mail.com") + service_id="Exampleservice", + payment_strategy_type = PaymentStrategyType.FREE_CALL) ``` ### Paid call +If you want to use regular paid calls you will need to choose `PaymentStrategyType.PAID_CALL` as the payment strategy type. +Creating a service client with paid calls would look like this: + +```python +service_client = snet_sdk.create_service_client(org_id="26072b8b6a0e448180f8c0e702ab6d2f", + service_id="Exampleservice", + payment_strategy_type = PaymentStrategyType.PAID_CALL) +``` + +There is no need to call functions for interacting with payment channels, because they are automatically +managed by the SDK. But anyway you can use them if you want. + #### Open channel with the specified amount of funds and expiration `open_channel()`[[1]](#1-this-method-uses-a-call-to-a-paid-smart-contract-function) opens a payment channel with the specified amount of AGIX tokens in cogs and expiration time. @@ -209,15 +233,20 @@ payment_channel.extend_and_add_funds(amount=123456, expiration=33333) ### Concurrent (Prepaid) call -Concurrent (prepaid) calls allow you to prepay for a batch of service calls in advance. This off-chain strategy is ideal for scenarios requiring high throughput and low latency. Unlike regular paid calls, the payment is done once upfront, and the SDK automatically manages the channel during usage. +Concurrent (prepaid) calls allow you to prepay for a batch of service calls in advance. This off-chain strategy +is ideal for scenarios requiring high throughput and low latency. Unlike regular paid calls, the payment is done +once upfront, and the SDK automatically manages the channel during usage. -To use concurrent prepaid calls, specify the `concurrent_calls` parameter when creating a service client: +If you want to use prepaid calls you will need to choose `PaymentStrategyType.PREPAID_CALL` as the payment strategy type +as well as pass the number of concurrent calls as the `concurrent_calls` parameter. Creating a service client with +prepaid calls would look like this: ```python service_client = snet_sdk.create_service_client( org_id="26072b8b6a0e448180f8c0e702ab6d2f", service_id="Exampleservice", group_name="default_group", + payment_strategy_type=PaymentStrategyType.PREPAID_CALL, concurrent_calls=5 # Number of prepaid calls to allocate ) ``` diff --git a/docs/main/init.md b/docs/main/init.md index 7a82f64..223b956 100644 --- a/docs/main/init.md +++ b/docs/main/init.md @@ -3,7 +3,8 @@ [Link](https://github.com/singnet/snet-sdk-python/blob/master/snet/sdk/__init__.py) to GitHub Entities: -1. [SnetSDK](#class-snetsdk) +1. [PaymentStrategyType](#class-paymentstrategytype) +2. [SnetSDK](#class-snetsdk) - [\_\_init\_\_](#__init__) - [create_service_client](#create_service_client) - [get_service_stub](#get_service_stub) @@ -15,6 +16,24 @@ Entities: - [get_organization_list](#get_organization_list) - [get_services_list](#get_services_list) +### Class `PaymentStrategyType` + +extends: `Enum` + +is extended by: - + +#### description + +This is an `enum` that represents the available payment strategies. It is used to determine which payment +strategy to use when initializing the SDK. + +#### members + +- `DEFAULT` +- `FREE_CALL` +- `PAID_CALL` +- `PREPAID_CALL` + ### Class `SnetSDK` extends: - @@ -73,8 +92,8 @@ of the `ServiceClient` class with all the required parameters, which is then ret - `service_id` (str): The ID of the service. - `group_name` (str): The name of the payment group. Defaults to _None_. - `payment_strategy` (PaymentStrategy): The payment channel management strategy. Defaults to _None_. -- `free_call_auth_token_bin` (str): The free call authentication token in binary format. Defaults to _None_. -- `free_call_token_expiry_block` (int): The block number when the free call token expires. Defaults to _None_. +- `payment_strategy_type` (PaymentStrategyType): The type of payment management strategy. +Defaults to `PaymentStrategyType.DEFAULT`. - `options` (dict): Additional options for the service client. Defaults to _None_. - `concurrent_calls` (int): The number of concurrent calls allowed. Defaults to 1. diff --git a/docs/payment_strategies/freecall_payment_strategy.md b/docs/payment_strategies/freecall_payment_strategy.md index 67424ac..7bb48e5 100644 --- a/docs/payment_strategies/freecall_payment_strategy.md +++ b/docs/payment_strategies/freecall_payment_strategy.md @@ -4,9 +4,10 @@ Entities: 1. [FreeCallPaymentStrategy](#class-freecallpaymentstrategy) - - [is_free_call_available](#is_free_call_available) + - [get_free_calls_available](#get_free_calls_available) - [get_payment_metadata](#get_payment_metadata) - [generate_signature](#generate_signature) + - [get_free_call_token_details](#get_free_call_token_details) ### Class `FreeCallPaymentStrategy` @@ -17,14 +18,15 @@ is extended by: - #### description The `FreeCallPaymentStrategy` class is a concrete implementation of the `PaymentStrategy` interface. -It allows you to use free calls (which can be received from the [Dapp](https://beta.singularitynet.io/)) to +It allows you to use free calls (which can be received from the daemon) to call services. #### methods -#### `is_free_call_available` +#### `get_free_calls_available` -Checks if a free call is available for a given service client. +Using grpc calls to the daemon, it gets a free call token, and also gets and returns the number of free calls +available. ###### args: @@ -32,13 +34,13 @@ Checks if a free call is available for a given service client. ###### returns: -- True if a free call is available, False otherwise. (bool) +- Amount of free calls available. (int) ###### raises: - Exception: If an error occurs while checking the free call availability. -_Note_: If any exception occurs during the process, it returns False. +_Note_: If an error occurs specifically during the grpc call to `GetFreeCallsAvailable`, 0 will be returned. #### `get_payment_metadata` @@ -60,6 +62,8 @@ Generates a signature for the given service client using the provided free call ###### args: - `service_client` (ServiceClient): The service client instance. +- `current_block_number` (int, optional): The current block number. Defaults to _None_. +- `with_token` (bool, optional): Whether to include the free call token in the signature. Defaults to _True_. ###### returns: @@ -68,3 +72,20 @@ Generates a signature for the given service client using the provided free call ###### raises: - Exception: If any of the required parameters for the free call strategy are missing. + +#### `get_free_call_token_details` + +Sends a request to the daemon and receives a free call token and its details. + +###### args: + +- `service_client` (ServiceClient): The service client instance. +- `current_block_number` (int, optional): The current block number. Defaults to _None_. + +###### returns: + +- A tuple containing the free call token and the token expiration block number. (tuple[str, int]) + +###### raises: + +- Exception: If an error occurred while receiving the token. diff --git a/requirements.txt b/requirements.txt index 4d81ede..91fc6b0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,22 +1,12 @@ -protobuf==4.21.6 -grpcio-tools==1.59.0 -wheel==0.41.2 -jsonrpcclient==4.0.3 -eth-hash==0.5.2 -rlp==3.0.0 -eth-rlp==0.3.0 -web3==6.11.1 -mnemonic==0.20 -pycoin==0.92.20230326 -pyyaml==6.0.1 -ipfshttpclient==0.4.13.2 -rfc3986==2.0.0 -pymultihash==0.8.2 -base58==2.1.1 -argcomplete==3.1.2 -grpcio-health-checking==1.59.0 -jsonschema==4.0.0 -eth-account==0.9.0 -snet-contracts==1.0.0 -lighthouseweb3==0.1.4 -zipp>=3.19.1 +protobuf==5.* +grpcio-tools>=1.71.0 +wheel>=0.45.0 +rlp>=4.1.0 +web3==7.* +ipfshttpclient==0.4.13 +rfc3986>=2.0.0 +base58>=2.1.1 +grpcio-health-checking>=1.71.0 +snet-contracts==1.0.1 +lighthouseweb3>=0.1.4 +pymultihash==0.* \ No newline at end of file diff --git a/snet/sdk/__init__.py b/snet/sdk/__init__.py index ee8b4e7..d35a77e 100644 --- a/snet/sdk/__init__.py +++ b/snet/sdk/__init__.py @@ -2,6 +2,7 @@ import os import sys import warnings +from enum import Enum import google.protobuf.internal.api_implementation @@ -26,18 +27,28 @@ from snet.sdk.client_lib_generator import ClientLibGenerator from snet.sdk.mpe.mpe_contract import MPEContract from snet.sdk.mpe.payment_channel_provider import PaymentChannelProvider -from snet.sdk.payment_strategies.default_payment_strategy import DefaultPaymentStrategy as PaymentStrategy +from snet.sdk.payment_strategies.default_payment_strategy import * from snet.sdk.service_client import ServiceClient from snet.sdk.storage_provider.storage_provider import StorageProvider from snet.sdk.custom_typing import ModuleName, ServiceStub -from snet.sdk.utils.utils import (bytes32_to_str, find_file_by_keyword, - type_converter) +from snet.sdk.utils.utils import ( + bytes32_to_str, + find_file_by_keyword, + type_converter +) google.protobuf.internal.api_implementation.Type = lambda: 'python' _sym_db = _symbol_database.Default() _sym_db.RegisterMessage = lambda x: None +class PaymentStrategyType(Enum): + PAID_CALL = PaidCallPaymentStrategy + FREE_CALL = FreeCallPaymentStrategy + PREPAID_CALL = PrePaidPaymentStrategy + DEFAULT = DefaultPaymentStrategy + + class SnetSDK: """Base Snet SDK""" @@ -91,11 +102,9 @@ def __init__(self, sdk_config: Config, metadata_provider=None): def create_service_client(self, org_id: str, service_id: str, - group_name=None, - payment_strategy=None, - free_call_auth_token_bin=None, - free_call_token_expiry_block=None, - email=None, + group_name: str=None, + payment_strategy: PaymentStrategy = None, + payment_strategy_type: PaymentStrategyType=PaymentStrategyType.DEFAULT, options=None, concurrent_calls: int = 1): @@ -120,22 +129,13 @@ def create_service_client(self, print("Generating client library...") self.lib_generator.generate_client_library() - if payment_strategy is None: - payment_strategy = PaymentStrategy( - concurrent_calls=concurrent_calls - ) - if options is None: options = dict() - options['free_call_auth_token-bin'] = ( - bytes.fromhex(free_call_auth_token_bin) if - free_call_token_expiry_block else "" - ) - options['free-call-token-expiry-block'] = ( - free_call_token_expiry_block if free_call_token_expiry_block else 0 - ) - options['email'] = email if email else "" options['concurrency'] = self._sdk_config.get("concurrency", True) + options['concurrent_calls'] = concurrent_calls + + if payment_strategy is None: + payment_strategy = payment_strategy_type.value() service_metadata = self._metadata_provider.enhance_service_metadata( org_id, service_id @@ -146,7 +146,8 @@ def create_service_client(self, pb2_module = self.get_module_by_keyword(keyword="pb2.py") _service_client = ServiceClient(org_id, service_id, service_metadata, - group, service_stubs, payment_strategy, + group, service_stubs, + payment_strategy, options, self.mpe_contract, self.account, self.web3, pb2_module, self.payment_channel_provider, diff --git a/snet/sdk/account.py b/snet/sdk/account.py index fa88ceb..b88a96f 100644 --- a/snet/sdk/account.py +++ b/snet/sdk/account.py @@ -84,7 +84,7 @@ def _send_signed_transaction(self, contract_fn, *args): signed_txn = self.web3.eth.account.sign_transaction( transaction, private_key=self.private_key) return self.web3.to_hex( - self.web3.eth.send_raw_transaction(signed_txn.rawTransaction) + self.web3.eth.send_raw_transaction(signed_txn.raw_transaction) ) def send_transaction(self, contract_fn, *args): diff --git a/snet/sdk/concurrency_manager.py b/snet/sdk/concurrency_manager.py index 5d86495..7dfb651 100644 --- a/snet/sdk/concurrency_manager.py +++ b/snet/sdk/concurrency_manager.py @@ -3,12 +3,11 @@ import grpc import web3 -from snet.sdk.service_client import ServiceClient from snet.sdk.utils.utils import RESOURCES_PATH, add_to_path class ConcurrencyManager: - def __init__(self, concurrent_calls: int): + def __init__(self, concurrent_calls: int=1): self.__concurrent_calls: int = concurrent_calls self.__token: str = '' self.__planned_amount: int = 0 @@ -18,6 +17,10 @@ def __init__(self, concurrent_calls: int): def concurrent_calls(self) -> int: return self.__concurrent_calls + @concurrent_calls.setter + def concurrent_calls(self, concurrent_calls: int): + self.__concurrent_calls = concurrent_calls + def get_token(self, service_client, channel, service_call_price): if len(self.__token) == 0: self.__token = self.__get_token(service_client, channel, service_call_price) @@ -25,7 +28,7 @@ def get_token(self, service_client, channel, service_call_price): self.__token = self.__get_token(service_client, channel, service_call_price, new_token=True) return self.__token - def __get_token(self, service_client: ServiceClient, channel, service_call_price, new_token=False): + def __get_token(self, service_client, channel, service_call_price, new_token=False): if not new_token: amount = channel.state["last_signed_amount"] if amount != 0: @@ -47,13 +50,13 @@ def __get_token(self, service_client: ServiceClient, channel, service_call_price self.__planned_amount = token_reply.planned_amount return token_reply.token - def __get_stub_for_get_token(self, service_client: ServiceClient): + def __get_stub_for_get_token(self, service_client): grpc_channel = service_client.get_grpc_base_channel() with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): token_service_pb2_grpc = importlib.import_module("token_service_pb2_grpc") return token_service_pb2_grpc.TokenServiceStub(grpc_channel) - def __get_token_for_amount(self, service_client: ServiceClient, channel, amount): + def __get_token_for_amount(self, service_client, channel, amount): nonce = channel.state["nonce"] stub = self.__get_stub_for_get_token(service_client) with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): diff --git a/snet/sdk/mpe/payment_channel.py b/snet/sdk/mpe/payment_channel.py index ead629e..0af915a 100644 --- a/snet/sdk/mpe/payment_channel.py +++ b/snet/sdk/mpe/payment_channel.py @@ -52,7 +52,7 @@ def _get_current_channel_state(self): self.channel_id,current_block_number ] ) - signature = self.web3.eth.account.signHash(defunct_hash_message(message), self.account.signer_private_key).signature + signature = self.web3.eth.account._sign_hash(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( diff --git a/snet/sdk/mpe/payment_channel_provider.py b/snet/sdk/mpe/payment_channel_provider.py index 1948abd..1cefcbe 100644 --- a/snet/sdk/mpe/payment_channel_provider.py +++ b/snet/sdk/mpe/payment_channel_provider.py @@ -18,9 +18,10 @@ def __init__(self, w3, mpe_contract): self.mpe_contract = mpe_contract self.event_topics = [self.web3.keccak( - text="ChannelOpen(uint256,uint256,address,address,address,bytes32,uint256,uint256)").hex()] + text="ChannelOpen(uint256,uint256,address,address,address,bytes32,uint256,uint256)")] self.deployment_block = get_contract_deployment_block(self.web3, "MultiPartyEscrow") self.mpe_address = mpe_contract.contract.address + print(self.mpe_address) self.channels_file = CHANNELS_DIR.joinpath(str(self.mpe_address), "channels.pickle") def update_cache(self): @@ -78,7 +79,7 @@ def _get_all_channels_from_blockchain_logs_to_dicts(self, starting_block_number, "topics": self.event_topics}) from_block = to_block + 1 - event_abi = self.mpe_contract.contract._find_matching_event_abi(event_name="ChannelOpen") + event_abi = self.mpe_contract.contract.events.ChannelOpen._get_event_abi() event_data_list = [get_event_data(codec, event_abi, l)["args"] for l in logs] channels_opened = list(map(self._event_data_args_to_dict, event_data_list)) diff --git a/snet/sdk/payment_strategies/default_payment_strategy.py b/snet/sdk/payment_strategies/default_payment_strategy.py index c66f711..4b189b1 100644 --- a/snet/sdk/payment_strategies/default_payment_strategy.py +++ b/snet/sdk/payment_strategies/default_payment_strategy.py @@ -1,4 +1,3 @@ -from snet.sdk.concurrency_manager import ConcurrencyManager from snet.sdk.payment_strategies.freecall_payment_strategy import FreeCallPaymentStrategy from snet.sdk.payment_strategies.paidcall_payment_strategy import PaidCallPaymentStrategy from snet.sdk.payment_strategies.prepaid_payment_strategy import PrePaidPaymentStrategy @@ -7,26 +6,22 @@ class DefaultPaymentStrategy(PaymentStrategy): - def __init__(self, concurrent_calls: int = 1): - self.concurrent_calls = concurrent_calls - self.concurrencyManager = ConcurrencyManager(concurrent_calls) + def __init__(self): self.channel = None - def set_concurrency_token(self, token): - self.concurrencyManager.__token = token - def set_channel(self, channel): self.channel = channel def get_payment_metadata(self, service_client): free_call_payment_strategy = FreeCallPaymentStrategy() - if free_call_payment_strategy.is_free_call_available(service_client): + if free_call_payment_strategy.get_free_calls_available(service_client) > 0: metadata = free_call_payment_strategy.get_payment_metadata(service_client) else: if service_client.get_concurrency_flag(): - payment_strategy = PrePaidPaymentStrategy(self.concurrencyManager) - metadata = payment_strategy.get_payment_metadata(service_client, self.channel) + concurrent_calls = service_client.get_concurrent_calls() + payment_strategy = PrePaidPaymentStrategy(concurrent_calls) + metadata = payment_strategy.get_payment_metadata(service_client) else: payment_strategy = PaidCallPaymentStrategy() metadata = payment_strategy.get_payment_metadata(service_client) @@ -37,5 +32,6 @@ def get_price(self, service_client): pass def get_concurrency_token_and_channel(self, service_client): - payment_strategy = PrePaidPaymentStrategy(self.concurrencyManager) + concurrent_calls = service_client.get_concurrent_calls() + payment_strategy = PrePaidPaymentStrategy(concurrent_calls) return payment_strategy.get_concurrency_token_and_channel(service_client) diff --git a/snet/sdk/payment_strategies/freecall_payment_strategy.py b/snet/sdk/payment_strategies/freecall_payment_strategy.py index aae70b6..099a4d7 100644 --- a/snet/sdk/payment_strategies/freecall_payment_strategy.py +++ b/snet/sdk/payment_strategies/freecall_payment_strategy.py @@ -1,88 +1,101 @@ import importlib -from urllib.parse import urlparse import grpc import web3 -from snet.sdk.resources.root_certificate import certificate -from snet.sdk.utils.utils import RESOURCES_PATH, add_to_path from snet.sdk.payment_strategies.payment_strategy import PaymentStrategy - +from snet.sdk.utils.utils import RESOURCES_PATH, add_to_path class FreeCallPaymentStrategy(PaymentStrategy): - def is_free_call_available(self, service_client): + def __init__(self): + self._user_address = None + self._free_call_token = None + self._token_expiration_block = None + + def get_free_calls_available(self, service_client) -> int: + if not self._user_address: + self._user_address = service_client.account.signer_address + + current_block_number = service_client.get_current_block_number() + + if (not self._free_call_token or + not self._token_expiration_block or + current_block_number > self._token_expiration_block): + self._free_call_token, self._token_expiration_block = self.get_free_call_token_details(service_client) + + with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): + state_service_pb2 = importlib.import_module("state_service_pb2") + + with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): + state_service_pb2_grpc = importlib.import_module("state_service_pb2_grpc") + + signature, _ = self.generate_signature(service_client, current_block_number) + request = state_service_pb2.FreeCallStateRequest( + address=self._user_address, + free_call_token=self._free_call_token, + signature=signature, + current_block=current_block_number + ) + + channel = service_client.get_grpc_base_channel() + stub = state_service_pb2_grpc.FreeCallStateServiceStub(channel) + try: - org_id, service_id, group_id, daemon_endpoint = service_client.get_service_details() - email, token_for_free_call, token_expiry_date_block = service_client.get_free_call_config() - - if not token_for_free_call: - return False - - signature, current_block_number = self.generate_signature(service_client) - with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): - state_service_pb2 = importlib.import_module("state_service_pb2") - - with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): - state_service_pb2_grpc = importlib.import_module("state_service_pb2_grpc") - - request = state_service_pb2.FreeCallStateRequest() - request.user_id = email - request.token_for_free_call = token_for_free_call - request.token_expiry_date_block = token_expiry_date_block - request.signature = signature - request.current_block = current_block_number - - endpoint_object = urlparse(daemon_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": - channel = grpc.insecure_channel(channel_endpoint) - elif endpoint_object.scheme == "https": - channel = grpc.secure_channel(channel_endpoint, grpc.ssl_channel_credentials(root_certificates=certificate)) - else: - raise ValueError('Unsupported scheme in service metadata ("{}")'.format(endpoint_object.scheme)) - - stub = state_service_pb2_grpc.FreeCallStateServiceStub(channel) response = stub.GetFreeCallsAvailable(request) - if response.free_calls_available > 0: - return True - return False - except Exception as e: - return False - - def get_payment_metadata(self, service_client): - email, token_for_free_call, token_expiry_date_block = service_client.get_free_call_config() + return response.free_calls_available + except grpc.RpcError as e: + if self._user_address: + print(f"Warning: {e.details()}") + return 0 + + def get_payment_metadata(self, service_client) -> list: + if self.get_free_calls_available(service_client) <= 0: + raise Exception(f"Free calls limit for address {self._user_address} has expired. Please use another payment strategy") signature, current_block_number = self.generate_signature(service_client) - metadata = [("snet-free-call-auth-token-bin", token_for_free_call), - ("snet-free-call-token-expiry-block", str(token_expiry_date_block)), + metadata = [("snet-free-call-auth-token-bin", self._free_call_token), ("snet-payment-type", "free-call"), - ("snet-free-call-user-id", email), + ("snet-free-call-user-address", self._user_address), ("snet-current-block-number", str(current_block_number)), ("snet-payment-channel-signature-bin", signature)] return metadata - def select_channel(self, service_client): - pass + def generate_signature(self, service_client, current_block_number=None, with_token=True) -> tuple[bytes, int]: + if not current_block_number: + current_block_number = service_client.get_current_block_number() + org_id, service_id, group_id, _ = service_client.get_service_details() - def generate_signature(self, service_client): - org_id, service_id, group_id, daemon_endpoint = service_client.get_service_details() - email, token_for_free_call, token_expiry_date_block = service_client.get_free_call_config() + message_types = ["string", "string", "string", "string", "string", "uint256", "bytes32"] + message_values = ["__prefix_free_trial", self._user_address, org_id, service_id, group_id, + current_block_number, self._free_call_token] - if token_expiry_date_block == 0 or len(email) == 0 or len(token_for_free_call) == 0: - raise Exception( - "You are using default 'FreeCallPaymentStrategy' to use this strategy you need to pass " - "'free_call_auth_token-bin','email','free-call-token-expiry-block' in config") + if not with_token: + message_types = message_types[:-1] + message_values = message_values[:-1] - current_block_number = service_client.get_current_block_number() + message = web3.Web3.solidity_keccak(message_types, message_values) + return service_client.generate_signature(message), current_block_number + + def get_free_call_token_details(self, service_client, current_block_number=None) -> tuple[bytes, int]: + + signature, current_block_number = self.generate_signature(service_client, current_block_number, with_token=False) - message = web3.Web3.solidity_keccak( - ["string", "string", "string", "string", "string", "uint256", "bytes32"], - ["__prefix_free_trial", email, org_id, service_id, group_id, current_block_number, - token_for_free_call] + with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): + state_service_pb2 = importlib.import_module("state_service_pb2") + + request = state_service_pb2.GetFreeCallTokenRequest( + address=self._user_address, + signature=signature, + current_block=current_block_number ) - return service_client.generate_signature(message), current_block_number + + with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): + state_service_pb2_grpc = importlib.import_module("state_service_pb2_grpc") + + channel = service_client.get_grpc_base_channel() + stub = state_service_pb2_grpc.FreeCallStateServiceStub(channel) + response = stub.GetFreeCallToken(request) + + return response.token, response.token_expiration_block + \ No newline at end of file diff --git a/snet/sdk/payment_strategies/prepaid_payment_strategy.py b/snet/sdk/payment_strategies/prepaid_payment_strategy.py index 3c6f016..4e41539 100644 --- a/snet/sdk/payment_strategies/prepaid_payment_strategy.py +++ b/snet/sdk/payment_strategies/prepaid_payment_strategy.py @@ -4,18 +4,19 @@ class PrePaidPaymentStrategy(PaymentStrategy): - def __init__(self, concurrency_manager: ConcurrencyManager, - block_offset: int = 240, call_allowance: int = 1): - self.concurrency_manager = concurrency_manager + def __init__(self, concurrent_calls: int=1, block_offset: int = 240, call_allowance: int = 1): + self.concurrency_manager = ConcurrencyManager(concurrent_calls) self.block_offset = block_offset self.call_allowance = call_allowance def get_price(self, service_client): return service_client.get_price() * self.concurrency_manager.concurrent_calls - def get_payment_metadata(self, service_client, channel): - if channel is None: - channel = self.select_channel(service_client) + def set_concurrent_calls(self, concurrent_calls): + self.concurrency_manager.concurrent_calls = concurrent_calls + + def get_payment_metadata(self, service_client): + channel = self.select_channel(service_client) token = self.concurrency_manager.get_token(service_client, channel, self.get_price(service_client)) metadata = [ ("snet-payment-type", "prepaid-call"), diff --git a/snet/sdk/resources/proto/state_service.proto b/snet/sdk/resources/proto/state_service.proto index a2ce9aa..0b8217a 100644 --- a/snet/sdk/resources/proto/state_service.proto +++ b/snet/sdk/resources/proto/state_service.proto @@ -1,81 +1,122 @@ -syntax = "proto3"; - -package escrow; - -option java_package = "io.singularitynet.daemon.escrow"; - -// PaymentChannelStateService contains methods to get the MultiPartyEscrow -// payment channel state. -// channel_id, channel_nonce, value and amount fields below in fact are -// Solidity uint256 values. Which are big-endian integers, see -// https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI#formal-specification-of-the-encoding -// These values may be or may be not padded by zeros, service supports both -// options. -service PaymentChannelStateService { - // GetChannelState method returns a channel state by channel id. - rpc GetChannelState(ChannelStateRequest) returns (ChannelStateReply) {} -} - -// ChanelStateRequest is a request for channel state. -message ChannelStateRequest { - // channel_id contains id of the channel which state is requested. - bytes channel_id = 1; - - // signature is a client signature of the message which contains - // channel_id. It is used for client authorization. - bytes signature = 2; - - //current block number (signature will be valid only for short time around this block number) - uint64 current_block = 3; -} - -// ChannelStateReply message contains a latest channel state. current_nonce and -// current_value fields can be different from ones stored in the blockchain if -// server started withdrawing funds froms channel but transaction is still not -// finished. -message ChannelStateReply { - // current_nonce is a latest nonce of the payment channel. - bytes current_nonce = 1; - - // current_signed_amount is a last amount which were signed by client with current_nonce - //it could be absent if none message was signed with current_nonce - bytes current_signed_amount = 2; - - // current_signature is a last signature sent by client with current_nonce - // it could be absent if none message was signed with current nonce - bytes current_signature = 3; - - // last amount which was signed by client with nonce=current_nonce - 1 - bytes old_nonce_signed_amount = 4; - - // last signature sent by client with nonce = current_nonce - 1 - bytes old_nonce_signature = 5; - } - -//Used to determine free calls available for a given user. -service FreeCallStateService { - rpc GetFreeCallsAvailable(FreeCallStateRequest) returns (FreeCallStateReply) {} -} - -message FreeCallStateRequest { - //Has the user email id - string user_id = 1; - //signer-token = (user@mail, user-public-key, token_issue_date), this is generated my Market place Dapp - //to leverage free calls from SDK/ snet-cli, you will need this signer-token to be downloaded from Dapp - bytes token_for_free_call = 2; - //Token expiration date in Block number - uint64 token_expiry_date_block = 3 ; - //Signature is made up of the below, user signs with the private key corresponding with the public key used to generate the authorized token - //free-call-metadata = ("__prefix_free_trial",user_id,organization_id,service_id,group_id,current_block,authorized_token) - bytes signature = 4; - //current block number (signature will be valid only for short time around this block number) - uint64 current_block = 5; - -} - -message FreeCallStateReply { - //Has the user email id - string user_id = 1; - //Balance number of free calls available - uint64 free_calls_available = 2; -} +syntax = "proto3"; + +package escrow; + +option java_package = "io.singularitynet.daemon.escrow"; +option go_package = "../escrow"; + +// PaymentChannelStateService contains methods to get the MultiPartyEscrow +// payment channel state. +// channel_id, channel_nonce, value and amount fields below in fact are +// Solidity uint256 values. Which are big-endian integers, see +// https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI#formal-specification-of-the-encoding +// These values may be or may be not padded by zeros, service supports both +// options. +service PaymentChannelStateService { + // GetChannelState method returns a channel state by channel id. + rpc GetChannelState(ChannelStateRequest) returns (ChannelStateReply) {} +} + +// ChanelStateRequest is a request for channel state. +message ChannelStateRequest { + // channel_id contains id of the channel which state is requested. + bytes channel_id = 1; + + // signature is a client signature of the message which contains + // channel_id. It is used for client authorization. + bytes signature = 2; + + //current block number (signature will be valid only for short time around this block number) + uint64 current_block = 3; +} + +// ChannelStateReply message contains a latest channel state. current_nonce and +// current_value fields can be different from ones stored in the blockchain if +// server started withdrawing funds froms channel but transaction is still not +// finished. +message ChannelStateReply { + // current_nonce is a latest nonce of the payment channel. + bytes current_nonce = 1; + + // current_signed_amount is a last amount which were signed by client with current_nonce + //it could be absent if none message was signed with current_nonce + bytes current_signed_amount = 2; + + // current_signature is a last signature sent by client with current_nonce + // it could be absent if none message was signed with current nonce + bytes current_signature = 3; + + // last amount which was signed by client with nonce=current_nonce - 1 + bytes old_nonce_signed_amount = 4; + + // last signature sent by client with nonce = current_nonce - 1 + bytes old_nonce_signature = 5; + + //If the client / user chooses to sign upfront , the planned amount in cogs will be indicative of this. + //For pay per use, this will be zero + uint64 planned_amount = 6; + + //If the client / user chooses to sign upfront , the usage amount in cogs will be indicative of how much of the + //planned amount has actually been used. + //For pay per use, this will be zero + uint64 used_amount = 7; +} + +//Used to determine free calls available for a given user. +service FreeCallStateService { + rpc GetFreeCallsAvailable(FreeCallStateRequest) returns (FreeCallStateReply) {} + rpc GetFreeCallToken(GetFreeCallTokenRequest) returns (FreeCallToken) {} +} + +message GetFreeCallTokenRequest{ + // required for all calls + string address = 1; + + // ("__prefix_free_trial",user_address, user_id, organization_id, service_id, group_id, current_block) + bytes signature = 2; + + uint64 current_block = 3; + + // only for calls for trusted signers + optional string user_id = 4; + + // Duration of the token's validity, measured in blocks. + // For example, if the average block time is ~12 seconds, then 100 blocks โ‰ˆ 20 minutes. + // Max value: 172800 + optional uint64 token_lifetime_in_blocks = 5; +} + + +message FreeCallToken{ + // Token with expiration block encoded in the format: _ + // Example: [binary signature][0x5f][ascii decimal block number] + bytes token = 1; + + // Hex-encoded representation of the `token` field + string token_hex = 2; + + // token_expiration_block = currentBlock + token_lifetime_in_blocks (deadline block) + uint64 token_expiration_block = 3; +} + +message FreeCallStateRequest { + string address = 1; + + // optional, specify if you trusted signer + optional string user_id = 2; + + // Previously issued token from GetFreeCallToken + bytes free_call_token = 3; + + // Signature is made up of the below, user signs with the private key corresponding with the public key used to generate the authorized token + // ("__prefix_free_trial",user_address, user_id, organization_id, service_id, group_id, current_block, token) + bytes signature = 4; + + //current block number (signature will be valid only for short time around this block number) + uint64 current_block = 5; +} + +message FreeCallStateReply { + // number of free calls available + uint64 free_calls_available = 1; +} diff --git a/snet/sdk/resources/proto/state_service_pb2.py b/snet/sdk/resources/proto/state_service_pb2.py index d298f8a..1661c44 100644 --- a/snet/sdk/resources/proto/state_service_pb2.py +++ b/snet/sdk/resources/proto/state_service_pb2.py @@ -13,24 +13,28 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13state_service.proto\x12\x06\x65scrow\"S\n\x13\x43hannelStateRequest\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\x12\x15\n\rcurrent_block\x18\x03 \x01(\x04\"\xa2\x01\n\x11\x43hannelStateReply\x12\x15\n\rcurrent_nonce\x18\x01 \x01(\x0c\x12\x1d\n\x15\x63urrent_signed_amount\x18\x02 \x01(\x0c\x12\x19\n\x11\x63urrent_signature\x18\x03 \x01(\x0c\x12\x1f\n\x17old_nonce_signed_amount\x18\x04 \x01(\x0c\x12\x1b\n\x13old_nonce_signature\x18\x05 \x01(\x0c\"\x8f\x01\n\x14\x46reeCallStateRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x1b\n\x13token_for_free_call\x18\x02 \x01(\x0c\x12\x1f\n\x17token_expiry_date_block\x18\x03 \x01(\x04\x12\x11\n\tsignature\x18\x04 \x01(\x0c\x12\x15\n\rcurrent_block\x18\x05 \x01(\x04\"C\n\x12\x46reeCallStateReply\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x1c\n\x14\x66ree_calls_available\x18\x02 \x01(\x04\x32i\n\x1aPaymentChannelStateService\x12K\n\x0fGetChannelState\x12\x1b.escrow.ChannelStateRequest\x1a\x19.escrow.ChannelStateReply\"\x00\x32k\n\x14\x46reeCallStateService\x12S\n\x15GetFreeCallsAvailable\x12\x1c.escrow.FreeCallStateRequest\x1a\x1a.escrow.FreeCallStateReply\"\x00\x42!\n\x1fio.singularitynet.daemon.escrowb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13state_service.proto\x12\x06\x65scrow\"S\n\x13\x43hannelStateRequest\x12\x12\n\nchannel_id\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\x12\x15\n\rcurrent_block\x18\x03 \x01(\x04\"\xcf\x01\n\x11\x43hannelStateReply\x12\x15\n\rcurrent_nonce\x18\x01 \x01(\x0c\x12\x1d\n\x15\x63urrent_signed_amount\x18\x02 \x01(\x0c\x12\x19\n\x11\x63urrent_signature\x18\x03 \x01(\x0c\x12\x1f\n\x17old_nonce_signed_amount\x18\x04 \x01(\x0c\x12\x1b\n\x13old_nonce_signature\x18\x05 \x01(\x0c\x12\x16\n\x0eplanned_amount\x18\x06 \x01(\x04\x12\x13\n\x0bused_amount\x18\x07 \x01(\x04\"\xba\x01\n\x17GetFreeCallTokenRequest\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x11\n\tsignature\x18\x02 \x01(\x0c\x12\x15\n\rcurrent_block\x18\x03 \x01(\x04\x12\x14\n\x07user_id\x18\x04 \x01(\tH\x00\x88\x01\x01\x12%\n\x18token_lifetime_in_blocks\x18\x05 \x01(\x04H\x01\x88\x01\x01\x42\n\n\x08_user_idB\x1b\n\x19_token_lifetime_in_blocks\"Q\n\rFreeCallToken\x12\r\n\x05token\x18\x01 \x01(\x0c\x12\x11\n\ttoken_hex\x18\x02 \x01(\t\x12\x1e\n\x16token_expiration_block\x18\x03 \x01(\x04\"\x8c\x01\n\x14\x46reeCallStateRequest\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x14\n\x07user_id\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x17\n\x0f\x66ree_call_token\x18\x03 \x01(\x0c\x12\x11\n\tsignature\x18\x04 \x01(\x0c\x12\x15\n\rcurrent_block\x18\x05 \x01(\x04\x42\n\n\x08_user_id\"2\n\x12\x46reeCallStateReply\x12\x1c\n\x14\x66ree_calls_available\x18\x01 \x01(\x04\x32i\n\x1aPaymentChannelStateService\x12K\n\x0fGetChannelState\x12\x1b.escrow.ChannelStateRequest\x1a\x19.escrow.ChannelStateReply\"\x00\x32\xb9\x01\n\x14\x46reeCallStateService\x12S\n\x15GetFreeCallsAvailable\x12\x1c.escrow.FreeCallStateRequest\x1a\x1a.escrow.FreeCallStateReply\"\x00\x12L\n\x10GetFreeCallToken\x12\x1f.escrow.GetFreeCallTokenRequest\x1a\x15.escrow.FreeCallToken\"\x00\x42,\n\x1fio.singularitynet.daemon.escrowZ\t../escrowb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'state_service_pb2', _globals) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\037io.singularitynet.daemon.escrow' + DESCRIPTOR._serialized_options = b'\n\037io.singularitynet.daemon.escrowZ\t../escrow' _globals['_CHANNELSTATEREQUEST']._serialized_start=31 _globals['_CHANNELSTATEREQUEST']._serialized_end=114 _globals['_CHANNELSTATEREPLY']._serialized_start=117 - _globals['_CHANNELSTATEREPLY']._serialized_end=279 - _globals['_FREECALLSTATEREQUEST']._serialized_start=282 - _globals['_FREECALLSTATEREQUEST']._serialized_end=425 - _globals['_FREECALLSTATEREPLY']._serialized_start=427 - _globals['_FREECALLSTATEREPLY']._serialized_end=494 - _globals['_PAYMENTCHANNELSTATESERVICE']._serialized_start=496 - _globals['_PAYMENTCHANNELSTATESERVICE']._serialized_end=601 - _globals['_FREECALLSTATESERVICE']._serialized_start=603 - _globals['_FREECALLSTATESERVICE']._serialized_end=710 + _globals['_CHANNELSTATEREPLY']._serialized_end=324 + _globals['_GETFREECALLTOKENREQUEST']._serialized_start=327 + _globals['_GETFREECALLTOKENREQUEST']._serialized_end=513 + _globals['_FREECALLTOKEN']._serialized_start=515 + _globals['_FREECALLTOKEN']._serialized_end=596 + _globals['_FREECALLSTATEREQUEST']._serialized_start=599 + _globals['_FREECALLSTATEREQUEST']._serialized_end=739 + _globals['_FREECALLSTATEREPLY']._serialized_start=741 + _globals['_FREECALLSTATEREPLY']._serialized_end=791 + _globals['_PAYMENTCHANNELSTATESERVICE']._serialized_start=793 + _globals['_PAYMENTCHANNELSTATESERVICE']._serialized_end=898 + _globals['_FREECALLSTATESERVICE']._serialized_start=901 + _globals['_FREECALLSTATESERVICE']._serialized_end=1086 # @@protoc_insertion_point(module_scope) diff --git a/snet/sdk/resources/proto/state_service_pb2_grpc.py b/snet/sdk/resources/proto/state_service_pb2_grpc.py index 977dbd7..6c47edb 100644 --- a/snet/sdk/resources/proto/state_service_pb2_grpc.py +++ b/snet/sdk/resources/proto/state_service_pb2_grpc.py @@ -103,6 +103,11 @@ def __init__(self, channel): request_serializer=state__service__pb2.FreeCallStateRequest.SerializeToString, response_deserializer=state__service__pb2.FreeCallStateReply.FromString, ) + self.GetFreeCallToken = channel.unary_unary( + '/escrow.FreeCallStateService/GetFreeCallToken', + request_serializer=state__service__pb2.GetFreeCallTokenRequest.SerializeToString, + response_deserializer=state__service__pb2.FreeCallToken.FromString, + ) class FreeCallStateServiceServicer(object): @@ -115,6 +120,12 @@ def GetFreeCallsAvailable(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def GetFreeCallToken(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_FreeCallStateServiceServicer_to_server(servicer, server): rpc_method_handlers = { @@ -123,6 +134,11 @@ def add_FreeCallStateServiceServicer_to_server(servicer, server): request_deserializer=state__service__pb2.FreeCallStateRequest.FromString, response_serializer=state__service__pb2.FreeCallStateReply.SerializeToString, ), + 'GetFreeCallToken': grpc.unary_unary_rpc_method_handler( + servicer.GetFreeCallToken, + request_deserializer=state__service__pb2.GetFreeCallTokenRequest.FromString, + response_serializer=state__service__pb2.FreeCallToken.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( 'escrow.FreeCallStateService', rpc_method_handlers) @@ -150,3 +166,20 @@ def GetFreeCallsAvailable(request, state__service__pb2.FreeCallStateReply.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def GetFreeCallToken(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/escrow.FreeCallStateService/GetFreeCallToken', + state__service__pb2.GetFreeCallTokenRequest.SerializeToString, + state__service__pb2.FreeCallToken.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/snet/sdk/service_client.py b/snet/sdk/service_client.py index 9294e59..9e367b6 100644 --- a/snet/sdk/service_client.py +++ b/snet/sdk/service_client.py @@ -5,19 +5,19 @@ from pathlib import Path from typing import Any -from eth_typing import BlockNumber, ChecksumAddress +from eth_typing import BlockNumber import grpc from hexbytes import HexBytes import web3 from eth_account.messages import defunct_hash_message from rfc3986 import urlparse -from snet.sdk import generic_client_interceptor +from snet.sdk import generic_client_interceptor, FreeCallPaymentStrategy from snet.sdk.account import Account from snet.sdk.mpe.mpe_contract import MPEContract from snet.sdk.mpe.payment_channel import PaymentChannel from snet.sdk.mpe.payment_channel_provider import PaymentChannelProvider -from snet.sdk.payment_strategies import default_payment_strategy as strategy +from snet.sdk.payment_strategies.prepaid_payment_strategy import PrePaidPaymentStrategy from snet.sdk.resources.root_certificate import certificate from snet.sdk.storage_provider.service_metadata import MPEServiceMetadata from snet.sdk.custom_typing import ModuleName, ServiceStub @@ -51,6 +51,8 @@ def __init__( self.service_metadata = service_metadata self.group = group self.payment_strategy = payment_strategy + if isinstance(payment_strategy, PrePaidPaymentStrategy): + self.payment_strategy.set_concurrent_calls(options["concurrent_calls"]) self.options = options self.mpe_address = mpe_contract.contract.address self.account = account @@ -197,7 +199,7 @@ def get_price(self) -> int: return self.group["pricing"][0]["price_in_cogs"] def generate_signature(self, message: bytes) -> bytes: - return bytes(self.sdk_web3.eth.account.signHash( + return bytes(self.sdk_web3.eth.account._sign_hash( defunct_hash_message(message), self.account.signer_private_key ).signature) @@ -208,15 +210,10 @@ def generate_training_signature(self, text: str, address: str, ["string", "address", "uint256"], [text, address, block_number] ) - return self.sdk_web3.eth.account.signHash( + return self.sdk_web3.eth.account._sign_hash( defunct_hash_message(message), self.account.signer_private_key ).signature - def get_free_call_config(self) -> tuple[str, str, int]: - return (self.options['email'], - self.options['free_call_auth_token-bin'], - self.options['free-call-token-expiry-block']) - def get_service_details(self) -> tuple[str, str, str, str]: return (self.org_id, self.service_id, @@ -240,6 +237,9 @@ def get_concurrency_flag(self) -> bool: def get_concurrency_token_and_channel(self) -> tuple[str, PaymentChannel]: return self.payment_strategy.get_concurrency_token_and_channel(self) + def get_concurrent_calls(self): + return self.options.get('concurrent_calls', 1) + def set_concurrency_token_and_channel(self, token: str, channel: PaymentChannel) -> None: self.payment_strategy.concurrency_token = token @@ -319,3 +319,10 @@ def get_services_and_messages_info_as_pretty_string(self) -> str: for field_type, field_name in fields: string_output += f" Field: {field_type} {field_name}\n" return string_output + + def get_free_calls_available(self) -> int: + payment_strategy = self.payment_strategy + if not isinstance(payment_strategy, FreeCallPaymentStrategy): + payment_strategy = FreeCallPaymentStrategy() + + return payment_strategy.get_free_calls_available(self) diff --git a/snet/sdk/utils/utils.py b/snet/sdk/utils/utils.py index b036be8..281840e 100644 --- a/snet/sdk/utils/utils.py +++ b/snet/sdk/utils/utils.py @@ -1,5 +1,4 @@ import json -import subprocess import sys import importlib.resources from urllib.parse import urlparse diff --git a/testcases/functional_tests/test_sdk_client.py b/testcases/functional_tests/test_sdk_client.py index f83f59b..c94c3b5 100644 --- a/testcases/functional_tests/test_sdk_client.py +++ b/testcases/functional_tests/test_sdk_client.py @@ -2,12 +2,12 @@ import os from snet import sdk +from snet.sdk import PaymentStrategyType class TestSDKClient(unittest.TestCase): def setUp(self): self.service_client = get_test_service_data() - channel = self.service_client.deposit_and_open_channel(123456, 33333) def test_call_to_service(self): result = self.service_client.call_rpc("mul", "Numbers", a=20, b=3) @@ -17,13 +17,13 @@ def test_call_to_service(self): def get_test_service_data(): config = sdk.config.Config(private_key=os.environ['SNET_TEST_WALLET_PRIVATE_KEY'], eth_rpc_endpoint=f"https://sepolia.infura.io/v3/{os.environ['SNET_TEST_INFURA_KEY']}", - concurrency=False, - force_update=False) + concurrency=False) snet_sdk = sdk.SnetSDK(config) service_client = snet_sdk.create_service_client(org_id="26072b8b6a0e448180f8c0e702ab6d2f", - service_id="Exampleservice", group_name="default_group") + service_id="Exampleservice", group_name="default_group", + payment_strategy_type=PaymentStrategyType.PAID_CALL) return service_client diff --git a/tests/unit_tests/test_service_client.py b/tests/unit_tests/test_service_client.py index d04ba37..93e2383 100644 --- a/tests/unit_tests/test_service_client.py +++ b/tests/unit_tests/test_service_client.py @@ -140,7 +140,7 @@ def test_get_current_block_number(self): def test_generate_signature(self): message = b"test_message" mock_signature = MagicMock() - self.client.sdk_web3.eth.account.signHash = MagicMock( + self.client.sdk_web3.eth.account._sign_hash = MagicMock( return_value=MagicMock(signature=mock_signature) ) result = self.client.generate_signature(message) @@ -153,7 +153,7 @@ def test_generate_training_signature(self, mock_solidity_keccak): block_number = "test_block_number" mock_solidity_keccak.return_value = b"test_message" mock_signature = MagicMock() - self.client.sdk_web3.eth.account.signHash = MagicMock( + self.client.sdk_web3.eth.account._sign_hash = MagicMock( return_value=MagicMock(signature=mock_signature) ) result = self.client.generate_training_signature(text, address, diff --git a/version.py b/version.py index ce1305b..76ad18b 100644 --- a/version.py +++ b/version.py @@ -1 +1 @@ -__version__ = "4.0.0" +__version__ = "4.0.1"