@bsv/sdk 2.0.11 → 2.0.13
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/auth/clients/__tests__/AuthFetch.additional.test.js +827 -0
- package/dist/cjs/src/auth/clients/__tests__/AuthFetch.additional.test.js.map +1 -0
- package/dist/cjs/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.js +654 -0
- package/dist/cjs/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.js.map +1 -0
- package/dist/cjs/src/overlay-tools/HostReputationTracker.js +21 -13
- package/dist/cjs/src/overlay-tools/HostReputationTracker.js.map +1 -1
- package/dist/cjs/src/primitives/PrivateKey.js +3 -3
- package/dist/cjs/src/primitives/PrivateKey.js.map +1 -1
- package/dist/cjs/src/script/Spend.js +17 -9
- package/dist/cjs/src/script/Spend.js.map +1 -1
- package/dist/cjs/src/storage/StorageDownloader.js +6 -6
- package/dist/cjs/src/storage/StorageDownloader.js.map +1 -1
- package/dist/cjs/src/storage/StorageUtils.js +1 -1
- package/dist/cjs/src/storage/StorageUtils.js.map +1 -1
- package/dist/cjs/src/transaction/MerklePath.js +168 -27
- package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/auth/clients/__tests__/AuthFetch.additional.test.js +825 -0
- package/dist/esm/src/auth/clients/__tests__/AuthFetch.additional.test.js.map +1 -0
- package/dist/esm/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.js +619 -0
- package/dist/esm/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.js.map +1 -0
- package/dist/esm/src/overlay-tools/HostReputationTracker.js +21 -13
- package/dist/esm/src/overlay-tools/HostReputationTracker.js.map +1 -1
- package/dist/esm/src/primitives/PrivateKey.js +3 -3
- package/dist/esm/src/primitives/PrivateKey.js.map +1 -1
- package/dist/esm/src/script/Spend.js +17 -9
- package/dist/esm/src/script/Spend.js.map +1 -1
- package/dist/esm/src/storage/StorageDownloader.js +6 -6
- package/dist/esm/src/storage/StorageDownloader.js.map +1 -1
- package/dist/esm/src/storage/StorageUtils.js +1 -1
- package/dist/esm/src/storage/StorageUtils.js.map +1 -1
- package/dist/esm/src/transaction/MerklePath.js +168 -27
- package/dist/esm/src/transaction/MerklePath.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/auth/clients/__tests__/AuthFetch.additional.test.d.ts +21 -0
- package/dist/types/src/auth/clients/__tests__/AuthFetch.additional.test.d.ts.map +1 -0
- package/dist/types/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.d.ts +2 -0
- package/dist/types/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.d.ts.map +1 -0
- package/dist/types/src/overlay-tools/HostReputationTracker.d.ts.map +1 -1
- package/dist/types/src/script/Spend.d.ts.map +1 -1
- package/dist/types/src/transaction/MerklePath.d.ts +27 -0
- 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/docs/reference/storage.md +1 -1
- package/docs/reference/transaction.md +40 -0
- package/package.json +1 -1
- package/src/auth/clients/__tests__/AuthFetch.additional.test.ts +1131 -0
- package/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.ts +770 -0
- package/src/auth/utils/__tests/validateCertificates.test.ts +12 -9
- package/src/compat/__tests/Mnemonic.additional.test.ts +64 -0
- package/src/identity/__tests/IdentityClient.additional.test.ts +767 -0
- package/src/kvstore/__tests/LocalKVStore.additional.test.ts +611 -0
- package/src/kvstore/__tests/LocalKVStore.test.ts +4 -6
- package/src/kvstore/__tests/kvStoreInterpreter.test.ts +327 -0
- package/src/overlay-tools/HostReputationTracker.ts +17 -14
- package/src/overlay-tools/__tests/HostReputationTracker.additional.test.ts +561 -0
- package/src/overlay-tools/__tests/LookupResolver.additional.test.ts +612 -0
- package/src/overlay-tools/__tests/withDoubleSpendRetry.test.ts +278 -0
- package/src/primitives/PrivateKey.ts +3 -3
- package/src/primitives/__tests/BigNumber.additional.test.ts +79 -0
- package/src/primitives/__tests/Curve.additional.test.ts +208 -0
- package/src/primitives/__tests/ECDSA.additional.test.ts +122 -0
- package/src/primitives/__tests/Hash.additional.test.ts +59 -0
- package/src/primitives/__tests/JacobianPoint.test.ts +308 -0
- package/src/primitives/__tests/Point.additional.test.ts +503 -0
- package/src/primitives/__tests/PublicKey.additional.test.ts +383 -0
- package/src/primitives/__tests/Random.additional.test.ts +262 -0
- package/src/primitives/__tests/Signature.test.ts +333 -0
- package/src/primitives/__tests/TransactionSignature.additional.test.ts +241 -0
- package/src/registry/__tests/RegistryClient.additional.test.ts +750 -0
- package/src/remittance/__tests/BasicBRC29.additional.test.ts +657 -0
- package/src/remittance/__tests/RemittanceManager.additional.test.ts +1272 -0
- package/src/script/Spend.ts +19 -11
- package/src/script/__tests/LockingUnlockingScript.test.ts +79 -0
- package/src/script/__tests/Script.additional.test.ts +100 -0
- package/src/script/__tests/ScriptEvaluationError.test.ts +98 -0
- package/src/script/__tests/Spend.additional.test.ts +837 -0
- package/src/script/templates/__tests/RPuzzle.test.ts +134 -0
- package/src/storage/StorageDownloader.ts +6 -6
- package/src/storage/StorageUtils.ts +1 -1
- package/src/transaction/MerklePath.ts +196 -36
- package/src/transaction/__tests/BeefParty.additional.test.ts +22 -0
- package/src/transaction/__tests/Broadcaster.test.ts +159 -0
- package/src/transaction/__tests/MerklePath.bench.test.ts +105 -0
- package/src/transaction/__tests/MerklePath.test.ts +232 -21
- package/src/transaction/__tests/Transaction.additional.test.ts +225 -0
- package/src/transaction/broadcasters/__tests/ARC.additional.test.ts +585 -0
- package/src/transaction/broadcasters/__tests/Teranode.test.ts +349 -0
- package/src/transaction/chaintrackers/__tests/BlockHeadersService.test.ts +253 -0
- package/src/transaction/chaintrackers/__tests/DefaultChainTracker.test.ts +44 -0
- package/src/transaction/chaintrackers/__tests/WhatsOnChain.additional.test.ts +193 -0
- package/src/transaction/fee-models/__tests/SatoshisPerKilobyte.test.ts +262 -0
- package/src/transaction/http/__tests/BinaryFetchClient.test.ts +212 -0
- package/src/transaction/http/__tests/DefaultHttpClient.additional.test.ts +192 -0
- package/src/transaction/http/__tests/DefaultHttpClient.test.ts +71 -0
- package/src/wallet/__tests/ProtoWallet.additional.test.ts +134 -0
- package/src/wallet/__tests/WERR.test.ts +212 -0
- package/src/wallet/__tests/WalletClient.additional.test.ts +699 -0
- package/src/wallet/__tests/WalletClient.substrate.test.ts +759 -0
- package/src/wallet/__tests/WalletError.test.ts +290 -0
- package/src/wallet/__tests/validationHelpers.test.ts +1218 -0
- package/src/wallet/substrates/__tests/HTTPWalletJSON.test.ts +496 -0
- package/src/wallet/substrates/__tests/HTTPWalletWire.test.ts +273 -0
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
import Point from '../../primitives/Point'
|
|
2
|
+
import BigNumber from '../../primitives/BigNumber'
|
|
3
|
+
import Curve from '../../primitives/Curve'
|
|
4
|
+
|
|
5
|
+
const G_COMPRESSED = '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'
|
|
6
|
+
|
|
7
|
+
describe('Point – additional coverage', () => {
|
|
8
|
+
const curve = new Curve()
|
|
9
|
+
const G = curve.g as Point
|
|
10
|
+
|
|
11
|
+
// --------------------------------------------------------------------------
|
|
12
|
+
// Constructor and coordinate handling
|
|
13
|
+
// --------------------------------------------------------------------------
|
|
14
|
+
describe('constructor', () => {
|
|
15
|
+
it('constructs an infinity point with null, null', () => {
|
|
16
|
+
const inf = new Point(null, null)
|
|
17
|
+
expect(inf.isInfinity()).toBe(true)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('constructs from number array x/y', () => {
|
|
21
|
+
const G2 = G.mul(new BigNumber(2))
|
|
22
|
+
const x = G2.getX().toArray()
|
|
23
|
+
const y = G2.getY().toArray()
|
|
24
|
+
const p = new Point(x, y)
|
|
25
|
+
expect(p.isInfinity()).toBe(false)
|
|
26
|
+
expect(p.getX().eq(G2.getX())).toBe(true)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('constructs with isRed=false', () => {
|
|
30
|
+
const G2 = G.mul(new BigNumber(2))
|
|
31
|
+
const xBN = G2.getX()
|
|
32
|
+
const yBN = G2.getY()
|
|
33
|
+
const p = new Point(xBN, yBN, false)
|
|
34
|
+
expect(p.isInfinity()).toBe(false)
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// --------------------------------------------------------------------------
|
|
39
|
+
// fromDER – uncompressed and hybrid formats
|
|
40
|
+
// --------------------------------------------------------------------------
|
|
41
|
+
describe('fromDER', () => {
|
|
42
|
+
it('parses uncompressed point (0x04)', () => {
|
|
43
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
44
|
+
const bytes = g.encode(false) as number[]
|
|
45
|
+
expect(bytes[0]).toBe(0x04)
|
|
46
|
+
const restored = Point.fromDER(bytes)
|
|
47
|
+
expect(restored.eq(g)).toBe(true)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('parses 0x06 hybrid (y-even) format', () => {
|
|
51
|
+
// Find a point where the last byte of the y coordinate is even
|
|
52
|
+
let gn: Point | null = null
|
|
53
|
+
for (let i = 2; i <= 20; i++) {
|
|
54
|
+
const candidate = G.mul(new BigNumber(i))
|
|
55
|
+
const uncompressed = candidate.encode(false) as number[]
|
|
56
|
+
if (uncompressed[uncompressed.length - 1] % 2 === 0) {
|
|
57
|
+
gn = candidate
|
|
58
|
+
break
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (gn === null) {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
const uncompressed = gn.encode(false) as number[]
|
|
65
|
+
// Replace prefix with 0x06 (even y last byte)
|
|
66
|
+
uncompressed[0] = 0x06
|
|
67
|
+
const restored = Point.fromDER(uncompressed)
|
|
68
|
+
expect(restored.validate()).toBe(true)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('parses 0x07 hybrid (y-odd) format', () => {
|
|
72
|
+
// Find a point where the last byte of the y coordinate (in the uncompressed encoding) is odd
|
|
73
|
+
let gn: Point | null = null
|
|
74
|
+
for (let i = 2; i <= 20; i++) {
|
|
75
|
+
const candidate = G.mul(new BigNumber(i))
|
|
76
|
+
const uncompressed = candidate.encode(false) as number[]
|
|
77
|
+
if (uncompressed[uncompressed.length - 1] % 2 === 1) {
|
|
78
|
+
gn = candidate
|
|
79
|
+
break
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (gn === null) {
|
|
83
|
+
// Skip if no suitable point found in range
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
const uncompressed = gn.encode(false) as number[]
|
|
87
|
+
// Replace prefix with 0x07 (odd y last byte)
|
|
88
|
+
uncompressed[0] = 0x07
|
|
89
|
+
const restored = Point.fromDER(uncompressed)
|
|
90
|
+
expect(restored.validate()).toBe(true)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('throws for 0x06 when last byte is odd', () => {
|
|
94
|
+
// Build a valid-looking 65-byte buffer but set last byte to odd
|
|
95
|
+
// 0x06 requires last byte to be even, so odd → throws
|
|
96
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
97
|
+
const uncompressed = g.encode(false) as number[]
|
|
98
|
+
uncompressed[0] = 0x06
|
|
99
|
+
// Force last byte to be odd
|
|
100
|
+
if (uncompressed[uncompressed.length - 1] % 2 === 0) {
|
|
101
|
+
uncompressed[uncompressed.length - 1] = 0x01
|
|
102
|
+
}
|
|
103
|
+
// Note: this may produce an invalid point too (validation error), either way it throws
|
|
104
|
+
expect(() => Point.fromDER(uncompressed)).toThrow()
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('throws for 0x07 when last byte is even', () => {
|
|
108
|
+
// Build a valid-looking 65-byte buffer but set last byte to even
|
|
109
|
+
// 0x07 requires last byte to be odd, so even → throws
|
|
110
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
111
|
+
const uncompressed = g.encode(false) as number[]
|
|
112
|
+
uncompressed[0] = 0x07
|
|
113
|
+
// Force last byte to be even
|
|
114
|
+
if (uncompressed[uncompressed.length - 1] % 2 === 1) {
|
|
115
|
+
uncompressed[uncompressed.length - 1] = 0x02
|
|
116
|
+
}
|
|
117
|
+
expect(() => Point.fromDER(uncompressed)).toThrow()
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('throws for unknown format', () => {
|
|
121
|
+
const der = [0x05, ...Array(32).fill(0x01)]
|
|
122
|
+
expect(() => Point.fromDER(der)).toThrow('Unknown point format')
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
// --------------------------------------------------------------------------
|
|
127
|
+
// fromString
|
|
128
|
+
// --------------------------------------------------------------------------
|
|
129
|
+
describe('fromString', () => {
|
|
130
|
+
it('parses a compressed hex point', () => {
|
|
131
|
+
const p = Point.fromString(G_COMPRESSED)
|
|
132
|
+
expect(p.validate()).toBe(true)
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
// --------------------------------------------------------------------------
|
|
137
|
+
// fromX
|
|
138
|
+
// --------------------------------------------------------------------------
|
|
139
|
+
describe('fromX', () => {
|
|
140
|
+
it('fromX accepts BigNumber', () => {
|
|
141
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
142
|
+
const xBN = g.getX()
|
|
143
|
+
const p = Point.fromX(xBN, false)
|
|
144
|
+
expect(p.validate()).toBe(true)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('fromX accepts number', () => {
|
|
148
|
+
// Use a valid x value that has a square root mod p
|
|
149
|
+
const g = G.mul(new BigNumber(7))
|
|
150
|
+
const xNum = parseInt(g.getX().toString(16).slice(-4), 16)
|
|
151
|
+
// fromX with a number, may produce a point
|
|
152
|
+
const p = Point.fromX(g.getX(), true)
|
|
153
|
+
expect(p.validate()).toBe(true)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('fromX accepts hex string', () => {
|
|
157
|
+
const g = G.mul(new BigNumber(7))
|
|
158
|
+
const xHex = g.getX().toString(16)
|
|
159
|
+
const p = Point.fromX(xHex, false)
|
|
160
|
+
expect(p.validate()).toBe(true)
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('fromX accepts number array', () => {
|
|
164
|
+
const g = G.mul(new BigNumber(7))
|
|
165
|
+
const xArr = g.getX().toArray()
|
|
166
|
+
const p = Point.fromX(xArr, true)
|
|
167
|
+
expect(p.validate()).toBe(true)
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
// --------------------------------------------------------------------------
|
|
172
|
+
// fromJSON
|
|
173
|
+
// --------------------------------------------------------------------------
|
|
174
|
+
describe('fromJSON', () => {
|
|
175
|
+
it('accepts JSON string', () => {
|
|
176
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
177
|
+
const json = JSON.stringify(g.toJSON())
|
|
178
|
+
const restored = Point.fromJSON(json, true)
|
|
179
|
+
expect(restored.eq(g)).toBe(true)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('fromJSON with naf precomputed data', () => {
|
|
183
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
184
|
+
// Precomputed data has naf but no doubles
|
|
185
|
+
const serialized = [g.getX(), g.getY(), { naf: { wnd: 2, points: [] }, doubles: null }]
|
|
186
|
+
const restored = Point.fromJSON(serialized as any, true)
|
|
187
|
+
expect(restored.validate()).toBe(true)
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it('fromJSON with doubles precomputed data', () => {
|
|
191
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
192
|
+
const serialized = [g.getX(), g.getY(), { doubles: { step: 4, points: [] }, naf: null }]
|
|
193
|
+
const restored = Point.fromJSON(serialized as any, true)
|
|
194
|
+
expect(restored.validate()).toBe(true)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('fromJSON with no precomputed (third element absent)', () => {
|
|
198
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
199
|
+
const serialized = [g.getX(), g.getY()]
|
|
200
|
+
const restored = Point.fromJSON(serialized as any, true)
|
|
201
|
+
expect(restored.validate()).toBe(true)
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
// --------------------------------------------------------------------------
|
|
206
|
+
// validate
|
|
207
|
+
// --------------------------------------------------------------------------
|
|
208
|
+
describe('validate', () => {
|
|
209
|
+
it('returns false for infinity point', () => {
|
|
210
|
+
const inf = new Point(null, null)
|
|
211
|
+
expect(inf.validate()).toBe(false)
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('returns true for a valid curve point', () => {
|
|
215
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
216
|
+
expect(g.validate()).toBe(true)
|
|
217
|
+
})
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
// --------------------------------------------------------------------------
|
|
221
|
+
// encode
|
|
222
|
+
// --------------------------------------------------------------------------
|
|
223
|
+
describe('encode', () => {
|
|
224
|
+
it('encodes infinity as [0x00]', () => {
|
|
225
|
+
const inf = new Point(null, null)
|
|
226
|
+
expect(inf.encode()).toEqual([0x00])
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it('encodes infinity as "00" when enc=hex', () => {
|
|
230
|
+
const inf = new Point(null, null)
|
|
231
|
+
expect(inf.encode(true, 'hex')).toBe('00')
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
it('encodes compressed point (default compact=true)', () => {
|
|
235
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
236
|
+
const encoded = g.encode()
|
|
237
|
+
expect(Array.isArray(encoded)).toBe(true)
|
|
238
|
+
const prefix = (encoded as number[])[0]
|
|
239
|
+
expect(prefix === 0x02 || prefix === 0x03).toBe(true)
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
it('encodes uncompressed point (compact=false)', () => {
|
|
243
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
244
|
+
const encoded = g.encode(false) as number[]
|
|
245
|
+
expect(encoded[0]).toBe(0x04)
|
|
246
|
+
expect(encoded.length).toBe(65)
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
it('returns hex when enc=hex', () => {
|
|
250
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
251
|
+
const hex = g.encode(true, 'hex')
|
|
252
|
+
expect(typeof hex).toBe('string')
|
|
253
|
+
expect(hex as string).toMatch(/^0[23][0-9a-f]+$/)
|
|
254
|
+
})
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
// --------------------------------------------------------------------------
|
|
258
|
+
// inspect
|
|
259
|
+
// --------------------------------------------------------------------------
|
|
260
|
+
describe('inspect', () => {
|
|
261
|
+
it('returns "<EC Point Infinity>" for infinity', () => {
|
|
262
|
+
const inf = new Point(null, null)
|
|
263
|
+
expect(inf.inspect()).toBe('<EC Point Infinity>')
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it('returns readable string for valid point', () => {
|
|
267
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
268
|
+
const s = g.inspect()
|
|
269
|
+
expect(s).toContain('<EC Point x:')
|
|
270
|
+
expect(s).toContain('y:')
|
|
271
|
+
})
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
// --------------------------------------------------------------------------
|
|
275
|
+
// add – edge cases
|
|
276
|
+
// --------------------------------------------------------------------------
|
|
277
|
+
describe('add edge cases', () => {
|
|
278
|
+
it('O + P = P (this is infinity)', () => {
|
|
279
|
+
const inf = new Point(null, null)
|
|
280
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
281
|
+
const result = inf.add(g)
|
|
282
|
+
expect(result.eq(g)).toBe(true)
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
it('P + O = P (argument is infinity)', () => {
|
|
286
|
+
const inf = new Point(null, null)
|
|
287
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
288
|
+
const result = g.add(inf)
|
|
289
|
+
expect(result.eq(g)).toBe(true)
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it('P + P = 2P', () => {
|
|
293
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
294
|
+
const g2 = g.dbl()
|
|
295
|
+
const result = g.add(g)
|
|
296
|
+
expect(result.eq(g2)).toBe(true)
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
it('P + (-P) = infinity', () => {
|
|
300
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
301
|
+
const negG = g.neg()
|
|
302
|
+
const result = g.add(negG)
|
|
303
|
+
expect(result.isInfinity()).toBe(true)
|
|
304
|
+
})
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
// --------------------------------------------------------------------------
|
|
308
|
+
// dbl
|
|
309
|
+
// --------------------------------------------------------------------------
|
|
310
|
+
describe('dbl', () => {
|
|
311
|
+
it('dbl of infinity is infinity', () => {
|
|
312
|
+
const inf = new Point(null, null)
|
|
313
|
+
const result = inf.dbl()
|
|
314
|
+
expect(result.isInfinity()).toBe(true)
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
it('dbl of a valid point', () => {
|
|
318
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
319
|
+
const g2 = g.dbl()
|
|
320
|
+
expect(g2.validate()).toBe(true)
|
|
321
|
+
expect(g2.eq(g)).toBe(false)
|
|
322
|
+
})
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
// --------------------------------------------------------------------------
|
|
326
|
+
// neg
|
|
327
|
+
// --------------------------------------------------------------------------
|
|
328
|
+
describe('neg', () => {
|
|
329
|
+
it('neg of infinity is infinity', () => {
|
|
330
|
+
const inf = new Point(null, null)
|
|
331
|
+
expect(inf.neg().isInfinity()).toBe(true)
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
it('neg of a valid point', () => {
|
|
335
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
336
|
+
const negG = g.neg()
|
|
337
|
+
expect(negG.validate()).toBe(true)
|
|
338
|
+
expect(g.add(negG).isInfinity()).toBe(true)
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
it('neg with _precompute=true propagates precomputed data', () => {
|
|
342
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
343
|
+
// Force precomputation via mul (which caches NAF)
|
|
344
|
+
g.mul(new BigNumber(2))
|
|
345
|
+
const negG = g.neg(true)
|
|
346
|
+
expect(negG.validate()).toBe(true)
|
|
347
|
+
})
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
// --------------------------------------------------------------------------
|
|
351
|
+
// dblp
|
|
352
|
+
// --------------------------------------------------------------------------
|
|
353
|
+
describe('dblp', () => {
|
|
354
|
+
it('dblp with k=0 returns same point', () => {
|
|
355
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
356
|
+
const result = g.dblp(0)
|
|
357
|
+
expect(result.eq(g)).toBe(true)
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
it('dblp with k=3 equals three doublings', () => {
|
|
361
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
362
|
+
const tripled = g.dbl().dbl().dbl()
|
|
363
|
+
const result = g.dblp(3)
|
|
364
|
+
expect(result.eq(tripled)).toBe(true)
|
|
365
|
+
})
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
// --------------------------------------------------------------------------
|
|
369
|
+
// getX / getY
|
|
370
|
+
// --------------------------------------------------------------------------
|
|
371
|
+
describe('getX / getY', () => {
|
|
372
|
+
it('getX and getY return BigNumber', () => {
|
|
373
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
374
|
+
expect(BigNumber.isBN(g.getX())).toBe(true)
|
|
375
|
+
expect(BigNumber.isBN(g.getY())).toBe(true)
|
|
376
|
+
})
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
// --------------------------------------------------------------------------
|
|
380
|
+
// mul – edge cases
|
|
381
|
+
// --------------------------------------------------------------------------
|
|
382
|
+
describe('mul', () => {
|
|
383
|
+
it('mul by 0 returns infinity', () => {
|
|
384
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
385
|
+
const result = g.mul(new BigNumber(0))
|
|
386
|
+
expect(result.isInfinity()).toBe(true)
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
it('mul infinity returns infinity', () => {
|
|
390
|
+
const inf = new Point(null, null)
|
|
391
|
+
const result = inf.mul(new BigNumber(5))
|
|
392
|
+
expect(result.isInfinity()).toBe(true)
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
it('mul by negative scalar', () => {
|
|
396
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
397
|
+
const k = new BigNumber(3)
|
|
398
|
+
const r1 = g.mul(k)
|
|
399
|
+
const r2 = g.mul(k.neg())
|
|
400
|
+
expect(r1.eq(r2.neg())).toBe(true)
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
it('mul by number', () => {
|
|
404
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
405
|
+
const r = g.mul(7)
|
|
406
|
+
expect(r.validate()).toBe(true)
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
it('mul by hex string', () => {
|
|
410
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
411
|
+
const r = g.mul('0a')
|
|
412
|
+
expect(r.validate()).toBe(true)
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
it('mul by number array', () => {
|
|
416
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
417
|
+
const r = g.mul([0x05])
|
|
418
|
+
expect(r.validate()).toBe(true)
|
|
419
|
+
})
|
|
420
|
+
})
|
|
421
|
+
|
|
422
|
+
// --------------------------------------------------------------------------
|
|
423
|
+
// mulAdd / jmulAdd
|
|
424
|
+
// --------------------------------------------------------------------------
|
|
425
|
+
describe('mulAdd / jmulAdd', () => {
|
|
426
|
+
it('mulAdd(1, G, 0) = G', () => {
|
|
427
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
428
|
+
const result = g.mulAdd(new BigNumber(1), g, new BigNumber(0))
|
|
429
|
+
expect(result.eq(g)).toBe(true)
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
it('mulAdd(1, G, 1) = 2G', () => {
|
|
433
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
434
|
+
const g2 = g.mul(new BigNumber(2))
|
|
435
|
+
const result = g.mulAdd(new BigNumber(1), g, new BigNumber(1))
|
|
436
|
+
expect(result.eq(g2)).toBe(true)
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
it('jmulAdd returns a JPoint', () => {
|
|
440
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
441
|
+
const result = g.jmulAdd(new BigNumber(2), g, new BigNumber(1))
|
|
442
|
+
expect(result).toBeDefined()
|
|
443
|
+
const asPoint = result.toP()
|
|
444
|
+
const g3 = g.mul(new BigNumber(3))
|
|
445
|
+
expect(asPoint.eq(g3)).toBe(true)
|
|
446
|
+
})
|
|
447
|
+
})
|
|
448
|
+
|
|
449
|
+
// --------------------------------------------------------------------------
|
|
450
|
+
// eq
|
|
451
|
+
// --------------------------------------------------------------------------
|
|
452
|
+
describe('eq', () => {
|
|
453
|
+
it('same instance is equal', () => {
|
|
454
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
455
|
+
expect(g.eq(g)).toBe(true)
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
it('both infinity are equal', () => {
|
|
459
|
+
const inf1 = new Point(null, null)
|
|
460
|
+
const inf2 = new Point(null, null)
|
|
461
|
+
expect(inf1.eq(inf2)).toBe(true)
|
|
462
|
+
})
|
|
463
|
+
|
|
464
|
+
it('infinity != non-infinity', () => {
|
|
465
|
+
const inf = new Point(null, null)
|
|
466
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
467
|
+
expect(inf.eq(g)).toBe(false)
|
|
468
|
+
})
|
|
469
|
+
})
|
|
470
|
+
|
|
471
|
+
// --------------------------------------------------------------------------
|
|
472
|
+
// toJ
|
|
473
|
+
// --------------------------------------------------------------------------
|
|
474
|
+
describe('toJ', () => {
|
|
475
|
+
it('toJ of infinity gives JPoint at infinity', () => {
|
|
476
|
+
const inf = new Point(null, null)
|
|
477
|
+
const j = inf.toJ()
|
|
478
|
+
expect(j.isInfinity()).toBe(true)
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
it('toJ of valid point gives equivalent JPoint', () => {
|
|
482
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
483
|
+
const j = g.toJ()
|
|
484
|
+
const restored = j.toP()
|
|
485
|
+
expect(restored.eq(g)).toBe(true)
|
|
486
|
+
})
|
|
487
|
+
})
|
|
488
|
+
|
|
489
|
+
// --------------------------------------------------------------------------
|
|
490
|
+
// toJSON with precomputed
|
|
491
|
+
// --------------------------------------------------------------------------
|
|
492
|
+
describe('toJSON with precomputed', () => {
|
|
493
|
+
it('toJSON returns array with precomputed when precomputed is set', () => {
|
|
494
|
+
// Force precomputation by using internal _getNAFPoints
|
|
495
|
+
const g = Point.fromString(G_COMPRESSED)
|
|
496
|
+
// Trigger precomputation via mul (which uses precomputed internally)
|
|
497
|
+
g.mul(new BigNumber(2))
|
|
498
|
+
const json = g.toJSON()
|
|
499
|
+
// Even without precomputed being set externally, should return at least 2 elements
|
|
500
|
+
expect(json.length).toBeGreaterThanOrEqual(2)
|
|
501
|
+
})
|
|
502
|
+
})
|
|
503
|
+
})
|