olas-operate-middleware 0.8.2__py3-none-any.whl → 0.10.0__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.
- {olas_operate_middleware-0.8.2.dist-info → olas_operate_middleware-0.10.0.dist-info}/METADATA +2 -2
- {olas_operate_middleware-0.8.2.dist-info → olas_operate_middleware-0.10.0.dist-info}/RECORD +34 -34
- operate/bridge/bridge_manager.py +5 -6
- operate/bridge/providers/native_bridge_provider.py +1 -1
- operate/bridge/providers/provider.py +4 -5
- operate/bridge/providers/relay_provider.py +1 -1
- operate/cli.py +128 -48
- operate/constants.py +9 -9
- operate/keys.py +26 -14
- operate/ledger/__init__.py +4 -4
- operate/ledger/profiles.py +9 -11
- operate/migration.py +326 -0
- operate/operate_types.py +9 -27
- operate/quickstart/analyse_logs.py +3 -6
- operate/quickstart/claim_staking_rewards.py +1 -4
- operate/quickstart/reset_configs.py +0 -3
- operate/quickstart/reset_password.py +0 -3
- operate/quickstart/reset_staking.py +3 -5
- operate/quickstart/run_service.py +5 -7
- operate/quickstart/stop_service.py +3 -4
- operate/quickstart/terminate_on_chain_service.py +1 -4
- operate/quickstart/utils.py +4 -7
- operate/resource.py +37 -5
- operate/services/deployment_runner.py +170 -38
- operate/services/health_checker.py +5 -8
- operate/services/manage.py +103 -164
- operate/services/protocol.py +5 -5
- operate/services/service.py +42 -242
- operate/utils/__init__.py +44 -0
- operate/utils/gnosis.py +25 -17
- operate/wallet/master.py +20 -24
- {olas_operate_middleware-0.8.2.dist-info → olas_operate_middleware-0.10.0.dist-info}/LICENSE +0 -0
- {olas_operate_middleware-0.8.2.dist-info → olas_operate_middleware-0.10.0.dist-info}/WHEEL +0 -0
- {olas_operate_middleware-0.8.2.dist-info → olas_operate_middleware-0.10.0.dist-info}/entry_points.txt +0 -0
operate/services/manage.py
CHANGED
|
@@ -23,8 +23,6 @@ import asyncio
|
|
|
23
23
|
import json
|
|
24
24
|
import logging
|
|
25
25
|
import os
|
|
26
|
-
import shutil
|
|
27
|
-
import time
|
|
28
26
|
import traceback
|
|
29
27
|
import typing as t
|
|
30
28
|
from collections import Counter, defaultdict
|
|
@@ -35,19 +33,19 @@ from pathlib import Path
|
|
|
35
33
|
|
|
36
34
|
import requests
|
|
37
35
|
from aea.helpers.base import IPFSHash
|
|
38
|
-
from
|
|
39
|
-
from aea_ledger_ethereum import EthereumCrypto, LedgerApi
|
|
36
|
+
from aea_ledger_ethereum import LedgerApi
|
|
40
37
|
from autonomy.chain.base import registry_contracts
|
|
41
38
|
from autonomy.chain.config import CHAIN_PROFILES, ChainType
|
|
39
|
+
from autonomy.chain.metadata import IPFS_URI_PREFIX
|
|
42
40
|
|
|
43
|
-
from operate.constants import ZERO_ADDRESS
|
|
41
|
+
from operate.constants import IPFS_ADDRESS, ZERO_ADDRESS
|
|
44
42
|
from operate.data import DATA_DIR
|
|
45
43
|
from operate.data.contracts.mech_activity.contract import MechActivityContract
|
|
46
44
|
from operate.data.contracts.requester_activity_checker.contract import (
|
|
47
45
|
RequesterActivityCheckerContract,
|
|
48
46
|
)
|
|
49
47
|
from operate.data.contracts.staking_token.contract import StakingTokenContract
|
|
50
|
-
from operate.keys import
|
|
48
|
+
from operate.keys import KeysManager
|
|
51
49
|
from operate.ledger import PUBLIC_RPCS, get_currency_denom
|
|
52
50
|
from operate.ledger.profiles import (
|
|
53
51
|
CONTRACTS,
|
|
@@ -71,41 +69,22 @@ from operate.operate_types import (
|
|
|
71
69
|
from operate.services.protocol import EthSafeTxBuilder, OnChainManager, StakingState
|
|
72
70
|
from operate.services.service import (
|
|
73
71
|
ChainConfig,
|
|
74
|
-
DELETE_PREFIX,
|
|
75
72
|
Deployment,
|
|
76
73
|
NON_EXISTENT_MULTISIG,
|
|
77
74
|
NON_EXISTENT_TOKEN,
|
|
78
75
|
OnChainData,
|
|
79
76
|
SERVICE_CONFIG_PREFIX,
|
|
77
|
+
SERVICE_CONFIG_VERSION,
|
|
80
78
|
Service,
|
|
81
79
|
)
|
|
82
80
|
from operate.services.utils.mech import deploy_mech
|
|
83
|
-
from operate.utils.gnosis import
|
|
84
|
-
NULL_ADDRESS,
|
|
85
|
-
drain_eoa,
|
|
86
|
-
get_asset_balance,
|
|
87
|
-
get_assets_balances,
|
|
88
|
-
)
|
|
81
|
+
from operate.utils.gnosis import drain_eoa, get_asset_balance, get_assets_balances
|
|
89
82
|
from operate.utils.gnosis import transfer as transfer_from_safe
|
|
90
83
|
from operate.utils.gnosis import transfer_erc20_from_safe
|
|
91
84
|
from operate.wallet.master import MasterWalletManager
|
|
92
85
|
|
|
93
86
|
|
|
94
87
|
# pylint: disable=redefined-builtin
|
|
95
|
-
|
|
96
|
-
OPERATE = ".operate"
|
|
97
|
-
CONFIG = "config.json"
|
|
98
|
-
SERVICES = "services"
|
|
99
|
-
KEYS = "keys"
|
|
100
|
-
DEPLOYMENT = "deployment"
|
|
101
|
-
CONFIG = "config.json"
|
|
102
|
-
KEY = "master-key.txt"
|
|
103
|
-
KEYS_JSON = "keys.json"
|
|
104
|
-
DOCKER_COMPOSE_YAML = "docker-compose.yaml"
|
|
105
|
-
SERVICE_YAML = "service.yaml"
|
|
106
|
-
HTTP_OK = 200
|
|
107
|
-
URI_HASH_POSITION = 7
|
|
108
|
-
IPFS_GATEWAY = "https://gateway.autonolas.tech/ipfs/"
|
|
109
88
|
DEFAULT_TOPUP_THRESHOLD = 0.5
|
|
110
89
|
# At the moment, we only support running one agent per service locally on a machine.
|
|
111
90
|
# If multiple agents are provided in the service.yaml file, only the 0th index config will be used.
|
|
@@ -118,9 +97,8 @@ class ServiceManager:
|
|
|
118
97
|
def __init__(
|
|
119
98
|
self,
|
|
120
99
|
path: Path,
|
|
121
|
-
keys_manager: KeysManager,
|
|
122
100
|
wallet_manager: MasterWalletManager,
|
|
123
|
-
logger:
|
|
101
|
+
logger: logging.Logger,
|
|
124
102
|
skip_dependency_check: t.Optional[bool] = False,
|
|
125
103
|
) -> None:
|
|
126
104
|
"""
|
|
@@ -132,43 +110,66 @@ class ServiceManager:
|
|
|
132
110
|
:param logger: logging.Logger object.
|
|
133
111
|
"""
|
|
134
112
|
self.path = path
|
|
135
|
-
self.keys_manager =
|
|
113
|
+
self.keys_manager = KeysManager()
|
|
136
114
|
self.wallet_manager = wallet_manager
|
|
137
|
-
self.logger = logger
|
|
115
|
+
self.logger = logger
|
|
138
116
|
self.skip_depencency_check = skip_dependency_check
|
|
139
117
|
|
|
140
118
|
def setup(self) -> None:
|
|
141
119
|
"""Setup service manager."""
|
|
142
120
|
self.path.mkdir(exist_ok=True)
|
|
143
121
|
|
|
144
|
-
def
|
|
122
|
+
def get_all_service_ids(self) -> t.List[str]:
|
|
123
|
+
"""
|
|
124
|
+
Get all service ids.
|
|
125
|
+
|
|
126
|
+
:return: List of service ids.
|
|
127
|
+
"""
|
|
128
|
+
return [
|
|
129
|
+
path.name
|
|
130
|
+
for path in self.path.iterdir()
|
|
131
|
+
if path.is_dir() and path.name.startswith(SERVICE_CONFIG_PREFIX)
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
def get_all_services(self) -> t.Tuple[t.List[Service], bool]:
|
|
135
|
+
"""Get all services."""
|
|
145
136
|
services = []
|
|
137
|
+
success = True
|
|
146
138
|
for path in self.path.iterdir():
|
|
147
139
|
if not path.name.startswith(SERVICE_CONFIG_PREFIX):
|
|
148
140
|
continue
|
|
149
141
|
try:
|
|
150
142
|
service = Service.load(path=path)
|
|
143
|
+
if service.version != SERVICE_CONFIG_VERSION:
|
|
144
|
+
self.logger.warning(
|
|
145
|
+
f"Service {path.name} has an unsupported version: {service.version}."
|
|
146
|
+
)
|
|
147
|
+
success = False
|
|
148
|
+
continue
|
|
149
|
+
|
|
151
150
|
services.append(service)
|
|
152
|
-
except ValueError as e:
|
|
153
|
-
raise e
|
|
154
151
|
except Exception as e: # pylint: disable=broad-except
|
|
155
152
|
self.logger.error(
|
|
156
153
|
f"Failed to load service: {path.name}. Exception {e}: {traceback.format_exc()}"
|
|
157
154
|
)
|
|
158
|
-
|
|
159
|
-
timestamp = int(time.time())
|
|
160
|
-
invalid_path = path.parent / f"invalid_{timestamp}_{path.name}"
|
|
161
|
-
os.rename(path, invalid_path)
|
|
162
|
-
self.logger.info(
|
|
163
|
-
f"Renamed invalid service: {path.name} to {invalid_path.name}"
|
|
164
|
-
)
|
|
155
|
+
success = False
|
|
165
156
|
|
|
166
|
-
return services
|
|
157
|
+
return services, success
|
|
158
|
+
|
|
159
|
+
def validate_services(self) -> bool:
|
|
160
|
+
"""
|
|
161
|
+
Validate all services.
|
|
162
|
+
|
|
163
|
+
:return: True if all services are valid, False otherwise.
|
|
164
|
+
"""
|
|
165
|
+
_, success = self.get_all_services()
|
|
166
|
+
return success
|
|
167
167
|
|
|
168
168
|
@property
|
|
169
169
|
def json(self) -> t.List[t.Dict]:
|
|
170
170
|
"""Returns the list of available services."""
|
|
171
|
-
|
|
171
|
+
services, _ = self.get_all_services()
|
|
172
|
+
return [service.json for service in services]
|
|
172
173
|
|
|
173
174
|
def exists(self, service_config_id: str) -> bool:
|
|
174
175
|
"""Check if service exists."""
|
|
@@ -196,14 +197,14 @@ class ServiceManager:
|
|
|
196
197
|
self,
|
|
197
198
|
hash: str,
|
|
198
199
|
service_template: t.Optional[ServiceTemplate] = None,
|
|
199
|
-
|
|
200
|
+
agent_addresses: t.Optional[t.List[str]] = None,
|
|
200
201
|
) -> Service:
|
|
201
202
|
"""
|
|
202
203
|
Create or load a service
|
|
203
204
|
|
|
204
205
|
:param hash: Service hash
|
|
205
206
|
:param service_template: Service template
|
|
206
|
-
:param
|
|
207
|
+
:param agent_addresses: Agents' addresses to be used for the service.
|
|
207
208
|
:return: Service instance
|
|
208
209
|
"""
|
|
209
210
|
path = self.path / hash
|
|
@@ -222,21 +223,10 @@ class ServiceManager:
|
|
|
222
223
|
"'service_template' cannot be None when creating a new service"
|
|
223
224
|
)
|
|
224
225
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
storage=self.path,
|
|
228
|
-
service_template=service_template,
|
|
226
|
+
return self.create(
|
|
227
|
+
service_template=service_template, agent_addresses=agent_addresses
|
|
229
228
|
)
|
|
230
229
|
|
|
231
|
-
if not service.keys:
|
|
232
|
-
service.keys = [
|
|
233
|
-
self.keys_manager.get(self.keys_manager.create())
|
|
234
|
-
for _ in range(NUM_LOCAL_AGENT_INSTANCES)
|
|
235
|
-
]
|
|
236
|
-
service.store()
|
|
237
|
-
|
|
238
|
-
return service
|
|
239
|
-
|
|
240
230
|
def load(
|
|
241
231
|
self,
|
|
242
232
|
service_config_id: str,
|
|
@@ -253,25 +243,24 @@ class ServiceManager:
|
|
|
253
243
|
def create(
|
|
254
244
|
self,
|
|
255
245
|
service_template: ServiceTemplate,
|
|
256
|
-
|
|
246
|
+
agent_addresses: t.Optional[t.List[str]] = None,
|
|
257
247
|
) -> Service:
|
|
258
248
|
"""
|
|
259
249
|
Create a service
|
|
260
250
|
|
|
261
251
|
:param service_template: Service template
|
|
262
|
-
:param
|
|
252
|
+
:param agent_addresses: Agents' addresses to be used for the service.
|
|
263
253
|
:return: Service instance
|
|
264
254
|
"""
|
|
265
255
|
service = Service.new(
|
|
266
|
-
|
|
256
|
+
agent_addresses=agent_addresses or [],
|
|
267
257
|
storage=self.path,
|
|
268
258
|
service_template=service_template,
|
|
269
259
|
)
|
|
270
260
|
|
|
271
|
-
if not service.
|
|
272
|
-
service.
|
|
273
|
-
self.keys_manager.
|
|
274
|
-
for _ in range(NUM_LOCAL_AGENT_INSTANCES)
|
|
261
|
+
if not service.agent_addresses:
|
|
262
|
+
service.agent_addresses = [
|
|
263
|
+
self.keys_manager.create() for _ in range(NUM_LOCAL_AGENT_INSTANCES)
|
|
275
264
|
]
|
|
276
265
|
service.store()
|
|
277
266
|
|
|
@@ -297,7 +286,7 @@ class ServiceManager:
|
|
|
297
286
|
sftxb = self.get_eth_safe_tx_builder(ledger_config=ledger_config)
|
|
298
287
|
info = sftxb.info(token_id=chain_data.token)
|
|
299
288
|
config_hash = info["config_hash"]
|
|
300
|
-
url =
|
|
289
|
+
url = IPFS_ADDRESS.format(hash=config_hash)
|
|
301
290
|
self.logger.info(f"Fetching {url=}...")
|
|
302
291
|
res = requests.get(url, timeout=30)
|
|
303
292
|
if res.status_code == HTTPStatus.OK:
|
|
@@ -334,8 +323,6 @@ class ServiceManager:
|
|
|
334
323
|
ledger_config = chain_config.ledger_config
|
|
335
324
|
chain_data = chain_config.chain_data
|
|
336
325
|
user_params = chain_config.chain_data.user_params
|
|
337
|
-
keys = service.keys
|
|
338
|
-
instances = [key.address for key in keys]
|
|
339
326
|
ocm = self.get_on_chain_manager(ledger_config=ledger_config)
|
|
340
327
|
|
|
341
328
|
# TODO fix this
|
|
@@ -403,7 +390,7 @@ class ServiceManager:
|
|
|
403
390
|
)
|
|
404
391
|
|
|
405
392
|
on_chain_metadata = self._get_on_chain_metadata(chain_config=chain_config)
|
|
406
|
-
on_chain_hash = on_chain_metadata.get("code_uri", "")[
|
|
393
|
+
on_chain_hash = on_chain_metadata.get("code_uri", "")[len(IPFS_URI_PREFIX) :]
|
|
407
394
|
on_chain_description = on_chain_metadata.get("description")
|
|
408
395
|
|
|
409
396
|
current_agent_bond = staking_params[
|
|
@@ -447,7 +434,7 @@ class ServiceManager:
|
|
|
447
434
|
if user_params.use_staking
|
|
448
435
|
else user_params.cost_of_bond
|
|
449
436
|
),
|
|
450
|
-
threshold=
|
|
437
|
+
threshold=len(service.agent_addresses),
|
|
451
438
|
nft=IPFSHash(user_params.nft),
|
|
452
439
|
update_token=chain_data.token if is_update else None,
|
|
453
440
|
token=(
|
|
@@ -479,8 +466,8 @@ class ServiceManager:
|
|
|
479
466
|
agent_id = staking_params["agent_ids"][0]
|
|
480
467
|
ocm.register(
|
|
481
468
|
service_id=chain_data.token,
|
|
482
|
-
instances=
|
|
483
|
-
agents=[agent_id for _ in
|
|
469
|
+
instances=service.agent_addresses,
|
|
470
|
+
agents=[agent_id for _ in service.agent_addresses],
|
|
484
471
|
token=(OLAS[ledger_config.chain] if user_params.use_staking else None),
|
|
485
472
|
)
|
|
486
473
|
on_chain_state = OnChainState.FINISHED_REGISTRATION
|
|
@@ -630,8 +617,6 @@ class ServiceManager:
|
|
|
630
617
|
ledger_config = chain_config.ledger_config
|
|
631
618
|
chain_data = chain_config.chain_data
|
|
632
619
|
user_params = chain_config.chain_data.user_params
|
|
633
|
-
keys = service.keys
|
|
634
|
-
instances = [key.address for key in keys]
|
|
635
620
|
wallet = self.wallet_manager.load(ledger_config.chain.ledger_type)
|
|
636
621
|
sftxb = self.get_eth_safe_tx_builder(ledger_config=ledger_config)
|
|
637
622
|
safe = wallet.safes[Chain(chain)]
|
|
@@ -653,13 +638,13 @@ class ServiceManager:
|
|
|
653
638
|
|
|
654
639
|
current_staking_program = self._get_current_staking_program(service, chain)
|
|
655
640
|
fallback_params = dict( # nosec
|
|
656
|
-
staking_contract=
|
|
641
|
+
staking_contract=ZERO_ADDRESS,
|
|
657
642
|
agent_ids=[user_params.agent_id],
|
|
658
643
|
service_registry="0x9338b5153AE39BB89f50468E608eD9d764B755fD", # nosec
|
|
659
|
-
staking_token=
|
|
644
|
+
staking_token=ZERO_ADDRESS, # nosec
|
|
660
645
|
service_registry_token_utility="0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8", # nosec
|
|
661
646
|
min_staking_deposit=20000000000000000000,
|
|
662
|
-
activity_checker=
|
|
647
|
+
activity_checker=ZERO_ADDRESS, # nosec
|
|
663
648
|
)
|
|
664
649
|
|
|
665
650
|
current_staking_params = sftxb.get_staking_params(
|
|
@@ -710,7 +695,7 @@ class ServiceManager:
|
|
|
710
695
|
"GNOSIS_LEDGER_RPC": PUBLIC_RPCS[Chain.GNOSIS],
|
|
711
696
|
"BASE_LEDGER_RPC": PUBLIC_RPCS[Chain.BASE],
|
|
712
697
|
"CELO_LEDGER_RPC": PUBLIC_RPCS[Chain.CELO],
|
|
713
|
-
"OPTIMISM_LEDGER_RPC": PUBLIC_RPCS[Chain.
|
|
698
|
+
"OPTIMISM_LEDGER_RPC": PUBLIC_RPCS[Chain.OPTIMISM],
|
|
714
699
|
"MODE_LEDGER_RPC": PUBLIC_RPCS[Chain.MODE],
|
|
715
700
|
f"{chain.upper()}_LEDGER_RPC": ledger_config.rpc,
|
|
716
701
|
"STAKING_CONTRACT_ADDRESS": target_staking_params.get(
|
|
@@ -739,11 +724,14 @@ class ServiceManager:
|
|
|
739
724
|
}
|
|
740
725
|
)
|
|
741
726
|
|
|
742
|
-
#
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
727
|
+
# Set environment variables for the service
|
|
728
|
+
for dir_name, env_var_name in (
|
|
729
|
+
("persistent_data", "STORE_PATH"),
|
|
730
|
+
("benchmarks", "LOG_DIR"),
|
|
731
|
+
):
|
|
732
|
+
dir_path = service.path / dir_name
|
|
733
|
+
dir_path.mkdir(parents=True, exist_ok=True)
|
|
734
|
+
env_var_to_value.update({env_var_name: str(dir_path)})
|
|
747
735
|
|
|
748
736
|
service.update_env_variables_values(env_var_to_value)
|
|
749
737
|
|
|
@@ -792,7 +780,7 @@ class ServiceManager:
|
|
|
792
780
|
target_staking_params["agent_ids"] = [agent_id]
|
|
793
781
|
|
|
794
782
|
on_chain_metadata = self._get_on_chain_metadata(chain_config=chain_config)
|
|
795
|
-
on_chain_hash = on_chain_metadata.get("code_uri", "")[
|
|
783
|
+
on_chain_hash = on_chain_metadata.get("code_uri", "")[len(IPFS_URI_PREFIX) :]
|
|
796
784
|
on_chain_description = on_chain_metadata.get("description")
|
|
797
785
|
|
|
798
786
|
current_agent_bond = sftxb.get_agent_bond(
|
|
@@ -854,7 +842,7 @@ class ServiceManager:
|
|
|
854
842
|
if user_params.use_staking
|
|
855
843
|
else user_params.cost_of_bond
|
|
856
844
|
),
|
|
857
|
-
threshold=
|
|
845
|
+
threshold=len(service.agent_addresses),
|
|
858
846
|
nft=IPFSHash(user_params.nft),
|
|
859
847
|
update_token=chain_data.token,
|
|
860
848
|
token=(
|
|
@@ -904,7 +892,7 @@ class ServiceManager:
|
|
|
904
892
|
if user_params.use_staking
|
|
905
893
|
else user_params.cost_of_bond
|
|
906
894
|
),
|
|
907
|
-
threshold=
|
|
895
|
+
threshold=len(service.agent_addresses),
|
|
908
896
|
nft=IPFSHash(user_params.nft),
|
|
909
897
|
update_token=None,
|
|
910
898
|
token=(
|
|
@@ -1033,10 +1021,10 @@ class ServiceManager:
|
|
|
1033
1021
|
self.logger.info(
|
|
1034
1022
|
f"Approved {token_utility_allowance} OLAS from {safe} to {token_utility}"
|
|
1035
1023
|
)
|
|
1036
|
-
cost_of_bond = 1 * len(
|
|
1024
|
+
cost_of_bond = 1 * len(service.agent_addresses)
|
|
1037
1025
|
|
|
1038
1026
|
self.logger.info(
|
|
1039
|
-
f"Registering agent instances: {chain_data.token} -> {
|
|
1027
|
+
f"Registering agent instances: {chain_data.token} -> {service.agent_addresses}"
|
|
1040
1028
|
)
|
|
1041
1029
|
|
|
1042
1030
|
native_balance = get_asset_balance(
|
|
@@ -1053,8 +1041,8 @@ class ServiceManager:
|
|
|
1053
1041
|
sftxb.new_tx().add(
|
|
1054
1042
|
sftxb.get_register_instances_data(
|
|
1055
1043
|
service_id=chain_data.token,
|
|
1056
|
-
instances=
|
|
1057
|
-
agents=[agent_id for _ in
|
|
1044
|
+
instances=service.agent_addresses,
|
|
1045
|
+
agents=[agent_id for _ in service.agent_addresses],
|
|
1058
1046
|
cost_of_bond=cost_of_bond,
|
|
1059
1047
|
)
|
|
1060
1048
|
).settle()
|
|
@@ -1067,7 +1055,7 @@ class ServiceManager:
|
|
|
1067
1055
|
|
|
1068
1056
|
reuse_multisig = True
|
|
1069
1057
|
info = sftxb.info(token_id=chain_data.token)
|
|
1070
|
-
if info["multisig"] ==
|
|
1058
|
+
if info["multisig"] == ZERO_ADDRESS:
|
|
1071
1059
|
reuse_multisig = False
|
|
1072
1060
|
|
|
1073
1061
|
self.logger.info(f"{reuse_multisig=}")
|
|
@@ -1135,9 +1123,7 @@ class ServiceManager:
|
|
|
1135
1123
|
staking_chain = None
|
|
1136
1124
|
for chain_, config in service.chain_configs.items():
|
|
1137
1125
|
if config.chain_data.user_params.use_staking:
|
|
1138
|
-
staking_chain = chain_
|
|
1139
|
-
"optimistic", "optimism"
|
|
1140
|
-
) # TODO: remove this hack, when it's renamed in open-autonomy
|
|
1126
|
+
staking_chain = chain_
|
|
1141
1127
|
break
|
|
1142
1128
|
|
|
1143
1129
|
service.update_env_variables_values(
|
|
@@ -1148,9 +1134,7 @@ class ServiceManager:
|
|
|
1148
1134
|
for chain, config in service.chain_configs.items()
|
|
1149
1135
|
},
|
|
1150
1136
|
separators=(",", ":"),
|
|
1151
|
-
)
|
|
1152
|
-
"optimistic", "optimism"
|
|
1153
|
-
), # TODO: remove this hack, when it's renamed in open-autonomy
|
|
1137
|
+
),
|
|
1154
1138
|
"STAKING_CHAIN": staking_chain,
|
|
1155
1139
|
}
|
|
1156
1140
|
)
|
|
@@ -1203,8 +1187,6 @@ class ServiceManager:
|
|
|
1203
1187
|
chain_config = service.chain_configs[chain]
|
|
1204
1188
|
ledger_config = chain_config.ledger_config
|
|
1205
1189
|
chain_data = chain_config.chain_data
|
|
1206
|
-
keys = service.keys
|
|
1207
|
-
instances = [key.address for key in keys]
|
|
1208
1190
|
wallet = self.wallet_manager.load(ledger_config.chain.ledger_type)
|
|
1209
1191
|
safe = wallet.safes[Chain(chain)] # type: ignore
|
|
1210
1192
|
|
|
@@ -1269,7 +1251,7 @@ class ServiceManager:
|
|
|
1269
1251
|
# Swap service safe
|
|
1270
1252
|
current_safe_owners = sftxb.get_service_safe_owners(service_id=chain_data.token)
|
|
1271
1253
|
counter_current_safe_owners = Counter(s.lower() for s in current_safe_owners)
|
|
1272
|
-
counter_instances = Counter(s.lower() for s in
|
|
1254
|
+
counter_instances = Counter(s.lower() for s in service.agent_addresses)
|
|
1273
1255
|
|
|
1274
1256
|
if withdrawal_address is not None:
|
|
1275
1257
|
# we don't drain signer yet, because the owner swapping tx may need to happen
|
|
@@ -1314,16 +1296,15 @@ class ServiceManager:
|
|
|
1314
1296
|
) # noqa: E800
|
|
1315
1297
|
|
|
1316
1298
|
if withdrawal_address is not None:
|
|
1299
|
+
ethereum_crypto = KeysManager().get_crypto_instance(
|
|
1300
|
+
service.agent_addresses[0]
|
|
1301
|
+
)
|
|
1317
1302
|
# drain all native tokens from service signer key
|
|
1318
1303
|
drain_eoa(
|
|
1319
1304
|
ledger_api=self.wallet_manager.load(
|
|
1320
1305
|
ledger_config.chain.ledger_type
|
|
1321
1306
|
).ledger_api(chain=ledger_config.chain, rpc=ledger_config.rpc),
|
|
1322
|
-
crypto=
|
|
1323
|
-
private_key_path=service.path
|
|
1324
|
-
/ "deployment"
|
|
1325
|
-
/ "ethereum_private_key.txt",
|
|
1326
|
-
),
|
|
1307
|
+
crypto=ethereum_crypto,
|
|
1327
1308
|
withdrawal_address=withdrawal_address,
|
|
1328
1309
|
chain_id=ledger_config.chain.id,
|
|
1329
1310
|
)
|
|
@@ -1799,11 +1780,11 @@ class ServiceManager:
|
|
|
1799
1780
|
on_chain_state = self._get_on_chain_state(service=service, chain=chain)
|
|
1800
1781
|
if on_chain_state != OnChainState.DEPLOYED:
|
|
1801
1782
|
if chain_data.user_params.use_staking:
|
|
1802
|
-
on_chain_operations_buffer = 1 + len(service.
|
|
1783
|
+
on_chain_operations_buffer = 1 + len(service.agent_addresses)
|
|
1803
1784
|
else:
|
|
1804
1785
|
on_chain_operations_buffer = (
|
|
1805
1786
|
chain_data.user_params.cost_of_bond
|
|
1806
|
-
* (1 + len(service.
|
|
1787
|
+
* (1 + len(service.agent_addresses))
|
|
1807
1788
|
)
|
|
1808
1789
|
|
|
1809
1790
|
asset_funding_values = (
|
|
@@ -1817,21 +1798,21 @@ class ServiceManager:
|
|
|
1817
1798
|
else fund_requirements.agent
|
|
1818
1799
|
)
|
|
1819
1800
|
|
|
1820
|
-
for
|
|
1801
|
+
for agent_address in service.agent_addresses:
|
|
1821
1802
|
agent_balance = get_asset_balance(
|
|
1822
1803
|
ledger_api=ledger_api,
|
|
1823
1804
|
asset_address=asset_address,
|
|
1824
|
-
address=
|
|
1805
|
+
address=agent_address,
|
|
1825
1806
|
)
|
|
1826
1807
|
self.logger.info(
|
|
1827
|
-
f"[FUNDING_JOB] Agent {
|
|
1808
|
+
f"[FUNDING_JOB] Agent {agent_address} Asset: {asset_address} balance: {agent_balance}"
|
|
1828
1809
|
)
|
|
1829
1810
|
if agent_fund_threshold > 0:
|
|
1830
1811
|
self.logger.info(
|
|
1831
1812
|
f"[FUNDING_JOB] Required balance: {agent_fund_threshold}"
|
|
1832
1813
|
)
|
|
1833
1814
|
if agent_balance < agent_fund_threshold:
|
|
1834
|
-
self.logger.info(f"[FUNDING_JOB] Funding agent {
|
|
1815
|
+
self.logger.info(f"[FUNDING_JOB] Funding agent {agent_address}")
|
|
1835
1816
|
target_balance = (
|
|
1836
1817
|
asset_funding_values["agent"]["topup"]
|
|
1837
1818
|
if asset_funding_values is not None
|
|
@@ -1849,11 +1830,11 @@ class ServiceManager:
|
|
|
1849
1830
|
min(available_balance, target_balance - agent_balance), 0
|
|
1850
1831
|
)
|
|
1851
1832
|
self.logger.info(
|
|
1852
|
-
f"[FUNDING_JOB] Transferring {to_transfer} units (asset {asset_address}) to agent {
|
|
1833
|
+
f"[FUNDING_JOB] Transferring {to_transfer} units (asset {asset_address}) to agent {agent_address}"
|
|
1853
1834
|
)
|
|
1854
1835
|
wallet.transfer_asset(
|
|
1855
1836
|
asset=asset_address,
|
|
1856
|
-
to=
|
|
1837
|
+
to=agent_address,
|
|
1857
1838
|
amount=int(to_transfer),
|
|
1858
1839
|
chain=ledger_config.chain,
|
|
1859
1840
|
from_safe=from_safe,
|
|
@@ -1955,9 +1936,9 @@ class ServiceManager:
|
|
|
1955
1936
|
or chain_data.user_params.fund_requirements[ZERO_ADDRESS].agent
|
|
1956
1937
|
)
|
|
1957
1938
|
|
|
1958
|
-
for
|
|
1959
|
-
agent_balance = ledger_api.get_balance(address=
|
|
1960
|
-
self.logger.info(f"Agent {
|
|
1939
|
+
for agent_address in service.agent_addresses:
|
|
1940
|
+
agent_balance = ledger_api.get_balance(address=agent_address)
|
|
1941
|
+
self.logger.info(f"Agent {agent_address} balance: {agent_balance}")
|
|
1961
1942
|
self.logger.info(f"Required balance: {agent_fund_threshold}")
|
|
1962
1943
|
if agent_balance < agent_fund_threshold:
|
|
1963
1944
|
self.logger.info("Funding agents")
|
|
@@ -1965,10 +1946,10 @@ class ServiceManager:
|
|
|
1965
1946
|
agent_topup
|
|
1966
1947
|
or chain_data.user_params.fund_requirements[ZERO_ADDRESS].agent
|
|
1967
1948
|
)
|
|
1968
|
-
self.logger.info(f"Transferring {to_transfer} units to {
|
|
1949
|
+
self.logger.info(f"Transferring {to_transfer} units to {agent_address}")
|
|
1969
1950
|
wallet.transfer_erc20(
|
|
1970
1951
|
token=token,
|
|
1971
|
-
to=
|
|
1952
|
+
to=agent_address,
|
|
1972
1953
|
amount=int(to_transfer),
|
|
1973
1954
|
chain=ledger_config.chain,
|
|
1974
1955
|
from_safe=from_safe,
|
|
@@ -2003,7 +1984,7 @@ class ServiceManager:
|
|
|
2003
1984
|
rpc=rpc or ledger_config.rpc,
|
|
2004
1985
|
)
|
|
2005
1986
|
|
|
2006
|
-
def drain_service_safe(
|
|
1987
|
+
def drain_service_safe( # pylint: disable=too-many-locals
|
|
2007
1988
|
self,
|
|
2008
1989
|
service_config_id: str,
|
|
2009
1990
|
withdrawal_address: str,
|
|
@@ -2019,9 +2000,7 @@ class ServiceManager:
|
|
|
2019
2000
|
chain_data = chain_config.chain_data
|
|
2020
2001
|
wallet = self.wallet_manager.load(ledger_config.chain.ledger_type)
|
|
2021
2002
|
ledger_api = wallet.ledger_api(chain=ledger_config.chain, rpc=ledger_config.rpc)
|
|
2022
|
-
ethereum_crypto =
|
|
2023
|
-
private_key_path=service.path / "deployment" / "ethereum_private_key.txt",
|
|
2024
|
-
)
|
|
2003
|
+
ethereum_crypto = KeysManager().get_crypto_instance(service.agent_addresses[0])
|
|
2025
2004
|
|
|
2026
2005
|
# drain ERC20 tokens from service safe
|
|
2027
2006
|
for token_name, token_address in (
|
|
@@ -2195,44 +2174,6 @@ class ServiceManager:
|
|
|
2195
2174
|
)
|
|
2196
2175
|
return service
|
|
2197
2176
|
|
|
2198
|
-
def migrate_service_configs(self) -> None:
|
|
2199
|
-
"""Migrate old service config formats to new ones, if applies."""
|
|
2200
|
-
|
|
2201
|
-
bafybei_count = sum(
|
|
2202
|
-
1 for path in self.path.iterdir() if path.name.startswith("bafybei")
|
|
2203
|
-
)
|
|
2204
|
-
if bafybei_count > 1:
|
|
2205
|
-
self.log_directories()
|
|
2206
|
-
raise RuntimeError(
|
|
2207
|
-
f"Your services folder contains {bafybei_count} folders starting with 'bafybei'. This is an unintended situation. Please contact support."
|
|
2208
|
-
)
|
|
2209
|
-
|
|
2210
|
-
paths = list(self.path.iterdir())
|
|
2211
|
-
for path in paths:
|
|
2212
|
-
try:
|
|
2213
|
-
if path.name.startswith(DELETE_PREFIX):
|
|
2214
|
-
shutil.rmtree(path)
|
|
2215
|
-
self.logger.info(f"Deleted folder: {path.name}")
|
|
2216
|
-
|
|
2217
|
-
if path.name.startswith(SERVICE_CONFIG_PREFIX) or path.name.startswith(
|
|
2218
|
-
"bafybei"
|
|
2219
|
-
):
|
|
2220
|
-
self.logger.info(f"migrate_service_configs {str(path)}")
|
|
2221
|
-
migrated = Service.migrate_format(path)
|
|
2222
|
-
if migrated:
|
|
2223
|
-
self.logger.info(f"Folder {str(path)} has been migrated.")
|
|
2224
|
-
except Exception as e: # pylint: disable=broad-except
|
|
2225
|
-
self.logger.error(
|
|
2226
|
-
f"Failed to migrate service: {path.name}. Exception {e}: {traceback.format_exc()}"
|
|
2227
|
-
)
|
|
2228
|
-
# Rename the invalid path
|
|
2229
|
-
timestamp = int(time.time())
|
|
2230
|
-
invalid_path = path.parent / f"invalid_{timestamp}_{path.name}"
|
|
2231
|
-
os.rename(path, invalid_path)
|
|
2232
|
-
self.logger.info(
|
|
2233
|
-
f"Renamed invalid service: {path.name} to {invalid_path.name}"
|
|
2234
|
-
)
|
|
2235
|
-
|
|
2236
2177
|
def refill_requirements( # pylint: disable=too-many-locals,too-many-statements,too-many-nested-blocks
|
|
2237
2178
|
self, service_config_id: str
|
|
2238
2179
|
) -> t.Dict:
|
|
@@ -2260,11 +2201,9 @@ class ServiceManager:
|
|
|
2260
2201
|
master_safe_exists = wallet.safes.get(Chain(chain)) is not None
|
|
2261
2202
|
master_safe = wallet.safes.get(Chain(chain), "master_safe")
|
|
2262
2203
|
|
|
2263
|
-
agent_addresses =
|
|
2204
|
+
agent_addresses = set(service.agent_addresses)
|
|
2264
2205
|
service_safe = (
|
|
2265
|
-
chain_data.multisig
|
|
2266
|
-
if chain_data.multisig and chain_data.multisig != NON_EXISTENT_MULTISIG
|
|
2267
|
-
else "service_safe"
|
|
2206
|
+
chain_data.multisig if chain_data.multisig else "service_safe"
|
|
2268
2207
|
)
|
|
2269
2208
|
|
|
2270
2209
|
if not master_safe_exists:
|
operate/services/protocol.py
CHANGED
|
@@ -61,6 +61,7 @@ from operate.constants import (
|
|
|
61
61
|
ON_CHAIN_INTERACT_RETRIES,
|
|
62
62
|
ON_CHAIN_INTERACT_SLEEP,
|
|
63
63
|
ON_CHAIN_INTERACT_TIMEOUT,
|
|
64
|
+
ZERO_ADDRESS,
|
|
64
65
|
)
|
|
65
66
|
from operate.data import DATA_DIR
|
|
66
67
|
from operate.data.contracts.dual_staking_token.contract import DualStakingTokenContract
|
|
@@ -69,7 +70,6 @@ from operate.operate_types import Chain as OperateChain
|
|
|
69
70
|
from operate.operate_types import ContractAddresses
|
|
70
71
|
from operate.utils.gnosis import (
|
|
71
72
|
MultiSendOperation,
|
|
72
|
-
NULL_ADDRESS,
|
|
73
73
|
SafeOperation,
|
|
74
74
|
hash_payload_to_hex,
|
|
75
75
|
skill_input_hex_to_payload,
|
|
@@ -710,7 +710,7 @@ class _ChainUtil:
|
|
|
710
710
|
token_id=service_id,
|
|
711
711
|
)
|
|
712
712
|
|
|
713
|
-
if multisig_address ==
|
|
713
|
+
if multisig_address == ZERO_ADDRESS:
|
|
714
714
|
return []
|
|
715
715
|
|
|
716
716
|
return registry_contracts.gnosis_safe.get_owners(
|
|
@@ -1605,7 +1605,7 @@ def get_reuse_multisig_from_safe_payload( # pylint: disable=too-many-locals
|
|
|
1605
1605
|
chain_type=chain_type,
|
|
1606
1606
|
token_id=service_id,
|
|
1607
1607
|
)
|
|
1608
|
-
if multisig_address ==
|
|
1608
|
+
if multisig_address == ZERO_ADDRESS:
|
|
1609
1609
|
return None, None, "Cannot reuse multisig, No previous deployment exist!"
|
|
1610
1610
|
|
|
1611
1611
|
multisend_address = ContractConfigs.get(MULTISEND_CONTRACT.name).contracts[
|
|
@@ -1723,8 +1723,8 @@ def get_reuse_multisig_from_safe_payload( # pylint: disable=too-many-locals
|
|
|
1723
1723
|
0, # safe tx gas
|
|
1724
1724
|
0, # bas gas
|
|
1725
1725
|
0, # safe gas price
|
|
1726
|
-
|
|
1727
|
-
|
|
1726
|
+
ZERO_ADDRESS, # gas token
|
|
1727
|
+
ZERO_ADDRESS, # refund receiver
|
|
1728
1728
|
signature_bytes, # signatures
|
|
1729
1729
|
],
|
|
1730
1730
|
)
|