Skip to content

Commit

Permalink
feat: allows CRN to publish a terms_and_condition
Browse files Browse the repository at this point in the history
  • Loading branch information
Psycojoker committed Nov 28, 2024
1 parent bc90d8f commit 3019a21
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 1 deletion.
3 changes: 3 additions & 0 deletions aleph_message/models/execution/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ class NodeRequirements(HashableModel):
node_hash: Optional[ItemHash] = Field(
default=None, description="Hash of the compute resource node that must be used"
)
terms_and_conditions: Optional[ItemHash] = Field(
default=None, description="Terms and conditions of this CRN"
)

class Config:
extra = Extra.forbid
Expand Down
19 changes: 18 additions & 1 deletion aleph_message/models/execution/instance.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from pydantic import Field
from pydantic import Field, validator

from aleph_message.models.abstract import HashableModel

Expand Down Expand Up @@ -32,3 +32,20 @@ class InstanceContent(BaseExecutableContent):
rootfs: RootfsVolume = Field(
description="Root filesystem of the system, will be booted by the kernel"
)

@validator("requirements")
def terms_and_conditions_only_for_payg_instances(cls, v, values, field, config):
if not v.node or not v.node.terms_and_conditions:
return v

if not values["payment"].is_stream:
raise ValueError(
f"only PAYG/stream instance can have a terms_and_conditions, not '{values['payment'].type}' instances"
)

if not v.node.node_hash:
raise ValueError(
"an instance with a terms_and_conditions needs a requirements.node.node_hash value"
)

return v
69 changes: 69 additions & 0 deletions aleph_message/tests/messages/instance_content.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"sender": "0x3E1aba4ad853Dd7Aa531aB59F10bd9f4d89aebaF",
"chain": "ETH",
"signature": "0x5ff79fb62190455b485e6a891d7bbee3a992ca8d0361bbd55f12a18176d9f668692601a75c3344cb880a479fc5920efc53036450bce8e891e13b9aa5618b2aba1b",
"type": "INSTANCE",
"item_content": "{\"address\":\"0x3E1aba4ad853Dd7Aa531aB59F10bd9f4d89aebaF\",\"time\":1732118563.2142403,\"allow_amend\":false,\"metadata\":{\"name\":\"test-payg-base\"},\"authorized_keys\":[\"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQVOxf7uOhIlt7GbPD1XnnSGPvj4t9cdjzB2AYRt3jlPT5w9gcUr2WYj+EPYDXig+imDYELtZNx6Pvg5jruKjFaAtugV3ta0NHPamGNIVPxSNdwx+teLIEs/A9FPT3jeohvU+JpjrKLjBtsfyVJ9iBf/Iswm2rcc/33k4LumwFgkpgNswIc4Da6L1K7EHrlkSbdtdBMdlxLGaU2eFp140JwOTCBBF4rL0JNzPDCzLdkKsj2hQOWg6IIwhQzg3B6o1lY2xoGrVWL4+s+TKSgv1VQY0I1Fmyw+33bWYw/sZlhEpflFYp565Nh2+n3nbYNvmouH4RTLwNUF4z2vvG3J3HVlkd5Q0SCNvPk6FAkAgZ2oXK2P79enlglL+NcFr21QInbacclAtESpPop3dSdLNbR8NvQwh53eELcBPtGFQLRPP9ZfOwtG1hZhVinCeb6fpgcOsBXn0Xo11DQ3XNgfMho632FPqujQtggkyZt/2a53oGEbrrfGgDhiLCGswUM1k= root@rpi\"],\"environment\":{\"internet\":true,\"aleph_api\":true,\"hypervisor\":\"qemu\",\"reproducible\":false,\"shared_cache\":false},\"resources\":{\"vcpus\":1,\"memory\":2048,\"seconds\":30},\"payment\":{\"chain\":\"BASE\",\"receiver\":\"0xFeF2b33478f906eDE5ee96110b2342861cF1569A\",\"type\":\"superfluid\"},\"requirements\":{\"node\":{\"node_hash\":\"2cdb78cf561c6f0f839edb817395d3b5ece20d89125c5afba658f9170d6932c8\"}},\"volumes\":[],\"rootfs\":{\"parent\":{\"ref\":\"b6ff5c3a8205d1ca4c7c3369300eeafff498b558f71b851aa2114afd0a532717\",\"use_latest\":true},\"persistence\":\"host\",\"size_mib\":10240}}",
"item_type": "inline",
"item_hash": "b28fa9a9ede14c9bbd6fde8959be07cfd25a3358d08e01405301adc5a1a2b2c8",
"time": "2024-11-20T16:02:43.214390+00:00",
"channel": "ALEPH-CLOUDSOLUTIONS",
"content": {
"address": "0x3E1aba4ad853Dd7Aa531aB59F10bd9f4d89aebaF",
"time": 1732118563.2142403,
"allow_amend": false,
"metadata": {
"name": "test-payg-base"
},
"authorized_keys": [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDQVOxf7uOhIlt7GbPD1XnnSGPvj4t9cdjzB2AYRt3jlPT5w9gcUr2WYj+EPYDXig+imDYELtZNx6Pvg5jruKjFaAtugV3ta0NHPamGNIVPxSNdwx+teLIEs/A9FPT3jeohvU+JpjrKLjBtsfyVJ9iBf/Iswm2rcc/33k4LumwFgkpgNswIc4Da6L1K7EHrlkSbdtdBMdlxLGaU2eFp140JwOTCBBF4rL0JNzPDCzLdkKsj2hQOWg6IIwhQzg3B6o1lY2xoGrVWL4+s+TKSgv1VQY0I1Fmyw+33bWYw/sZlhEpflFYp565Nh2+n3nbYNvmouH4RTLwNUF4z2vvG3J3HVlkd5Q0SCNvPk6FAkAgZ2oXK2P79enlglL+NcFr21QInbacclAtESpPop3dSdLNbR8NvQwh53eELcBPtGFQLRPP9ZfOwtG1hZhVinCeb6fpgcOsBXn0Xo11DQ3XNgfMho632FPqujQtggkyZt/2a53oGEbrrfGgDhiLCGswUM1k= root@rpi"
],
"variables": null,
"environment": {
"internet": true,
"aleph_api": true,
"hypervisor": "qemu",
"trusted_execution": null,
"reproducible": false,
"shared_cache": false
},
"resources": {
"vcpus": 1,
"memory": 2048,
"seconds": 30,
"published_ports": null
},
"payment": {
"chain": "BASE",
"receiver": "0xFeF2b33478f906eDE5ee96110b2342861cF1569A",
"type": "superfluid"
},
"requirements": {
"cpu": null,
"node": {
"owner": null,
"address_regex": null,
"node_hash": "2cdb78cf561c6f0f839edb817395d3b5ece20d89125c5afba658f9170d6932c8",
"terms_and_conditions": "2cdb78cf561c6f0f839edb817395d3b5ece20d89125c5afba658f9170d6932c8"
}
},
"volumes": [],
"replaces": null,
"rootfs": {
"parent": {
"ref": "b6ff5c3a8205d1ca4c7c3369300eeafff498b558f71b851aa2114afd0a532717",
"use_latest": true
},
"persistence": "host",
"size_mib": 10240
}
},
"confirmed": true,
"confirmations": [
{
"chain": "ETH",
"height": 21230293,
"hash": "0x8bd25e2384dbb2da8c516880c47148304ed9ee1b7dfff8a327f8ccfa97ea29f4"
}
]
}
33 changes: 33 additions & 0 deletions aleph_message/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
AggregateMessage,
ForgetMessage,
InstanceMessage,
InstanceContent,
ItemType,
MessagesResponse,
MessageType,
Expand Down Expand Up @@ -349,3 +350,35 @@ def test_messages_from_disk():
console.print(message_dict)
console.print_json(e.json())
raise


def test_terms_and_conditions_only_for_payg_instances():
"""Ensure that only instance with PAYG and a node_hash can have a terms_and_conditions"""
path = os.path.abspath(os.path.join(__file__, "../messages/instance_content.json"))
with open(path) as fd:
message_dict = json.load(fd)

# this one is valid
instance_message = create_message_from_json(
json.dumps(message_dict), factory=InstanceMessage
)

assert isinstance(instance_message.content, InstanceContent)
assert instance_message.content.payment and instance_message.content.payment.is_stream

message_dict["content"]["payment"]["type"] = "hold"

# can't have a terms_and_conditions with hold
with pytest.raises(ValueError):
instance_message = create_message_from_json(
json.dumps(message_dict), factory=InstanceMessage
)

message_dict["content"]["payment"]["type"] = "superfluid"

# a node_hash is needed for a terms_and_conditions
del message_dict["content"]["requirements"]["node"]["node_hash"]
with pytest.raises(ValueError):
instance_message = create_message_from_json(
json.dumps(message_dict), factory=InstanceMessage
)

0 comments on commit 3019a21

Please sign in to comment.