From 8ef4b78612d616cb895026a64e3dfb25ea7d6c88 Mon Sep 17 00:00:00 2001 From: "Andres D. Molins" Date: Wed, 8 Oct 2025 12:37:48 +0200 Subject: [PATCH 1/2] Fix: Solved type saving issue ensuring to get proper attributes on model validations. --- aleph_message/models/execution/instance.py | 36 +++++++----- aleph_message/tests/test_models.py | 65 ++++++++++++++++++++++ 2 files changed, 87 insertions(+), 14 deletions(-) diff --git a/aleph_message/models/execution/instance.py b/aleph_message/models/execution/instance.py index 8a4a44f..1c7ca4d 100644 --- a/aleph_message/models/execution/instance.py +++ b/aleph_message/models/execution/instance.py @@ -44,34 +44,42 @@ class InstanceContent(BaseExecutableContent): @model_validator(mode="after") def check_requirements(cls, values): - if values.requirements: + if getattr(values, "requirements", None): + if ( + getattr(values.payment, "is_stream", None) + or getattr(values.payment, "is_credit", None) + ) and ( + not getattr(values.requirements, "node", None) + or not getattr(values.requirements.node, "node_hash", None) + ): + raise ValueError( + "Node hash assignment is needed for PAYG or Credit payments" + ) # GPU filter only supported for QEmu instances with node_hash assigned - if values.requirements.gpu: - if ( - not values.requirements.node - or not values.requirements.node.node_hash + if getattr(values.requirements, "gpu", None): + if not getattr(values.requirements, "node", None) or not getattr( + values.requirements.node, "node_hash", None ): raise ValueError("Node hash assignment is needed for GPU support") if ( - values.environment - and values.environment.hypervisor != HypervisorType.qemu + getattr(values, "environment", None) + and getattr(values.environment, "hypervisor", None) + != HypervisorType.qemu ): raise ValueError("GPU option is only supported for QEmu hypervisor") # Terms and conditions filter only supported for PAYG/coco instances with node_hash assigned - if ( - values.requirements.node - and values.requirements.node.terms_and_conditions + if getattr(values.requirements, "node", None) and getattr( + values.requirements.node, "terms_and_conditions", None ): - if not values.requirements.node.node_hash: + if not getattr(values.requirements.node, "node_hash", None): raise ValueError( "Terms_and_conditions field needs a requirements.node.node_hash value" ) - if ( - not values.payment.is_stream - and not values.environment.trusted_execution + if not getattr(values.payment, "is_stream", None) and not getattr( + values.environment, "trusted_execution", None ): raise ValueError( "Only PAYG/coco instances can have a terms_and_conditions" diff --git a/aleph_message/tests/test_models.py b/aleph_message/tests/test_models.py index 7b5cdc7..4620292 100644 --- a/aleph_message/tests/test_models.py +++ b/aleph_message/tests/test_models.py @@ -245,6 +245,71 @@ def test_validation_on_gpu_hypervisor_options(): ) # Ignore "Value error, ..." +def test_validation_on_payg_payment_options(): + """Ensure that a node_hash option is required for stream payments.""" + path = Path(__file__).parent / "messages/instance_gpu_machine.json" + message_dict = json.loads(path.read_text()) + message_dict["content"]["requirements"]["gpu"] = None + message = create_new_message(message_dict, factory=InstanceMessage) + + assert isinstance(message, InstanceMessage) + assert hash(message.content) + assert message.content.payment + assert message.content.payment.type == PaymentType.superfluid + assert message.content.environment.hypervisor == HypervisorType.qemu + + message_dict["content"]["requirements"]["node"] = None + try: + _ = create_new_message(message_dict, factory=InstanceMessage) + raise AssertionError("An exception should have been raised before this point.") + except ValidationError as e: + assert e.errors()[0]["loc"] in [("content",), ("content", "__root__")] + + error_msg = e.errors()[0]["msg"] + assert ( + "Node hash assignment is needed for PAYG or Credit payments" in error_msg + ) # Ignore "Value error, ..." + + +def test_validation_on_credits_payment_options(): + """Ensure that a node_hash option is required for credit payments.""" + path = Path(__file__).parent / "messages/instance_gpu_machine.json" + message_dict = json.loads(path.read_text()) + # Patch the gpu field with some info + message_dict["content"]["payment"]["type"] = "credit" + message_dict["content"]["payment"]["receiver"] = None + message = create_new_message(message_dict, factory=InstanceMessage) + + assert isinstance(message, InstanceMessage) + assert hash(message.content) + assert message.content.payment + assert message.content.payment.type == PaymentType.credit + assert message.content.environment.hypervisor == HypervisorType.qemu + + # Remove gpu field with some info + message_dict["content"]["requirements"]["gpu"] = None + message = create_new_message(message_dict, factory=InstanceMessage) + + assert isinstance(message, InstanceMessage) + assert hash(message.content) + assert message.content.payment + assert message.content.payment.type == PaymentType.credit + assert message.content.environment.hypervisor == HypervisorType.qemu + assert message.content.requirements.gpu is None + + message_dict["content"]["requirements"]["node"] = None + try: + _ = create_new_message(message_dict, factory=InstanceMessage) + raise AssertionError("An exception should have been raised before this point.") + except ValidationError as e: + assert e.errors()[0]["loc"] in [("content",), ("content", "__root__")] + + error_msg = e.errors()[0]["msg"] + assert ( + "Node hash assignment is needed for PAYG or Credit payments" in error_msg + ) # Ignore "Value error, ..." + + def test_message_machine_port_mapping(): message_dict = { "chain": "ETH", From 4ef17a0050a09b2d11cb9197fa1be40c087112d6 Mon Sep 17 00:00:00 2001 From: "Andres D. Molins" Date: Wed, 8 Oct 2025 13:11:01 +0200 Subject: [PATCH 2/2] Fix: Fixed class method signature to use the good one from Pydantic, they introduced in their last version 2.12. --- aleph_message/models/execution/instance.py | 38 +++++++++------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/aleph_message/models/execution/instance.py b/aleph_message/models/execution/instance.py index 1c7ca4d..29a34cf 100644 --- a/aleph_message/models/execution/instance.py +++ b/aleph_message/models/execution/instance.py @@ -3,6 +3,7 @@ from typing import List, Optional from pydantic import Field, model_validator +from typing_extensions import Self from aleph_message.models.abstract import HashableModel @@ -43,46 +44,37 @@ class InstanceContent(BaseExecutableContent): ) @model_validator(mode="after") - def check_requirements(cls, values): - if getattr(values, "requirements", None): + def check_requirements(self) -> Self: + if self.requirements: if ( - getattr(values.payment, "is_stream", None) - or getattr(values.payment, "is_credit", None) - ) and ( - not getattr(values.requirements, "node", None) - or not getattr(values.requirements.node, "node_hash", None) - ): + self.payment and (self.payment.is_stream or self.payment.is_credit) + ) and (not self.requirements.node or not self.requirements.node.node_hash): raise ValueError( "Node hash assignment is needed for PAYG or Credit payments" ) # GPU filter only supported for QEmu instances with node_hash assigned - if getattr(values.requirements, "gpu", None): - if not getattr(values.requirements, "node", None) or not getattr( - values.requirements.node, "node_hash", None - ): + if self.requirements.gpu: + if not self.requirements.node or not self.requirements.node.node_hash: raise ValueError("Node hash assignment is needed for GPU support") if ( - getattr(values, "environment", None) - and getattr(values.environment, "hypervisor", None) - != HypervisorType.qemu + self.environment + and self.environment.hypervisor != HypervisorType.qemu ): raise ValueError("GPU option is only supported for QEmu hypervisor") # Terms and conditions filter only supported for PAYG/coco instances with node_hash assigned - if getattr(values.requirements, "node", None) and getattr( - values.requirements.node, "terms_and_conditions", None - ): - if not getattr(values.requirements.node, "node_hash", None): + if self.requirements.node and self.requirements.node.terms_and_conditions: + if not self.requirements.node.node_hash: raise ValueError( "Terms_and_conditions field needs a requirements.node.node_hash value" ) - if not getattr(values.payment, "is_stream", None) and not getattr( - values.environment, "trusted_execution", None - ): + if ( + not self.payment or not self.payment.is_stream + ) and not self.environment.trusted_execution: raise ValueError( "Only PAYG/coco instances can have a terms_and_conditions" ) - return values + return self