@bsv/sdk 2.0.10 → 2.0.11
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/dist/cjs/package.json +1 -1
- package/dist/cjs/src/transaction/MerklePath.js +9 -4
- package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/transaction/MerklePath.js +9 -4
- package/dist/esm/src/transaction/MerklePath.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/transaction/MerklePath.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +3 -3
- package/dist/umd/bundle.js.map +1 -1
- package/package.json +1 -1
- package/src/transaction/MerklePath.ts +10 -6
- package/src/transaction/__tests/MerklePath.test.ts +60 -22
package/package.json
CHANGED
|
@@ -281,9 +281,13 @@ export default class MerklePath {
|
|
|
281
281
|
// special case for blocks with only one transaction
|
|
282
282
|
if (this.path.length === 1 && this.path[0].length === 1) return workingHash
|
|
283
283
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
284
|
+
// Determine effective tree height. For a compound path where all txids are at level 0
|
|
285
|
+
// (path.length === 1 or intermediate levels are empty/trimmed), we need to compute up
|
|
286
|
+
// to the height implied by the highest offset present in path[0].
|
|
287
|
+
const maxOffset = this.path[0].reduce((max, l) => Math.max(max, l.offset), 0)
|
|
288
|
+
const treeHeight = Math.max(this.path.length, 32 - Math.clz32(maxOffset))
|
|
289
|
+
|
|
290
|
+
for (let height = 0; height < treeHeight; height++) {
|
|
287
291
|
const offset = (index >> height) ^ 1
|
|
288
292
|
const leaf = this.findOrComputeLeaf(height, offset)
|
|
289
293
|
if (typeof leaf !== 'object') {
|
|
@@ -315,9 +319,9 @@ export default class MerklePath {
|
|
|
315
319
|
const hash = (m: string): string =>
|
|
316
320
|
toHex(hash256(toArray(m, 'hex').reverse()).reverse())
|
|
317
321
|
|
|
318
|
-
let leaf: MerklePathLeaf | undefined = this.path
|
|
319
|
-
(l) => l.offset === offset
|
|
320
|
-
|
|
322
|
+
let leaf: MerklePathLeaf | undefined = height < this.path.length
|
|
323
|
+
? this.path[height].find((l) => l.offset === offset)
|
|
324
|
+
: undefined
|
|
321
325
|
|
|
322
326
|
if (leaf != null) return leaf
|
|
323
327
|
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import ChainTracker from '../ChainTracker'
|
|
2
2
|
import MerklePath from '../../transaction/MerklePath'
|
|
3
|
+
import { hash256 } from '../../primitives/Hash'
|
|
4
|
+
import { toHex, toArray } from '../../primitives/utils'
|
|
3
5
|
import invalidBumps from './bump.invalid.vectors'
|
|
4
6
|
import validBumps from './bump.valid.vectors'
|
|
5
7
|
|
|
8
|
+
const merkleHash = (m: string): string => toHex(hash256(toArray(m, 'hex').reverse()).reverse())
|
|
9
|
+
|
|
6
10
|
const BRC74Hex =
|
|
7
11
|
'fe8a6a0c000c04fde80b0011774f01d26412f0d16ea3f0447be0b5ebec67b0782e321a7a01cbdf7f734e30fde90b02004e53753e3fe4667073063a17987292cfdea278824e9888e52180581d7188d8fdea0b025e441996fc53f0191d649e68a200e752fb5f39e0d5617083408fa179ddc5c998fdeb0b0102fdf405000671394f72237d08a4277f4435e5b6edf7adc272f25effef27cdfe805ce71a81fdf50500262bccabec6c4af3ed00cc7a7414edea9c5efa92fb8623dd6160a001450a528201fdfb020101fd7c010093b3efca9b77ddec914f8effac691ecb54e2c81d0ab81cbc4c4b93befe418e8501bf01015e005881826eb6973c54003a02118fe270f03d46d02681c8bc71cd44c613e86302f8012e00e07a2bb8bb75e5accff266022e1e5e6e7b4d6d943a04faadcf2ab4a22f796ff30116008120cafa17309c0bb0e0ffce835286b3a2dcae48e4497ae2d2b7ced4f051507d010a00502e59ac92f46543c23006bff855d96f5e648043f0fb87a7a5949e6a9bebae430104001ccd9f8f64f4d0489b30cc815351cf425e0e78ad79a589350e4341ac165dbe45010301010000af8764ce7e1cc132ab5ed2229a005c87201c9a5ee15c0f91dd53eff31ab30cd4'
|
|
8
12
|
|
|
@@ -131,6 +135,26 @@ class FakeChainTracker implements ChainTracker {
|
|
|
131
135
|
}
|
|
132
136
|
}
|
|
133
137
|
|
|
138
|
+
/** Splits BRC74JSON into two partial paths (A covers txid2, B covers txid3) ready to combine. */
|
|
139
|
+
function buildSplitPaths (): [MerklePath, MerklePath] {
|
|
140
|
+
const path0A = [...BRC74JSON.path[0]]
|
|
141
|
+
const path0B = [...BRC74JSON.path[0]]
|
|
142
|
+
const path1A = [...BRC74JSON.path[1]]
|
|
143
|
+
const path1B = [...BRC74JSON.path[1]]
|
|
144
|
+
const pathRest = [...BRC74JSON.path]
|
|
145
|
+
pathRest.shift()
|
|
146
|
+
pathRest.shift()
|
|
147
|
+
path0A.splice(2, 2)
|
|
148
|
+
path0B.shift()
|
|
149
|
+
path0B.shift()
|
|
150
|
+
path1A.shift()
|
|
151
|
+
path1B.pop()
|
|
152
|
+
return [
|
|
153
|
+
new MerklePath(BRC74JSON.blockHeight, [path0A, path1A, ...pathRest]),
|
|
154
|
+
new MerklePath(BRC74JSON.blockHeight, [path0B, path1B, ...pathRest])
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
|
|
134
158
|
describe('MerklePath', () => {
|
|
135
159
|
it('Parses from hex', () => {
|
|
136
160
|
const path = MerklePath.fromHex(BRC74Hex)
|
|
@@ -163,28 +187,7 @@ describe('MerklePath', () => {
|
|
|
163
187
|
)
|
|
164
188
|
})
|
|
165
189
|
it('Combines two paths', () => {
|
|
166
|
-
const
|
|
167
|
-
const path0B = [...BRC74JSON.path[0]]
|
|
168
|
-
const path1A = [...BRC74JSON.path[1]]
|
|
169
|
-
const path1B = [...BRC74JSON.path[1]]
|
|
170
|
-
const pathRest = [...BRC74JSON.path]
|
|
171
|
-
pathRest.shift()
|
|
172
|
-
pathRest.shift()
|
|
173
|
-
path0A.splice(2, 2)
|
|
174
|
-
path0B.shift()
|
|
175
|
-
path0B.shift()
|
|
176
|
-
path1A.shift()
|
|
177
|
-
path1B.pop()
|
|
178
|
-
const pathAJSON = {
|
|
179
|
-
blockHeight: BRC74JSON.blockHeight,
|
|
180
|
-
path: [path0A, path1A, ...pathRest]
|
|
181
|
-
}
|
|
182
|
-
const pathBJSON = {
|
|
183
|
-
blockHeight: BRC74JSON.blockHeight,
|
|
184
|
-
path: [path0B, path1B, ...pathRest]
|
|
185
|
-
}
|
|
186
|
-
const pathA = new MerklePath(pathAJSON.blockHeight, pathAJSON.path)
|
|
187
|
-
const pathB = new MerklePath(pathBJSON.blockHeight, pathBJSON.path)
|
|
190
|
+
const [pathA, pathB] = buildSplitPaths()
|
|
188
191
|
expect(pathA.computeRoot(BRC74TXID2)).toEqual(BRC74Root)
|
|
189
192
|
expect(() => pathA.computeRoot(BRC74TXID3)).toThrow()
|
|
190
193
|
expect(() => pathB.computeRoot(BRC74TXID2)).toThrow()
|
|
@@ -194,6 +197,41 @@ describe('MerklePath', () => {
|
|
|
194
197
|
expect(pathA.computeRoot(BRC74TXID2)).toEqual(BRC74Root)
|
|
195
198
|
expect(pathA.computeRoot(BRC74TXID3)).toEqual(BRC74Root)
|
|
196
199
|
})
|
|
200
|
+
it('Serializes and deserializes a combined trimmed path', () => {
|
|
201
|
+
const [pathA, pathB] = buildSplitPaths()
|
|
202
|
+
pathA.combine(pathB)
|
|
203
|
+
let deserialized: MerklePath
|
|
204
|
+
expect(() => { deserialized = MerklePath.fromHex(pathA.toHex()) }).not.toThrow()
|
|
205
|
+
expect(deserialized!.computeRoot(BRC74TXID2)).toEqual(BRC74Root)
|
|
206
|
+
expect(deserialized!.computeRoot(BRC74TXID3)).toEqual(BRC74Root)
|
|
207
|
+
})
|
|
208
|
+
it('Constructs a compound path from all txids at level 0 only', () => {
|
|
209
|
+
// A single-level compound path: all txids for a block given at level 0, no higher levels.
|
|
210
|
+
// The implementation should be able to compute the merkle root by calculating up from the leaves.
|
|
211
|
+
const tx0 = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
|
212
|
+
const tx1 = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
|
|
213
|
+
const tx2 = 'cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'
|
|
214
|
+
const tx3 = 'dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'
|
|
215
|
+
const root4 = merkleHash(merkleHash(tx3 + tx2) + merkleHash(tx1 + tx0))
|
|
216
|
+
let mp: MerklePath
|
|
217
|
+
expect(() => {
|
|
218
|
+
mp = new MerklePath(100, [[
|
|
219
|
+
{ offset: 0, txid: true, hash: tx0 },
|
|
220
|
+
{ offset: 1, txid: true, hash: tx1 },
|
|
221
|
+
{ offset: 2, txid: true, hash: tx2 },
|
|
222
|
+
{ offset: 3, txid: true, hash: tx3 }
|
|
223
|
+
]])
|
|
224
|
+
}).not.toThrow()
|
|
225
|
+
expect(mp!.computeRoot(tx0)).toEqual(root4)
|
|
226
|
+
expect(mp!.computeRoot(tx1)).toEqual(root4)
|
|
227
|
+
expect(mp!.computeRoot(tx2)).toEqual(root4)
|
|
228
|
+
expect(mp!.computeRoot(tx3)).toEqual(root4)
|
|
229
|
+
// Serializing and deserializing a single-level compound path should also work
|
|
230
|
+
let deserialized: MerklePath
|
|
231
|
+
expect(() => { deserialized = MerklePath.fromHex(mp!.toHex()) }).not.toThrow()
|
|
232
|
+
expect(deserialized!.computeRoot(tx0)).toEqual(root4)
|
|
233
|
+
expect(deserialized!.computeRoot(tx3)).toEqual(root4)
|
|
234
|
+
})
|
|
197
235
|
it('Rejects invalid bumps', () => {
|
|
198
236
|
for (const invalid of invalidBumps) {
|
|
199
237
|
expect(() => MerklePath.fromHex(invalid.bump)).toThrow(invalid.error)
|