olas-operate-middleware 0.11.5__py3-none-any.whl → 0.12.1__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.
operate/migration.py CHANGED
@@ -28,8 +28,11 @@ from pathlib import Path
28
28
  from time import time
29
29
 
30
30
  from aea_cli_ipfs.ipfs_utils import IPFSTool
31
+ from aea_ledger_ethereum import EthereumCrypto
32
+ from web3 import Web3
31
33
 
32
34
  from operate.constants import USER_JSON, ZERO_ADDRESS
35
+ from operate.keys import KeysManager
33
36
  from operate.operate_types import Chain, LedgerType
34
37
  from operate.services.manage import ServiceManager
35
38
  from operate.services.service import (
@@ -38,7 +41,7 @@ from operate.services.service import (
38
41
  SERVICE_CONFIG_VERSION,
39
42
  Service,
40
43
  )
41
- from operate.utils import create_backup
44
+ from operate.utils import create_backup, unrecoverable_delete
42
45
  from operate.wallet.master import LEDGER_TYPE_TO_WALLET_CLASS, MasterWalletManager
43
46
 
44
47
 
@@ -454,3 +457,65 @@ class MigrationManager:
454
457
  self.logger.info(
455
458
  "[MIGRATION MANAGER] Migrated quickstart config: %s.", qs_config.name
456
459
  )
460
+
461
+ def migrate_keys(self, keys_manager: KeysManager) -> None:
462
+ """Migrate keys format if needed."""
463
+ self.logger.info("Migrating keys...")
464
+
465
+ for key_file in keys_manager.path.iterdir():
466
+ if (
467
+ not key_file.is_file()
468
+ or key_file.suffix == ".bak"
469
+ or not Web3.is_address(key_file.name)
470
+ ):
471
+ self.logger.warning(f"Skipping non-key file: {key_file}")
472
+ continue
473
+
474
+ migrated = False
475
+ backup_path = key_file.with_suffix(".bak")
476
+
477
+ try:
478
+ with open(key_file, "r", encoding="utf-8") as file:
479
+ data = json.load(file)
480
+ except Exception as e: # pylint: disable=broad-except
481
+ self.logger.error(
482
+ f"Failed to read key file: {key_file}\n"
483
+ f"Key file content:\n{key_file.read_text(encoding='utf-8')}\n"
484
+ f"Exception {e}: {traceback.format_exc()}"
485
+ )
486
+ raise e
487
+
488
+ old_to_new_ledgers = {0: "ethereum", 1: "solana"}
489
+ if data.get("ledger") in old_to_new_ledgers:
490
+ data["ledger"] = old_to_new_ledgers.get(data["ledger"])
491
+ with open(key_file, "w", encoding="utf-8") as file:
492
+ json.dump(data, file, indent=2)
493
+
494
+ migrated = True
495
+
496
+ private_key = data.get("private_key")
497
+ if (
498
+ private_key
499
+ and keys_manager.password is not None
500
+ and private_key.startswith("0x")
501
+ ):
502
+ crypto: EthereumCrypto = keys_manager.private_key_to_crypto(
503
+ private_key=private_key,
504
+ password=None,
505
+ )
506
+ encrypted_private_key = crypto.encrypt(password=keys_manager.password)
507
+ data["private_key"] = encrypted_private_key
508
+ if backup_path.exists():
509
+ unrecoverable_delete(backup_path)
510
+
511
+ migrated = True
512
+
513
+ if migrated:
514
+ with open(key_file, "w", encoding="utf-8") as file:
515
+ json.dump(data, file, indent=2)
516
+
517
+ if not backup_path.exists():
518
+ shutil.copyfile(key_file, backup_path)
519
+
520
+ if migrated:
521
+ self.logger.info(f"Key {key_file.name} has been migrated.")
@@ -22,10 +22,9 @@ from typing import TYPE_CHECKING
22
22
 
23
23
  from operate.account.user import UserAccount
24
24
  from operate.constants import USER_JSON
25
- from operate.operate_types import LedgerType
25
+ from operate.keys import KeysManager
26
26
  from operate.quickstart.run_service import ask_confirm_password
27
27
  from operate.quickstart.utils import ask_or_get_from_env, print_section, print_title
28
- from operate.wallet.master import EthereumMasterWallet
29
28
 
30
29
 
31
30
  if TYPE_CHECKING:
@@ -66,10 +65,7 @@ def reset_password(operate: "OperateApp") -> None:
66
65
 
67
66
  print('Resetting password of "ethereum" wallet...')
68
67
  operate.password = old_password
69
- operate.wallet_manager.password = old_password
70
- wallet: EthereumMasterWallet = operate.wallet_manager.load(
71
- ledger_type=LedgerType.ETHEREUM
72
- )
73
- wallet.update_password(new_password=new_password)
68
+ operate.wallet_manager.update_password(new_password=new_password)
69
+ KeysManager().update_password(new_password=new_password)
74
70
 
75
71
  print_section("Password reset done!")
operate/resource.py CHANGED
@@ -24,12 +24,13 @@ import json
24
24
  import os
25
25
  import platform
26
26
  import shutil
27
- import time
28
27
  import types
29
28
  import typing as t
30
29
  from dataclasses import asdict, is_dataclass
31
30
  from pathlib import Path
32
31
 
32
+ from operate.utils import safe_file_operation
33
+
33
34
 
34
35
  # pylint: disable=too-many-return-statements,no-member
35
36
 
@@ -94,23 +95,6 @@ def deserialize(obj: t.Any, otype: t.Any) -> t.Any:
94
95
  return obj
95
96
 
96
97
 
97
- def _safe_file_operation(operation: t.Callable, *args: t.Any, **kwargs: t.Any) -> None:
98
- """Safely perform file operation with retries on Windows."""
99
- max_retries = 3 if platform.system() == "Windows" else 1
100
-
101
- for attempt in range(max_retries):
102
- try:
103
- operation(*args, **kwargs)
104
- return
105
- except (PermissionError, FileNotFoundError, OSError) as e:
106
- if attempt == max_retries - 1:
107
- raise e
108
-
109
- if platform.system() == "Windows":
110
- # On Windows, wait a bit and retry
111
- time.sleep(0.1)
112
-
113
-
114
98
  class LocalResource:
115
99
  """Initialize local resource."""
116
100
 
@@ -163,13 +147,13 @@ class LocalResource:
163
147
  bak0 = path.with_name(f"{path.name}.0.bak")
164
148
 
165
149
  if path.exists() and not bak0.exists():
166
- _safe_file_operation(shutil.copy2, path, bak0)
150
+ safe_file_operation(shutil.copy2, path, bak0)
167
151
 
168
152
  tmp_path = path.parent / f".{path.name}.tmp"
169
153
 
170
154
  # Clean up any existing tmp file
171
155
  if tmp_path.exists():
172
- _safe_file_operation(tmp_path.unlink)
156
+ safe_file_operation(tmp_path.unlink)
173
157
 
174
158
  tmp_path.write_text(
175
159
  json.dumps(
@@ -181,11 +165,11 @@ class LocalResource:
181
165
 
182
166
  # Atomic replace to avoid corruption
183
167
  try:
184
- _safe_file_operation(os.replace, tmp_path, path)
168
+ safe_file_operation(os.replace, tmp_path, path)
185
169
  except (PermissionError, FileNotFoundError):
186
170
  # On Windows, if the replace fails, clean up and skip
187
171
  if platform.system() == "Windows":
188
- _safe_file_operation(tmp_path.unlink)
172
+ safe_file_operation(tmp_path.unlink)
189
173
 
190
174
  self.load(self.path) # Validate before making backup
191
175
 
@@ -195,7 +179,7 @@ class LocalResource:
195
179
  older = path.with_name(f"{path.name}.{i + 1}.bak")
196
180
  if newer.exists():
197
181
  if older.exists():
198
- _safe_file_operation(older.unlink)
199
- _safe_file_operation(newer.rename, older)
182
+ safe_file_operation(older.unlink)
183
+ safe_file_operation(newer.rename, older)
200
184
 
201
- _safe_file_operation(shutil.copy2, path, bak0)
185
+ safe_file_operation(shutil.copy2, path, bak0)
@@ -56,7 +56,7 @@ class AbstractDeploymentRunner(ABC):
56
56
  self._work_directory = work_directory
57
57
 
58
58
  @abstractmethod
59
- def start(self) -> None:
59
+ def start(self, password: str) -> None:
60
60
  """Start the deployment."""
61
61
 
62
62
  @abstractmethod
@@ -182,7 +182,7 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
182
182
  )
183
183
  return env
184
184
 
185
- def _setup_agent(self) -> None:
185
+ def _setup_agent(self, password: str) -> None:
186
186
  """Setup agent."""
187
187
  working_dir = self._work_directory
188
188
  env = self._prepare_agent_env()
@@ -223,18 +223,37 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
223
223
  working_dir / "agent" / "ethereum_private_key.txt",
224
224
  )
225
225
 
226
- self._run_aea_command("-s", "add-key", "ethereum", cwd=working_dir / "agent")
227
226
  self._run_aea_command(
228
- "-s", "add-key", "ethereum", "--connection", cwd=working_dir / "agent"
227
+ "-s",
228
+ "add-key",
229
+ "--password",
230
+ password,
231
+ "ethereum",
232
+ cwd=working_dir / "agent",
233
+ )
234
+ self._run_aea_command(
235
+ "-s",
236
+ "add-key",
237
+ "--password",
238
+ password,
239
+ "ethereum",
240
+ "--connection",
241
+ cwd=working_dir / "agent",
229
242
  )
230
243
 
231
- self._run_aea_command("-s", "issue-certificates", cwd=working_dir / "agent")
244
+ self._run_aea_command(
245
+ "-s",
246
+ "issue-certificates",
247
+ "--password",
248
+ password,
249
+ cwd=working_dir / "agent",
250
+ )
232
251
 
233
- def start(self) -> None:
252
+ def start(self, password: str) -> None:
234
253
  """Start the deployment with retries."""
235
254
  for _ in range(self.START_TRIES):
236
255
  try:
237
- self._start()
256
+ self._start(password=password)
238
257
  return
239
258
  except Exception as e: # pylint: disable=broad-except
240
259
  self.logger.exception(f"Error on starting deployment: {e}")
@@ -242,11 +261,11 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
242
261
  f"Failed to start the deployment after {self.START_TRIES} attempts! Check logs"
243
262
  )
244
263
 
245
- def _start(self) -> None:
264
+ def _start(self, password: str) -> None:
246
265
  """Start the deployment."""
247
- self._setup_agent()
266
+ self._setup_agent(password=password)
248
267
  self._start_tendermint()
249
- self._start_agent()
268
+ self._start_agent(password=password)
250
269
 
251
270
  def stop(self) -> None:
252
271
  """Stop the deployment."""
@@ -285,7 +304,7 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
285
304
  """Start tendermint process."""
286
305
 
287
306
  @abstractmethod
288
- def _start_agent(self) -> None:
307
+ def _start_agent(self, password: str) -> None:
289
308
  """Start aea process."""
290
309
 
291
310
  @property
@@ -318,7 +337,7 @@ class PyInstallerHostDeploymentRunner(BaseDeploymentRunner):
318
337
  """Return tendermint path."""
319
338
  return str(Path(os.path.dirname(sys.executable)) / "tendermint_bin") # type: ignore # pylint: disable=protected-access
320
339
 
321
- def _start_agent(self) -> None:
340
+ def _start_agent(self, password: str) -> None:
322
341
  """Start agent process."""
323
342
  working_dir = self._work_directory
324
343
  env = json.loads((working_dir / "agent.json").read_text(encoding="utf-8"))
@@ -326,13 +345,17 @@ class PyInstallerHostDeploymentRunner(BaseDeploymentRunner):
326
345
  env["PYTHONIOENCODING"] = "utf8"
327
346
  env = {**os.environ, **env}
328
347
 
329
- process = self._start_agent_process(env=env, working_dir=working_dir)
348
+ process = self._start_agent_process(
349
+ env=env, working_dir=working_dir, password=password
350
+ )
330
351
  (working_dir / "agent.pid").write_text(
331
352
  data=str(process.pid),
332
353
  encoding="utf-8",
333
354
  )
334
355
 
335
- def _start_agent_process(self, env: Dict, working_dir: Path) -> subprocess.Popen:
356
+ def _start_agent_process(
357
+ self, env: Dict, working_dir: Path, password: str
358
+ ) -> subprocess.Popen:
336
359
  """Start agent process."""
337
360
  raise NotImplementedError
338
361
 
@@ -364,7 +387,9 @@ class PyInstallerHostDeploymentRunner(BaseDeploymentRunner):
364
387
  class PyInstallerHostDeploymentRunnerMac(PyInstallerHostDeploymentRunner):
365
388
  """Mac deployment runner."""
366
389
 
367
- def _start_agent_process(self, env: Dict, working_dir: Path) -> subprocess.Popen:
390
+ def _start_agent_process(
391
+ self, env: Dict, working_dir: Path, password: str
392
+ ) -> subprocess.Popen:
368
393
  """Start agent process."""
369
394
  agent_runner_log_file = self._open_agent_runner_log_file()
370
395
  process = subprocess.Popen( # pylint: disable=consider-using-with,subprocess-popen-preexec-fn # nosec
@@ -372,6 +397,8 @@ class PyInstallerHostDeploymentRunnerMac(PyInstallerHostDeploymentRunner):
372
397
  self._agent_runner_bin,
373
398
  "-s",
374
399
  "run",
400
+ "--password",
401
+ password,
375
402
  ],
376
403
  cwd=working_dir / "agent",
377
404
  stdout=agent_runner_log_file,
@@ -486,7 +513,9 @@ class PyInstallerHostDeploymentRunnerWindows(PyInstallerHostDeploymentRunner):
486
513
  """Return tendermint path."""
487
514
  return str(Path(os.path.dirname(sys.executable)) / "tendermint_win.exe") # type: ignore # pylint: disable=protected-access
488
515
 
489
- def _start_agent_process(self, env: Dict, working_dir: Path) -> subprocess.Popen:
516
+ def _start_agent_process(
517
+ self, env: Dict, working_dir: Path, password: str
518
+ ) -> subprocess.Popen:
490
519
  """Start agent process."""
491
520
  agent_runner_log_file = self._open_agent_runner_log_file()
492
521
  process = subprocess.Popen( # pylint: disable=consider-using-with # nosec
@@ -494,6 +523,8 @@ class PyInstallerHostDeploymentRunnerWindows(PyInstallerHostDeploymentRunner):
494
523
  self._agent_runner_bin,
495
524
  "-s",
496
525
  "run",
526
+ "--password",
527
+ password,
497
528
  ], # TODO: Patch for Windows failing hash
498
529
  cwd=working_dir / "agent",
499
530
  stdout=agent_runner_log_file,
@@ -533,7 +564,7 @@ class HostPythonHostDeploymentRunner(BaseDeploymentRunner):
533
564
  """Return aea_bin path."""
534
565
  return str(self._venv_dir / "bin" / "aea")
535
566
 
536
- def _start_agent(self) -> None:
567
+ def _start_agent(self, password: str) -> None:
537
568
  """Start agent process."""
538
569
  working_dir = self._work_directory
539
570
  env = json.loads((working_dir / "agent.json").read_text(encoding="utf-8"))
@@ -546,6 +577,8 @@ class HostPythonHostDeploymentRunner(BaseDeploymentRunner):
546
577
  self._agent_runner_bin,
547
578
  "-s",
548
579
  "run",
580
+ "--password",
581
+ password,
549
582
  ], # TODO: Patch for Windows failing hash
550
583
  cwd=str(working_dir / "agent"),
551
584
  env={**os.environ, **env},
@@ -613,14 +646,15 @@ class HostPythonHostDeploymentRunner(BaseDeploymentRunner):
613
646
  # Install tendermint dependencies
614
647
  "flask",
615
648
  "requests",
649
+ "multiaddr==0.0.9", # TODO: remove when pinned on open-aea
616
650
  ],
617
651
  )
618
652
 
619
- def _setup_agent(self) -> None:
653
+ def _setup_agent(self, password: str) -> None:
620
654
  """Prepare agent."""
621
655
  multiprocessing.set_start_method("spawn")
622
656
  self._setup_venv()
623
- super()._setup_agent()
657
+ super()._setup_agent(password=password)
624
658
  # Install agent dependencies
625
659
  self._run_cmd(
626
660
  args=[
@@ -707,7 +741,7 @@ class DeploymentManager:
707
741
  "Failed to perform test connection to ipfs to check network connection!"
708
742
  )
709
743
 
710
- def run_deployment(self, build_dir: Path) -> None:
744
+ def run_deployment(self, build_dir: Path, password: str) -> None:
711
745
  """Run deployment."""
712
746
  if self._is_stopping:
713
747
  raise RuntimeError("deployment manager stopped")
@@ -721,7 +755,7 @@ class DeploymentManager:
721
755
  self._states[build_dir] = States.STARTING
722
756
  try:
723
757
  deployment_runner = self._get_deployment_runner(build_dir=build_dir)
724
- deployment_runner.start()
758
+ deployment_runner.start(password=password)
725
759
  self.logger.info(f"Started deployment {build_dir}")
726
760
  self._states[build_dir] = States.STARTED
727
761
  except Exception: # pylint: disable=broad-except
@@ -729,15 +763,15 @@ class DeploymentManager:
729
763
  f"Starting deployment failed {build_dir}. so try to stop"
730
764
  )
731
765
  self._states[build_dir] = States.ERROR
732
- self.stop_deployemnt(build_dir=build_dir, force=True)
766
+ self.stop_deployment(build_dir=build_dir, force=True)
733
767
 
734
768
  if self._is_stopping:
735
769
  self.logger.warning(
736
770
  f"Deployment at {build_dir} started when it was going to stop, so stop it"
737
771
  )
738
- self.stop_deployemnt(build_dir=build_dir, force=True)
772
+ self.stop_deployment(build_dir=build_dir, force=True)
739
773
 
740
- def stop_deployemnt(self, build_dir: Path, force: bool = False) -> None:
774
+ def stop_deployment(self, build_dir: Path, force: bool = False) -> None:
741
775
  """Stop the deployment."""
742
776
  if (
743
777
  self.get_state(build_dir=build_dir) in [States.STARTING, States.STOPPING]
@@ -760,14 +794,14 @@ class DeploymentManager:
760
794
  deployment_manager = DeploymentManager()
761
795
 
762
796
 
763
- def run_host_deployment(build_dir: Path) -> None:
797
+ def run_host_deployment(build_dir: Path, password: str) -> None:
764
798
  """Run host deployment."""
765
- deployment_manager.run_deployment(build_dir=build_dir)
799
+ deployment_manager.run_deployment(build_dir=build_dir, password=password)
766
800
 
767
801
 
768
802
  def stop_host_deployment(build_dir: Path) -> None:
769
803
  """Stop host deployment."""
770
- deployment_manager.stop_deployemnt(build_dir=build_dir)
804
+ deployment_manager.stop_deployment(build_dir=build_dir)
771
805
 
772
806
 
773
807
  def stop_deployment_manager() -> None:
@@ -2232,10 +2232,11 @@ class ServiceManager:
2232
2232
  use_kubernetes=use_kubernetes,
2233
2233
  force=True,
2234
2234
  chain=chain or service.home_chain,
2235
+ password=self.wallet_manager.password,
2235
2236
  )
2236
2237
  if build_only:
2237
2238
  return deployment
2238
- deployment.start(use_docker=use_docker)
2239
+ deployment.start(password=self.wallet_manager.password, use_docker=use_docker)
2239
2240
  return deployment
2240
2241
 
2241
2242
  def stop_service_locally(
@@ -28,7 +28,7 @@ import os
28
28
  import tempfile
29
29
  import typing as t
30
30
  from enum import Enum
31
- from functools import cache
31
+ from functools import cache, cached_property
32
32
  from pathlib import Path
33
33
  from typing import Optional, Union, cast
34
34
 
@@ -780,6 +780,17 @@ class _ChainUtil:
780
780
  rpc=self.rpc,
781
781
  )
782
782
 
783
+ @cached_property
784
+ def service_manager_address(self) -> str: # TODO: backport to OA
785
+ """Get service manager contract address."""
786
+ service_registry = registry_contracts.service_registry.get_instance(
787
+ ledger_api=self.ledger_api,
788
+ contract_address=CONTRACTS[OperateChain(self.chain_type.value)][
789
+ "service_registry"
790
+ ],
791
+ )
792
+ return service_registry.functions.manager().call()
793
+
783
794
  @property
784
795
  def service_manager_instance(self) -> Contract:
785
796
  """Load service manager contract instance."""
@@ -788,7 +799,7 @@ class _ChainUtil:
788
799
  )
789
800
  instance = self.ledger_api.get_contract_instance(
790
801
  contract_interface,
791
- self.contracts["service_manager"],
802
+ self.service_manager_address,
792
803
  )
793
804
  return instance
794
805
 
@@ -1334,7 +1345,7 @@ class EthSafeTxBuilder(_ChainUtil):
1334
1345
  )
1335
1346
 
1336
1347
  return {
1337
- "to": self.contracts["service_manager"],
1348
+ "to": self.service_manager_address,
1338
1349
  "data": txd[2:],
1339
1350
  "operation": MultiSendOperation.CALL,
1340
1351
  "value": 0,
@@ -1366,7 +1377,7 @@ class EthSafeTxBuilder(_ChainUtil):
1366
1377
  """Get activate tx data."""
1367
1378
  instance = registry_contracts.service_manager.get_instance(
1368
1379
  ledger_api=self.ledger_api,
1369
- contract_address=self.contracts["service_manager"],
1380
+ contract_address=self.service_manager_address,
1370
1381
  )
1371
1382
  txd = instance.encodeABI(
1372
1383
  fn_name="activateRegistration",
@@ -1374,7 +1385,7 @@ class EthSafeTxBuilder(_ChainUtil):
1374
1385
  )
1375
1386
  return {
1376
1387
  "from": self.safe,
1377
- "to": self.contracts["service_manager"],
1388
+ "to": self.service_manager_address,
1378
1389
  "data": txd[2:],
1379
1390
  "operation": MultiSendOperation.CALL,
1380
1391
  "value": cost_of_bond,
@@ -1390,7 +1401,7 @@ class EthSafeTxBuilder(_ChainUtil):
1390
1401
  """Get register instances tx data."""
1391
1402
  instance = registry_contracts.service_manager.get_instance(
1392
1403
  ledger_api=self.ledger_api,
1393
- contract_address=self.contracts["service_manager"],
1404
+ contract_address=self.service_manager_address,
1394
1405
  )
1395
1406
  txd = instance.encodeABI(
1396
1407
  fn_name="registerAgents",
@@ -1402,7 +1413,7 @@ class EthSafeTxBuilder(_ChainUtil):
1402
1413
  )
1403
1414
  return {
1404
1415
  "from": self.safe,
1405
- "to": self.contracts["service_manager"],
1416
+ "to": self.service_manager_address,
1406
1417
  "data": txd[2:],
1407
1418
  "operation": MultiSendOperation.CALL,
1408
1419
  "value": cost_of_bond,
@@ -1418,7 +1429,7 @@ class EthSafeTxBuilder(_ChainUtil):
1418
1429
  """Get the deploy data instructions for a safe"""
1419
1430
  registry_instance = registry_contracts.service_manager.get_instance(
1420
1431
  ledger_api=self.ledger_api,
1421
- contract_address=self.contracts["service_manager"],
1432
+ contract_address=self.service_manager_address,
1422
1433
  )
1423
1434
  approve_hash_message = None
1424
1435
  if reuse_multisig:
@@ -1476,7 +1487,7 @@ class EthSafeTxBuilder(_ChainUtil):
1476
1487
  ],
1477
1488
  )
1478
1489
  deploy_message = {
1479
- "to": self.contracts["service_manager"],
1490
+ "to": self.service_manager_address,
1480
1491
  "data": deploy_data[2:],
1481
1492
  "operation": MultiSendOperation.CALL,
1482
1493
  "value": 0,
@@ -1665,14 +1676,14 @@ class EthSafeTxBuilder(_ChainUtil):
1665
1676
  """Get terminate tx data."""
1666
1677
  instance = registry_contracts.service_manager.get_instance(
1667
1678
  ledger_api=self.ledger_api,
1668
- contract_address=self.contracts["service_manager"],
1679
+ contract_address=self.service_manager_address,
1669
1680
  )
1670
1681
  txd = instance.encodeABI(
1671
1682
  fn_name="terminate",
1672
1683
  args=[service_id],
1673
1684
  )
1674
1685
  return {
1675
- "to": self.contracts["service_manager"],
1686
+ "to": self.service_manager_address,
1676
1687
  "data": txd[2:],
1677
1688
  "operation": MultiSendOperation.CALL,
1678
1689
  "value": 0,
@@ -1682,14 +1693,14 @@ class EthSafeTxBuilder(_ChainUtil):
1682
1693
  """Get unbond tx data."""
1683
1694
  instance = registry_contracts.service_manager.get_instance(
1684
1695
  ledger_api=self.ledger_api,
1685
- contract_address=self.contracts["service_manager"],
1696
+ contract_address=self.service_manager_address,
1686
1697
  )
1687
1698
  txd = instance.encodeABI(
1688
1699
  fn_name="unbond",
1689
1700
  args=[service_id],
1690
1701
  )
1691
1702
  return {
1692
- "to": self.contracts["service_manager"],
1703
+ "to": self.service_manager_address,
1693
1704
  "data": txd[2:],
1694
1705
  "operation": MultiSendOperation.CALL,
1695
1706
  "value": 0,
@@ -97,6 +97,7 @@ from operate.operate_types import (
97
97
  from operate.resource import LocalResource
98
98
  from operate.services.deployment_runner import run_host_deployment, stop_host_deployment
99
99
  from operate.services.utils import tendermint
100
+ from operate.utils import unrecoverable_delete
100
101
  from operate.utils.gnosis import get_asset_balance
101
102
  from operate.utils.ssl import create_ssl_certificate
102
103
 
@@ -394,7 +395,7 @@ class Deployment(LocalResource):
394
395
  if source_path.exists():
395
396
  shutil.copy(source_path, destination_path)
396
397
 
397
- def _build_kubernetes(self, force: bool = True) -> None:
398
+ def _build_kubernetes(self, password: str, force: bool = True) -> None:
398
399
  """Build kubernetes deployment."""
399
400
  k8s_build = self.path / DEPLOYMENT_DIR / "abci_build_k8s"
400
401
  if k8s_build.exists() and force:
@@ -402,11 +403,23 @@ class Deployment(LocalResource):
402
403
  mkdirs(build_dir=k8s_build)
403
404
 
404
405
  service = Service.load(path=self.path)
406
+ keys_file = self.path / DEFAULT_KEYS_FILE
407
+ keys_file.write_text(
408
+ json.dumps(
409
+ [
410
+ KeysManager().get(address).get_decrypted(password)
411
+ for address in service.agent_addresses
412
+ ],
413
+ indent=4,
414
+ ),
415
+ encoding="utf-8",
416
+ )
405
417
  builder = ServiceBuilder.from_dir(
406
418
  path=service.package_absolute_path,
407
- keys_file=self.path / DEFAULT_KEYS_FILE,
419
+ keys_file=keys_file,
408
420
  number_of_agents=len(service.agent_addresses),
409
421
  )
422
+ unrecoverable_delete(keys_file)
410
423
  builder.deplopyment_type = KubernetesGenerator.deployment_type
411
424
  (
412
425
  KubernetesGenerator(
@@ -424,6 +437,7 @@ class Deployment(LocalResource):
424
437
 
425
438
  def _build_docker(
426
439
  self,
440
+ password: str,
427
441
  force: bool = True,
428
442
  chain: t.Optional[str] = None,
429
443
  ) -> None:
@@ -448,7 +462,7 @@ class Deployment(LocalResource):
448
462
  keys_file.write_text(
449
463
  json.dumps(
450
464
  [
451
- KeysManager().get(address).json
465
+ KeysManager().get(address).get_decrypted(password)
452
466
  for address in service.agent_addresses
453
467
  ],
454
468
  indent=4,
@@ -461,6 +475,7 @@ class Deployment(LocalResource):
461
475
  keys_file=keys_file,
462
476
  number_of_agents=len(service.agent_addresses),
463
477
  )
478
+ unrecoverable_delete(keys_file)
464
479
  builder.deplopyment_type = DockerComposeGenerator.deployment_type
465
480
  builder.try_update_abci_connection_params()
466
481
 
@@ -614,6 +629,7 @@ class Deployment(LocalResource):
614
629
 
615
630
  def build(
616
631
  self,
632
+ password: str,
617
633
  use_docker: bool = False,
618
634
  use_kubernetes: bool = False,
619
635
  force: bool = True,
@@ -649,9 +665,9 @@ class Deployment(LocalResource):
649
665
  )
650
666
  service.consume_env_variables()
651
667
  if use_docker:
652
- self._build_docker(force=force, chain=chain)
668
+ self._build_docker(password=password, force=force, chain=chain)
653
669
  if use_kubernetes:
654
- self._build_kubernetes(force=force)
670
+ self._build_kubernetes(password=password, force=force)
655
671
  else:
656
672
  ssl_key_path, ssl_cert_path = create_ssl_certificate(
657
673
  ssl_dir=service.path / DEPLOYMENT_DIR / "ssl"
@@ -668,7 +684,7 @@ class Deployment(LocalResource):
668
684
  os.environ.clear()
669
685
  os.environ.update(original_env)
670
686
 
671
- def start(self, use_docker: bool = False) -> None:
687
+ def start(self, password: str, use_docker: bool = False) -> None:
672
688
  """Start the service"""
673
689
  if self.status != DeploymentStatus.BUILT:
674
690
  raise NotAllowed(
@@ -686,7 +702,9 @@ class Deployment(LocalResource):
686
702
  project_name=self.path.name,
687
703
  )
688
704
  else:
689
- run_host_deployment(build_dir=self.path / "deployment")
705
+ run_host_deployment(
706
+ build_dir=self.path / "deployment", password=password
707
+ )
690
708
  except Exception:
691
709
  self.status = DeploymentStatus.BUILT
692
710
  self.store()