onematrix / tracing-sdk
Tracing SDK: canonicalize (RFC 8785 JSON Canonicalization Scheme, or W3C C14N for XML) and hash (Keccak-256) data-integrity records, then send them to an Indexer service over HTTP.
Requires
- php: ^7.1 || ^8.0
- ext-curl: *
- ext-dom: *
- ext-json: *
- ext-libxml: *
- ext-mbstring: *
Requires (Dev)
- phpunit/phpunit: ^10.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
PHP implementation of the Tracing SDK. Canonicalizes a record, hashes it with Keccak-256, and sends the hash to an Indexer service. Canonicalization and hashing happen inside send()/sendBatch(), which return the hash alongside the Indexer's response. The SDK has no buffering, timers, or background sending — you decide when to send, one record at a time or a batch. Once a record is anchored, it can be looked up again by its hash and verified against the chain through your own RPC node.
Requires PHP 7.1+ or 8.x, plus the json, dom, libxml, curl, and mbstring extensions.
Install
composer require onematrix/tracing-sdk
Usage
use Tracing\Sdk\SendOptions; use Tracing\Sdk\TracingSDK; $sdk = new TracingSDK([ 'endpoint' => 'https://indexer.example.com', 'options' => SendOptions::dataType('json'), // default: 'json' | 'xml' | 'raw' 'auth' => [ 'type' => 'apiToken', // 'mTLS' | 'basic' | 'apiToken' 'token' => 'your-api-token', ], ]); // Canonicalize + hash + POST {endpoint}/api/anchors $result = $sdk->send($rawJson, time()); // ['hash' => '0x1c8a…', 'response' => ['statusCode' => 200, 'body' => …, 'recordCount' => 1]]
That is the whole happy path for anchoring. Everything else — batching, XML and raw data, timeouts, the auth types, looking an anchor up by hash, and verifying it against the chain — is covered in the usage guide:
- docs/USAGE.md — full usage guide (English)
- docs/USAGE.vi.md — bản tiếng Việt
example/php/— runnable scripts, includingverify-example.phpfor the query-then-verify flow
Design notes
- Canonicalization. JSON follows RFC 8785 (the JSON Canonicalization Scheme / JCS): object member names are sorted by UTF-16 code unit value (not byte order — the two disagree for characters outside the Basic Multilingual Plane), numbers are formatted per the ECMAScript
Number::toStringalgorithm (so1,1.0, and1e0all canonicalize identically, and-0normalizes to0), and a JSON object is never mistaken for a JSON array even when its keys happen to be sequential integers starting at 0. XML is canonicalized with Exclusive XML Canonicalization 1.0 (http://www.w3.org/2001/10/xml-exc-c14n#) without comments, viaDOMDocument::C14N(true), which normalizes attribute order and insignificant whitespace and emits only the namespace declarations actually used by the document — a declared-but-unusedxmlnsdoes not affect the hash. Namespace prefixes are still significant: Exclusive 1.0 has no prefix rewriting (that is a Canonical XML 2.0 feature, which libxml does not implement), so re-serializing a document with different prefixes changes its hash. External entity resolution is disabled for XML input to prevent XXE, sincerawDatais untrusted.dataType: 'raw'skips canonicalization entirely — the input is hashed exactly as given, with no parsing; use it when the caller already guarantees a single deterministic representation. - Hashing. Keccak-256 (the original Keccak, as used by Ethereum — not FIPS-202 SHA3-256), via
kornrunner/keccakrather than a hand-rolled implementation. Output is a0x-prefixed hex string. - Verification.
verify()trusts nothing but the chain: it reads the proof transaction's receipt straight from an RPC endpoint you configure, decodes the logs whosetopics[0]iskeccak256("Anchored(bytes32,uint64)"), and compares the event'sbytes32argument with the record hash. The Indexer is never asked to vouch for itself.
Testing
composer install vendor/bin/phpunit