File size: 4,641 Bytes
977ccde
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
"""Tests for HDK bug fixes (Task 1 verification)."""


def test_hash_separator_prevents_collision():
    """Hash with parents must use || separator to prevent collisions."""
    from hdk.core.hash_engine import HashEngine

    e = HashEngine()
    h1 = e.hash_with_parents("abc", ["def", "ghi"])
    h2 = e.hash_with_parents("abcdef", ["ghi"])
    assert h1 != h2, "Different inputs must produce different hashes"


def test_hash_separator_present():
    """Hash computation must use || separator, not concatenation."""
    from hdk.core.hash_engine import HashEngine
    import hashlib

    e = HashEngine()
    result = e.hash_with_parents("a", ["b", "c"])

    # Expected: sha256("a||b||c")
    expected_combined = "a||b||c"
    expected = hashlib.sha256(expected_combined.encode()).hexdigest()
    assert result == expected


def test_actor_fields_in_node():
    """HDKNode must support actor fields: user_id_token, llm_model, llm_provider, agent_name."""
    from hdk.models import HDKNode, NodeLevel, EventType
    from datetime import datetime, timezone

    node = HDKNode(
        id="test",
        level=NodeLevel.EVENT,
        timestamp=datetime.now(timezone.utc),
        content_hash="hash",
        parent_chain=[],
        self_hash="self",
        node_hash="node",
        event_type=EventType.LLM_RESPONSE,
        user_id_token="u-123",
        llm_model="gemini-2.0-flash",
        llm_provider="google",
        agent_name="write_spin",
    )
    assert node.user_id_token == "u-123"
    assert node.llm_model == "gemini-2.0-flash"
    assert node.llm_provider == "google"
    assert node.agent_name == "write_spin"


def test_actor_fields_in_hash():
    """Actor fields must be included in node hash computation."""
    from hdk.core.hash_engine import HashEngine
    from hdk.models import HDKNode, NodeLevel, EventType
    from datetime import datetime, timezone

    e = HashEngine()
    base_node = HDKNode(
        id="test",
        level=NodeLevel.EVENT,
        timestamp=datetime.now(timezone.utc),
        content_hash="content_hash",
        parent_chain=[],
        self_hash="self",
        node_hash="",
        event_type=EventType.LLM_RESPONSE,
    )

    node_with_model = HDKNode(
        id="test",
        level=NodeLevel.EVENT,
        timestamp=base_node.timestamp,
        content_hash="content_hash",
        parent_chain=[],
        self_hash="self",
        node_hash="",
        event_type=EventType.LLM_RESPONSE,
        llm_model="gemini-2.0-flash",
    )

    h1 = e.create_node_hash(base_node)
    h2 = e.create_node_hash(node_with_model)
    assert h1 != h2, "Different actor fields must produce different hashes"


def test_canary_counter_advances():
    """Canary read counter must advance with each authorized_read."""
    from hdk.core.dag import DAGStorage
    from hdk.core.canary import CanaryLayer

    dag = DAGStorage(":memory:")
    canary = CanaryLayer(dag, key=bytes(range(32)))
    canary.initialize()

    c1 = canary.authorized_read()
    c2 = canary.authorized_read()

    assert canary.get_read_counter() == 2
    assert c1 != c2


def test_anchor_backward_compat_fields():
    """Anchor payload must include hash, version, timestamp for backward compat."""
    from hdk.core.dag import DAGStorage
    from hdk.core.canary import CanaryLayer
    from hdk.hedera.anchor import OfflineAnchorService
    from hdk.config import HederaConfig

    dag = DAGStorage(":memory:")
    canary = CanaryLayer(dag, key=bytes(range(32)))
    canary.initialize()
    config = HederaConfig(anchor_interval=1)
    service = OfflineAnchorService(dag, canary, config)

    from hdk.models import HCSAnchor
    from datetime import datetime, timezone

    import json as _json
    anchor = HCSAnchor(
        anchor_sequence=0,
        merkle_root="abc123",
        node_count=5,
        timestamp=datetime.now(timezone.utc),
        canary_checkpoint=_json.dumps({"commitment": "commit", "generation": 42}),
        prev_anchor_hash="",
    )

    payload = service.build_anchor_payload(anchor)
    assert "hash" in payload
    assert payload["hash"] == anchor.merkle_root
    assert "version" in payload
    assert "timestamp" in payload


def test_new_event_types_exist():
    """All new EventType values must exist."""
    from hdk.models import EventType

    expected = [
        "GRADE", "SYSTEM", "EDITOR_TYPE", "EDITOR_PASTE",
        "EDITOR_AI_INSERT", "EDITOR_DROP", "EDITOR_DELETE",
        "EDITOR_UNDO", "EDITOR_SNAPSHOT", "CERTIFICATE_INVALIDATED",
        "AGENT_STEP",
    ]
    for name in expected:
        assert hasattr(EventType, name), f"EventType.{name} missing"