olas-operate-middleware 0.1.0rc59__py3-none-any.whl → 0.13.2__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.
Files changed (98) hide show
  1. olas_operate_middleware-0.13.2.dist-info/METADATA +75 -0
  2. olas_operate_middleware-0.13.2.dist-info/RECORD +101 -0
  3. {olas_operate_middleware-0.1.0rc59.dist-info → olas_operate_middleware-0.13.2.dist-info}/WHEEL +1 -1
  4. operate/__init__.py +17 -0
  5. operate/account/user.py +35 -9
  6. operate/bridge/bridge_manager.py +470 -0
  7. operate/bridge/providers/lifi_provider.py +377 -0
  8. operate/bridge/providers/native_bridge_provider.py +677 -0
  9. operate/bridge/providers/provider.py +469 -0
  10. operate/bridge/providers/relay_provider.py +457 -0
  11. operate/cli.py +1565 -417
  12. operate/constants.py +60 -12
  13. operate/data/README.md +19 -0
  14. operate/data/contracts/{service_staking_token → dual_staking_token}/__init__.py +2 -2
  15. operate/data/contracts/dual_staking_token/build/DualStakingToken.json +443 -0
  16. operate/data/contracts/dual_staking_token/contract.py +132 -0
  17. operate/data/contracts/dual_staking_token/contract.yaml +23 -0
  18. operate/{ledger/base.py → data/contracts/foreign_omnibridge/__init__.py} +2 -19
  19. operate/data/contracts/foreign_omnibridge/build/ForeignOmnibridge.json +1372 -0
  20. operate/data/contracts/foreign_omnibridge/contract.py +130 -0
  21. operate/data/contracts/foreign_omnibridge/contract.yaml +23 -0
  22. operate/{ledger/solana.py → data/contracts/home_omnibridge/__init__.py} +2 -20
  23. operate/data/contracts/home_omnibridge/build/HomeOmnibridge.json +1421 -0
  24. operate/data/contracts/home_omnibridge/contract.py +80 -0
  25. operate/data/contracts/home_omnibridge/contract.yaml +23 -0
  26. operate/data/contracts/l1_standard_bridge/__init__.py +20 -0
  27. operate/data/contracts/l1_standard_bridge/build/L1StandardBridge.json +831 -0
  28. operate/data/contracts/l1_standard_bridge/contract.py +158 -0
  29. operate/data/contracts/l1_standard_bridge/contract.yaml +23 -0
  30. operate/data/contracts/l2_standard_bridge/__init__.py +20 -0
  31. operate/data/contracts/l2_standard_bridge/build/L2StandardBridge.json +626 -0
  32. operate/data/contracts/l2_standard_bridge/contract.py +130 -0
  33. operate/data/contracts/l2_standard_bridge/contract.yaml +23 -0
  34. operate/data/contracts/mech_activity/__init__.py +20 -0
  35. operate/data/contracts/mech_activity/build/MechActivity.json +111 -0
  36. operate/data/contracts/mech_activity/contract.py +44 -0
  37. operate/data/contracts/mech_activity/contract.yaml +23 -0
  38. operate/data/contracts/optimism_mintable_erc20/__init__.py +20 -0
  39. operate/data/contracts/optimism_mintable_erc20/build/OptimismMintableERC20.json +491 -0
  40. operate/data/contracts/optimism_mintable_erc20/contract.py +45 -0
  41. operate/data/contracts/optimism_mintable_erc20/contract.yaml +23 -0
  42. operate/data/contracts/recovery_module/__init__.py +20 -0
  43. operate/data/contracts/recovery_module/build/RecoveryModule.json +811 -0
  44. operate/data/contracts/recovery_module/contract.py +61 -0
  45. operate/data/contracts/recovery_module/contract.yaml +23 -0
  46. operate/data/contracts/requester_activity_checker/__init__.py +20 -0
  47. operate/data/contracts/requester_activity_checker/build/RequesterActivityChecker.json +111 -0
  48. operate/data/contracts/requester_activity_checker/contract.py +33 -0
  49. operate/data/contracts/requester_activity_checker/contract.yaml +23 -0
  50. operate/data/contracts/staking_token/__init__.py +20 -0
  51. operate/data/contracts/staking_token/build/StakingToken.json +1336 -0
  52. operate/data/contracts/{service_staking_token → staking_token}/contract.py +27 -13
  53. operate/data/contracts/staking_token/contract.yaml +23 -0
  54. operate/data/contracts/uniswap_v2_erc20/contract.yaml +3 -1
  55. operate/data/contracts/uniswap_v2_erc20/tests/__init__.py +20 -0
  56. operate/data/contracts/uniswap_v2_erc20/tests/test_contract.py +363 -0
  57. operate/keys.py +118 -33
  58. operate/ledger/__init__.py +159 -56
  59. operate/ledger/profiles.py +321 -18
  60. operate/migration.py +555 -0
  61. operate/{http → operate_http}/__init__.py +3 -2
  62. operate/{http → operate_http}/exceptions.py +6 -4
  63. operate/operate_types.py +544 -0
  64. operate/pearl.py +13 -1
  65. operate/quickstart/analyse_logs.py +118 -0
  66. operate/quickstart/claim_staking_rewards.py +104 -0
  67. operate/quickstart/reset_configs.py +106 -0
  68. operate/quickstart/reset_password.py +70 -0
  69. operate/quickstart/reset_staking.py +145 -0
  70. operate/quickstart/run_service.py +726 -0
  71. operate/quickstart/stop_service.py +72 -0
  72. operate/quickstart/terminate_on_chain_service.py +83 -0
  73. operate/quickstart/utils.py +298 -0
  74. operate/resource.py +62 -3
  75. operate/services/agent_runner.py +202 -0
  76. operate/services/deployment_runner.py +868 -0
  77. operate/services/funding_manager.py +929 -0
  78. operate/services/health_checker.py +280 -0
  79. operate/services/manage.py +2356 -620
  80. operate/services/protocol.py +1246 -340
  81. operate/services/service.py +756 -391
  82. operate/services/utils/mech.py +103 -0
  83. operate/services/utils/tendermint.py +86 -12
  84. operate/settings.py +70 -0
  85. operate/utils/__init__.py +135 -0
  86. operate/utils/gnosis.py +407 -80
  87. operate/utils/single_instance.py +226 -0
  88. operate/utils/ssl.py +133 -0
  89. operate/wallet/master.py +708 -123
  90. operate/wallet/wallet_recovery_manager.py +507 -0
  91. olas_operate_middleware-0.1.0rc59.dist-info/METADATA +0 -304
  92. olas_operate_middleware-0.1.0rc59.dist-info/RECORD +0 -41
  93. operate/data/contracts/service_staking_token/build/ServiceStakingToken.json +0 -1273
  94. operate/data/contracts/service_staking_token/contract.yaml +0 -23
  95. operate/ledger/ethereum.py +0 -48
  96. operate/types.py +0 -260
  97. {olas_operate_middleware-0.1.0rc59.dist-info → olas_operate_middleware-0.13.2.dist-info}/entry_points.txt +0 -0
  98. {olas_operate_middleware-0.1.0rc59.dist-info → olas_operate_middleware-0.13.2.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,280 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # ------------------------------------------------------------------------------
4
+ #
5
+ # Copyright 2024 Valory AG
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ # ------------------------------------------------------------------------------
20
+ """Source code for checking aea is alive.."""
21
+ import asyncio
22
+ import json
23
+ import logging
24
+ import time
25
+ import typing as t
26
+ from concurrent.futures import ThreadPoolExecutor
27
+ from http import HTTPStatus
28
+ from pathlib import Path
29
+ from traceback import print_exc
30
+
31
+ import aiohttp # type: ignore
32
+
33
+ from operate.constants import HEALTHCHECK_JSON, HEALTH_CHECK_URL
34
+ from operate.services.manage import ServiceManager # type: ignore
35
+
36
+
37
+ class HealthChecker:
38
+ """Health checker manager."""
39
+
40
+ SLEEP_PERIOD_DEFAULT = 5 # seconds
41
+ PORT_UP_TIMEOUT_DEFAULT = 300 # seconds
42
+ REQUEST_TIMEOUT_DEFAULT = 90 # seconds
43
+ NUMBER_OF_FAILS_DEFAULT = 60
44
+ FAILFAST_NUM = 15
45
+ FAILFAST_TIMEOUT = 15 * 60 # 15 minutes
46
+
47
+ def __init__(
48
+ self,
49
+ service_manager: ServiceManager,
50
+ logger: logging.Logger,
51
+ port_up_timeout: int | None = None,
52
+ sleep_period: int | None = None,
53
+ number_of_fails: int | None = None,
54
+ ) -> None:
55
+ """Init the healtch checker."""
56
+ self._jobs: t.Dict[str, asyncio.Task] = {}
57
+ self._service_manager = service_manager
58
+ self.logger = logger
59
+ self.port_up_timeout = port_up_timeout or self.PORT_UP_TIMEOUT_DEFAULT
60
+ self.sleep_period = sleep_period or self.SLEEP_PERIOD_DEFAULT
61
+ self.number_of_fails = number_of_fails or self.NUMBER_OF_FAILS_DEFAULT
62
+
63
+ def start_for_service(self, service_config_id: str) -> None:
64
+ """Start for a specific service."""
65
+ self.logger.info(
66
+ f"[HEALTH_CHECKER]: Starting healthcheck job for {service_config_id}"
67
+ )
68
+ if service_config_id in self._jobs:
69
+ self.stop_for_service(service_config_id=service_config_id)
70
+
71
+ loop = asyncio.get_running_loop()
72
+ self._jobs[service_config_id] = loop.create_task(
73
+ self.healthcheck_job(
74
+ service_config_id=service_config_id,
75
+ )
76
+ )
77
+
78
+ def stop_for_service(self, service_config_id: str) -> None:
79
+ """Stop for a specific service."""
80
+ if service_config_id not in self._jobs:
81
+ return
82
+ self.logger.info(
83
+ f"[HEALTH_CHECKER]: Cancelling existing healthcheck_jobs job for {service_config_id}"
84
+ )
85
+ status = self._jobs[service_config_id].cancel()
86
+ if not status:
87
+ self.logger.info(
88
+ f"[HEALTH_CHECKER]: Healthcheck job cancellation for {service_config_id} failed"
89
+ )
90
+
91
+ async def check_service_health(
92
+ self, service_config_id: str, service_path: t.Optional[Path] = None
93
+ ) -> bool:
94
+ """Check the service health"""
95
+ del service_config_id
96
+ timeout = aiohttp.ClientTimeout(total=self.REQUEST_TIMEOUT_DEFAULT)
97
+ async with aiohttp.ClientSession(timeout=timeout) as session:
98
+ async with session.get(HEALTH_CHECK_URL) as resp:
99
+ try:
100
+ status = resp.status
101
+
102
+ if status != HTTPStatus.OK:
103
+ # not HTTP OK -> not healthy for sure
104
+ content = await resp.text()
105
+ self.logger.warning(
106
+ f"[HEALTH_CHECKER] Bad http status code : {status} content: {content}. not healthy!"
107
+ )
108
+ return False
109
+
110
+ response_json = await resp.json()
111
+
112
+ if service_path:
113
+ healthcheck_json_path = service_path / HEALTHCHECK_JSON
114
+ healthcheck_json_path.write_text(
115
+ json.dumps(response_json, indent=2), encoding="utf-8"
116
+ )
117
+
118
+ return response_json.get(
119
+ "is_healthy", response_json.get("is_transitioning_fast", False)
120
+ ) # TODO: remove is_transitioning_fast after all the services start reporting is_healthy
121
+ except Exception as e: # pylint: disable=broad-except
122
+ self.logger.error(
123
+ f"[HEALTH_CHECKER] error {e}. set not healthy!", exc_info=True
124
+ )
125
+ return False
126
+
127
+ async def healthcheck_job( # pylint: disable=too-many-statements
128
+ self,
129
+ service_config_id: str,
130
+ ) -> None:
131
+ """Start a background health check job."""
132
+
133
+ service_path = self._service_manager.load(service_config_id).path
134
+ try:
135
+ self.logger.info(
136
+ f"[HEALTH_CHECKER] Start healthcheck job for service: {service_config_id}"
137
+ )
138
+
139
+ async def _wait_for_port(sleep_period: int = 15) -> None:
140
+ self.logger.info("[HEALTH_CHECKER]: wait port is up")
141
+ while True:
142
+ try:
143
+ await self.check_service_health(service_config_id, service_path)
144
+ self.logger.info("[HEALTH_CHECKER]: port is UP")
145
+ return
146
+ except aiohttp.ClientConnectionError:
147
+ self.logger.error(
148
+ "[HEALTH_CHECKER]: error connecting http port"
149
+ )
150
+ await asyncio.sleep(sleep_period)
151
+
152
+ async def _check_port_ready(
153
+ timeout: int = self.port_up_timeout, sleep_period: int = 15
154
+ ) -> bool:
155
+ try:
156
+ await asyncio.wait_for(
157
+ _wait_for_port(sleep_period=sleep_period), timeout=timeout
158
+ )
159
+ return True
160
+ except asyncio.TimeoutError:
161
+ return False
162
+
163
+ async def _check_health(
164
+ number_of_fails: int = 5, sleep_period: int = self.sleep_period
165
+ ) -> None:
166
+ fails = 0
167
+ while True:
168
+ try:
169
+ # Check the service health
170
+ healthy = await self.check_service_health(
171
+ service_config_id, service_path
172
+ )
173
+ except aiohttp.ClientConnectionError as e:
174
+ print_exc()
175
+ self.logger.warning(
176
+ f"[HEALTH_CHECKER] {service_config_id} port read failed. assume not healthy {e}"
177
+ )
178
+ healthy = False
179
+
180
+ if not healthy:
181
+ fails += 1
182
+ self.logger.warning(
183
+ f"[HEALTH_CHECKER] {service_config_id} not healthy for {fails} time in a row"
184
+ )
185
+ else:
186
+ self.logger.debug(
187
+ f"[HEALTH_CHECKER] {service_config_id} is HEALTHY"
188
+ )
189
+ # reset fails if comes healty
190
+ fails = 0
191
+
192
+ if fails >= number_of_fails:
193
+ # too much fails, exit
194
+ self.logger.error(
195
+ f"[HEALTH_CHECKER] {service_config_id} failed {fails} times in a row. restart"
196
+ )
197
+ return
198
+
199
+ await asyncio.sleep(sleep_period)
200
+
201
+ async def _restart(
202
+ service_manager: ServiceManager, service_config_id: str
203
+ ) -> None:
204
+ def _do_restart() -> None:
205
+ service_manager.stop_service_locally(
206
+ service_config_id=service_config_id
207
+ )
208
+ service_manager.deploy_service_locally(
209
+ service_config_id=service_config_id
210
+ )
211
+
212
+ loop = asyncio.get_event_loop()
213
+ with ThreadPoolExecutor() as executor:
214
+ future = loop.run_in_executor(executor, _do_restart)
215
+ await future
216
+ exception = future.exception()
217
+ if exception is not None:
218
+ raise exception
219
+
220
+ async def _stop(
221
+ service_manager: ServiceManager, service_config_id: str
222
+ ) -> None:
223
+ def _do_stop() -> None:
224
+ service_manager.stop_service_locally(
225
+ service_config_id=service_config_id
226
+ )
227
+
228
+ loop = asyncio.get_event_loop()
229
+ with ThreadPoolExecutor() as executor:
230
+ future = loop.run_in_executor(executor, _do_stop)
231
+ await future
232
+ exception = future.exception()
233
+ if exception is not None:
234
+ raise exception
235
+
236
+ # upper cycle
237
+ failfast_records: t.List[float] = []
238
+ while True:
239
+ self.logger.info(
240
+ f"[HEALTH_CHECKER] {service_config_id} wait for port ready"
241
+ )
242
+ if await _check_port_ready(timeout=self.port_up_timeout):
243
+ # blocking till restart needed
244
+ self.logger.info(
245
+ f"[HEALTH_CHECKER] {service_config_id} port is ready, checking health every {self.sleep_period}"
246
+ )
247
+ failfast_records = []
248
+ await _check_health(
249
+ number_of_fails=self.number_of_fails,
250
+ sleep_period=self.sleep_period,
251
+ )
252
+
253
+ else:
254
+ self.logger.info(
255
+ "[HEALTH_CHECKER] port not ready within timeout. restart deployment"
256
+ )
257
+
258
+ # perform restart
259
+ # TODO: blocking!!!!!!!
260
+ while True:
261
+ # we count every restart till success (port is up and healtcheck started)
262
+ failfast_records.append(time.time())
263
+ try:
264
+ await _restart(self._service_manager, service_config_id)
265
+ break
266
+ except Exception: # pylint: disable=broad-except
267
+ if (len(failfast_records) >= self.FAILFAST_NUM) or (
268
+ time.time() - failfast_records[0]
269
+ ) > self.FAILFAST_TIMEOUT:
270
+ await _stop(self._service_manager, service_config_id)
271
+ raise
272
+
273
+ self.logger.exception(f"Restart problem: {service_config_id}")
274
+ await asyncio.sleep(30)
275
+
276
+ except Exception:
277
+ self.logger.exception(
278
+ f"Problems running healthcheck job for {service_config_id}"
279
+ )
280
+ raise