@smartledger/bsv 9.16.1 → 9.19.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,145 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [9.19.0] - 2026-10-03
11
+
12
+ ### Security — `merkle.leafIndex` was not validated against the path beside it
13
+
14
+ A batch certificate's `merkle.leafIndex` could be changed to any other value in range and the
15
+ inclusion proof still verified. On a 25-leaf batch, a proof for leaf 3 verified while claiming to
16
+ be leaf 0, leaf 7 or leaf 24.
17
+
18
+ The cause is a seam between the two certificate formats. A sided audit path folds by its **own**
19
+ sides, and `Certificate.normalize` returns a non-legacy certificate untouched — so for a
20
+ **reference** certificate `leafIndex` was read and never used. A **legacy** certificate was fine,
21
+ because normalising it derives the sides from `leafIndex`, so a wrong index produced a wrong fold
22
+ and failed. Moving a certificate from the legacy format to the reference one therefore silently
23
+ lost a check.
24
+
25
+ **This was never a false accept of an invalid proof.** A corrupted path hash and a flipped side
26
+ were both refused before this release and are refused now; inclusion itself was sound. What was
27
+ wrong is that `leafIndex` was an **unvalidated assertion sitting beside validated ones**, and a
28
+ reader shown "leaf 3 of 25" had no way to tell which of those fields was load-bearing. For a
29
+ numbered edition — "card 3 of 25" — that field is the claim the holder cares about.
30
+
31
+ `NotaryHash.verifyBatchInclusion` now recomputes the side sequence the stated index implies and
32
+ refuses a mismatch, naming it:
33
+
34
+ ```
35
+ merkle.leafIndex 7 disagrees with the path: node 2 is marked "right" but leaf 7 of 25 requires "left"
36
+ merkle.leafIndex 24 in a tree of 25 leaves needs 2 path nodes; the path has 5
37
+ ```
38
+
39
+ No hashing is added: RFC 6962 already determines the sides from the index and the tree size, so
40
+ the check is one pass over the path. Verified across a 25-leaf tree that every leaf verifies at its
41
+ own index and **every leaf is refused at any other index**, 25 of 25.
42
+
43
+ `leafCount` is deliberately not treated the same way. A mis-stated count that implies the same side
44
+ sequence is indistinguishable here by construction, which is exactly what the on-chain
45
+ `u32be(leafCount)` in the batch record is for — `recordMatchesCertificate` compares it, and that
46
+ comparison is the authority.
47
+
48
+ Found by the ordinals mint that anchors BRC-220 proofs: its own tamper test caught the difference
49
+ when it moved from the legacy format to the reference one, and it reported the seam rather than
50
+ the symptom.
51
+
52
+ ## [9.18.0] - 2026-10-03
53
+
54
+ 9.17.0 was committed but never published to npm; its contents are included here. The version
55
+ number is skipped on the registry rather than reused, so a reader comparing git to npm cannot find
56
+ two different 9.17.0s.
57
+
58
+ ### Fixed — `NotaryHash.verifySignature` reported a missing `createdAt` as a signature failure
59
+
60
+ It derived its inputs from `Certificate.toProofInput`, which also decodes `createdAt` because the
61
+ proofHash commits to the creation time. **The signature does not.** So on a certificate with no
62
+ `createdAt`, `toUnixSeconds(undefined)` threw, the function's `try/catch` turned that into `false`,
63
+ and the caller was told the signature did not match its payloadHash and public key.
64
+
65
+ That is the one answer a caller cannot act on correctly. `verifySignature` returning `false` means
66
+ exactly one thing to every reader — the cryptography does not check out — so the remedy they reach
67
+ for is their key, their digest convention or their endian handling. On this library that has
68
+ historically been the right place to look, which makes the misdirection worse rather than better.
69
+
70
+ The regression arrived with the BRC-220 reference format work; 9.3.0 answered `true` for all of
71
+ these. Measured, identical inputs:
72
+
73
+ | certificate | 9.3.0 | 9.16.1 | 9.18.0 |
74
+ |---|---|---|---|
75
+ | no `createdAt` | true | **false** | true |
76
+ | with `createdAt` | true | true | true |
77
+ | `anchor: {txid}`, no `createdAt` | true | **false** | true |
78
+ | `anchor: {}`, no `createdAt` | true | **false** | true |
79
+
80
+ `Certificate.toSignatureInput` now returns only the five fields a signature check uses, and
81
+ `toProofInput` builds on it by adding `createdAtUnix`. Nothing that should fail now passes: a
82
+ signature over a different digest, a different public key, a flipped byte, a truncated signature
83
+ and a certificate missing `signature`, `publicKey` or `payloadHash` are all still `false`.
84
+ **Certificate completeness is unchanged** — `Certificate.validateShape` lists every missing
85
+ required field by name, and `NotaryHash.verify` runs it first and returns before the signature
86
+ check, so a certificate with no `createdAt` is still not a valid certificate.
87
+
88
+ ### Added — `NotaryHash.verifySignatureOnly(payloadHash, signature, publicKey, algorithm?)`
89
+
90
+ For the case that exposed the bug: verifying a submitted signature **before** spending anything to
91
+ anchor it, when no certificate exists yet and so there is no `createdAt`, `proofHash` or `anchor`.
92
+ Certificate metadata cannot influence the answer because none is passed.
93
+
94
+ It takes Buffers or hex, and **throws** on input it cannot decode rather than returning `false`. A
95
+ boolean that means both "the signature does not match" and "your hex was malformed" is the
96
+ ambiguity this entry point exists to remove, and a pre-flight check is where acting on the wrong
97
+ one costs money.
98
+
99
+ Reported with a complete reproduction by the ordinals mint that anchors BRC-220 proofs, which hit
100
+ it as a pre-anchor check rejecting every valid submission with a 400.
101
+
102
+ ## [9.17.0] - 2026-10-03
103
+
104
+ ### Deprecated — `Script.fromHex` accepts a string that does not decode whole
105
+
106
+ `Buffer.from(str, 'hex')` stops at the first character it cannot decode — including the trailing
107
+ nibble of an odd-length string — and returns what it had. So `Script.fromHex` has always answered
108
+ a malformed string with a **shorter script, or none, and no error**:
109
+
110
+ ```js
111
+ Script.fromHex('<html>503</html>') // 0 bytes
112
+ Script.fromHex('76a91') // 2 bytes, the last nibble dropped
113
+ Script.fromHex('<p2pkh hex>' + '\nmore') // the 25-byte prefix, still a valid P2PKH
114
+ ```
115
+
116
+ A caller that builds an output from the result pays to a truncated script with nothing to tell it,
117
+ and `Transaction#addOutput` accepts a zero-byte script without complaint. `Script.fromString`,
118
+ directly below it in the source, has always refused non-hex with `JSUtil.isHexa`; this function
119
+ never did.
120
+
121
+ The damage is bounded in one useful way: **truncation can only drop a suffix.** It cannot alter the
122
+ bytes it did decode, so an address derived from the result is always the one the input's leading
123
+ bytes named, never a third party's. That makes this a correctness problem rather than a theft one.
124
+
125
+ A string that does not decode whole now emits a deprecation notice naming the replacement, once per
126
+ process. **Nothing throws and no verdict changes**; the notice is the whole change. `10.0.0` will
127
+ refuse it, which under STABILITY.md cannot land before 2027-09-01.
128
+
129
+ Callers wanting the strict behaviour today should check the round trip, which separates every
130
+ truncating input from every whole one:
131
+
132
+ ```js
133
+ if (Script.fromHex(h).toHex() !== h.toLowerCase()) throw new Error('not whole hex')
134
+ ```
135
+
136
+ Found while answering a downstream consumer about an upgrade: its paymail resolver passes a remote
137
+ host's string to `Script.fromHex`. That consumer was not exposed — it gates the result on
138
+ `isPublicKeyHashOut()` — but it had no help from this library in getting there.
139
+
140
+
141
+ ### Changed — `@noble/curves`, `@noble/hashes` and `@noble/ciphers` to `^2.4.0`
142
+
143
+ The declared range was already `^2.3.0`, so a fresh consumer install had been resolving 2.4.0
144
+ while the shipped bundles still inlined 2.3.0. This release realigns them: the floor moves to
145
+ `^2.4.0` and the bundles are a reproducible build of it. All three declare `engines` of
146
+ `node >= 20.19.0`, unchanged, so the supported runtime floor does not move. Five bundles grew by
147
+ 2-3 KB.
148
+
10
149
  ## [9.16.1] - 2026-10-01
11
150
 
12
151
  Three resource and validation defects in the shift and binary-conversion opcodes, found by a
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.16.1-blue.svg)](https://www.npmjs.com/package/@smartledger/bsv)
5
+ [![Version](https://img.shields.io/badge/version-9.19.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** | 1062KB | Core BSV + SmartContract | `unpkg.com/@smartledger/bsv@9.16.1/bsv.min.js` |
159
- | **bsv.bundle.js** | 1062KB | Everything in one file | `unpkg.com/@smartledger/bsv@9.16.1/bsv.bundle.js` |
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` |
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.16.1/bsv-didweb.min.js` |
165
- | **🟢 bsv-vcjwt.min.js** | 166KB | **VC-JWT issue/verify** | `unpkg.com/@smartledger/bsv@9.16.1/bsv-vcjwt.min.js` |
166
- | **🟢 bsv-statuslist.min.js** | 256KB | **StatusList2021 revocation** | `unpkg.com/@smartledger/bsv@9.16.1/bsv-statuslist.min.js` |
167
- | **🟢 bsv-anchor.min.js** | 164KB | **BSV anchoring (hash-only)** | `unpkg.com/@smartledger/bsv@9.16.1/bsv-anchor.min.js` |
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` |
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.16.1/bsv-smartcontract.min.js` |
173
- | **bsv-covenant.min.js** | 35KB | Covenant operations | `unpkg.com/@smartledger/bsv@9.16.1/bsv-covenant.min.js` |
174
- | **bsv-script-helper.min.js** | 33KB | Custom script tools | `unpkg.com/@smartledger/bsv@9.16.1/bsv-script-helper.min.js` |
175
- | **bsv-security.min.js** | 32KB | Security enhancements | `unpkg.com/@smartledger/bsv@9.16.1/bsv-security.min.js` |
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` |
176
176
 
177
177
  ### **Legal & Compliance**
178
178
  | Module | Size | Use Case | CDN |
179
179
  |--------|------|----------|-----|
180
- | **bsv-ltp.min.js** | 542KB | Legal Token Protocol | `unpkg.com/@smartledger/bsv@9.16.1/bsv-ltp.min.js` |
181
- | **bsv-gdaf.min.js** | 1062KB | Digital Identity & Attestation | `unpkg.com/@smartledger/bsv@9.16.1/bsv-gdaf.min.js` |
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` |
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.16.1/bsv-shamir.min.js` |
186
+ | **bsv-shamir.min.js** | 177KB | Threshold Cryptography | `unpkg.com/@smartledger/bsv@9.19.0/bsv-shamir.min.js` |
187
187
 
188
188
  ### **Utilities**
189
189
  | Module | Size | Use Case | CDN |
190
190
  |--------|------|----------|-----|
191
- | **bsv-ecies.min.js** | 137KB | Encryption | `unpkg.com/@smartledger/bsv@9.16.1/bsv-ecies.min.js` |
192
- | **bsv-message.min.js** | 34KB | Message signing | `unpkg.com/@smartledger/bsv@9.16.1/bsv-message.min.js` |
193
- | **bsv-mnemonic.min.js** | 320KB | HD wallets | `unpkg.com/@smartledger/bsv@9.16.1/bsv-mnemonic.min.js` |
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` |
194
194
  ```html
195
- <script src="https://unpkg.com/@smartledger/bsv@9.16.1/bsv.min.js"></script>
195
+ <script src="https://unpkg.com/@smartledger/bsv@9.19.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.16.1**
243
+ **SmartLedger-BSV v9.19.0**