4. Evidence Graph Data Model
4.1 Graph Structure
The Evidence Graph is a directed, typed, temporally-anchored multigraph.
{
"graphId": "string // Unique identifier",
"claimRef": "string // ClaimId this graph was constructed for",
"nodes": "Node[]",
"edges": "Edge[]",
"metadata": {
"constructionTime": "string // ISO 8601 (informational, not used in deterministic replay)",
"nodeCount": "number",
"edgeCount": "number",
"expansionOps": "number // Total BFS expansion operations performed",
"maxDepth": "number // Actual expansion depth reached",
"scopeExhausted": "boolean // True if any deterministic scope limit was hit",
"scopeConstraint": "string | null // Which constraint was hit: maxNodes | maxEdges | maxHops | maxExpansionOps"
}
}
4.2 Node Types
NodeType = ACCOUNT | TOKEN | POOL | CONTRACT | TX
{
"nodeId": "string // Canonical address or hash",
"type": "NodeType",
"chain": "ChainType",
"properties": {
"balance": "u64 | null",
"isContract": "boolean | null",
"createdAt": "number | null // Block/slot of first appearance",
"label": "string | null // 'CEX_HOT_WALLET' | 'DEX_ROUTER' | 'UNKNOWN'"
},
"evidence": "string[] // EvidenceUnit IDs supporting this node"
}
4.3 Edge Types
EdgeType = TRANSFER | FUNDING | DEPLOYMENT | SWAP | LP_ACTION | TEMPORAL_PROXIMITY
{
"edgeId": "string",
"type": "EdgeType",
"source": "string // Source nodeId",
"target": "string // Target nodeId",
"properties": {
"amount": "u64 | null // Transfer/swap amount",
"block": "number // Block/slot of this interaction",
"time": "string // ISO 8601",
"txHash": "string | null"
},
"evidence": "string[] // EvidenceUnit IDs supporting this edge"
}
4.4 Construction Algorithm
Graph construction is initiated for each claim evaluation and proceeds as follows:
Phase 1: Seed Resolution
- Resolve the claim subject to one or more seed nodes
- For
TOKENsubjects: seed = the token mint account - For
POOLsubjects: seed = the pool address - For
ACCOUNTsubjects: seed = the wallet address
Phase 2: Expansion
- Starting from seed nodes, expand outward following edges defined by the predicate's required evidence types
- Respect scope constraints:
maxHops,maxNodes,maxEdges,temporalLookback - At each hop, retrieve evidence for all discovered nodes within the context window
Phase 3: Annotation
- Attach retrieved evidence units to nodes and edges
- Label known entities (CEX wallets, DEX routers, bridge contracts) from the Entity Registry
- Compute derived properties (funding paths, timing correlations)
Phase 4: Pruning
- Remove nodes and edges outside the context window
- Remove nodes unreachable from the seed within scope constraints
- Record whether scope limits were exhausted (
metadata.scopeExhausted)
Deterministic Complexity Bounds:
| Parameter | Default | Description |
|---|---|---|
| Maximum nodes | scope.maxNodes (10,000) |
Hard cap on graph node count |
| Maximum edges | scope.maxEdges (50,000) |
Hard cap on graph edge count |
| Maximum depth | scope.maxHops (5) |
Max BFS expansion depth |
| Maximum expansion ops | scope.maxExpansionOps (20,000) |
Total node-expansion operations (each BFS dequeue counts as 1) |
All complexity bounds are deterministic — they depend only on the graph structure being traversed, not on wall-clock time or hardware performance. This is critical for reproducible replay: a validator replaying graph construction from the same evidence MUST produce a byte-identical graph regardless of hardware speed.
Why no wall-clock timeout: Wall-clock timeouts and deterministic replay are fundamentally in tension. If an engine operator hits a timeout at 29.8 seconds and includes a node, but a validator's replay takes 30.1 seconds on different hardware and prunes that node, graph hashes diverge and replay fails. Therefore, construction bounds MUST be expressed solely in terms of graph-structural limits.
Operational Timeout (Non-Deterministic, Failure Only):
Implementations MAY enforce a wall-clock timeout (recommended: 30 seconds) as an operational safeguard. However, if the timeout is hit:
- Construction MUST be aborted entirely — no partial graph is produced
- The VO enters
CONSTRUCTION_FAILEDstatus, notQUALIFIEDorUNQUALIFIED - The failed construction is not submitted for attestation or replay
- The operator MAY retry with tighter scope parameters (lower
maxNodes,maxHops, etc.)
This ensures wall-clock timeouts never influence the deterministic output. Either construction completes within all deterministic bounds (producing a replayable graph), or it fails and produces nothing.
4.5 Scope Enforcement
Scope is enforced strictly and deterministically. If construction would exceed any scope limit:
- Stop expansion at the boundary (do not process the operation that would exceed the limit)
- Set
metadata.scopeExhausted = true - Record which scope constraint was hit (one of:
maxNodes,maxEdges,maxHops,maxExpansionOps) - The engine MAY adjust qualification accordingly (Section 6.2)
BFS ordering determinism: Expansion follows canonical BFS order. At each depth level, nodes are expanded in lexicographic order of their nodeId (UTF-8 byte order, matching BCE field sorting from Appendix B). This ensures that when maxNodes or maxExpansionOps is reached mid-level, the same nodes are included regardless of implementation.