@smartledger/bsv 9.19.0 → 9.21.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 CHANGED
@@ -7,6 +7,139 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [9.21.0] - 2026-10-05
11
+
12
+ ### Added — `opts.network` on `NotaryHash.verify`, opt-in
13
+
14
+ `anchor.network` is the last field in a certificate that no signature covers. A mainnet certificate
15
+ relabelled `"bsv-testnet"` verified, and so did one relabelled `"not-a-chain"`.
16
+
17
+ BRC-220 calls the field descriptive — the headers decide the chain, not the label — so accepting
18
+ any value is conformant, and this check is therefore **opt-in**: a default would break every
19
+ testnet caller for no gain. Pass `opts.network` and a certificate naming another chain is refused
20
+ by name; omit it and nothing changes.
21
+
22
+ Worth exactly what it claims: **it compares a label against the caller's own setting, and proves
23
+ nothing about which chain the header source serves.** A caller who needs that must obtain headers
24
+ from a source it trusts for the chain it means — the same reason this library never fetches one.
25
+
26
+ Raised by the NotaryHash SDK session after a third session relabelled a mainnet certificate and
27
+ both verifiers accepted it. Their verifier takes the same option, so the two agree.
28
+
29
+ ### Documented — 8.3.1 was a signature-convention break, and it orphaned certificates
30
+
31
+ **If you anchored NotaryHash certificates with 8.3.0, some of them can no longer be verified by any
32
+ later version, and there was no symptom until the fix landed.**
33
+
34
+ 8.3.0 reversed the payload digest in **both** the signer and the verifier. The two cancelled, so
35
+ certificates it produced verified perfectly under 8.3.0. 8.3.1 corrected both halves at once —
36
+ which was right — and in doing so made every 8.3.0-signed certificate unverifiable, because the
37
+ signature is genuinely over `reverse(payloadHash)`.
38
+
39
+ Measured on five mainnet certificates, three library versions, nothing else changed:
40
+
41
+ | | 8.3.0 | 8.3.1 | 9.18–9.21 |
42
+ |---|---|---|---|
43
+ | four certificates signed 2026-08-16 | **verify** | fail | fail |
44
+ | one control signed 2026-08-20 | fail | **verify** | **verify** |
45
+
46
+ The two eras are mutually unverifiable, which is only possible if both sides of 8.3.0 reversed.
47
+ Confirmed four ways: in Python with no SDK, through `NotaryHash.verify`, through
48
+ `crypto.ECDSA.verify` directly, and here against the reporter's committed fixture — the four
49
+ failing certificates verify over `reverse(payloadHash)` and not over `payloadHash`, and the control
50
+ is the exact inverse.
51
+
52
+ **No reversing path will be added.** Accepting two derivations of one value is how the 8.3.0–9.8.0
53
+ certificate-format split happened, and a compatibility mode would reintroduce it. These
54
+ certificates also cannot be rescued by re-anchoring: the signature does not survive, so the key
55
+ holder would have to sign again — a new statement made today, not a restoration of an old one. The
56
+ remedy is to label them with the version that verifies them.
57
+
58
+ Recorded because the general shape is worth more than the incident: **a convention that is wrong
59
+ but self-consistent leaves no symptom until the day it is corrected.** Reported and measured by the
60
+ ordinals session that holds the affected certificates.
61
+
62
+ ## [9.20.0] - 2026-10-05
63
+
64
+ A security release. One verifier accepted a forged proof outright; another accepted eight tampered
65
+ certificates. Both defects are the same shape: **a field that looks checked and is not.**
66
+
67
+ ### Security — `ZKProver.verifyMembershipProof` accepted a forged proof
68
+
69
+ It took the proof alone and returned
70
+
71
+ ```js
72
+ proof.setCommitments.includes(proof.valueCommitment) && proof.isMember
73
+ ```
74
+
75
+ where **every value in that expression came from the prover**. Nothing bound the commitments to a
76
+ set the verifier knew, nothing opened the value commitment, and `isMember` was the prover's own
77
+ claim. So this returned `true`:
78
+
79
+ ```js
80
+ verifyMembershipProof({ type: 'MembershipProof',
81
+ setCommitments: ['x'], valueCommitment: 'x', isMember: true })
82
+ ```
83
+
84
+ A forgery needed no key, no salt and no set. This is the same defect `verifyAgeProof` and
85
+ `verifyRangeProof` had before 8.2.0, in the one function that fix did not reach.
86
+
87
+ The verifier now takes `(proof, opening, set)`: the **verifier** supplies the set it believes in,
88
+ the holder supplies `{ value, salt }`, both commitments are recomputed, the prover's array must
89
+ equal the verifier's set commitment-for-commitment in order — so a prover cannot append one — and
90
+ membership is decided against the verifier's set. `proof.isMember` is no longer consulted.
91
+ `generateMembershipProof` now returns the `salt` so a holder can build the opening.
92
+
93
+ This is **not zero-knowledge and cannot be**, consistent with this module's header note. One salt
94
+ covers every member, so a verifier holding the set and the salt can recompute every commitment, and
95
+ a low-entropy set is not hidden from anyone who sees the proof. The honest claim is "this value is
96
+ in a set the verifier already holds" — a membership *check*. A caller needing the value or set
97
+ hidden needs a different primitive; per-attribute fresh salts under an issuer-signed RFC 6962 root
98
+ is the construction to reach for.
99
+
100
+ Reported against 9.19.0 by a consumer that had reviewed these proofs in July and re-tested them.
101
+ **No test in this repository covered the forged path** — the suite count did not move when the fix
102
+ landed.
103
+
104
+ ### Security — `NotaryHash.verify` accepted eight tampered certificates
105
+
106
+ Each is a single edited field, covered by no signature and checked by nothing, on one real mainnet
107
+ batch certificate (block 954784):
108
+
109
+ | case | change | now |
110
+ |---|---|---|
111
+ | T01 | `anchor.blockTime` + 3600 | refused |
112
+ | T02 | `anchor.blockTime` − 100000000 | refused |
113
+ | T03 | `anchor.blockHeight` − 1000 | refused |
114
+ | T05 | `anchor.vout` = 1 (a payment output) | refused |
115
+ | T06 | `anchor.vout` = 99 (past the last output) | refused |
116
+ | T07 | `spv.merkleProof.index` + 2^(nodes+3) | refused |
117
+ | T19 | `createdAt` `.000Z` → `.123Z` | refused |
118
+ | T20 | `createdAt` `.000Z` → `Z` | refused |
119
+
120
+ The rules now enforced:
121
+
122
+ - **`anchor.blockTime` must equal the time in the 80-byte header**, and `anchor.blockHeight` must
123
+ equal `spv.blockHeight`. A `null` is not a claim — a certificate issued before confirmation
124
+ legitimately carries nulls, and only a stated value is checked.
125
+ - **The record is read from `anchor.vout` and no other output.** Scanning every output made
126
+ `anchor.vout` decorative: it could name a payment output, or one past the end, and the record was
127
+ still found elsewhere in the transaction.
128
+ - **A TSC `index` must fit its path.** A path of n nodes addresses at most 2^n leaves, so a larger
129
+ index describes a tree the path cannot belong to.
130
+ - **`createdAt` must be the canonical rendering of its second**, in the reference format. The
131
+ proofHash commits to `createdAtUnix`, so `...04.000Z`, `...04.527Z` and `...04Z` produce the
132
+ **same** commitment — the sub-second component is not covered by the proof at all. Applied to the
133
+ reference format only: certificates written by 8.3.0–9.8.0 carry a millisecond component, exist
134
+ in the wild, and are still read.
135
+
136
+ Verified against an external 27-case oracle with seven pinned mainnet headers: **27/27 agreement**,
137
+ with both honest cases still accepted.
138
+
139
+ Found and measured independently by two consumer sessions — the verification API that uses this
140
+ library as a second, independent anchor verifier, and the NotaryHash SDK session whose SPEC the
141
+ rules come from. The eight were reproduced here with a separate harness before any change.
142
+
10
143
  ## [9.19.0] - 2026-10-03
11
144
 
12
145
  ### Security — `merkle.leafIndex` was not validated against the path beside it
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Bitcoin SV library with an interpreter-verified script engine.
4
4
 
5
- [![Version](https://img.shields.io/badge/version-9.19.0-blue.svg)](https://www.npmjs.com/package/@smartledger/bsv)
5
+ [![Version](https://img.shields.io/badge/version-9.21.0-blue.svg)](https://www.npmjs.com/package/@smartledger/bsv)
6
6
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
7
7
  [![Stability](https://img.shields.io/badge/9.x%20stable%20until-2027--09--01-brightgreen.svg)](STABILITY.md)
8
8
 
@@ -155,44 +155,44 @@ const bsv = require('@smartledger/bsv') // 128 modules
155
155
  ### **Core Modules**
156
156
  | Module | Size | Use Case | CDN |
157
157
  |--------|------|----------|-----|
158
- | **bsv.min.js** | 1067KB | Core BSV + SmartContract | `unpkg.com/@smartledger/bsv@9.19.0/bsv.min.js` |
159
- | **bsv.bundle.js** | 1067KB | Everything in one file | `unpkg.com/@smartledger/bsv@9.19.0/bsv.bundle.js` |
158
+ | **bsv.min.js** | 1069KB | Core BSV + SmartContract | `unpkg.com/@smartledger/bsv@9.21.0/bsv.min.js` |
159
+ | **bsv.bundle.js** | 1069KB | Everything in one file | `unpkg.com/@smartledger/bsv@9.21.0/bsv.bundle.js` |
160
160
 
161
161
  ### **W3C Verifiable Credentials**
162
162
  | Module | Size | Use Case | CDN |
163
163
  |--------|------|----------|-----|
164
- | **🟢 bsv-didweb.min.js** | 166KB | **DID:web generation** | `unpkg.com/@smartledger/bsv@9.19.0/bsv-didweb.min.js` |
165
- | **🟢 bsv-vcjwt.min.js** | 166KB | **VC-JWT issue/verify** | `unpkg.com/@smartledger/bsv@9.19.0/bsv-vcjwt.min.js` |
166
- | **🟢 bsv-statuslist.min.js** | 256KB | **StatusList2021 revocation** | `unpkg.com/@smartledger/bsv@9.19.0/bsv-statuslist.min.js` |
167
- | **🟢 bsv-anchor.min.js** | 164KB | **BSV anchoring (hash-only)** | `unpkg.com/@smartledger/bsv@9.19.0/bsv-anchor.min.js` |
164
+ | **🟢 bsv-didweb.min.js** | 166KB | **DID:web generation** | `unpkg.com/@smartledger/bsv@9.21.0/bsv-didweb.min.js` |
165
+ | **🟢 bsv-vcjwt.min.js** | 166KB | **VC-JWT issue/verify** | `unpkg.com/@smartledger/bsv@9.21.0/bsv-vcjwt.min.js` |
166
+ | **🟢 bsv-statuslist.min.js** | 256KB | **StatusList2021 revocation** | `unpkg.com/@smartledger/bsv@9.21.0/bsv-statuslist.min.js` |
167
+ | **🟢 bsv-anchor.min.js** | 164KB | **BSV anchoring (hash-only)** | `unpkg.com/@smartledger/bsv@9.21.0/bsv-anchor.min.js` |
168
168
 
169
169
  ### **Smart Contract & Development**
170
170
  | Module | Size | Use Case | CDN |
171
171
  |--------|------|----------|-----|
172
- | **bsv-smartcontract.min.js** | 141KB | Complete covenant framework | `unpkg.com/@smartledger/bsv@9.19.0/bsv-smartcontract.min.js` |
173
- | **bsv-covenant.min.js** | 35KB | Covenant operations | `unpkg.com/@smartledger/bsv@9.19.0/bsv-covenant.min.js` |
174
- | **bsv-script-helper.min.js** | 33KB | Custom script tools | `unpkg.com/@smartledger/bsv@9.19.0/bsv-script-helper.min.js` |
175
- | **bsv-security.min.js** | 32KB | Security enhancements | `unpkg.com/@smartledger/bsv@9.19.0/bsv-security.min.js` |
172
+ | **bsv-smartcontract.min.js** | 141KB | Complete covenant framework | `unpkg.com/@smartledger/bsv@9.21.0/bsv-smartcontract.min.js` |
173
+ | **bsv-covenant.min.js** | 35KB | Covenant operations | `unpkg.com/@smartledger/bsv@9.21.0/bsv-covenant.min.js` |
174
+ | **bsv-script-helper.min.js** | 33KB | Custom script tools | `unpkg.com/@smartledger/bsv@9.21.0/bsv-script-helper.min.js` |
175
+ | **bsv-security.min.js** | 32KB | Security enhancements | `unpkg.com/@smartledger/bsv@9.21.0/bsv-security.min.js` |
176
176
 
177
177
  ### **Legal & Compliance**
178
178
  | Module | Size | Use Case | CDN |
179
179
  |--------|------|----------|-----|
180
- | **bsv-ltp.min.js** | 544KB | Legal Token Protocol | `unpkg.com/@smartledger/bsv@9.19.0/bsv-ltp.min.js` |
181
- | **bsv-gdaf.min.js** | 1067KB | Digital Identity & Attestation | `unpkg.com/@smartledger/bsv@9.19.0/bsv-gdaf.min.js` |
180
+ | **bsv-ltp.min.js** | 544KB | Legal Token Protocol | `unpkg.com/@smartledger/bsv@9.21.0/bsv-ltp.min.js` |
181
+ | **bsv-gdaf.min.js** | 1069KB | Digital Identity & Attestation | `unpkg.com/@smartledger/bsv@9.21.0/bsv-gdaf.min.js` |
182
182
 
183
183
  ### **Advanced Cryptography**
184
184
  | Module | Size | Use Case | CDN |
185
185
  |--------|------|----------|-----|
186
- | **bsv-shamir.min.js** | 177KB | Threshold Cryptography | `unpkg.com/@smartledger/bsv@9.19.0/bsv-shamir.min.js` |
186
+ | **bsv-shamir.min.js** | 177KB | Threshold Cryptography | `unpkg.com/@smartledger/bsv@9.21.0/bsv-shamir.min.js` |
187
187
 
188
188
  ### **Utilities**
189
189
  | Module | Size | Use Case | CDN |
190
190
  |--------|------|----------|-----|
191
- | **bsv-ecies.min.js** | 139KB | Encryption | `unpkg.com/@smartledger/bsv@9.19.0/bsv-ecies.min.js` |
192
- | **bsv-message.min.js** | 34KB | Message signing | `unpkg.com/@smartledger/bsv@9.19.0/bsv-message.min.js` |
193
- | **bsv-mnemonic.min.js** | 320KB | HD wallets | `unpkg.com/@smartledger/bsv@9.19.0/bsv-mnemonic.min.js` |
191
+ | **bsv-ecies.min.js** | 139KB | Encryption | `unpkg.com/@smartledger/bsv@9.21.0/bsv-ecies.min.js` |
192
+ | **bsv-message.min.js** | 34KB | Message signing | `unpkg.com/@smartledger/bsv@9.21.0/bsv-message.min.js` |
193
+ | **bsv-mnemonic.min.js** | 320KB | HD wallets | `unpkg.com/@smartledger/bsv@9.21.0/bsv-mnemonic.min.js` |
194
194
  ```html
195
- <script src="https://unpkg.com/@smartledger/bsv@9.19.0/bsv.min.js"></script>
195
+ <script src="https://unpkg.com/@smartledger/bsv@9.21.0/bsv.min.js"></script>
196
196
  <script>
197
197
  const key = bsv.PrivateKey.fromRandom()
198
198
  </script>
@@ -240,4 +240,4 @@ MIT
240
240
 
241
241
  ---
242
242
 
243
- **SmartLedger-BSV v9.19.0**
243
+ **SmartLedger-BSV v9.21.0**