ocp-verify 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/README.md +89 -365
- package/package.json +32 -31
- package/src/hash.js +205 -0
- package/src/index.js +42 -0
- package/src/index.mjs +26 -0
- package/src/normalize.js +89 -0
- package/src/profiles.js +78 -0
- package/src/verify.js +141 -0
- package/LICENSE +0 -20
- package/reference-cli/README.md +0 -82
- package/reference-cli/commit.js +0 -37
- package/reference-cli/hash-browser.js +0 -67
- package/reference-cli/revoke.js +0 -148
- package/reference-cli/temporal-bounds.js +0 -164
- package/reference-cli/verify.js +0 -241
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this package will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [2.0.0] — 2026-06-10
|
|
6
|
+
|
|
7
|
+
First release implementing ERC-8281 (`erc8281/1`) with the Extraction Profiles extension.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Full 5-step verification invariant: recompute → compare → confirm inclusion → confirm committer → confirm block, with fail-closed semantics (partial verification is never reported as success).
|
|
12
|
+
- Extraction profile registry with both registered profiles:
|
|
13
|
+
- `recorded` (default) — `Recorded(bytes32,address)`, 3 topics, digest at `topics[1]`, committer at `topics[2]`.
|
|
14
|
+
- `erc8263-anchorproof` — `AnchorProof(uint8,bytes32,bytes32,address,bytes)`, 4 topics, digest (`proofHash`) at `topics[2]`, committer (`operator`) at `topics[3]`, with the `proofHash != bytes32(0)` canonical-form assertion. `agentId` is not authenticated.
|
|
15
|
+
- Dependency-free hash implementations for the full registry: `sha2-256` (node:crypto), `keccak-256` (pure JS), `blake2b-256` (pure JS, parameterized digest length per RFC 7693 — not truncated BLAKE2b-512), each validated against known vectors via `selfTest()`.
|
|
16
|
+
- Envelope schema validation and field normalization: exact-match `erc8281/1` version check, decoded-value comparisons throughout, EIP-55 checksum validation with rejection of invalid addresses, unknown-field tolerance.
|
|
17
|
+
- `eth_chainId` verification against the envelope's `chain_id` before any receipt fetch.
|
|
18
|
+
- Log selection by `receipt_log_position` (receipt-array position, explicitly not the block-scoped JSON-RPC `logIndex`).
|
|
19
|
+
- Strict topic-count assertions per profile (exactly 3 for `recorded`, exactly 4 for `erc8263-anchorproof`), rejecting crafted logs with a matching topic-0 but wrong shape.
|
|
20
|
+
- Optional `block_hash` handled as MUST-when-present against the receipt's `blockHash` (reorg detection).
|
|
21
|
+
- Rejection of unregistered extraction profiles and non-registry hash functions.
|
|
22
|
+
- Verified constants exported: `TOPIC0_RECORDED` (`0xdca60c2087041cbb12d9a57628c6cad28ecbd0437e47c7ab6c3aa6e162bf4497`), `TOPIC0_ERC8263_ANCHORPROOF` (`0x9fe832d83a52f83bd7d54181e4cc7ff8b4e227cc1d3a0144376894b5df6c23cc`), `ERC165_INTERFACE_ID` (`0xb5c645bd`).
|
|
23
|
+
- `httpProvider(rpcUrl)` convenience provider (global fetch, Node 18+) and injectable-provider design for multi-endpoint cross-checking or offline fixtures.
|
|
24
|
+
- Dual ESM/CommonJS entry points with zero runtime dependencies; Node.js >= 18.
|
|
25
|
+
- Conformance suite: ERC-8281 test vectors 1–21 plus the BLAKE2b-truncation negative companion (03b) as offline fixtures, hash self-tests, constant recomputation, EIP-55 unit vectors, and an authenticated-field mutation sweep. 26 tests.
|
|
26
|
+
|
|
27
|
+
### Breaking changes from pre-release (`ocp/1.0`) tooling
|
|
28
|
+
|
|
29
|
+
- Event signature is `Recorded(bytes32,address)` — the v0 `Recorded(bytes32,address,uint256)` signature and its topic-0 are not supported.
|
|
30
|
+
- Envelope requires `receipt_log_position`; the v0 `timestamp` field is removed; version string is `erc8281/1`.
|
package/README.md
CHANGED
|
@@ -1,399 +1,123 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ocp-verify
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Reference verifier for **ERC-8281 — Observation Commitment Protocol (OCP)**.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Independently confirm that a specific byte sequence was committed on-chain — *recompute → compare → confirm inclusion* — using only an RPC endpoint or local node. No trusted SDK, gateway, or indexer.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- Implements the full 5-step ERC-8281 verification invariant (`erc8281/1`)
|
|
8
|
+
- Supports both registered extraction profiles: `recorded` (default) and `erc8263-anchorproof`
|
|
9
|
+
- All three registry hash functions — `sha2-256`, `keccak-256`, `blake2b-256` — implemented dependency-free and validated against known vectors at test time
|
|
10
|
+
- **Zero runtime dependencies.** Node.js 18+. ESM and CommonJS.
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
## Installation
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
cd examples/oh-shit-demo
|
|
15
|
-
./run-demo.sh
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Expected:
|
|
19
|
-
|
|
20
|
-
```
|
|
21
|
-
VALID
|
|
22
|
-
INVALID
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
If one byte changes, verification fails — across any system.
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## 🔌 Integrate in 2 Minutes
|
|
30
|
-
|
|
31
|
-
### Install
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
npm install -g ocp-verify
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Or use without installing:
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
npx ocp-verify myfile.txt myfile.proof.json
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
### 1) Commit (produce a proof)
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
npx ocp-commit report.txt
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
This automatically creates:
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
report.proof.json
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### 2) Verify (anywhere, later)
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
npx ocp-verify report.txt
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
Expected:
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
VALID
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
If any byte changes:
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
INVALID: hash mismatch
|
|
14
|
+
```sh
|
|
15
|
+
npm install ocp-verify
|
|
71
16
|
```
|
|
72
17
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
npx ocp-verify tampered.txt report.proof.json
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### 4) Use in your system
|
|
80
|
-
|
|
81
|
-
- Save the file + proof together
|
|
82
|
-
- Or store the proof alongside records/logs
|
|
83
|
-
- Verification requires only the file and the proof
|
|
84
|
-
|
|
85
|
-
No API. No platform dependency.
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
## The Problem
|
|
90
|
-
|
|
91
|
-
Every AI system running today has the same problem.
|
|
92
|
-
|
|
93
|
-
You can't verify what it did.
|
|
94
|
-
|
|
95
|
-
Not really. You can ask the platform. You can trust the logs. You can hope the provider is telling the truth. But there is no independent, tamper-proof record of what an AI received, what it produced, and whether anything was changed in between.
|
|
96
|
-
|
|
97
|
-
Most digital systems can prove things — but only **inside themselves**.
|
|
98
|
-
|
|
99
|
-
Step outside the system, and verification depends on:
|
|
100
|
-
- APIs
|
|
101
|
-
- platforms
|
|
102
|
-
- intermediaries
|
|
103
|
-
|
|
104
|
-
OCP eliminates that dependency entirely.
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
## Where This Breaks Without OCP
|
|
109
|
-
|
|
110
|
-
- AI outputs cannot be independently verified
|
|
111
|
-
- Legal evidence depends on originating systems
|
|
112
|
-
- APIs and platforms are non-permanent
|
|
113
|
-
- Digital artifacts become disputable over time
|
|
114
|
-
|
|
115
|
-
Without a system-independent verification boundary,
|
|
116
|
-
"what actually happened" becomes ambiguous.
|
|
117
|
-
|
|
118
|
-
---
|
|
119
|
-
|
|
120
|
-
## Use Cases
|
|
121
|
-
|
|
122
|
-
OCP can be used anywhere a digital artifact may need to be independently verified later:
|
|
123
|
-
|
|
124
|
-
- AI outputs and execution traces
|
|
125
|
-
- Legal evidence and filings
|
|
126
|
-
- Audit logs and compliance records
|
|
127
|
-
- Media provenance
|
|
128
|
-
- File integrity
|
|
129
|
-
- Institutional records
|
|
130
|
-
|
|
131
|
-
### Example: Verifying an AI Output
|
|
132
|
-
|
|
133
|
-
An AI system generates a report:
|
|
134
|
-
|
|
135
|
-
```text
|
|
136
|
-
AI Risk Assessment: MEDIUM_RISK
|
|
137
|
-
Approved for internal review.
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
That output is committed using OCP.
|
|
141
|
-
|
|
142
|
-
Later, the report is modified:
|
|
143
|
-
|
|
144
|
-
```text
|
|
145
|
-
AI Risk Assessment: LOW_RISK
|
|
146
|
-
Approved for internal review.
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
Using the original proof:
|
|
150
|
-
|
|
151
|
-
- the original output verifies as VALID
|
|
152
|
-
- the modified output returns INVALID
|
|
18
|
+
## Usage
|
|
153
19
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
## Proof Envelope
|
|
186
|
-
|
|
187
|
-
OCP proofs are self-describing, chain-agnostic JSON artifacts. A valid proof envelope is verifiable against raw ledger data — no SDK, no RPC provider, no indexer required.
|
|
188
|
-
|
|
189
|
-
```json
|
|
190
|
-
{
|
|
191
|
-
"ocp": "1.0",
|
|
192
|
-
"chain": {
|
|
193
|
-
"id": "eip155:84532",
|
|
194
|
-
"namespace": "evm"
|
|
195
|
-
},
|
|
196
|
-
"commitment": {
|
|
197
|
-
"digest": "14cca453684a18c1ef3e1c0b9a7744cfa06942660719bba373ef5fc36208bf73",
|
|
198
|
-
"hash_function": "sha2-256",
|
|
199
|
-
"serialization": "raw-bytes"
|
|
200
|
-
},
|
|
201
|
-
"ledger_ref": {
|
|
202
|
-
"transaction_id": "0xf2e1f6c085768b4e3d60463717d52bb2a338803a74a4cfd48aea5738d2595ddd",
|
|
203
|
-
"block_height": 41658348,
|
|
204
|
-
"block_hash": "0x...",
|
|
205
|
-
"finality": {
|
|
206
|
-
"depth": 3,
|
|
207
|
-
"assertion_time_utc": "2026-05-17T12:00:00Z"
|
|
208
|
-
}
|
|
209
|
-
},
|
|
210
|
-
"extraction": {
|
|
211
|
-
"rule_id": "evm/event-log",
|
|
212
|
-
"rule_version": "1.0.0"
|
|
213
|
-
},
|
|
214
|
-
"meta": {
|
|
215
|
-
"created_utc": "2026-05-17T12:00:05Z",
|
|
216
|
-
"envelope_version": "1.0"
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
---
|
|
222
|
-
|
|
223
|
-
## Reference Implementation — Live on Base Sepolia
|
|
224
|
-
|
|
225
|
-
Contract: `0x0963Fd33DF80c94360F2DC22e5c09517AeE7ED5c`
|
|
226
|
-
|
|
227
|
-
```solidity
|
|
228
|
-
contract ObservationCommitment {
|
|
229
|
-
event Recorded(bytes32 indexed digest, address indexed recorder);
|
|
230
|
-
|
|
231
|
-
function record(bytes32 digest) external {
|
|
232
|
-
emit Recorded(digest, msg.sender);
|
|
233
|
-
}
|
|
20
|
+
```js
|
|
21
|
+
// ESM
|
|
22
|
+
import { verify, httpProvider } from 'ocp-verify';
|
|
23
|
+
|
|
24
|
+
// CommonJS
|
|
25
|
+
// const { verify, httpProvider } = require('ocp-verify');
|
|
26
|
+
|
|
27
|
+
const envelope = {
|
|
28
|
+
version: 'erc8281/1',
|
|
29
|
+
digest: '0x6f3a…', // hash of the observation bytes
|
|
30
|
+
hash_function: 'sha2-256',
|
|
31
|
+
chain_id: '42161',
|
|
32
|
+
contract: '0x0C0117AC70000000000000000000000000000001',
|
|
33
|
+
tx_hash: '0x9be1…',
|
|
34
|
+
block_number: '352800417',
|
|
35
|
+
receipt_log_position: '0',
|
|
36
|
+
committer: '0xc0Aa17dE0000000000000000000000000000beEf',
|
|
37
|
+
// optional:
|
|
38
|
+
// block_hash: '0x…', // MUST match the receipt's blockHash when present
|
|
39
|
+
// extraction_profile: 'recorded', // defaults to 'recorded' when absent
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const observationBytes = new Uint8Array(/* the observed bytes, supplied out-of-band */);
|
|
43
|
+
|
|
44
|
+
const result = await verify(envelope, observationBytes, httpProvider('https://arb1.arbitrum.io/rpc'));
|
|
45
|
+
|
|
46
|
+
if (result.valid) {
|
|
47
|
+
// the observation bytes were committed on-chain at the referenced block
|
|
48
|
+
// by the declared committer
|
|
49
|
+
} else {
|
|
50
|
+
console.error('rejected:', result.reason); // e.g. 'digest_mismatch'
|
|
234
51
|
}
|
|
235
52
|
```
|
|
236
53
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
```
|
|
240
|
-
hash MATCH 14cca453684a18c1ef3e1c0b9a7744cfa06942660719bba373ef5fc36208bf73
|
|
241
|
-
chain eip155:84532
|
|
242
|
-
rpc https://sepolia.base.org
|
|
243
|
-
tx 0xf2e1f6c085768b4e3d60463717d52bb2a338803a74a4cfd48aea5738d2595ddd
|
|
244
|
-
logs found 1 Recorded event(s)
|
|
245
|
-
digest MATCH 14cca453684a18c1ef3e1c0b9a7744cfa06942660719bba373ef5fc36208bf73
|
|
246
|
-
|
|
247
|
-
VALID
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
---
|
|
251
|
-
|
|
252
|
-
## What OCP Defines
|
|
253
|
-
|
|
254
|
-
- A minimal verification model
|
|
255
|
-
- A system-independent verification boundary
|
|
256
|
-
- A portable, self-describing proof envelope (chain-agnostic)
|
|
257
|
-
- A formal extraction rule registry (`evm/event-log`, `solana/instruction-data`)
|
|
258
|
-
|
|
259
|
-
---
|
|
260
|
-
|
|
261
|
-
## What OCP Does Not Define
|
|
262
|
-
|
|
263
|
-
- Storage
|
|
264
|
-
- Identity
|
|
265
|
-
- Authorship
|
|
266
|
-
- Canonical encoding
|
|
267
|
-
- Application-layer semantics
|
|
268
|
-
- Sanitization or preprocessing pipelines
|
|
269
|
-
|
|
270
|
-
---
|
|
54
|
+
The result is `{ valid: true }` or `{ valid: false, reason }`. Partial verification is never reported as success.
|
|
271
55
|
|
|
272
|
-
|
|
56
|
+
### Custom providers
|
|
273
57
|
|
|
274
|
-
|
|
58
|
+
`verify()` takes any object with `eth_chainId()` and `eth_getTransactionReceipt(txHash)`. Use this to cross-check multiple RPC endpoints, route through a light client, or run offline against fixtures (this is how the conformance suite works):
|
|
275
59
|
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
The identity pipeline sentinel hash has been confirmed between two independent implementations:
|
|
284
|
-
|
|
285
|
-
```
|
|
286
|
-
8116eec29078e8f57c07077d5e8080a35bde73036581df3abb93755d1b1a16ea
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
The full stack is live in production. L3 tx on Base Sepolia, block 41731493:
|
|
290
|
-
https://sepolia.basescan.org/tx/0xc3aeb16d0aef167e2ebc6d4afc9333fcd13a71b8c02e5485bc6be7491e393319
|
|
291
|
-
|
|
292
|
-
Thread: https://ethereum-magicians.org/t/draft-erc-universal-ai-inference-verification-registry/28083/20
|
|
293
|
-
|
|
294
|
-
---
|
|
295
|
-
|
|
296
|
-
## Why It Matters
|
|
297
|
-
|
|
298
|
-
OCP separates **verification from systems**.
|
|
299
|
-
|
|
300
|
-
A verifier does not ask what's true —
|
|
301
|
-
they compute it.
|
|
302
|
-
|
|
303
|
-
The network only confirms that a commitment exists.
|
|
304
|
-
|
|
305
|
-
---
|
|
306
|
-
|
|
307
|
-
## Start Here
|
|
308
|
-
|
|
309
|
-
- 📄 Core Specification → `/docs/spec/ocp-v1.0.0.md`
|
|
310
|
-
- 🗂️ Proof Envelope → `/docs/spec/ocp-proof-envelope-v1.0.0.md`
|
|
311
|
-
- ⛓️ EVM Extraction Rule → `/docs/spec/appendix-evm-r.md`
|
|
312
|
-
- 🔗 Solana Extraction Rule → `/docs/spec/appendix-solana-r.md`
|
|
313
|
-
- 🤖 AI Inference Attestation → `/docs/spec/appendix-ai-inference-attestation.md`
|
|
314
|
-
- 🧾 Proof Format → `/docs/spec/proof-format-v1.md`
|
|
315
|
-
- 🔍 Examples → `/examples`
|
|
316
|
-
- ✅ Conformance Suite → `/conformance/run-conformance.sh`
|
|
317
|
-
- ⚙️ Contracts → `/contracts`
|
|
318
|
-
- 🌐 Live Demo → https://observation-commitment-protocol.vercel.app/
|
|
319
|
-
|
|
320
|
-
Reference implementation (VeraFile):
|
|
321
|
-
https://github.com/damonzwicker/verafile
|
|
322
|
-
|
|
323
|
-
---
|
|
324
|
-
|
|
325
|
-
## Quick Verify
|
|
326
|
-
|
|
327
|
-
```bash
|
|
328
|
-
npx ocp-verify examples/example-observation.txt
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
Expected output:
|
|
332
|
-
|
|
333
|
-
```
|
|
334
|
-
VALID
|
|
60
|
+
```js
|
|
61
|
+
const provider = {
|
|
62
|
+
eth_chainId: async () => '0xa4b1',
|
|
63
|
+
eth_getTransactionReceipt: async (txHash) => myReceiptSource(txHash),
|
|
64
|
+
};
|
|
335
65
|
```
|
|
336
66
|
|
|
337
|
-
|
|
67
|
+
## What verification proves — and what it doesn't
|
|
338
68
|
|
|
339
|
-
|
|
69
|
+
A passing verification authenticates these envelope fields: `digest`, `hash_function`, `chain_id`, `contract`, `tx_hash`, `receipt_log_position`, `committer`, `block_number`, `block_hash` (when present), and `extraction_profile` (when present). It proves the supplied observation bytes hash to a digest that the named contract emitted in an event of the declared profile's shape, in the referenced transaction and block, by the declared committer.
|
|
340
70
|
|
|
341
|
-
|
|
342
|
-
Phase 1 complete — proof envelope schema
|
|
343
|
-
Phase 2 complete — EVM reference implementation live
|
|
344
|
-
Phase 3 complete — Solana devnet live, Gate 3 verified — same observation committed on EVM and Solana
|
|
345
|
-
Phase 4 complete — ocp-verify published to npm, zero dependencies
|
|
346
|
-
Phase 5 complete — conformance suite, 11/11 tests pass
|
|
347
|
-
First external contribution merged — dinamic.eth / ERC-8004 (PR #1)
|
|
71
|
+
It does **not** prove that the contract is a conforming ERC-8281 deployment (any contract can emit an event with the canonical signature — check ERC-165 interface ID `0xb5c645bd` or maintain a registry of trusted addresses), that the committer authored the observation, or anything about fields a profile doesn't extract (under `erc8263-anchorproof`, the `agentId` is **not** authenticated).
|
|
348
72
|
|
|
73
|
+
## Envelope schema (`erc8281/1`)
|
|
349
74
|
|
|
350
|
-
|
|
75
|
+
| Field | Required | Format |
|
|
76
|
+
|---|---|---|
|
|
77
|
+
| `version` | yes | exact string `erc8281/1` |
|
|
78
|
+
| `digest` | yes | 0x-prefixed lowercase hex, 32 bytes |
|
|
79
|
+
| `hash_function` | yes | `sha2-256` \| `keccak-256` \| `blake2b-256` |
|
|
80
|
+
| `chain_id` | yes | decimal string, EIP-155 chain ID |
|
|
81
|
+
| `contract` | yes | EIP-55 checksummed address (invalid checksums are rejected) |
|
|
82
|
+
| `tx_hash` | yes | 0x-prefixed lowercase hex, 32 bytes |
|
|
83
|
+
| `block_number` | yes | decimal string |
|
|
84
|
+
| `receipt_log_position` | yes | decimal string — array position within `receipt.logs`. **NOT the JSON-RPC `logIndex` field**, which is block-scoped |
|
|
85
|
+
| `committer` | yes | EIP-55 checksummed address |
|
|
86
|
+
| `block_hash` | no | 0x-prefixed lowercase hex, 32 bytes; when present, MUST equal the receipt's `blockHash` (reorg detection) |
|
|
87
|
+
| `extraction_profile` | no | registered profile identifier; defaults to `recorded` |
|
|
351
88
|
|
|
352
|
-
|
|
89
|
+
Unknown additional fields are ignored. All comparisons are over decoded bytes/integers, never raw strings. `blake2b-256` is the *parameterized* form (digest length in the BLAKE2b parameter block) — not a truncation of BLAKE2b-512.
|
|
353
90
|
|
|
354
|
-
|
|
91
|
+
## Extraction profiles
|
|
355
92
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
digest,
|
|
361
|
-
asOfTimestamp,
|
|
362
|
-
'eip155:84532'
|
|
363
|
-
);
|
|
364
|
-
// returns VALID | REVOKED | NOT_FOUND
|
|
365
|
-
```
|
|
93
|
+
| Profile | Event | Topics | Digest | Committer |
|
|
94
|
+
|---|---|---|---|---|
|
|
95
|
+
| `recorded` (default) | `Recorded(bytes32,address)` | exactly 3 | `topics[1]` | `topics[2]` |
|
|
96
|
+
| `erc8263-anchorproof` | `AnchorProof(uint8,bytes32,bytes32,address,bytes)` | exactly 4 | `topics[2]` (`proofHash`) | `topics[3]` (`operator`) |
|
|
366
97
|
|
|
367
|
-
-
|
|
368
|
-
- ⛓️ Deployed Contract → `0x2fa07c85439850ff6C5688d926bDa6DaEe62Db15` (Base Sepolia)
|
|
369
|
-
- ✅ Revocation Conformance → `/conformance/revocation/run-revocation-conformance.sh`
|
|
370
|
-
- 🔑 Event Topic → `0xc19951599a519bc320c0f352b2f92f315e8a2368bd0efb2e5dca3b1196e76112`
|
|
98
|
+
Verified topic-0 constants:
|
|
371
99
|
|
|
372
|
-
|
|
100
|
+
- `recorded`: `0xdca60c2087041cbb12d9a57628c6cad28ecbd0437e47c7ab6c3aa6e162bf4497`
|
|
101
|
+
- `erc8263-anchorproof`: `0x9fe832d83a52f83bd7d54181e4cc7ff8b4e227cc1d3a0144376894b5df6c23cc`
|
|
373
102
|
|
|
374
|
-
|
|
103
|
+
Profiles are a closed registry — envelopes cannot supply inline event signatures or topic indices, which would hand the prover control over what the verifier reads. Unregistered profile identifiers are rejected (`profile_not_registered`). The `erc8263-anchorproof` profile additionally enforces the ERC-8263 canonical-form guard `proofHash != bytes32(0)`.
|
|
375
104
|
|
|
376
|
-
|
|
377
|
-
Phase 6 complete — additive revocation primitive, 8/8 conformance tests pass
|
|
105
|
+
## Security notes
|
|
378
106
|
|
|
379
|
-
|
|
107
|
+
- **RPC trust.** A single RPC endpoint is a trusted party. `verify()` checks the endpoint's `eth_chainId` against the envelope before fetching, but an endpoint can still lie about receipts. For stronger guarantees, supply a provider that cross-checks independent endpoints or verifies receipts against light-client headers.
|
|
108
|
+
- **Finality.** A commitment is only as final as its block. Require finalized blocks or a confirmation depth appropriate to the chain and the value at risk; on L2s, understand the settlement tier you're getting. Include `block_hash` in envelopes to detect post-reorg block migration.
|
|
109
|
+
- **Hash allowlist.** Only the three registry functions are accepted; everything else is rejected (`hash_function_not_allowed`). This prevents a prover from declaring a weak hash and presenting a colliding observation.
|
|
110
|
+
- **Low-entropy observations.** The commitment is binding but not hiding — a digest of a predictable document is a dictionary-attack target. Salt low-entropy observations before hashing and supply the salt as part of the observation bytes.
|
|
111
|
+
- **Reverted transactions** carry no valid commitments; receipts with `status != 1` are rejected.
|
|
380
112
|
|
|
381
|
-
|
|
113
|
+
## Conformance testing
|
|
382
114
|
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const result = await getTemporalBound(txHash, 'eip155:84532');
|
|
387
|
-
// result.upper_bound_iso — observation existed no later than this time
|
|
388
|
-
// result.finalized — whether safe finality depth has been reached
|
|
115
|
+
```sh
|
|
116
|
+
npm test
|
|
389
117
|
```
|
|
390
118
|
|
|
391
|
-
-
|
|
392
|
-
- ✅ Conformance → `/conformance/temporal-bounds/run-temporal-bounds-conformance.sh`
|
|
393
|
-
|
|
394
|
-
---
|
|
119
|
+
Runs the full ERC-8281 vector suite (vectors 1–21 plus the BLAKE2b negative companion 03b) against offline fixtures, validates all three hash implementations against known vectors, verifies the topic-0 and ERC-165 constants by recomputation, and runs a mutation sweep confirming that altering any authenticated field flips verification to rejection. No network access required.
|
|
395
120
|
|
|
396
|
-
##
|
|
121
|
+
## License
|
|
397
122
|
|
|
398
|
-
|
|
399
|
-
Phase 7 complete — finality-derived temporal bounds, 8/8 conformance tests pass
|
|
123
|
+
CC0-1.0
|
package/package.json
CHANGED
|
@@ -1,41 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ocp-verify",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"license": "
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"ocp-commit": "reference-cli/commit.js",
|
|
12
|
-
"ocp-verify": "reference-cli/verify.js"
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "ERC-8281 Observation Commitment Protocol (OCP) reference verifier: recompute → compare → confirm inclusion. Dependency-free, supports the recorded and erc8263-anchorproof extraction profiles.",
|
|
5
|
+
"license": "CC0-1.0",
|
|
6
|
+
"author": "Damon Zwicker <damon@ocp-labs.org>",
|
|
7
|
+
"homepage": "https://ocp-labs.org",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/damonzwicker/observation-commitment-protocol"
|
|
13
11
|
},
|
|
14
|
-
"files": [
|
|
15
|
-
"reference-cli",
|
|
16
|
-
"README.md",
|
|
17
|
-
"LICENSE"
|
|
18
|
-
],
|
|
19
12
|
"keywords": [
|
|
13
|
+
"erc-8281",
|
|
20
14
|
"ocp",
|
|
21
|
-
"observation-commitment
|
|
22
|
-
"blockchain",
|
|
15
|
+
"observation-commitment",
|
|
23
16
|
"verification",
|
|
24
|
-
"proof",
|
|
25
17
|
"ethereum",
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"zero-dependency",
|
|
30
|
-
"cryptography"
|
|
18
|
+
"proof",
|
|
19
|
+
"commitment",
|
|
20
|
+
"erc-8263"
|
|
31
21
|
],
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
|
|
22
|
+
"type": "commonjs",
|
|
23
|
+
"main": "./src/index.js",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"import": "./src/index.mjs",
|
|
27
|
+
"require": "./src/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"src/",
|
|
32
|
+
"README.md",
|
|
33
|
+
"CHANGELOG.md"
|
|
34
|
+
],
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"test": "node --test"
|
|
37
40
|
},
|
|
38
|
-
"
|
|
39
|
-
"url": "https://github.com/damonzwicker/observation-commitment-protocol/issues"
|
|
40
|
-
}
|
|
41
|
+
"dependencies": {}
|
|
41
42
|
}
|