tonutils 2.0.1b3__py3-none-any.whl → 2.0.1b5__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.
- tonutils/__meta__.py +1 -1
- tonutils/cli.py +111 -0
- tonutils/clients/__init__.py +4 -4
- tonutils/clients/adnl/__init__.py +4 -4
- tonutils/clients/adnl/balancer.py +58 -58
- tonutils/clients/adnl/client.py +20 -20
- tonutils/clients/adnl/provider/config.py +13 -8
- tonutils/clients/adnl/provider/provider.py +39 -42
- tonutils/clients/adnl/provider/transport.py +30 -25
- tonutils/clients/base.py +5 -1
- tonutils/exceptions.py +41 -31
- tonutils/tonconnect/__init__.py +0 -0
- tonutils/tools/__init__.py +6 -0
- tonutils/tools/block_scanner/__init__.py +16 -0
- tonutils/tools/block_scanner/annotations.py +23 -0
- tonutils/tools/block_scanner/dispatcher.py +141 -0
- tonutils/tools/block_scanner/events.py +31 -0
- tonutils/tools/block_scanner/scanner.py +313 -0
- tonutils/tools/block_scanner/traversal.py +97 -0
- tonutils/tools/block_scanner/where.py +53 -0
- tonutils/tools/status_monitor/__init__.py +3 -0
- tonutils/tools/status_monitor/console.py +157 -0
- tonutils/tools/status_monitor/models.py +27 -0
- tonutils/tools/status_monitor/monitor.py +295 -0
- tonutils/types.py +12 -4
- {tonutils-2.0.1b3.dist-info → tonutils-2.0.1b5.dist-info}/METADATA +2 -5
- {tonutils-2.0.1b3.dist-info → tonutils-2.0.1b5.dist-info}/RECORD +31 -16
- tonutils-2.0.1b5.dist-info/entry_points.txt +2 -0
- {tonutils-2.0.1b3.dist-info → tonutils-2.0.1b5.dist-info}/WHEEL +0 -0
- {tonutils-2.0.1b3.dist-info → tonutils-2.0.1b5.dist-info}/licenses/LICENSE +0 -0
- {tonutils-2.0.1b3.dist-info → tonutils-2.0.1b5.dist-info}/top_level.txt +0 -0
tonutils/__meta__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2.0.
|
|
1
|
+
__version__ = "2.0.1b5"
|
tonutils/cli.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import asyncio
|
|
3
|
+
import typing as t
|
|
4
|
+
|
|
5
|
+
from tonutils.__meta__ import __version__
|
|
6
|
+
from tonutils.clients.adnl.provider.config import (
|
|
7
|
+
get_mainnet_global_config,
|
|
8
|
+
get_testnet_global_config,
|
|
9
|
+
load_global_config,
|
|
10
|
+
)
|
|
11
|
+
from tonutils.tools.status_monitor import LiteServerMonitor
|
|
12
|
+
from tonutils.types import NetworkGlobalID
|
|
13
|
+
|
|
14
|
+
NETWORK_MAP: t.Dict[str, NetworkGlobalID] = {
|
|
15
|
+
"mainnet": NetworkGlobalID.MAINNET,
|
|
16
|
+
"testnet": NetworkGlobalID.TESTNET,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def parse_network(value: str) -> NetworkGlobalID:
|
|
21
|
+
value = value.lower().strip()
|
|
22
|
+
if value not in NETWORK_MAP:
|
|
23
|
+
raise argparse.ArgumentTypeError(f"Unknown network: {value}")
|
|
24
|
+
return NETWORK_MAP[value]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def cmd_status(args: argparse.Namespace) -> None:
|
|
28
|
+
if args.config:
|
|
29
|
+
config = load_global_config(args.config)
|
|
30
|
+
else:
|
|
31
|
+
config_getter = {
|
|
32
|
+
NetworkGlobalID.MAINNET: get_mainnet_global_config,
|
|
33
|
+
NetworkGlobalID.TESTNET: get_testnet_global_config,
|
|
34
|
+
}
|
|
35
|
+
config = config_getter[args.network]()
|
|
36
|
+
|
|
37
|
+
async def _run() -> None:
|
|
38
|
+
monitor = LiteServerMonitor.from_config(
|
|
39
|
+
config=config,
|
|
40
|
+
network=args.network,
|
|
41
|
+
rps_limit=args.rps,
|
|
42
|
+
)
|
|
43
|
+
try:
|
|
44
|
+
await monitor.run()
|
|
45
|
+
finally:
|
|
46
|
+
await monitor.stop()
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
asyncio.run(_run())
|
|
50
|
+
except KeyboardInterrupt:
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _create_parser() -> argparse.ArgumentParser:
|
|
55
|
+
parser = argparse.ArgumentParser(
|
|
56
|
+
prog="tonutils",
|
|
57
|
+
description="Tonutils CLI.",
|
|
58
|
+
)
|
|
59
|
+
parser.add_argument(
|
|
60
|
+
"-v",
|
|
61
|
+
"--version",
|
|
62
|
+
action="version",
|
|
63
|
+
version=f"tonutils {__version__}",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
subs = parser.add_subparsers(dest="command", metavar="command")
|
|
67
|
+
|
|
68
|
+
status = subs.add_parser(
|
|
69
|
+
"status",
|
|
70
|
+
help="Monitor lite-servers status.",
|
|
71
|
+
)
|
|
72
|
+
status.add_argument(
|
|
73
|
+
"-n",
|
|
74
|
+
"--network",
|
|
75
|
+
type=parse_network,
|
|
76
|
+
metavar="NET",
|
|
77
|
+
default=NetworkGlobalID.MAINNET,
|
|
78
|
+
help="mainnet (default) or testnet",
|
|
79
|
+
)
|
|
80
|
+
status.add_argument(
|
|
81
|
+
"-c",
|
|
82
|
+
"--config",
|
|
83
|
+
type=str,
|
|
84
|
+
metavar="PATH",
|
|
85
|
+
help="Config file path or URL",
|
|
86
|
+
)
|
|
87
|
+
status.add_argument(
|
|
88
|
+
"-r",
|
|
89
|
+
"--rps",
|
|
90
|
+
type=int,
|
|
91
|
+
metavar="N",
|
|
92
|
+
default=100,
|
|
93
|
+
help="Requests per second (default: 100)",
|
|
94
|
+
)
|
|
95
|
+
status.set_defaults(func=cmd_status)
|
|
96
|
+
|
|
97
|
+
return parser
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def main() -> None:
|
|
101
|
+
parser = _create_parser()
|
|
102
|
+
args = parser.parse_args()
|
|
103
|
+
|
|
104
|
+
if hasattr(args, "func"):
|
|
105
|
+
args.func(args)
|
|
106
|
+
else:
|
|
107
|
+
parser.print_help()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
if __name__ == "__main__":
|
|
111
|
+
main()
|
tonutils/clients/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from .adnl import (
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
LiteBalancer,
|
|
3
|
+
LiteClient,
|
|
4
4
|
AdnlProvider,
|
|
5
5
|
)
|
|
6
6
|
from .http import (
|
|
@@ -15,8 +15,8 @@ from .http import (
|
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
__all__ = [
|
|
18
|
-
"
|
|
19
|
-
"
|
|
18
|
+
"LiteBalancer",
|
|
19
|
+
"LiteClient",
|
|
20
20
|
"AdnlProvider",
|
|
21
21
|
"HttpBalancer",
|
|
22
22
|
"ChainstackHttpClient",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
from .balancer import
|
|
2
|
-
from .client import
|
|
1
|
+
from .balancer import LiteBalancer
|
|
2
|
+
from .client import LiteClient
|
|
3
3
|
from .provider import AdnlProvider
|
|
4
4
|
|
|
5
5
|
__all__ = [
|
|
6
|
-
"
|
|
7
|
-
"
|
|
6
|
+
"LiteBalancer",
|
|
7
|
+
"LiteClient",
|
|
8
8
|
"AdnlProvider",
|
|
9
9
|
]
|
|
@@ -9,7 +9,7 @@ from itertools import cycle
|
|
|
9
9
|
|
|
10
10
|
from pytoniq_core import Address, BlockIdExt, Block, Transaction
|
|
11
11
|
|
|
12
|
-
from tonutils.clients.adnl.client import
|
|
12
|
+
from tonutils.clients.adnl.client import LiteClient
|
|
13
13
|
from tonutils.clients.adnl.provider import AdnlProvider
|
|
14
14
|
from tonutils.clients.adnl.provider.config import (
|
|
15
15
|
get_mainnet_global_config,
|
|
@@ -40,21 +40,21 @@ _T = t.TypeVar("_T")
|
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
@dataclass
|
|
43
|
-
class
|
|
43
|
+
class LiteClientState:
|
|
44
44
|
"""
|
|
45
|
-
Internal state container for
|
|
45
|
+
Internal state container for a lite-server client.
|
|
46
46
|
|
|
47
47
|
Tracks error count and cooldown timeout for retry scheduling.
|
|
48
48
|
"""
|
|
49
49
|
|
|
50
|
-
client:
|
|
50
|
+
client: LiteClient
|
|
51
51
|
retry_after: t.Optional[float] = None
|
|
52
52
|
error_count: int = 0
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
class
|
|
55
|
+
class LiteBalancer(BaseClient):
|
|
56
56
|
"""
|
|
57
|
-
Multi-
|
|
57
|
+
Multi-client lite-server balancer with automatic failover and load balancing.
|
|
58
58
|
|
|
59
59
|
Selects the best available lite-server using height, ping metrics and
|
|
60
60
|
round-robin tie-breaking.
|
|
@@ -66,14 +66,14 @@ class AdnlBalancer(BaseClient):
|
|
|
66
66
|
self,
|
|
67
67
|
*,
|
|
68
68
|
network: NetworkGlobalID = NetworkGlobalID.MAINNET,
|
|
69
|
-
clients: t.List[
|
|
69
|
+
clients: t.List[LiteClient],
|
|
70
70
|
connect_timeout: float = 2.0,
|
|
71
71
|
request_timeout: float = 12.0,
|
|
72
72
|
) -> None:
|
|
73
73
|
"""
|
|
74
|
-
Initialize
|
|
74
|
+
Initialize lite-server balancer.
|
|
75
75
|
|
|
76
|
-
It is recommended to build underlying
|
|
76
|
+
It is recommended to build underlying LiteClient instances from
|
|
77
77
|
private lite-server configurations for better stability and performance.
|
|
78
78
|
You can obtain private lite-server configs from:
|
|
79
79
|
- Tonconsole website: https://tonconsole.com/
|
|
@@ -82,15 +82,15 @@ class AdnlBalancer(BaseClient):
|
|
|
82
82
|
Public free lite-server data may also be used via `from_network_config()`.
|
|
83
83
|
|
|
84
84
|
:param network: Target TON network (mainnet or testnet)
|
|
85
|
-
:param clients: List of
|
|
85
|
+
:param clients: List of LiteClient instances to balance between
|
|
86
86
|
:param connect_timeout: Timeout in seconds for connect/reconnect attempts
|
|
87
87
|
:param request_timeout: Maximum total time in seconds for a balancer operation,
|
|
88
|
-
including all failover attempts across
|
|
88
|
+
including all failover attempts across lite-servers
|
|
89
89
|
"""
|
|
90
90
|
self.network: NetworkGlobalID = network
|
|
91
91
|
|
|
92
|
-
self._clients: t.List[
|
|
93
|
-
self._states: t.List[
|
|
92
|
+
self._clients: t.List[LiteClient] = []
|
|
93
|
+
self._states: t.List[LiteClientState] = []
|
|
94
94
|
self.__init_clients(clients)
|
|
95
95
|
|
|
96
96
|
self._rr = cycle(self._clients)
|
|
@@ -106,30 +106,30 @@ class AdnlBalancer(BaseClient):
|
|
|
106
106
|
|
|
107
107
|
def __init_clients(
|
|
108
108
|
self,
|
|
109
|
-
clients: t.List[
|
|
109
|
+
clients: t.List[LiteClient],
|
|
110
110
|
) -> None:
|
|
111
111
|
"""
|
|
112
|
-
Validate and register input
|
|
112
|
+
Validate and register input lite-server clients.
|
|
113
113
|
|
|
114
114
|
Ensures correct client type and network assignment.
|
|
115
115
|
"""
|
|
116
116
|
for client in clients:
|
|
117
117
|
if client.TYPE != ClientType.ADNL:
|
|
118
118
|
raise ClientError(
|
|
119
|
-
"
|
|
119
|
+
"LiteBalancer can work only with LiteClient instances, "
|
|
120
120
|
f"got {client.__class__.__name__}."
|
|
121
121
|
)
|
|
122
122
|
|
|
123
123
|
client.network = self.network
|
|
124
124
|
|
|
125
|
-
state =
|
|
125
|
+
state = LiteClientState(client=client)
|
|
126
126
|
self._clients.append(client)
|
|
127
127
|
self._states.append(state)
|
|
128
128
|
|
|
129
129
|
@property
|
|
130
130
|
def provider(self) -> AdnlProvider:
|
|
131
131
|
"""
|
|
132
|
-
Provider of the currently selected
|
|
132
|
+
Provider of the currently selected lite-server client.
|
|
133
133
|
|
|
134
134
|
:return: AdnlProvider instance of chosen client
|
|
135
135
|
"""
|
|
@@ -139,27 +139,27 @@ class AdnlBalancer(BaseClient):
|
|
|
139
139
|
@property
|
|
140
140
|
def is_connected(self) -> bool:
|
|
141
141
|
"""
|
|
142
|
-
Check whether at least one underlying
|
|
142
|
+
Check whether at least one underlying lite-server client is connected.
|
|
143
143
|
|
|
144
144
|
:return: True if any client is connected, otherwise False
|
|
145
145
|
"""
|
|
146
146
|
return any(c.is_connected for c in self._clients)
|
|
147
147
|
|
|
148
148
|
@property
|
|
149
|
-
def clients(self) -> t.Tuple[
|
|
149
|
+
def clients(self) -> t.Tuple[LiteClient, ...]:
|
|
150
150
|
"""
|
|
151
|
-
List of all registered
|
|
151
|
+
List of all registered lite-server clients.
|
|
152
152
|
|
|
153
|
-
:return: Tuple of
|
|
153
|
+
:return: Tuple of LiteClient objects
|
|
154
154
|
"""
|
|
155
155
|
return tuple(self._clients)
|
|
156
156
|
|
|
157
157
|
@property
|
|
158
|
-
def alive_clients(self) -> t.Tuple[
|
|
158
|
+
def alive_clients(self) -> t.Tuple[LiteClient, ...]:
|
|
159
159
|
"""
|
|
160
|
-
|
|
160
|
+
Lite-server clients that are allowed to send requests now.
|
|
161
161
|
|
|
162
|
-
:return: Tuple of available
|
|
162
|
+
:return: Tuple of available LiteClient instances
|
|
163
163
|
"""
|
|
164
164
|
now = time.monotonic()
|
|
165
165
|
return tuple(
|
|
@@ -170,11 +170,11 @@ class AdnlBalancer(BaseClient):
|
|
|
170
170
|
)
|
|
171
171
|
|
|
172
172
|
@property
|
|
173
|
-
def dead_clients(self) -> t.Tuple[
|
|
173
|
+
def dead_clients(self) -> t.Tuple[LiteClient, ...]:
|
|
174
174
|
"""
|
|
175
|
-
|
|
175
|
+
Lite-server clients currently in cooldown or disconnected.
|
|
176
176
|
|
|
177
|
-
:return: Tuple of unavailable
|
|
177
|
+
:return: Tuple of unavailable LiteClient instances
|
|
178
178
|
"""
|
|
179
179
|
now = time.monotonic()
|
|
180
180
|
return tuple(
|
|
@@ -184,7 +184,7 @@ class AdnlBalancer(BaseClient):
|
|
|
184
184
|
or (state.retry_after is not None and state.retry_after > now)
|
|
185
185
|
)
|
|
186
186
|
|
|
187
|
-
async def __aenter__(self) ->
|
|
187
|
+
async def __aenter__(self) -> LiteBalancer:
|
|
188
188
|
"""
|
|
189
189
|
Enter async context manager and connect underlying clients.
|
|
190
190
|
|
|
@@ -217,9 +217,9 @@ class AdnlBalancer(BaseClient):
|
|
|
217
217
|
rps_period: float = 1.0,
|
|
218
218
|
rps_per_client: bool = False,
|
|
219
219
|
retry_policy: t.Optional[RetryPolicy] = None,
|
|
220
|
-
) ->
|
|
220
|
+
) -> LiteBalancer:
|
|
221
221
|
"""
|
|
222
|
-
Build
|
|
222
|
+
Build lite-server balancer from a configuration.
|
|
223
223
|
|
|
224
224
|
For best performance, it is recommended to use a private lite-server
|
|
225
225
|
configuration. You can obtain private configs from:
|
|
@@ -235,14 +235,14 @@ class AdnlBalancer(BaseClient):
|
|
|
235
235
|
:param request_timeout: Maximum total time in seconds for a single balancer operation,
|
|
236
236
|
including all failover attempts across clients.
|
|
237
237
|
:param client_connect_timeout: Timeout in seconds for connect/handshake performed by an
|
|
238
|
-
individual
|
|
238
|
+
individual lite-server client.
|
|
239
239
|
:param client_request_timeout: Timeout in seconds for a single request executed by an
|
|
240
|
-
individual
|
|
240
|
+
individual lite-server client.
|
|
241
241
|
:param rps_limit: Optional requests-per-second limit
|
|
242
242
|
:param rps_period: Time window in seconds for RPS limit
|
|
243
243
|
:param rps_per_client: Whether to create per-client limiters
|
|
244
244
|
:param retry_policy: Optional retry policy that defines per-error-code retry rules
|
|
245
|
-
:return: Configured
|
|
245
|
+
:return: Configured LiteBalancer instance
|
|
246
246
|
"""
|
|
247
247
|
if isinstance(config, dict):
|
|
248
248
|
config = GlobalConfig(**config)
|
|
@@ -251,8 +251,8 @@ class AdnlBalancer(BaseClient):
|
|
|
251
251
|
if rps_limit is not None and not rps_per_client:
|
|
252
252
|
shared_limiter = RateLimiter(rps_limit, rps_period)
|
|
253
253
|
|
|
254
|
-
clients: t.List[
|
|
255
|
-
for
|
|
254
|
+
clients: t.List[LiteClient] = []
|
|
255
|
+
for ls in config.liteservers:
|
|
256
256
|
limiter = (
|
|
257
257
|
RateLimiter(rps_limit, rps_period)
|
|
258
258
|
if rps_per_client and rps_limit is not None
|
|
@@ -261,11 +261,11 @@ class AdnlBalancer(BaseClient):
|
|
|
261
261
|
client_rps_limit = rps_limit if rps_per_client else None
|
|
262
262
|
|
|
263
263
|
clients.append(
|
|
264
|
-
|
|
264
|
+
LiteClient(
|
|
265
265
|
network=network,
|
|
266
|
-
ip=
|
|
267
|
-
port=
|
|
268
|
-
public_key=
|
|
266
|
+
ip=ls.host,
|
|
267
|
+
port=ls.port,
|
|
268
|
+
public_key=ls.id,
|
|
269
269
|
connect_timeout=client_connect_timeout,
|
|
270
270
|
request_timeout=client_request_timeout,
|
|
271
271
|
rps_limit=client_rps_limit,
|
|
@@ -295,9 +295,9 @@ class AdnlBalancer(BaseClient):
|
|
|
295
295
|
rps_period: float = 1.0,
|
|
296
296
|
rps_per_client: bool = False,
|
|
297
297
|
retry_policy: t.Optional[RetryPolicy] = None,
|
|
298
|
-
) ->
|
|
298
|
+
) -> LiteBalancer:
|
|
299
299
|
"""
|
|
300
|
-
Build
|
|
300
|
+
Build lite-server balancer using global config fetched from ton.org.
|
|
301
301
|
|
|
302
302
|
Public lite-servers available in the global network configuration are
|
|
303
303
|
free to use but may be unstable under load. For higher reliability and
|
|
@@ -312,14 +312,14 @@ class AdnlBalancer(BaseClient):
|
|
|
312
312
|
:param request_timeout: Maximum total time in seconds for a single balancer operation,
|
|
313
313
|
including all failover attempts across clients.
|
|
314
314
|
:param client_connect_timeout: Timeout in seconds for connect/handshake performed by an
|
|
315
|
-
individual
|
|
315
|
+
individual lite-server client.
|
|
316
316
|
:param client_request_timeout: Timeout in seconds for a single request executed by an
|
|
317
|
-
individual
|
|
317
|
+
individual lite-server client.
|
|
318
318
|
:param rps_limit: Optional requests-per-second limit
|
|
319
319
|
:param rps_period: Time window in seconds for RPS limit
|
|
320
320
|
:param rps_per_client: Whether to create per-client limiters
|
|
321
321
|
:param retry_policy: Optional retry policy that defines per-error-code retry rules
|
|
322
|
-
:return: Configured
|
|
322
|
+
:return: Configured LiteBalancer instance
|
|
323
323
|
"""
|
|
324
324
|
config_getters = {
|
|
325
325
|
NetworkGlobalID.MAINNET: get_mainnet_global_config,
|
|
@@ -339,9 +339,9 @@ class AdnlBalancer(BaseClient):
|
|
|
339
339
|
retry_policy=retry_policy,
|
|
340
340
|
)
|
|
341
341
|
|
|
342
|
-
def _pick_client(self) ->
|
|
342
|
+
def _pick_client(self) -> LiteClient:
|
|
343
343
|
"""
|
|
344
|
-
Select the best available
|
|
344
|
+
Select the best available lite-server client.
|
|
345
345
|
|
|
346
346
|
Selection criteria:
|
|
347
347
|
- highest known masterchain seqno
|
|
@@ -358,7 +358,7 @@ class AdnlBalancer(BaseClient):
|
|
|
358
358
|
int,
|
|
359
359
|
t.Optional[float],
|
|
360
360
|
t.Optional[float],
|
|
361
|
-
|
|
361
|
+
LiteClient,
|
|
362
362
|
]
|
|
363
363
|
] = []
|
|
364
364
|
|
|
@@ -391,7 +391,7 @@ class AdnlBalancer(BaseClient):
|
|
|
391
391
|
|
|
392
392
|
return alive[0]
|
|
393
393
|
|
|
394
|
-
def _mark_success(self, client:
|
|
394
|
+
def _mark_success(self, client: LiteClient) -> None:
|
|
395
395
|
"""
|
|
396
396
|
Reset error state for a successful client.
|
|
397
397
|
|
|
@@ -403,7 +403,7 @@ class AdnlBalancer(BaseClient):
|
|
|
403
403
|
state.retry_after = None
|
|
404
404
|
break
|
|
405
405
|
|
|
406
|
-
def _mark_error(self, client:
|
|
406
|
+
def _mark_error(self, client: LiteClient, is_rate_limit: bool) -> None:
|
|
407
407
|
"""
|
|
408
408
|
Update error state and schedule retry cooldown.
|
|
409
409
|
|
|
@@ -437,7 +437,7 @@ class AdnlBalancer(BaseClient):
|
|
|
437
437
|
Execute a provider operation with automatic failover.
|
|
438
438
|
|
|
439
439
|
Iterates through available lite-servers until one succeeds
|
|
440
|
-
or all
|
|
440
|
+
or all fail.
|
|
441
441
|
|
|
442
442
|
:param func: Callable performing an operation using an AdnlProvider
|
|
443
443
|
:return: Result of the successful invocation
|
|
@@ -484,14 +484,14 @@ class AdnlBalancer(BaseClient):
|
|
|
484
484
|
if last_exc is not None:
|
|
485
485
|
raise last_exc
|
|
486
486
|
|
|
487
|
-
raise BalancerError("all lite-
|
|
487
|
+
raise BalancerError("all lite-servers failed to process request")
|
|
488
488
|
|
|
489
489
|
try:
|
|
490
490
|
return await asyncio.wait_for(_run(), timeout=self._request_timeout)
|
|
491
491
|
except asyncio.TimeoutError as exc:
|
|
492
492
|
raise ProviderTimeoutError(
|
|
493
493
|
timeout=self._request_timeout,
|
|
494
|
-
endpoint="
|
|
494
|
+
endpoint="lite balancer",
|
|
495
495
|
operation="failover request",
|
|
496
496
|
) from exc
|
|
497
497
|
|
|
@@ -600,12 +600,12 @@ class AdnlBalancer(BaseClient):
|
|
|
600
600
|
|
|
601
601
|
async def _health_loop(self) -> None:
|
|
602
602
|
"""
|
|
603
|
-
Periodically attempt to reconnect dead
|
|
603
|
+
Periodically attempt to reconnect dead lite-server clients.
|
|
604
604
|
|
|
605
605
|
Runs until cancelled.
|
|
606
606
|
"""
|
|
607
607
|
|
|
608
|
-
async def _recon(c:
|
|
608
|
+
async def _recon(c: LiteClient) -> None:
|
|
609
609
|
with suppress(Exception):
|
|
610
610
|
await asyncio.wait_for(
|
|
611
611
|
c.reconnect(),
|
|
@@ -629,7 +629,7 @@ class AdnlBalancer(BaseClient):
|
|
|
629
629
|
self._ensure_health_task()
|
|
630
630
|
return
|
|
631
631
|
|
|
632
|
-
async def _con(client:
|
|
632
|
+
async def _con(client: LiteClient) -> None:
|
|
633
633
|
with suppress(asyncio.TimeoutError):
|
|
634
634
|
await asyncio.wait_for(
|
|
635
635
|
client.connect(),
|
|
@@ -723,7 +723,7 @@ class AdnlBalancer(BaseClient):
|
|
|
723
723
|
self,
|
|
724
724
|
workchain: WorkchainID,
|
|
725
725
|
shard: int,
|
|
726
|
-
seqno: int =
|
|
726
|
+
seqno: t.Optional[int] = None,
|
|
727
727
|
lt: t.Optional[int] = None,
|
|
728
728
|
utime: t.Optional[int] = None,
|
|
729
729
|
) -> t.Tuple[BlockIdExt, Block]:
|
|
@@ -732,7 +732,7 @@ class AdnlBalancer(BaseClient):
|
|
|
732
732
|
|
|
733
733
|
:param workchain: Workchain identifier
|
|
734
734
|
:param shard: Shard identifier
|
|
735
|
-
:param seqno: Block
|
|
735
|
+
:param seqno: Block sequence number
|
|
736
736
|
:param lt: Logical time filter
|
|
737
737
|
:param utime: UNIX time filter
|
|
738
738
|
:return: Tuple of BlockIdExt and deserialized Block
|
tonutils/clients/adnl/client.py
CHANGED
|
@@ -27,8 +27,8 @@ from tonutils.types import (
|
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
class
|
|
31
|
-
"""TON blockchain client
|
|
30
|
+
class LiteClient(BaseClient):
|
|
31
|
+
"""TON blockchain client for lite-server communication over ADNL provider."""
|
|
32
32
|
|
|
33
33
|
TYPE = ClientType.ADNL
|
|
34
34
|
|
|
@@ -47,7 +47,7 @@ class AdnlClient(BaseClient):
|
|
|
47
47
|
limiter: t.Optional[RateLimiter] = None,
|
|
48
48
|
) -> None:
|
|
49
49
|
"""
|
|
50
|
-
Initialize
|
|
50
|
+
Initialize lite-server client.
|
|
51
51
|
|
|
52
52
|
To obtain lite-server connection parameters (ip, port, public_key),
|
|
53
53
|
it is recommended to use a private configuration for better stability
|
|
@@ -91,20 +91,20 @@ class AdnlClient(BaseClient):
|
|
|
91
91
|
"""
|
|
92
92
|
Underlying ADNL provider.
|
|
93
93
|
|
|
94
|
-
:return: AdnlProvider instance used for all
|
|
94
|
+
:return: AdnlProvider instance used for all lite-server requests
|
|
95
95
|
"""
|
|
96
96
|
return self._provider
|
|
97
97
|
|
|
98
98
|
@property
|
|
99
99
|
def is_connected(self) -> bool:
|
|
100
100
|
"""
|
|
101
|
-
Check whether
|
|
101
|
+
Check whether the lite-server connection is established.
|
|
102
102
|
|
|
103
103
|
:return: True if connected, False otherwise
|
|
104
104
|
"""
|
|
105
105
|
return self._provider.is_connected
|
|
106
106
|
|
|
107
|
-
async def __aenter__(self) ->
|
|
107
|
+
async def __aenter__(self) -> LiteClient:
|
|
108
108
|
await self.connect()
|
|
109
109
|
return self
|
|
110
110
|
|
|
@@ -128,9 +128,9 @@ class AdnlClient(BaseClient):
|
|
|
128
128
|
rps_limit: t.Optional[int] = None,
|
|
129
129
|
rps_period: float = 1.0,
|
|
130
130
|
retry_policy: t.Optional[RetryPolicy] = None,
|
|
131
|
-
) ->
|
|
131
|
+
) -> LiteClient:
|
|
132
132
|
"""
|
|
133
|
-
Create
|
|
133
|
+
Create lite-server client from a configuration.
|
|
134
134
|
|
|
135
135
|
To obtain lite-server connection parameters, it is recommended to use
|
|
136
136
|
a private lite-server configuration for better stability and performance.
|
|
@@ -150,16 +150,16 @@ class AdnlClient(BaseClient):
|
|
|
150
150
|
:param rps_limit: Optional requests-per-second limit for this client
|
|
151
151
|
:param rps_period: Time window in seconds for RPS limit
|
|
152
152
|
:param retry_policy: Optional retry policy that defines per-error-code retry rules
|
|
153
|
-
:return: Configured
|
|
153
|
+
:return: Configured LiteClient instance
|
|
154
154
|
"""
|
|
155
155
|
if isinstance(config, dict):
|
|
156
156
|
config = GlobalConfig(**config)
|
|
157
|
-
|
|
157
|
+
ls = config.liteservers[index]
|
|
158
158
|
return cls(
|
|
159
159
|
network=network,
|
|
160
|
-
ip=
|
|
161
|
-
port=
|
|
162
|
-
public_key=
|
|
160
|
+
ip=ls.host,
|
|
161
|
+
port=ls.port,
|
|
162
|
+
public_key=ls.id,
|
|
163
163
|
connect_timeout=connect_timeout,
|
|
164
164
|
request_timeout=request_timeout,
|
|
165
165
|
rps_limit=rps_limit,
|
|
@@ -178,9 +178,9 @@ class AdnlClient(BaseClient):
|
|
|
178
178
|
rps_limit: t.Optional[int] = None,
|
|
179
179
|
rps_period: float = 1.0,
|
|
180
180
|
retry_policy: t.Optional[RetryPolicy] = None,
|
|
181
|
-
) ->
|
|
181
|
+
) -> LiteClient:
|
|
182
182
|
"""
|
|
183
|
-
Create
|
|
183
|
+
Create lite-server client using global network configuration fetched from ton.org.
|
|
184
184
|
|
|
185
185
|
Public lite-servers available in the global network configuration are
|
|
186
186
|
free to use but may be unstable under load. For higher reliability and
|
|
@@ -198,7 +198,7 @@ class AdnlClient(BaseClient):
|
|
|
198
198
|
:param rps_limit: Optional requests-per-second limit for this client
|
|
199
199
|
:param rps_period: Time window in seconds for RPS limit
|
|
200
200
|
:param retry_policy: Optional retry policy that defines per-error-code retry rules
|
|
201
|
-
:return: Configured
|
|
201
|
+
:return: Configured LiteClient instance
|
|
202
202
|
"""
|
|
203
203
|
config_getters = {
|
|
204
204
|
NetworkGlobalID.MAINNET: get_mainnet_global_config,
|
|
@@ -289,7 +289,7 @@ class AdnlClient(BaseClient):
|
|
|
289
289
|
return decode_stack(result or [])
|
|
290
290
|
|
|
291
291
|
async def connect(self) -> None:
|
|
292
|
-
"""
|
|
292
|
+
"""Establish connection to the lite-server."""
|
|
293
293
|
await self.provider.connect()
|
|
294
294
|
|
|
295
295
|
async def reconnect(self) -> None:
|
|
@@ -297,7 +297,7 @@ class AdnlClient(BaseClient):
|
|
|
297
297
|
await self.provider.reconnect()
|
|
298
298
|
|
|
299
299
|
async def close(self) -> None:
|
|
300
|
-
"""Close
|
|
300
|
+
"""Close the lite-server connection."""
|
|
301
301
|
await self.provider.close()
|
|
302
302
|
|
|
303
303
|
async def get_time(self) -> int:
|
|
@@ -351,7 +351,7 @@ class AdnlClient(BaseClient):
|
|
|
351
351
|
self,
|
|
352
352
|
workchain: WorkchainID,
|
|
353
353
|
shard: int,
|
|
354
|
-
seqno: int =
|
|
354
|
+
seqno: t.Optional[int] = None,
|
|
355
355
|
lt: t.Optional[int] = None,
|
|
356
356
|
utime: t.Optional[int] = None,
|
|
357
357
|
) -> t.Tuple[BlockIdExt, Block]:
|
|
@@ -360,7 +360,7 @@ class AdnlClient(BaseClient):
|
|
|
360
360
|
|
|
361
361
|
:param workchain: Workchain identifier
|
|
362
362
|
:param shard: Shard identifier
|
|
363
|
-
:param seqno: Block
|
|
363
|
+
:param seqno: Block sequence number
|
|
364
364
|
:param lt: Logical time filter
|
|
365
365
|
:param utime: UNIX time filter
|
|
366
366
|
:return: Tuple of BlockIdExt and deserialized Block
|
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import json
|
|
2
|
+
import urllib.request
|
|
3
|
+
from pathlib import Path
|
|
2
4
|
|
|
3
5
|
from tonutils.clients.adnl.provider.models import GlobalConfig
|
|
4
6
|
|
|
5
7
|
|
|
6
|
-
def
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
def load_global_config(source: str) -> GlobalConfig:
|
|
9
|
+
if source.startswith(("http://", "https://")):
|
|
10
|
+
with urllib.request.urlopen(source) as response:
|
|
11
|
+
data = json.loads(response.read().decode())
|
|
12
|
+
else:
|
|
13
|
+
data = json.loads(Path(source).read_text())
|
|
14
|
+
|
|
15
|
+
return GlobalConfig.model_validate(data)
|
|
11
16
|
|
|
12
17
|
|
|
13
18
|
def get_mainnet_global_config() -> GlobalConfig:
|
|
@@ -16,7 +21,7 @@ def get_mainnet_global_config() -> GlobalConfig:
|
|
|
16
21
|
|
|
17
22
|
:return: Parsed GlobalConfig instance
|
|
18
23
|
"""
|
|
19
|
-
return
|
|
24
|
+
return load_global_config("https://ton.org/global-config.json")
|
|
20
25
|
|
|
21
26
|
|
|
22
27
|
def get_testnet_global_config() -> GlobalConfig:
|
|
@@ -25,4 +30,4 @@ def get_testnet_global_config() -> GlobalConfig:
|
|
|
25
30
|
|
|
26
31
|
:return: Parsed GlobalConfig instance
|
|
27
32
|
"""
|
|
28
|
-
return
|
|
33
|
+
return load_global_config("https://ton.org/testnet-global-config.json")
|