|
9 | 9 | from pydantic import BaseModel, Field, validator |
10 | 10 | from typing_extensions import TypeAlias |
11 | 11 |
|
| 12 | +from ..utils import dump_content |
12 | 13 | from .abstract import BaseContent |
13 | 14 | from .base import Chain, HashType, MessageType |
14 | 15 | from .execution.base import MachineType, Payment, PaymentType # noqa |
@@ -105,7 +106,7 @@ class ForgetContent(BaseContent): |
105 | 106 | """Content of a FORGET message""" |
106 | 107 |
|
107 | 108 | hashes: List[ItemHash] |
108 | | - aggregates: List[ItemHash] = Field(default_factory=list) |
| 109 | + aggregates: Optional[List[ItemHash]] = None |
109 | 110 | reason: Optional[str] = None |
110 | 111 |
|
111 | 112 | def __hash__(self): |
@@ -179,6 +180,36 @@ def check_item_content(cls, v: Optional[str], values) -> Optional[str]: |
179 | 180 | ) |
180 | 181 | return v |
181 | 182 |
|
| 183 | + @validator("content") |
| 184 | + def check_content(cls, v, values): |
| 185 | + item_type = values["item_type"] |
| 186 | + if item_type == ItemType.inline: |
| 187 | + try: |
| 188 | + item_content = json.loads(values["item_content"]) |
| 189 | + except JSONDecodeError: |
| 190 | + raise ValueError( |
| 191 | + "Field 'item_content' does not appear to be valid JSON" |
| 192 | + ) |
| 193 | + json_dump = json.loads(v.json()) |
| 194 | + for key, value in json_dump.items(): |
| 195 | + if value != item_content[key]: |
| 196 | + if isinstance(value, list): |
| 197 | + for item in value: |
| 198 | + if item not in item_content[key]: |
| 199 | + raise ValueError( |
| 200 | + f"Field 'content.{key}' does not match 'item_content.{key}': {item} != {item_content[key]}" |
| 201 | + ) |
| 202 | + if isinstance(value, dict): |
| 203 | + for item in value.items(): |
| 204 | + if item not in item_content[key].items(): |
| 205 | + raise ValueError( |
| 206 | + f"Field 'content.{key}' does not match 'item_content.{key}': {value} != {item_content[key]}" |
| 207 | + ) |
| 208 | + raise ValueError( |
| 209 | + f"Field 'content.{key}' does not match 'item_content.{key}': {value} != {item_content[key]} or type mismatch ({type(value)} != {type(item_content[key])})" |
| 210 | + ) |
| 211 | + return v |
| 212 | + |
182 | 213 | @validator("item_hash") |
183 | 214 | def check_item_hash(cls, v: ItemHash, values) -> ItemHash: |
184 | 215 | item_type = values["item_type"] |
@@ -255,20 +286,6 @@ class ProgramMessage(BaseMessage): |
255 | 286 | type: Literal[MessageType.program] |
256 | 287 | content: ProgramContent |
257 | 288 |
|
258 | | - @validator("content") |
259 | | - def check_content(cls, v, values): |
260 | | - item_type = values["item_type"] |
261 | | - if item_type == ItemType.inline: |
262 | | - item_content = json.loads(values["item_content"]) |
263 | | - if v.dict(exclude_none=True) != item_content: |
264 | | - # Print differences |
265 | | - vdict = v.dict(exclude_none=True) |
266 | | - for key, value in item_content.items(): |
267 | | - if vdict[key] != value: |
268 | | - print(f"{key}: {vdict[key]} != {value}") |
269 | | - raise ValueError("Content and item_content differ") |
270 | | - return v |
271 | | - |
272 | 289 |
|
273 | 290 | class InstanceMessage(BaseMessage): |
274 | 291 | type: Literal[MessageType.instance] |
@@ -315,12 +332,12 @@ def parse_message(message_dict: Dict) -> AlephMessage: |
315 | 332 |
|
316 | 333 |
|
317 | 334 | def add_item_content_and_hash(message_dict: Dict, inplace: bool = False): |
| 335 | + # TODO: I really don't like this function. There is no validation of the |
| 336 | + # message_dict, if it is indeed a real message, and can lead to unexpected results. |
318 | 337 | if not inplace: |
319 | 338 | message_dict = copy(message_dict) |
320 | 339 |
|
321 | | - message_dict["item_content"] = json.dumps( |
322 | | - message_dict["content"], separators=(",", ":") |
323 | | - ) |
| 340 | + message_dict["item_content"] = dump_content(message_dict["content"]) |
324 | 341 | message_dict["item_hash"] = sha256( |
325 | 342 | message_dict["item_content"].encode() |
326 | 343 | ).hexdigest() |
|
0 commit comments