87 lines
2.9 KiB
Python
87 lines
2.9 KiB
Python
|
|
# ============================================================
|
|||
|
|
# 完整 Merkle 树黄金测试(plan.md §3.6 / 矩阵「全审计」剩余项)
|
|||
|
|
# 覆盖:确定性、顺序/数量/内容敏感、成员证明校验、篡改检测、导出携带根。
|
|||
|
|
# ============================================================
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import copy
|
|||
|
|
import hashlib
|
|||
|
|
import json
|
|||
|
|
|
|||
|
|
from server.agent_core.audit_export import export_events
|
|||
|
|
from server.agent_core.audit_ledger import GENESIS_ROOT
|
|||
|
|
from server.agent_core.audit_merkle import (
|
|||
|
|
_leaf_hash,
|
|||
|
|
merkle_proof,
|
|||
|
|
merkle_root,
|
|||
|
|
verify_proof,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _events(n: int = 5) -> list[dict]:
|
|||
|
|
out: list[dict] = []
|
|||
|
|
prev = "GENESIS"
|
|||
|
|
for i in range(n):
|
|||
|
|
ev = {"id": i + 1, "action": f"action-{i}", "prevHash": prev}
|
|||
|
|
prev = hashlib.sha256((prev + json.dumps(ev, sort_keys=True)).encode()).hexdigest()
|
|||
|
|
ev["hash"] = prev
|
|||
|
|
out.append(ev)
|
|||
|
|
return out
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_merkle_root_deterministic_and_sensitive():
|
|||
|
|
"""Merkle 根:同事件同根;空树=创世根;顺序/数量/内容变化均断根。"""
|
|||
|
|
evs = _events()
|
|||
|
|
assert merkle_root(evs) == merkle_root(evs)
|
|||
|
|
assert merkle_root([]) == GENESIS_ROOT
|
|||
|
|
assert merkle_root(evs) != merkle_root(list(reversed(evs)))
|
|||
|
|
assert merkle_root(evs) != merkle_root(evs[:-1])
|
|||
|
|
tampered = copy.deepcopy(evs)
|
|||
|
|
tampered[2]["hash"] = "0" * 64
|
|||
|
|
assert merkle_root(evs) != merkle_root(tampered)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_merkle_proof_every_index_verifies():
|
|||
|
|
"""成员证明:每个事件都能给出 O(log n) proof 且 verify_proof 通过。"""
|
|||
|
|
evs = _events(8)
|
|||
|
|
root = merkle_root(evs)
|
|||
|
|
for i in range(len(evs)):
|
|||
|
|
proof = merkle_proof(evs, i)
|
|||
|
|
assert proof["root"] == root
|
|||
|
|
assert verify_proof(root, _leaf_hash(evs[i]), proof) is True
|
|||
|
|
# sibling 数应等于树高(8 叶 → 3 层)
|
|||
|
|
assert len(proof["siblings"]) >= 2
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_merkle_proof_rejects_wrong_leaf():
|
|||
|
|
"""错误叶子无法通过证明校验(防伪造成员)。"""
|
|||
|
|
evs = _events(6)
|
|||
|
|
root = merkle_root(evs)
|
|||
|
|
proof = merkle_proof(evs, 1)
|
|||
|
|
other_leaf = _leaf_hash(evs[4])
|
|||
|
|
assert verify_proof(root, other_leaf, proof) is False
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_merkle_proof_out_of_range():
|
|||
|
|
"""越界索引返回错误,不抛异常。"""
|
|||
|
|
evs = _events(3)
|
|||
|
|
assert merkle_proof(evs, -1)["error"]
|
|||
|
|
assert merkle_proof(evs, 99)["error"]
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_merkle_single_and_empty():
|
|||
|
|
"""单事件/空事件边界:根互异且可复算。"""
|
|||
|
|
one = _events(1)
|
|||
|
|
root1 = merkle_root(one)
|
|||
|
|
assert root1 != GENESIS_ROOT
|
|||
|
|
assert merkle_root(_events(1)) == root1
|
|||
|
|
proof = merkle_proof(one, 0)
|
|||
|
|
assert verify_proof(root1, _leaf_hash(one[0]), proof) is True
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_export_json_carries_merkle_root():
|
|||
|
|
"""合规导出 JSON 携带 merkleRoot,且与本地复算一致。"""
|
|||
|
|
evs = _events(4)
|
|||
|
|
payload = json.loads(export_events(evs, fmt="json"))
|
|||
|
|
assert payload["merkleRoot"] == merkle_root(evs)
|