py-near 1.1.36__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 +9 -2
- py_near/providers.py +44 -15
- {py_near-1.1.36.dist-info → py_near-1.1.38.dist-info}/METADATA +1 -1
- {py_near-1.1.36.dist-info → py_near-1.1.38.dist-info}/RECORD +6 -6
- {py_near-1.1.36.dist-info → py_near-1.1.38.dist-info}/LICENSE +0 -0
- {py_near-1.1.36.dist-info → py_near-1.1.38.dist-info}/WHEEL +0 -0
py_near/account.py
CHANGED
@@ -15,6 +15,7 @@ from py_near.dapps.ft.async_client import FT
|
|
15
15
|
from py_near.dapps.staking.async_client import Staking
|
16
16
|
from py_near.exceptions.provider import (
|
17
17
|
JsonProviderError,
|
18
|
+
RPCTimeoutError,
|
18
19
|
)
|
19
20
|
from py_near.models import (
|
20
21
|
TransactionResult,
|
@@ -151,7 +152,11 @@ class Account(object):
|
|
151
152
|
|
152
153
|
try:
|
153
154
|
if included:
|
154
|
-
|
155
|
+
try:
|
156
|
+
await self._provider.send_tx_included(serialized_tx)
|
157
|
+
except RPCTimeoutError as e:
|
158
|
+
if "Transaction not included" in str(e):
|
159
|
+
logger.error(f"Transaction not included {trx_hash}")
|
155
160
|
return trx_hash
|
156
161
|
elif nowait:
|
157
162
|
return await self._provider.send_tx(serialized_tx)
|
@@ -389,6 +394,7 @@ class Account(object):
|
|
389
394
|
method_name: str,
|
390
395
|
args: dict,
|
391
396
|
block_id: Optional[int] = None,
|
397
|
+
threshold: Optional[int] = None,
|
392
398
|
) -> ViewFunctionResult:
|
393
399
|
"""
|
394
400
|
Call view function on smart contract. View function is read only function, it can't change state
|
@@ -396,10 +402,11 @@ class Account(object):
|
|
396
402
|
:param method_name: method name to call
|
397
403
|
:param args: json args to call method
|
398
404
|
:param block_id: execution view transaction in block with given id
|
405
|
+
:param threshold: minimal amount of nodes with same result
|
399
406
|
:return: result of view function call
|
400
407
|
"""
|
401
408
|
result = await self._provider.view_call(
|
402
|
-
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
|
403
410
|
)
|
404
411
|
if "error" in result:
|
405
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
|
@@ -23,7 +24,8 @@ from py_near.exceptions.provider import (
|
|
23
24
|
InvalidTransactionError,
|
24
25
|
RPCTimeoutError,
|
25
26
|
UnknownAccessKeyError,
|
26
|
-
ERROR_CODE_TO_EXCEPTION,
|
27
|
+
ERROR_CODE_TO_EXCEPTION,
|
28
|
+
InvalidNonce,
|
27
29
|
)
|
28
30
|
from py_near.models import TransactionResult
|
29
31
|
|
@@ -115,12 +117,13 @@ class JsonProvider(object):
|
|
115
117
|
self._available_rpcs = available_rpcs
|
116
118
|
|
117
119
|
async def call_rpc_request(
|
118
|
-
self, method, params, timeout=TIMEOUT_WAIT_RPC, broadcast=False
|
120
|
+
self, method, params, timeout=TIMEOUT_WAIT_RPC, broadcast=False, threshold=None
|
119
121
|
):
|
120
122
|
await self.check_available_rpcs()
|
121
123
|
j = {"method": method, "params": params, "id": "dontcare", "jsonrpc": "2.0"}
|
122
124
|
res = {}
|
123
|
-
if broadcast:
|
125
|
+
if broadcast or threshold:
|
126
|
+
|
124
127
|
async def f(rpc_call_addr):
|
125
128
|
async with aiohttp.ClientSession() as session:
|
126
129
|
r = await session.post(
|
@@ -128,22 +131,48 @@ class JsonProvider(object):
|
|
128
131
|
json=j,
|
129
132
|
timeout=timeout,
|
130
133
|
headers={
|
131
|
-
"Referer": "https://tgapp.herewallet.app
|
134
|
+
"Referer": "https://tgapp.herewallet.app"
|
132
135
|
}, # NEAR RPC requires Referer header
|
133
136
|
)
|
134
|
-
r.
|
135
|
-
|
136
|
-
|
137
|
+
if r.status == 200:
|
138
|
+
return json.loads(await r.text())
|
139
|
+
|
137
140
|
tasks = [
|
138
141
|
asyncio.create_task(f(rpc_addr)) for rpc_addr in self._available_rpcs
|
139
142
|
]
|
143
|
+
responses = []
|
140
144
|
for t in tasks:
|
141
145
|
try:
|
142
|
-
|
143
|
-
return res
|
146
|
+
responses.append(await t)
|
144
147
|
except Exception as e:
|
145
148
|
logger.error(f"Rpc error: {e}")
|
146
149
|
continue
|
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
|
+
|
147
176
|
for rpc_addr in self._available_rpcs:
|
148
177
|
try:
|
149
178
|
async with aiohttp.ClientSession() as session:
|
@@ -191,9 +220,11 @@ class JsonProvider(object):
|
|
191
220
|
break
|
192
221
|
return error
|
193
222
|
|
194
|
-
async def json_rpc(
|
223
|
+
async def json_rpc(
|
224
|
+
self, method, params, timeout=TIMEOUT_WAIT_RPC, broadcast=False, threshold=None
|
225
|
+
):
|
195
226
|
content = await self.call_rpc_request(
|
196
|
-
method, params, timeout, broadcast=broadcast
|
227
|
+
method, params, timeout, broadcast=broadcast, threshold=threshold
|
197
228
|
)
|
198
229
|
if not content:
|
199
230
|
raise RpcEmptyResponse("RPC returned empty response")
|
@@ -236,9 +267,6 @@ class JsonProvider(object):
|
|
236
267
|
except InvalidNonce:
|
237
268
|
logger.warning("Invalid nonce during broadcast included transaction")
|
238
269
|
return None
|
239
|
-
except RPCTimeoutError:
|
240
|
-
raise RPCTimeoutError("Transaction not included")
|
241
|
-
|
242
270
|
|
243
271
|
async def wait_for_trx(self, trx_hash, receiver_id) -> TransactionResult:
|
244
272
|
for _ in range(6):
|
@@ -356,6 +384,7 @@ class JsonProvider(object):
|
|
356
384
|
args,
|
357
385
|
finality="optimistic",
|
358
386
|
block_id: Optional[int] = None,
|
387
|
+
threshold: Optional[int] = None,
|
359
388
|
):
|
360
389
|
body = {
|
361
390
|
"request_type": "call_function",
|
@@ -367,7 +396,7 @@ class JsonProvider(object):
|
|
367
396
|
body["block_id"] = block_id
|
368
397
|
else:
|
369
398
|
body["finality"] = finality
|
370
|
-
return await self.json_rpc("query", body)
|
399
|
+
return await self.json_rpc("query", body, threshold=threshold)
|
371
400
|
|
372
401
|
async def get_block(self, block_id):
|
373
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
|