Data Integrity
This page explains how Sylan keeps data tamper‑evident across the request lifecycle: from client payload hashing to provider‑signed snapshots and on‑chain checks. It focuses on what to hash/sign, how to canonicalize, and which time bounds protect against replay—leaving contract specifics to the Contracts section.
Why integrity matters
- Tamper evidence: clients and nodes can independently verify that the data settled on‑chain corresponds to a Provider‑authorized snapshot.
- Determinism: identical inputs yield the same hashes, enabling quorum and cacheability.
- Privacy by reference: only compact hashes are posted on‑chain; large payloads live off‑chain.
On‑chain anchors
Descriptor integrity
Each API listing has a descriptor recorded in AccessRegistry:
uri— canonical location for docs/schema or response pointer roots.contentHash— hash of the descriptor artifact (e.g., IPFS CID → keccak256 digest).updatedAt,version— allow clients and nodes to detect changes.
Clients and nodes must verify that the fetched descriptor matches contentHash. If the descriptor updates, its version and updatedAt advance.
Plan integrity
Per‑API plan fields constrain acceptable data:
maxSkewMs— how far future‑dated provider timestamps can be.maxTtlMs— upper bound for snapshot freshness (see Provider snapshot below).seqMonotonic— require increasingseqNoacross finalized results to prevent regressions.
Client payload hashing (requestHash)
Each PPC request binds funds to one canonical payload via requestHash (bytes32). To ensure cross‑implementation determinism, use a canonical JSON approach (JCS‑style):
Recommended canonicalization rules
- UTF‑8 encode with no BOM.
- Sort object keys lexicographically by Unicode code point.
- No insignificant whitespace (no pretty‑printing).
- Booleans as
true/false; null asnull. - Integers as base‑10 without leading zeros; decimals as strings (avoid IEEE‑754 rounding issues).
- Strings escaped per RFC 8259 (e.g.,
\n,\"). - Byte data represented as lowercase
0x‑prefixed hex strings.
Payload shape (suggested)
{
"method": "GET",
"path": "/v1/price",
"query": { "symbol": "BTC" },
"headers": { "accept": "application/json" },
"body": null
}Compute requestHash = keccak256( canonicalJSONString(payload) ) and submit it in lockForCall(...) along with expiresAtMs.
Tip: Normalize trivial differences that change meaning: remove trailing slashes in
path, lowercase header names, trim header values, and ensurequerykeys are sorted and percent‑decoding rules are consistent in your SDKs.
Provider snapshot integrity (EIP‑712)
Providers issue an EIP‑712 signature over a compact Snapshot, handed to Nodes:
Snapshot {
apiId: bytes32 // API identifier
seqNo: uint256 // monotonic if enabled
providerTs: uint64 // provider epoch millis
ttl: uint64 // millis; 0 = no TTL (skew check still applies)
contentHash: bytes32 // address of the concrete response content
}- Signer =
providerSignerregistered forapiId. - Freshness: chain enforces
providerTs ≤ now + maxSkewMs; ifttl ≠ 0, alsonow ≤ providerTs + min(ttl, maxTtlMs). - Content addressing:
contentHashis a digest of the exact bytes of the response artifact (or a Merkle root for large/streamed data). ApointerURI(e.g.,ipfs://...orhttps://...) can accompany submissions for retrieval; integrity is checked by the hash, not by the location.
Equivocation
If two different contentHash values appear for the same (apiId, seqNo), consensus raises ProviderEquivocation. Nodes should prefer the fork with the stronger vote tally; operators investigate the cause (key rotation, multi‑origin drift, or malicious behavior).
Time bounds & replay protection
expiresAtMs(client → escrow): caps how long a PPC request remains open. Prevents late or replayed settlements.providerTs+ttl(provider → consensus): bound the freshness window for snapshots. Rejects future‑dated or stale data.- Grace window (consensus): allows finalization shortly after expiry to account for propagation.
Combine these to ensure: requests have a maximum life, snapshots prove timely origin, and finalization can’t be forced far outside the intended window.
Encoding pitfalls & best practices
- Floats/decimals: never hash IEEE‑754 floating values directly—encode as strings.
- Timestamps: use milliseconds since Unix epoch; clamp to 64‑bit unsigned range.
- Case sensitivity: use lowercase hex for all
0xstrings; normalize case for header keys. - Whitespace: remove all insignificant whitespace before hashing.
- Compression: if you compress the artifact before hashing, always hash the compressed bytes and publish that choice (descriptor/docs) so everyone reproduces the digest.
- Personal data: don’t leak PII in on‑chain events; only hashes/IDs go on‑chain. Keep raw content off‑chain.
Verification workflow (who checks what)
| Role | Verifies |
|---|---|
| Client | Descriptor contentHash; canonical payload → requestHash; allowance & expiry. |
| Node | Descriptor contentHash; Provider EIP‑712 signature; contentHash vs downloaded artifact; freshness rules; one‑vote rule. |
| Consensus | Provider signature, skew/TTL bounds, node eligibility, vote tally & fork‑choice, equivocation. |
| Escrow | Fee snapshot at lock/purchase; success/failure settlement; idempotent withdrawals. |
Checklist (copy into your SDK PR)
- Canonical JSON function with unit tests (golden vectors).
-
requestHashderivation documented with examples. - Header normalization (lowercase keys, trimmed values) and query key sorting.
- Timestamp utilities (ms, monotonic clocks where possible).
- Artifact hasher that accepts bytes/streams and returns lowercase
0xhex. - EIP‑712 snapshot verifier with domain separation and chain id tests.
- End‑to‑end test: descriptor → requestHash → snapshot → finalize → settle.
What this page omits
- Contract addresses and ABIs → Architecture → Addresses & ABIs.
- Full event reference → Architecture → Events.
- Operational guides for Nodes/Providers → Node, Provider.
See also
- Consensus (vote tally, quorum, equivocation)
- Request Lifecycle (phase overview)
- Security (key management, slashing, DOS considerations)