|
1 | 1 | import importlib |
2 | | -from urllib.parse import urlparse |
3 | 2 |
|
4 | 3 | import grpc |
5 | | -from grpc import _channel |
6 | 4 | import web3 |
7 | 5 |
|
8 | 6 | from snet.sdk.payment_strategies.payment_strategy import PaymentStrategy |
9 | | -from snet.sdk.resources.root_certificate import certificate |
10 | 7 | from snet.sdk.utils.utils import RESOURCES_PATH, add_to_path |
11 | 8 |
|
12 | 9 | class FreeCallPaymentStrategy(PaymentStrategy): |
13 | 10 |
|
14 | | - def is_free_call_available(self, service_client) -> bool: |
15 | | - try: |
16 | | - self._user_address = service_client.options["user_address"] |
17 | | - self._free_call_token, self._token_expiry_date_block = self.get_free_call_token_details(service_client) |
| 11 | + def __init__(self): |
| 12 | + self._user_address = None |
| 13 | + self._free_call_token = None |
| 14 | + self._token_expiration_block = None |
18 | 15 |
|
19 | | - if not self._free_call_token: |
20 | | - return False |
| 16 | + def get_free_calls_available(self, service_client) -> int: |
| 17 | + if not self._user_address: |
| 18 | + self._user_address = service_client.account.signer_address |
21 | 19 |
|
22 | | - with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): |
23 | | - state_service_pb2 = importlib.import_module("state_service_pb2") |
| 20 | + current_block_number = service_client.get_current_block_number() |
24 | 21 |
|
25 | | - with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): |
26 | | - state_service_pb2_grpc = importlib.import_module("state_service_pb2_grpc") |
| 22 | + if (not self._free_call_token or |
| 23 | + not self._token_expiration_block or |
| 24 | + current_block_number > self._token_expiration_block): |
| 25 | + self._free_call_token, self._token_expiration_block = self.get_free_call_token_details(service_client) |
27 | 26 |
|
28 | | - signature, current_block_number = self.generate_signature(service_client) |
| 27 | + with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): |
| 28 | + state_service_pb2 = importlib.import_module("state_service_pb2") |
29 | 29 |
|
30 | | - request = state_service_pb2.FreeCallStateRequest() |
31 | | - request.user_address = self._user_address |
32 | | - request.token_for_free_call = self._free_call_token |
33 | | - request.token_expiry_date_block = self._token_expiry_date_block |
34 | | - request.signature = signature |
35 | | - request.current_block = current_block_number |
| 30 | + with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): |
| 31 | + state_service_pb2_grpc = importlib.import_module("state_service_pb2_grpc") |
36 | 32 |
|
37 | | - channel = self.select_channel(service_client) |
| 33 | + signature, _ = self.generate_signature(service_client, current_block_number) |
| 34 | + request = state_service_pb2.FreeCallStateRequest( |
| 35 | + address=self._user_address, |
| 36 | + free_call_token=self._free_call_token, |
| 37 | + signature=signature, |
| 38 | + current_block=current_block_number |
| 39 | + ) |
38 | 40 |
|
39 | | - stub = state_service_pb2_grpc.FreeCallStateServiceStub(channel) |
| 41 | + channel = service_client.get_grpc_base_channel() |
| 42 | + stub = state_service_pb2_grpc.FreeCallStateServiceStub(channel) |
| 43 | + |
| 44 | + try: |
40 | 45 | response = stub.GetFreeCallsAvailable(request) |
41 | | - if response.free_calls_available > 0: |
42 | | - return True |
43 | | - return False |
| 46 | + return response.free_calls_available |
44 | 47 | except grpc.RpcError as e: |
45 | 48 | if self._user_address: |
46 | 49 | print(f"Warning: {e.details()}") |
47 | | - return False |
48 | | - except Exception as e: |
49 | | - return False |
| 50 | + return 0 |
50 | 51 |
|
51 | 52 | def get_payment_metadata(self, service_client) -> list: |
| 53 | + if self.get_free_calls_available(service_client) <= 0: |
| 54 | + raise Exception(f"Free calls limit for address {self._user_address} has expired. Please use another payment strategy") |
52 | 55 | signature, current_block_number = self.generate_signature(service_client) |
53 | 56 | metadata = [("snet-free-call-auth-token-bin", self._free_call_token), |
54 | | - ("snet-free-call-token-expiry-block", str(self._token_expiry_date_block)), |
55 | 57 | ("snet-payment-type", "free-call"), |
56 | | - ("snet-free-call-user-id", self._user_address), |
| 58 | + ("snet-free-call-user-address", self._user_address), |
57 | 59 | ("snet-current-block-number", str(current_block_number)), |
58 | 60 | ("snet-payment-channel-signature-bin", signature)] |
59 | 61 |
|
60 | 62 | return metadata |
61 | 63 |
|
62 | | - def select_channel(self, service_client) -> _channel.Channel: |
63 | | - _, _, _, daemon_endpoint = service_client.get_service_details() |
64 | | - endpoint_object = urlparse(daemon_endpoint) |
65 | | - if endpoint_object.port is not None: |
66 | | - channel_endpoint = endpoint_object.hostname + ":" + str(endpoint_object.port) |
67 | | - else: |
68 | | - channel_endpoint = endpoint_object.hostname |
69 | | - |
70 | | - if endpoint_object.scheme == "http": |
71 | | - channel = grpc.insecure_channel(channel_endpoint) |
72 | | - elif endpoint_object.scheme == "https": |
73 | | - channel = grpc.secure_channel(channel_endpoint, grpc.ssl_channel_credentials(root_certificates=certificate)) |
74 | | - else: |
75 | | - raise ValueError('Unsupported scheme in service metadata ("{}")'.format(endpoint_object.scheme)) |
76 | | - return channel |
77 | | - |
78 | | - def generate_signature(self, service_client) -> tuple[bytes, int]: |
| 64 | + def generate_signature(self, service_client, current_block_number=None, with_token=True) -> tuple[bytes, int]: |
| 65 | + if not current_block_number: |
| 66 | + current_block_number = service_client.get_current_block_number() |
79 | 67 | org_id, service_id, group_id, _ = service_client.get_service_details() |
80 | 68 |
|
81 | | - if self._token_expiry_date_block == 0 or len(self._user_address) == 0 or len(self._free_call_token) == 0: |
82 | | - raise Exception( |
83 | | - "You are using default 'FreeCallPaymentStrategy' to use this strategy you need to pass " |
84 | | - "'free_call_auth_token-bin','user_address','free-call-token-expiry-block' in config") |
| 69 | + message_types = ["string", "string", "string", "string", "string", "uint256", "bytes32"] |
| 70 | + message_values = ["__prefix_free_trial", self._user_address, org_id, service_id, group_id, |
| 71 | + current_block_number, self._free_call_token] |
85 | 72 |
|
86 | | - current_block_number = service_client.get_current_block_number() |
| 73 | + if not with_token: |
| 74 | + message_types = message_types[:-1] |
| 75 | + message_values = message_values[:-1] |
87 | 76 |
|
88 | | - message = web3.Web3.solidity_keccak( |
89 | | - ["string", "string", "string", "string", "string", "uint256", "bytes32"], |
90 | | - ["__prefix_free_trial", self._user_address, org_id, service_id, group_id, current_block_number, |
91 | | - self._free_call_token] |
92 | | - ) |
| 77 | + message = web3.Web3.solidity_keccak(message_types, message_values) |
93 | 78 | return service_client.generate_signature(message), current_block_number |
94 | 79 |
|
95 | | - def get_free_call_token_details(self, service_client) -> tuple[bytes, int]: |
| 80 | + def get_free_call_token_details(self, service_client, current_block_number=None) -> tuple[bytes, int]: |
| 81 | + |
| 82 | + signature, current_block_number = self.generate_signature(service_client, current_block_number, with_token=False) |
| 83 | + |
96 | 84 | with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): |
97 | 85 | state_service_pb2 = importlib.import_module("state_service_pb2") |
98 | 86 |
|
99 | | - request = state_service_pb2.GetFreeCallTokenRequest() |
100 | | - request.address = self._user_address |
| 87 | + request = state_service_pb2.GetFreeCallTokenRequest( |
| 88 | + address=self._user_address, |
| 89 | + signature=signature, |
| 90 | + current_block=current_block_number |
| 91 | + ) |
101 | 92 |
|
102 | 93 | with add_to_path(str(RESOURCES_PATH.joinpath("proto"))): |
103 | 94 | state_service_pb2_grpc = importlib.import_module("state_service_pb2_grpc") |
104 | 95 |
|
105 | | - channel = self.select_channel(service_client) |
| 96 | + channel = service_client.get_grpc_base_channel() |
106 | 97 | stub = state_service_pb2_grpc.FreeCallStateServiceStub(channel) |
107 | 98 | response = stub.GetFreeCallToken(request) |
108 | 99 |
|
|
0 commit comments