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 +3 -1
- py_near/providers.py +40 -13
- {py_near-1.1.37.dist-info → py_near-1.1.38.dist-info}/METADATA +1 -1
- {py_near-1.1.37.dist-info → py_near-1.1.38.dist-info}/RECORD +6 -6
- {py_near-1.1.37.dist-info → py_near-1.1.38.dist-info}/LICENSE +0 -0
- {py_near-1.1.37.dist-info → py_near-1.1.38.dist-info}/WHEEL +0 -0
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.
|
136
|
-
|
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
|
-
|
143
|
+
responses = []
|
143
144
|
for t in tasks:
|
144
145
|
try:
|
145
|
-
|
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
|
-
|
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(
|
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,5 +1,5 @@
|
|
1
1
|
py_near/__init__.py,sha256=t5fAxjaU8dN8xpQR2vz0ZGhfTkdVy2RCbkhJhZFglk4,50
|
2
|
-
py_near/account.py,sha256=
|
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=
|
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.
|
27
|
-
py_near-1.1.
|
28
|
-
py_near-1.1.
|
29
|
-
py_near-1.1.
|
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,,
|
File without changes
|
File without changes
|