diff --git a/snet/sdk/__init__.py b/snet/sdk/__init__.py index 3bdf743..89a2585 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 +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,8 +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, + group_name: str=None, + payment_strategy: PaymentStrategy = None, + payment_strategy_type: PaymentStrategyType=PaymentStrategyType.DEFAULT, address=None, options=None, concurrent_calls: int = 1): @@ -118,15 +130,14 @@ def create_service_client(self, print("Generating client library...") self.lib_generator.generate_client_library() - if payment_strategy is None: - payment_strategy = DefaultPaymentStrategy( - concurrent_calls=concurrent_calls - ) - if options is None: options = dict() options['user_address'] = address if address 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 @@ -137,7 +148,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/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/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 5134b50..099a4d7 100644 --- a/snet/sdk/payment_strategies/freecall_payment_strategy.py +++ b/snet/sdk/payment_strategies/freecall_payment_strategy.py @@ -1,108 +1,99 @@ import importlib -from urllib.parse import urlparse import grpc -from grpc import _channel import web3 from snet.sdk.payment_strategies.payment_strategy import PaymentStrategy -from snet.sdk.resources.root_certificate import certificate from snet.sdk.utils.utils import RESOURCES_PATH, add_to_path class FreeCallPaymentStrategy(PaymentStrategy): - def is_free_call_available(self, service_client) -> bool: - try: - self._user_address = service_client.options["user_address"] - self._free_call_token, self._token_expiry_date_block = self.get_free_call_token_details(service_client) + def __init__(self): + self._user_address = None + self._free_call_token = None + self._token_expiration_block = None - if not self._free_call_token: - return False + def get_free_calls_available(self, service_client) -> int: + if not self._user_address: + self._user_address = service_client.account.signer_address - with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): - state_service_pb2 = importlib.import_module("state_service_pb2") + current_block_number = service_client.get_current_block_number() - with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): - state_service_pb2_grpc = importlib.import_module("state_service_pb2_grpc") + 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) - 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") - request = state_service_pb2.FreeCallStateRequest() - request.user_address = self._user_address - request.token_for_free_call = self._free_call_token - request.token_expiry_date_block = self._token_expiry_date_block - request.signature = signature - request.current_block = current_block_number + with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): + state_service_pb2_grpc = importlib.import_module("state_service_pb2_grpc") - channel = self.select_channel(service_client) + 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 + ) - stub = state_service_pb2_grpc.FreeCallStateServiceStub(channel) + channel = service_client.get_grpc_base_channel() + stub = state_service_pb2_grpc.FreeCallStateServiceStub(channel) + + try: response = stub.GetFreeCallsAvailable(request) - if response.free_calls_available > 0: - return True - return False + return response.free_calls_available except grpc.RpcError as e: if self._user_address: print(f"Warning: {e.details()}") - return False - except Exception as e: - return False + 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", self._free_call_token), - ("snet-free-call-token-expiry-block", str(self._token_expiry_date_block)), ("snet-payment-type", "free-call"), - ("snet-free-call-user-id", self._user_address), + ("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) -> _channel.Channel: - _, _, _, daemon_endpoint = service_client.get_service_details() - 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)) - return channel - - def generate_signature(self, service_client) -> tuple[bytes, int]: + 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() - if self._token_expiry_date_block == 0 or len(self._user_address) == 0 or len(self._free_call_token) == 0: - raise Exception( - "You are using default 'FreeCallPaymentStrategy' to use this strategy you need to pass " - "'free_call_auth_token-bin','user_address','free-call-token-expiry-block' in 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] - current_block_number = service_client.get_current_block_number() + if not with_token: + message_types = message_types[:-1] + message_values = message_values[:-1] - message = web3.Web3.solidity_keccak( - ["string", "string", "string", "string", "string", "uint256", "bytes32"], - ["__prefix_free_trial", self._user_address, org_id, service_id, group_id, current_block_number, - self._free_call_token] - ) + 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) -> tuple[bytes, int]: + 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) + with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): state_service_pb2 = importlib.import_module("state_service_pb2") - request = state_service_pb2.GetFreeCallTokenRequest() - request.address = self._user_address + request = state_service_pb2.GetFreeCallTokenRequest( + address=self._user_address, + signature=signature, + current_block=current_block_number + ) with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): state_service_pb2_grpc = importlib.import_module("state_service_pb2_grpc") - channel = self.select_channel(service_client) + channel = service_client.get_grpc_base_channel() stub = state_service_pb2_grpc.FreeCallStateServiceStub(channel) response = stub.GetFreeCallToken(request) 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 e6d505a..0b8217a 100644 --- a/snet/sdk/resources/proto/state_service.proto +++ b/snet/sdk/resources/proto/state_service.proto @@ -69,24 +69,49 @@ service FreeCallStateService { } 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_hex == token but just in another format - string token_hex = 1; - bytes token = 2; + // 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 user_address = 1; - 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_address ,organization_id,service_id,group_id,current_block,authorized_token) + 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; } diff --git a/snet/sdk/resources/proto/state_service_pb2.py b/snet/sdk/resources/proto/state_service_pb2.py index 8d2530d..1661c44 100644 --- a/snet/sdk/resources/proto/state_service_pb2.py +++ b/snet/sdk/resources/proto/state_service_pb2.py @@ -13,7 +13,7 @@ -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\"*\n\x17GetFreeCallTokenRequest\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\"Q\n\rFreeCallToken\x12\x11\n\ttoken_hex\x18\x01 \x01(\t\x12\r\n\x05token\x18\x02 \x01(\x0c\x12\x1e\n\x16token_expiration_block\x18\x03 \x01(\x04\"\x94\x01\n\x14\x46reeCallStateRequest\x12\x14\n\x0cuser_address\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\"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') +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) @@ -25,16 +25,16 @@ _globals['_CHANNELSTATEREQUEST']._serialized_end=114 _globals['_CHANNELSTATEREPLY']._serialized_start=117 _globals['_CHANNELSTATEREPLY']._serialized_end=324 - _globals['_GETFREECALLTOKENREQUEST']._serialized_start=326 - _globals['_GETFREECALLTOKENREQUEST']._serialized_end=368 - _globals['_FREECALLTOKEN']._serialized_start=370 - _globals['_FREECALLTOKEN']._serialized_end=451 - _globals['_FREECALLSTATEREQUEST']._serialized_start=454 - _globals['_FREECALLSTATEREQUEST']._serialized_end=602 - _globals['_FREECALLSTATEREPLY']._serialized_start=604 - _globals['_FREECALLSTATEREPLY']._serialized_end=654 - _globals['_PAYMENTCHANNELSTATESERVICE']._serialized_start=656 - _globals['_PAYMENTCHANNELSTATESERVICE']._serialized_end=761 - _globals['_FREECALLSTATESERVICE']._serialized_start=764 - _globals['_FREECALLSTATESERVICE']._serialized_end=949 + _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 e29ae1d..6c47edb 100644 --- a/snet/sdk/resources/proto/state_service_pb2_grpc.py +++ b/snet/sdk/resources/proto/state_service_pb2_grpc.py @@ -6,13 +6,13 @@ class PaymentChannelStateServiceStub(object): - """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. + """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. """ def __init__(self, channel): @@ -29,17 +29,17 @@ def __init__(self, channel): class PaymentChannelStateServiceServicer(object): - """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. + """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. """ def GetChannelState(self, request, context): - """GetChannelState method returns a channel state by channel id. + """GetChannelState method returns a channel state by channel id. """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') @@ -61,13 +61,13 @@ def add_PaymentChannelStateServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. class PaymentChannelStateService(object): - """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. + """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. """ @staticmethod @@ -89,7 +89,7 @@ def GetChannelState(request, class FreeCallStateServiceStub(object): - """Used to determine free calls available for a given user. + """Used to determine free calls available for a given user. """ def __init__(self, channel): @@ -111,7 +111,7 @@ def __init__(self, channel): class FreeCallStateServiceServicer(object): - """Used to determine free calls available for a given user. + """Used to determine free calls available for a given user. """ def GetFreeCallsAvailable(self, request, context): @@ -147,7 +147,7 @@ def add_FreeCallStateServiceServicer_to_server(servicer, server): # This class is part of an EXPERIMENTAL API. class FreeCallStateService(object): - """Used to determine free calls available for a given user. + """Used to determine free calls available for a given user. """ @staticmethod diff --git a/snet/sdk/service_client.py b/snet/sdk/service_client.py index ffc35b6..bf840d3 100644 --- a/snet/sdk/service_client.py +++ b/snet/sdk/service_client.py @@ -12,12 +12,12 @@ 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 @@ -240,6 +242,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 +324,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