olas-operate-middleware 0.12.2__py3-none-any.whl → 0.13.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.
@@ -105,6 +105,11 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
105
105
  START_TRIES = constants.DEPLOYMENT_START_TRIES_NUM
106
106
  logger = setup_logger(name="operate.base_deployment_runner")
107
107
 
108
+ def __init__(self, work_directory: Path, is_aea: bool) -> None:
109
+ """Initialize the deployment runner."""
110
+ super().__init__(work_directory)
111
+ self._is_aea = is_aea
112
+
108
113
  def _open_agent_runner_log_file(self) -> TextIOWrapper:
109
114
  """Open agent_runner.log file."""
110
115
  return (
@@ -186,6 +191,21 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
186
191
  """Setup agent."""
187
192
  working_dir = self._work_directory
188
193
  env = self._prepare_agent_env()
194
+ agent_alias_name = "agent"
195
+ agent_dir_full_path = Path(working_dir) / agent_alias_name
196
+ if not self._is_aea:
197
+ if agent_dir_full_path.exists():
198
+ # remove if exists before fetching! can have issues with retry mechanism of multiple start attempts
199
+ with suppress(Exception):
200
+ shutil.rmtree(agent_dir_full_path, ignore_errors=True)
201
+
202
+ # Add keys
203
+ agent_dir_full_path.mkdir(exist_ok=True, parents=True)
204
+ shutil.copy(
205
+ working_dir / "ethereum_private_key.txt",
206
+ working_dir / "agent" / "ethereum_private_key.txt",
207
+ )
208
+ return
189
209
 
190
210
  self._run_aea_command(
191
211
  "init",
@@ -199,10 +219,6 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
199
219
  cwd=working_dir,
200
220
  )
201
221
 
202
- agent_alias_name = "agent"
203
-
204
- agent_dir_full_path = Path(working_dir) / agent_alias_name
205
-
206
222
  if agent_dir_full_path.exists():
207
223
  # remove if exists before fetching! can have issues with retry mechanism of multiple start attempts
208
224
  with suppress(Exception):
@@ -264,13 +280,16 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
264
280
  def _start(self, password: str) -> None:
265
281
  """Start the deployment."""
266
282
  self._setup_agent(password=password)
267
- self._start_tendermint()
283
+ if self._is_aea:
284
+ self._start_tendermint()
285
+
268
286
  self._start_agent(password=password)
269
287
 
270
288
  def stop(self) -> None:
271
289
  """Stop the deployment."""
272
290
  self._stop_agent()
273
- self._stop_tendermint()
291
+ if self._is_aea:
292
+ self._stop_tendermint()
274
293
 
275
294
  def _stop_agent(self) -> None:
276
295
  """Start process."""
@@ -313,6 +332,24 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
313
332
  """Return aea_bin path."""
314
333
  raise NotImplementedError
315
334
 
335
+ def get_agent_start_args(self, password: str) -> List[str]:
336
+ """Return agent start arguments."""
337
+ return (
338
+ [self._agent_runner_bin]
339
+ + (
340
+ [
341
+ "-s",
342
+ "run",
343
+ ]
344
+ if self._is_aea
345
+ else []
346
+ )
347
+ + [
348
+ "--password",
349
+ password,
350
+ ]
351
+ )
352
+
316
353
 
317
354
  class PyInstallerHostDeploymentRunner(BaseDeploymentRunner):
318
355
  """Deployment runner within pyinstaller env."""
@@ -320,16 +357,8 @@ class PyInstallerHostDeploymentRunner(BaseDeploymentRunner):
320
357
  @property
321
358
  def _agent_runner_bin(self) -> str:
322
359
  """Return aea_bin path."""
323
- env = json.loads(
324
- (self._work_directory / "agent.json").read_text(encoding="utf-8")
325
- )
326
-
327
- agent_publicid_str = env["AEA_AGENT"]
328
360
  service_dir = self._work_directory.parent
329
-
330
- agent_runner_bin = get_agent_runner_path(
331
- service_dir=service_dir, agent_public_id_str=agent_publicid_str
332
- )
361
+ agent_runner_bin = get_agent_runner_path(service_dir=service_dir)
333
362
  return str(agent_runner_bin)
334
363
 
335
364
  @property
@@ -393,13 +422,7 @@ class PyInstallerHostDeploymentRunnerMac(PyInstallerHostDeploymentRunner):
393
422
  """Start agent process."""
394
423
  agent_runner_log_file = self._open_agent_runner_log_file()
395
424
  process = subprocess.Popen( # pylint: disable=consider-using-with,subprocess-popen-preexec-fn # nosec
396
- args=[
397
- self._agent_runner_bin,
398
- "-s",
399
- "run",
400
- "--password",
401
- password,
402
- ],
425
+ args=self.get_agent_start_args(password=password),
403
426
  cwd=working_dir / "agent",
404
427
  stdout=agent_runner_log_file,
405
428
  stderr=agent_runner_log_file,
@@ -431,9 +454,9 @@ class PyInstallerHostDeploymentRunnerMac(PyInstallerHostDeploymentRunner):
431
454
  class PyInstallerHostDeploymentRunnerWindows(PyInstallerHostDeploymentRunner):
432
455
  """Windows deployment runner."""
433
456
 
434
- def __init__(self, work_directory: Path) -> None:
457
+ def __init__(self, work_directory: Path, is_aea: bool) -> None:
435
458
  """Init the runner."""
436
- super().__init__(work_directory)
459
+ super().__init__(work_directory, is_aea=is_aea)
437
460
  self._job = self.set_windows_object_job()
438
461
 
439
462
  @staticmethod
@@ -519,13 +542,7 @@ class PyInstallerHostDeploymentRunnerWindows(PyInstallerHostDeploymentRunner):
519
542
  """Start agent process."""
520
543
  agent_runner_log_file = self._open_agent_runner_log_file()
521
544
  process = subprocess.Popen( # pylint: disable=consider-using-with # nosec
522
- args=[
523
- self._agent_runner_bin,
524
- "-s",
525
- "run",
526
- "--password",
527
- password,
528
- ], # TODO: Patch for Windows failing hash
545
+ args=self.get_agent_start_args(password=password),
529
546
  cwd=working_dir / "agent",
530
547
  stdout=agent_runner_log_file,
531
548
  stderr=agent_runner_log_file,
@@ -562,7 +579,12 @@ class HostPythonHostDeploymentRunner(BaseDeploymentRunner):
562
579
  @property
563
580
  def _agent_runner_bin(self) -> str:
564
581
  """Return aea_bin path."""
565
- return str(self._venv_dir / "bin" / "aea")
582
+ if self._is_aea:
583
+ return str(self._venv_dir / "bin" / "aea")
584
+
585
+ service_dir = self._work_directory.parent
586
+ agent_runner_bin = get_agent_runner_path(service_dir=service_dir)
587
+ return str(agent_runner_bin)
566
588
 
567
589
  def _start_agent(self, password: str) -> None:
568
590
  """Start agent process."""
@@ -573,13 +595,7 @@ class HostPythonHostDeploymentRunner(BaseDeploymentRunner):
573
595
  agent_runner_log_file = self._open_agent_runner_log_file()
574
596
 
575
597
  process = subprocess.Popen( # pylint: disable=consider-using-with # nosec
576
- args=[
577
- self._agent_runner_bin,
578
- "-s",
579
- "run",
580
- "--password",
581
- password,
582
- ], # TODO: Patch for Windows failing hash
598
+ args=self.get_agent_start_args(password=password),
583
599
  cwd=str(working_dir / "agent"),
584
600
  env={**os.environ, **env},
585
601
  stdout=agent_runner_log_file,
@@ -629,6 +645,9 @@ class HostPythonHostDeploymentRunner(BaseDeploymentRunner):
629
645
 
630
646
  def _setup_venv(self) -> None:
631
647
  """Perform venv setup, install deps."""
648
+ if not self._is_aea:
649
+ return
650
+
632
651
  self._venv_dir.mkdir(exist_ok=True)
633
652
  venv_cli(args=[str(self._venv_dir)])
634
653
  pbin = str(self._venv_dir / "bin" / "python")
@@ -655,6 +674,9 @@ class HostPythonHostDeploymentRunner(BaseDeploymentRunner):
655
674
  multiprocessing.set_start_method("spawn")
656
675
  self._setup_venv()
657
676
  super()._setup_agent(password=password)
677
+ if not self._is_aea:
678
+ return
679
+
658
680
  # Install agent dependencies
659
681
  self._run_cmd(
660
682
  args=[
@@ -690,9 +712,11 @@ class DeploymentManager:
690
712
  self.logger = setup_logger(name="operate.deployment_manager")
691
713
  self._states: Dict[Path, States] = {}
692
714
 
693
- def _get_deployment_runner(self, build_dir: Path) -> BaseDeploymentRunner:
715
+ def _get_deployment_runner(
716
+ self, build_dir: Path, is_aea: bool
717
+ ) -> BaseDeploymentRunner:
694
718
  """Get deploymnent runner instance."""
695
- return self._deployment_runner_class(build_dir)
719
+ return self._deployment_runner_class(build_dir, is_aea=is_aea)
696
720
 
697
721
  @staticmethod
698
722
  def _get_host_deployment_runner_class() -> Type[BaseDeploymentRunner]:
@@ -741,7 +765,9 @@ class DeploymentManager:
741
765
  "Failed to perform test connection to ipfs to check network connection!"
742
766
  )
743
767
 
744
- def run_deployment(self, build_dir: Path, password: str) -> None:
768
+ def run_deployment(
769
+ self, build_dir: Path, password: str, is_aea: bool = True
770
+ ) -> None:
745
771
  """Run deployment."""
746
772
  if self._is_stopping:
747
773
  raise RuntimeError("deployment manager stopped")
@@ -754,7 +780,9 @@ class DeploymentManager:
754
780
  self.logger.info(f"Starting deployment {build_dir}...")
755
781
  self._states[build_dir] = States.STARTING
756
782
  try:
757
- deployment_runner = self._get_deployment_runner(build_dir=build_dir)
783
+ deployment_runner = self._get_deployment_runner(
784
+ build_dir=build_dir, is_aea=is_aea
785
+ )
758
786
  deployment_runner.start(password=password)
759
787
  self.logger.info(f"Started deployment {build_dir}")
760
788
  self._states[build_dir] = States.STARTED
@@ -771,7 +799,9 @@ class DeploymentManager:
771
799
  )
772
800
  self.stop_deployment(build_dir=build_dir, force=True)
773
801
 
774
- def stop_deployment(self, build_dir: Path, force: bool = False) -> None:
802
+ def stop_deployment(
803
+ self, build_dir: Path, force: bool = False, is_aea: bool = True
804
+ ) -> None:
775
805
  """Stop the deployment."""
776
806
  if (
777
807
  self.get_state(build_dir=build_dir) in [States.STARTING, States.STOPPING]
@@ -780,7 +810,9 @@ class DeploymentManager:
780
810
  raise ValueError("Service already in transition")
781
811
  self.logger.info(f"Stopping deployment {build_dir}...")
782
812
  self._states[build_dir] = States.STOPPING
783
- deployment_runner = self._get_deployment_runner(build_dir=build_dir)
813
+ deployment_runner = self._get_deployment_runner(
814
+ build_dir=build_dir, is_aea=is_aea
815
+ )
784
816
  try:
785
817
  deployment_runner.stop()
786
818
  self.logger.info(f"Stopped deployment {build_dir}...")
@@ -794,14 +826,16 @@ class DeploymentManager:
794
826
  deployment_manager = DeploymentManager()
795
827
 
796
828
 
797
- def run_host_deployment(build_dir: Path, password: str) -> None:
829
+ def run_host_deployment(build_dir: Path, password: str, is_aea: bool = True) -> None:
798
830
  """Run host deployment."""
799
- deployment_manager.run_deployment(build_dir=build_dir, password=password)
831
+ deployment_manager.run_deployment(
832
+ build_dir=build_dir, password=password, is_aea=is_aea
833
+ )
800
834
 
801
835
 
802
- def stop_host_deployment(build_dir: Path) -> None:
836
+ def stop_host_deployment(build_dir: Path, is_aea: bool = True) -> None:
803
837
  """Stop host deployment."""
804
- deployment_manager.stop_deployment(build_dir=build_dir)
838
+ deployment_manager.stop_deployment(build_dir=build_dir, is_aea=is_aea)
805
839
 
806
840
 
807
841
  def stop_deployment_manager() -> None:
@@ -79,11 +79,13 @@ class FundingManager:
79
79
 
80
80
  def __init__(
81
81
  self,
82
+ keys_manager: KeysManager,
82
83
  wallet_manager: MasterWalletManager,
83
84
  logger: Logger,
84
85
  funding_requests_cooldown_seconds: int = DEFAULT_FUNDING_REQUESTS_COOLDOWN_SECONDS,
85
86
  ) -> None:
86
87
  """Initialize funding manager."""
88
+ self.keys_manager = keys_manager
87
89
  self.wallet_manager = wallet_manager
88
90
  self.logger = logger
89
91
  self.funding_requests_cooldown_seconds = funding_requests_cooldown_seconds
@@ -102,7 +104,7 @@ class FundingManager:
102
104
  f"Draining service agents {service.name} ({service_config_id=})"
103
105
  )
104
106
  for agent_address in service.agent_addresses:
105
- ethereum_crypto = KeysManager().get_crypto_instance(agent_address)
107
+ ethereum_crypto = self.keys_manager.get_crypto_instance(agent_address)
106
108
  balance = ledger_api.get_balance(agent_address)
107
109
  self.logger.info(
108
110
  f"Draining {balance} (approx) {get_currency_denom(chain)} from {agent_address} (agent) to {withdrawal_address}"
@@ -167,7 +169,7 @@ class FundingManager:
167
169
 
168
170
  # Safe not swapped
169
171
  if set(owners) == set(service.agent_addresses):
170
- ethereum_crypto = KeysManager().get_crypto_instance(
172
+ ethereum_crypto = self.keys_manager.get_crypto_instance(
171
173
  service.agent_addresses[0]
172
174
  )
173
175
  transfer_erc20_from_safe(
@@ -207,7 +209,7 @@ class FundingManager:
207
209
  )
208
210
 
209
211
  if set(owners) == set(service.agent_addresses):
210
- ethereum_crypto = KeysManager().get_crypto_instance(
212
+ ethereum_crypto = self.keys_manager.get_crypto_instance(
211
213
  service.agent_addresses[0]
212
214
  )
213
215
  transfer_from_safe(
@@ -22,6 +22,7 @@
22
22
  import json
23
23
  import logging
24
24
  import os
25
+ import time
25
26
  import traceback
26
27
  import typing as t
27
28
  from collections import Counter, defaultdict
@@ -105,13 +106,16 @@ from operate.wallet.master import InsufficientFundsException, MasterWalletManage
105
106
  # If multiple agents are provided in the service.yaml file, only the 0th index config will be used.
106
107
  NUM_LOCAL_AGENT_INSTANCES = 1
107
108
 
109
+ RPC_SYNC_TIMEOUT = 15
110
+
108
111
 
109
112
  class ServiceManager:
110
113
  """Service manager."""
111
114
 
112
- def __init__(
115
+ def __init__( # pylint: disable=too-many-arguments
113
116
  self,
114
117
  path: Path,
118
+ keys_manager: KeysManager,
115
119
  wallet_manager: MasterWalletManager,
116
120
  funding_manager: FundingManager,
117
121
  logger: logging.Logger,
@@ -126,7 +130,7 @@ class ServiceManager:
126
130
  :param logger: logging.Logger object.
127
131
  """
128
132
  self.path = path
129
- self.keys_manager = KeysManager()
133
+ self.keys_manager = keys_manager
130
134
  self.wallet_manager = wallet_manager
131
135
  self.funding_manager = funding_manager
132
136
  self.logger = logger
@@ -812,6 +816,9 @@ class ServiceManager:
812
816
  on_chain_metadata = self._get_on_chain_metadata(chain_config=chain_config)
813
817
  on_chain_hash = on_chain_metadata.get("code_uri", "")[len(IPFS_URI_PREFIX) :]
814
818
  on_chain_description = on_chain_metadata.get("description")
819
+ needs_update_agent_addresses = set(chain_data.instances) != set(
820
+ service.agent_addresses
821
+ )
815
822
 
816
823
  current_agent_bond = sftxb.get_agent_bond(
817
824
  service_id=chain_data.token, agent_id=target_staking_params["agent_ids"][0]
@@ -840,6 +847,7 @@ class ServiceManager:
840
847
  or current_staking_params["staking_token"]
841
848
  != target_staking_params["staking_token"]
842
849
  or on_chain_description != service.description
850
+ or needs_update_agent_addresses
843
851
  )
844
852
  )
845
853
 
@@ -959,6 +967,10 @@ class ServiceManager:
959
967
  chain_data.token = event_data["args"]["serviceId"]
960
968
  service.store()
961
969
 
970
+ if is_first_mint: # Hotfix to prevent RPC out-of-sync issues
971
+ time.sleep(RPC_SYNC_TIMEOUT)
972
+
973
+ # Activate service
962
974
  if (
963
975
  self._get_on_chain_state(service=service, chain=chain)
964
976
  == OnChainState.PRE_REGISTRATION
@@ -1023,6 +1035,10 @@ class ServiceManager:
1023
1035
  )
1024
1036
  ).settle()
1025
1037
 
1038
+ if is_first_mint: # Hotfix to prevent RPC out-of-sync issues
1039
+ time.sleep(RPC_SYNC_TIMEOUT)
1040
+
1041
+ # Register agent instances
1026
1042
  if (
1027
1043
  self._get_on_chain_state(service=service, chain=chain)
1028
1044
  == OnChainState.ACTIVE_REGISTRATION
@@ -1089,6 +1105,9 @@ class ServiceManager:
1089
1105
  )
1090
1106
  ).settle()
1091
1107
 
1108
+ if is_first_mint: # Hotfix to prevent RPC out-of-sync issues
1109
+ time.sleep(RPC_SYNC_TIMEOUT)
1110
+
1092
1111
  # Deploy service
1093
1112
  is_initial_funding = False
1094
1113
  if (
@@ -1923,7 +1942,9 @@ class ServiceManager:
1923
1942
  # TODO: remove after staking contract directly starts sending the rewards to master safe
1924
1943
  amount_claimed = int(receipt["logs"][0]["data"].hex(), 16)
1925
1944
  self.logger.info(f"Claimed amount: {amount_claimed}")
1926
- ethereum_crypto = KeysManager().get_crypto_instance(service.agent_addresses[0])
1945
+ ethereum_crypto = self.keys_manager.get_crypto_instance(
1946
+ service.agent_addresses[0]
1947
+ )
1927
1948
  transfer_erc20_from_safe(
1928
1949
  ledger_api=ledger_api,
1929
1950
  crypto=ethereum_crypto,
@@ -2206,7 +2227,7 @@ class ServiceManager:
2206
2227
  chain=chain,
2207
2228
  )
2208
2229
 
2209
- def deploy_service_locally(
2230
+ def deploy_service_locally( # pylint: disable=too-many-arguments
2210
2231
  self,
2211
2232
  service_config_id: str,
2212
2233
  chain: t.Optional[str] = None,
@@ -2232,11 +2253,15 @@ class ServiceManager:
2232
2253
  use_kubernetes=use_kubernetes,
2233
2254
  force=True,
2234
2255
  chain=chain or service.home_chain,
2235
- password=self.wallet_manager.password,
2256
+ keys_manager=self.keys_manager,
2236
2257
  )
2237
2258
  if build_only:
2238
2259
  return deployment
2239
- deployment.start(password=self.wallet_manager.password, use_docker=use_docker)
2260
+ deployment.start(
2261
+ password=self.wallet_manager.password,
2262
+ use_docker=use_docker,
2263
+ is_aea=service.agent_release["is_aea"],
2264
+ )
2240
2265
  return deployment
2241
2266
 
2242
2267
  def stop_service_locally(
@@ -2256,7 +2281,11 @@ class ServiceManager:
2256
2281
  service = self.load(service_config_id=service_config_id)
2257
2282
  service.remove_latest_healthcheck()
2258
2283
  deployment = service.deployment
2259
- deployment.stop(use_docker=use_docker, force=force)
2284
+ deployment.stop(
2285
+ use_docker=use_docker,
2286
+ force=force,
2287
+ is_aea=service.agent_release["is_aea"],
2288
+ )
2260
2289
  if delete:
2261
2290
  deployment.delete()
2262
2291
  return deployment