py-near 1.1.37__py3-none-any.whl → 1.1.38__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
py_near/account.py CHANGED
@@ -394,6 +394,7 @@ class Account(object):
394
394
  method_name: str,
395
395
  args: dict,
396
396
  block_id: Optional[int] = None,
397
+ threshold: Optional[int] = None,
397
398
  ) -> ViewFunctionResult:
398
399
  """
399
400
  Call view function on smart contract. View function is read only function, it can't change state
@@ -401,10 +402,11 @@ class Account(object):
401
402
  :param method_name: method name to call
402
403
  :param args: json args to call method
403
404
  :param block_id: execution view transaction in block with given id
405
+ :param threshold: minimal amount of nodes with same result
404
406
  :return: result of view function call
405
407
  """
406
408
  result = await self._provider.view_call(
407
- contract_id, method_name, json.dumps(args).encode("utf8"), block_id=block_id
409
+ contract_id, method_name, json.dumps(args).encode("utf8"), block_id=block_id, threshold=threshold
408
410
  )
409
411
  if "error" in result:
410
412
  raise ViewFunctionError(result["error"])
py_near/providers.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import asyncio
2
2
  import base64
3
3
  import json
4
+ from collections import Counter
4
5
  from typing import Optional
5
6
 
6
7
  import aiohttp
@@ -116,12 +117,13 @@ class JsonProvider(object):
116
117
  self._available_rpcs = available_rpcs
117
118
 
118
119
  async def call_rpc_request(
119
- self, method, params, timeout=TIMEOUT_WAIT_RPC, broadcast=False
120
+ self, method, params, timeout=TIMEOUT_WAIT_RPC, broadcast=False, threshold=None
120
121
  ):
121
122
  await self.check_available_rpcs()
122
123
  j = {"method": method, "params": params, "id": "dontcare", "jsonrpc": "2.0"}
123
124
  res = {}
124
- if broadcast:
125
+ if broadcast or threshold:
126
+
125
127
  async def f(rpc_call_addr):
126
128
  async with aiohttp.ClientSession() as session:
127
129
  r = await session.post(
@@ -132,23 +134,45 @@ class JsonProvider(object):
132
134
  "Referer": "https://tgapp.herewallet.app"
133
135
  }, # NEAR RPC requires Referer header
134
136
  )
135
- r.raise_for_status()
136
- res = json.loads(await r.text())
137
- return res
137
+ if r.status == 200:
138
+ return json.loads(await r.text())
138
139
 
139
140
  tasks = [
140
141
  asyncio.create_task(f(rpc_addr)) for rpc_addr in self._available_rpcs
141
142
  ]
142
- res = None
143
+ responses = []
143
144
  for t in tasks:
144
145
  try:
145
- res = await t
146
- if "error" not in res:
147
- return res
146
+ responses.append(await t)
148
147
  except Exception as e:
149
148
  logger.error(f"Rpc error: {e}")
150
149
  continue
151
- return res
150
+
151
+ def most_frequent_by_hash(array):
152
+ counter = Counter(array)
153
+ most_frequent = counter.most_common(1)[0][0]
154
+ return most_frequent
155
+
156
+ if threshold:
157
+ # return first most frequent response
158
+ array = [hash(json.dumps(x)) for x in responses]
159
+ most_frequent_element = most_frequent_by_hash(array)
160
+ correct_responses = [
161
+ x for x in responses if hash(json.dumps(x)) == most_frequent_element
162
+ ]
163
+ if len(correct_responses) >= threshold:
164
+ return responses[0]
165
+ raise Exception(
166
+ f"Threshold not reached: {len(correct_responses)}/{threshold}"
167
+ )
168
+
169
+ if broadcast:
170
+ # return first response without errors
171
+ for res in responses:
172
+ if "error" not in res:
173
+ return res
174
+ return responses[0]
175
+
152
176
  for rpc_addr in self._available_rpcs:
153
177
  try:
154
178
  async with aiohttp.ClientSession() as session:
@@ -196,9 +220,11 @@ class JsonProvider(object):
196
220
  break
197
221
  return error
198
222
 
199
- async def json_rpc(self, method, params, timeout=TIMEOUT_WAIT_RPC, broadcast=False):
223
+ async def json_rpc(
224
+ self, method, params, timeout=TIMEOUT_WAIT_RPC, broadcast=False, threshold=None
225
+ ):
200
226
  content = await self.call_rpc_request(
201
- method, params, timeout, broadcast=broadcast
227
+ method, params, timeout, broadcast=broadcast, threshold=threshold
202
228
  )
203
229
  if not content:
204
230
  raise RpcEmptyResponse("RPC returned empty response")
@@ -358,6 +384,7 @@ class JsonProvider(object):
358
384
  args,
359
385
  finality="optimistic",
360
386
  block_id: Optional[int] = None,
387
+ threshold: Optional[int] = None,
361
388
  ):
362
389
  body = {
363
390
  "request_type": "call_function",
@@ -369,7 +396,7 @@ class JsonProvider(object):
369
396
  body["block_id"] = block_id
370
397
  else:
371
398
  body["finality"] = finality
372
- return await self.json_rpc("query", body)
399
+ return await self.json_rpc("query", body, threshold=threshold)
373
400
 
374
401
  async def get_block(self, block_id):
375
402
  return await self.json_rpc("block", [block_id])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: py-near
3
- Version: 1.1.37
3
+ Version: 1.1.38
4
4
  Summary: Pretty simple and fully asynchronous framework for working with NEAR blockchain
5
5
  Author: pvolnov
6
6
  Author-email: petr@herewallet.app
@@ -1,5 +1,5 @@
1
1
  py_near/__init__.py,sha256=t5fAxjaU8dN8xpQR2vz0ZGhfTkdVy2RCbkhJhZFglk4,50
2
- py_near/account.py,sha256=kg3Nhq1vaOh10amO8gadBlGCpOADWbL1H8zm5rXBQJ8,17488
2
+ py_near/account.py,sha256=CX0fYvWJj8g7QANLUr3G5eUvO347UW1jlunphN47Heg,17617
3
3
  py_near/constants.py,sha256=inaWIuwmF1EB5JSB0ynnZY5rKY_QsxhF9KuCOhPsM6k,164
4
4
  py_near/dapps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  py_near/dapps/core.py,sha256=LtN9aW2gw2mvEdhzQcQJIidtjv-XL1xjb0LK8DzqtqE,231
@@ -20,10 +20,10 @@ py_near/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
20
20
  py_near/exceptions/exceptions.py,sha256=DEFipaAHm0y7oCuN2QKzHsiQvUTUQVl-Ce36Ag7n7hs,5509
21
21
  py_near/exceptions/provider.py,sha256=K-wexgjPJ8sw42JePwaP7R5dJEIn9DoFJRvVcURsx6s,7718
22
22
  py_near/models.py,sha256=GZQD1TKGWlwqsJsKRXrVNBjCdAIpk7GQypU-QOtAPFs,11533
23
- py_near/providers.py,sha256=fUEMTpy-LadTZ32lVmGjKKPrTgm3cCFVGatMeWaAa9c,15120
23
+ py_near/providers.py,sha256=A_h587h5e-HXJeko7z2YdDw0SkC3snNJ8hk90POOge8,16190
24
24
  py_near/transactions.py,sha256=QAXegv2JpKISk92NaChtIH6-QPHrcWbrwdKH_lH4TsU,3186
25
25
  py_near/utils.py,sha256=FirRH93ydH1cwjn0-sNrZeIn3BRD6QHedrP2VkAdJ6g,126
26
- py_near-1.1.37.dist-info/LICENSE,sha256=I_GOA9xJ35FiL-KnYXZJdATkbO2KcV2dK2enRGVxzKM,1023
27
- py_near-1.1.37.dist-info/METADATA,sha256=DA8ZhE1CgjLDaxvKImj1ASgwUXwYSYxvvJelUwiov_Y,4713
28
- py_near-1.1.37.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
29
- py_near-1.1.37.dist-info/RECORD,,
26
+ py_near-1.1.38.dist-info/LICENSE,sha256=I_GOA9xJ35FiL-KnYXZJdATkbO2KcV2dK2enRGVxzKM,1023
27
+ py_near-1.1.38.dist-info/METADATA,sha256=YD1v8lUXSE_IgAcDNcwkEPbY16lL12k3Ri_YORwqmO0,4713
28
+ py_near-1.1.38.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
29
+ py_near-1.1.38.dist-info/RECORD,,