@bsv/sdk 1.1.22 → 1.1.23
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/compat/BSM.js +10 -3
- package/dist/cjs/src/compat/BSM.js.map +1 -1
- package/dist/cjs/src/primitives/utils.js +39 -1
- package/dist/cjs/src/primitives/utils.js.map +1 -1
- package/dist/cjs/src/script/Spend.js +2 -39
- package/dist/cjs/src/script/Spend.js.map +1 -1
- package/dist/cjs/src/script/templates/P2PKH.js +2 -2
- package/dist/cjs/src/script/templates/P2PKH.js.map +1 -1
- package/dist/cjs/src/script/templates/RPuzzle.js +2 -2
- package/dist/cjs/src/script/templates/RPuzzle.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/compat/BSM.js +10 -3
- package/dist/esm/src/compat/BSM.js.map +1 -1
- package/dist/esm/src/primitives/utils.js +37 -0
- package/dist/esm/src/primitives/utils.js.map +1 -1
- package/dist/esm/src/script/Spend.js +1 -38
- package/dist/esm/src/script/Spend.js.map +1 -1
- package/dist/esm/src/script/templates/P2PKH.js +2 -2
- package/dist/esm/src/script/templates/P2PKH.js.map +1 -1
- package/dist/esm/src/script/templates/RPuzzle.js +2 -2
- package/dist/esm/src/script/templates/RPuzzle.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/compat/BSM.d.ts +3 -2
- package/dist/types/src/compat/BSM.d.ts.map +1 -1
- package/dist/types/src/primitives/utils.d.ts +1 -0
- package/dist/types/src/primitives/utils.d.ts.map +1 -1
- package/dist/types/src/script/Spend.d.ts.map +1 -1
- package/dist/types/src/script/templates/P2PKH.d.ts +1 -1
- package/dist/types/src/script/templates/P2PKH.d.ts.map +1 -1
- package/dist/types/src/script/templates/RPuzzle.d.ts +1 -1
- package/dist/types/src/script/templates/RPuzzle.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/docs/compat.md +10 -4
- package/docs/primitives.md +44 -9
- package/docs/script.md +4 -4
- package/docs/transaction.md +1 -0
- package/package.json +1 -1
- package/src/compat/BSM.ts +10 -3
- package/src/compat/__tests/BSM.test.ts +7 -2
- package/src/primitives/utils.ts +43 -0
- package/src/script/Spend.ts +1 -44
- package/src/script/templates/P2PKH.ts +7 -7
- package/src/script/templates/RPuzzle.ts +8 -8
package/src/compat/BSM.ts
CHANGED
|
@@ -30,11 +30,18 @@ export const magicHash = (messageBuf: number[]): number[] => {
|
|
|
30
30
|
* @deprecated Replaced by BRC-77 which employs BRC-42 key derivation and BRC-43 invoice numbers for enhanced security and privacy.
|
|
31
31
|
* @param message The message to be signed as a number array.
|
|
32
32
|
* @param privateKey The private key used for signing the message.
|
|
33
|
-
* @
|
|
33
|
+
* @param mode The mode of operation. When "base64", the BSM format signature is returned. When "raw", a Signature object is returned. Default: "base64".
|
|
34
|
+
* @returns The signature object when in raw mode, or the BSM base64 string when in base64 mode.
|
|
34
35
|
*/
|
|
35
|
-
export const sign = (message: number[], privateKey: PrivateKey): Signature => {
|
|
36
|
+
export const sign = (message: number[], privateKey: PrivateKey, mode: 'raw' | 'base64' = 'base64'): Signature | string => {
|
|
36
37
|
const hashBuf = magicHash(message)
|
|
37
|
-
|
|
38
|
+
const sig = ECDSA.sign(new BigNumber(hashBuf), privateKey, true)
|
|
39
|
+
if (mode === 'raw') {
|
|
40
|
+
return sig
|
|
41
|
+
}
|
|
42
|
+
const h = new BigNumber(hashBuf)
|
|
43
|
+
const r = sig.CalculateRecoveryFactor(privateKey.toPublicKey(), h)
|
|
44
|
+
return sig.toCompact(r, true, 'base64') as string
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
/**
|
|
@@ -17,16 +17,21 @@ describe('BSM', () => {
|
|
|
17
17
|
const messageBuf = toArray('this is my message', 'utf8')
|
|
18
18
|
const privateKey = new PrivateKey(42)
|
|
19
19
|
it('should return a signature', () => {
|
|
20
|
-
const sig = sign(messageBuf, privateKey).toDER()
|
|
20
|
+
const sig = sign(messageBuf, privateKey, 'raw').toDER()
|
|
21
21
|
expect(sig.length).toEqual(70)
|
|
22
22
|
})
|
|
23
|
+
it('Creates the correct base64 signature', () => {
|
|
24
|
+
const privateKey = PrivateKey.fromWif("L211enC224G1kV8pyyq7bjVd9SxZebnRYEzzM3i7ZHCc1c5E7dQu")
|
|
25
|
+
const sig = sign(toArray('hello world', 'utf8'), privateKey, 'base64')
|
|
26
|
+
expect(sig).toEqual('H4T8Asr0WkC6wYfBESR6pCAfECtdsPM4fwiSQ2qndFi8dVtv/mrOFaySx9xQE7j24ugoJ4iGnsRwAC8QwaoHOXk=')
|
|
27
|
+
})
|
|
23
28
|
})
|
|
24
29
|
describe('verify', () => {
|
|
25
30
|
const messageBuf = toArray('this is my message', 'utf8')
|
|
26
31
|
const privateKey = new PrivateKey(42)
|
|
27
32
|
|
|
28
33
|
it('should verify a signed message', () => {
|
|
29
|
-
const sig = sign(messageBuf, privateKey)
|
|
34
|
+
const sig = sign(messageBuf, privateKey, 'raw')
|
|
30
35
|
expect(verify(messageBuf, sig, privateKey.toPublicKey())).toEqual(true)
|
|
31
36
|
})
|
|
32
37
|
it('Should verify a signed message in base64', () => {
|
package/src/primitives/utils.ts
CHANGED
|
@@ -662,3 +662,46 @@ export class Reader {
|
|
|
662
662
|
}
|
|
663
663
|
}
|
|
664
664
|
}
|
|
665
|
+
|
|
666
|
+
export const minimallyEncode = (buf: number[]): number[] => {
|
|
667
|
+
if (buf.length === 0) {
|
|
668
|
+
return buf
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// If the last byte is not 0x00 or 0x80, we are minimally encoded.
|
|
672
|
+
const last = buf[buf.length - 1]
|
|
673
|
+
if ((last & 0x7f) !== 0) {
|
|
674
|
+
return buf
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// If the script is one byte long, then we have a zero, which encodes as an
|
|
678
|
+
// empty array.
|
|
679
|
+
if (buf.length === 1) {
|
|
680
|
+
return []
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// If the next byte has it sign bit set, then we are minimaly encoded.
|
|
684
|
+
if ((buf[buf.length - 2] & 0x80) !== 0) {
|
|
685
|
+
return buf
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// We are not minimally encoded, we need to figure out how much to trim.
|
|
689
|
+
for (let i = buf.length - 1; i > 0; i--) {
|
|
690
|
+
// We found a non zero byte, time to encode.
|
|
691
|
+
if (buf[i - 1] !== 0) {
|
|
692
|
+
if ((buf[i - 1] & 0x80) !== 0) {
|
|
693
|
+
// We found a byte with it sign bit set so we need one more
|
|
694
|
+
// byte.
|
|
695
|
+
buf[i++] = last
|
|
696
|
+
} else {
|
|
697
|
+
// the sign bit is clear, we can use it.
|
|
698
|
+
buf[i - 1] |= last
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
return buf.slice(0, i)
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// If we found the whole thing is zeros, then we have a zero.
|
|
706
|
+
return []
|
|
707
|
+
}
|
package/src/script/Spend.ts
CHANGED
|
@@ -4,7 +4,7 @@ import Script from './Script.js'
|
|
|
4
4
|
import BigNumber from '../primitives/BigNumber.js'
|
|
5
5
|
import OP from './OP.js'
|
|
6
6
|
import ScriptChunk from './ScriptChunk.js'
|
|
7
|
-
import { toHex } from '../primitives/utils.js'
|
|
7
|
+
import { toHex, minimallyEncode } from '../primitives/utils.js'
|
|
8
8
|
import * as Hash from '../primitives/Hash.js'
|
|
9
9
|
import TransactionSignature from '../primitives/TransactionSignature.js'
|
|
10
10
|
import PublicKey from '../primitives/PublicKey.js'
|
|
@@ -206,49 +206,6 @@ export default class Spend {
|
|
|
206
206
|
return true
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
-
const minimallyEncode = (buf: number[]): number[] => {
|
|
210
|
-
if (buf.length === 0) {
|
|
211
|
-
return buf
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// If the last byte is not 0x00 or 0x80, we are minimally encoded.
|
|
215
|
-
const last = buf[buf.length - 1]
|
|
216
|
-
if ((last & 0x7f) !== 0) {
|
|
217
|
-
return buf
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// If the script is one byte long, then we have a zero, which encodes as an
|
|
221
|
-
// empty array.
|
|
222
|
-
if (buf.length === 1) {
|
|
223
|
-
return []
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// If the next byte has it sign bit set, then we are minimaly encoded.
|
|
227
|
-
if ((buf[buf.length - 2] & 0x80) !== 0) {
|
|
228
|
-
return buf
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// We are not minimally encoded, we need to figure out how much to trim.
|
|
232
|
-
for (let i = buf.length - 1; i > 0; i--) {
|
|
233
|
-
// We found a non zero byte, time to encode.
|
|
234
|
-
if (buf[i - 1] !== 0) {
|
|
235
|
-
if ((buf[i - 1] & 0x80) !== 0) {
|
|
236
|
-
// We found a byte with it sign bit set so we need one more
|
|
237
|
-
// byte.
|
|
238
|
-
buf[i++] = last
|
|
239
|
-
} else {
|
|
240
|
-
// the sign bit is clear, we can use it.
|
|
241
|
-
buf[i - 1] |= last
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return buf.slice(0, i)
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// If we found the whole thing is zeros, then we have a zero.
|
|
249
|
-
return []
|
|
250
|
-
}
|
|
251
|
-
|
|
252
209
|
const padDataToSize = (buf: number[], len: number): number[] => {
|
|
253
210
|
let b = buf
|
|
254
211
|
while (b.length < len) {
|
|
@@ -21,7 +21,7 @@ export default class P2PKH implements ScriptTemplate {
|
|
|
21
21
|
* @param {number[] | string} pubkeyhash or address - An array or address representing the public key hash.
|
|
22
22
|
* @returns {LockingScript} - A P2PKH locking script.
|
|
23
23
|
*/
|
|
24
|
-
lock
|
|
24
|
+
lock(pubkeyhash: string | number[]): LockingScript {
|
|
25
25
|
let data: number[]
|
|
26
26
|
if (typeof pubkeyhash === 'string') {
|
|
27
27
|
const hash = fromBase58Check(pubkeyhash)
|
|
@@ -54,16 +54,16 @@ export default class P2PKH implements ScriptTemplate {
|
|
|
54
54
|
* @param {Script} lockingScript - Optional. The lockinScript. Otherwise the input.sourceTransaction is required.
|
|
55
55
|
* @returns {Object} - An object containing the `sign` and `estimateLength` functions.
|
|
56
56
|
*/
|
|
57
|
-
unlock
|
|
57
|
+
unlock(
|
|
58
58
|
privateKey: PrivateKey,
|
|
59
59
|
signOutputs: 'all' | 'none' | 'single' = 'all',
|
|
60
60
|
anyoneCanPay: boolean = false,
|
|
61
61
|
sourceSatoshis?: number,
|
|
62
62
|
lockingScript?: Script
|
|
63
63
|
): {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>
|
|
65
|
+
estimateLength: () => Promise<108>
|
|
66
|
+
} {
|
|
67
67
|
return {
|
|
68
68
|
sign: async (tx: Transaction, inputIndex: number) => {
|
|
69
69
|
let signatureScope = TransactionSignature.SIGHASH_FORKID
|
|
@@ -130,9 +130,9 @@ export default class P2PKH implements ScriptTemplate {
|
|
|
130
130
|
])
|
|
131
131
|
},
|
|
132
132
|
estimateLength: async () => {
|
|
133
|
-
// public key (1+33) + signature (1+
|
|
133
|
+
// public key (1+33) + signature (1+73)
|
|
134
134
|
// Note: We add 1 to each element's length because of the associated OP_PUSH
|
|
135
|
-
return
|
|
135
|
+
return 108
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
}
|
|
@@ -23,7 +23,7 @@ export default class RPuzzle implements ScriptTemplate {
|
|
|
23
23
|
*
|
|
24
24
|
* @param {'raw'|'SHA1'|'SHA256'|'HASH256'|'RIPEMD160'|'HASH160'} type Denotes the type of puzzle to create
|
|
25
25
|
*/
|
|
26
|
-
constructor
|
|
26
|
+
constructor(type: 'raw' | 'SHA1' | 'SHA256' | 'HASH256' | 'RIPEMD160' | 'HASH160' = 'raw') {
|
|
27
27
|
this.type = type
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -33,7 +33,7 @@ export default class RPuzzle implements ScriptTemplate {
|
|
|
33
33
|
* @param {number[]} value - An array representing the R value or its hash.
|
|
34
34
|
* @returns {LockingScript} - An R puzzle locking script.
|
|
35
35
|
*/
|
|
36
|
-
lock
|
|
36
|
+
lock(value: number[]): LockingScript {
|
|
37
37
|
const chunks: ScriptChunk[] = [
|
|
38
38
|
{ op: OP.OP_OVER },
|
|
39
39
|
{ op: OP.OP_3 },
|
|
@@ -70,15 +70,15 @@ export default class RPuzzle implements ScriptTemplate {
|
|
|
70
70
|
* @param {boolean} anyoneCanPay - Flag indicating if the signature allows for other inputs to be added later.
|
|
71
71
|
* @returns {Object} - An object containing the `sign` and `estimateLength` functions.
|
|
72
72
|
*/
|
|
73
|
-
unlock
|
|
73
|
+
unlock(
|
|
74
74
|
k: BigNumber,
|
|
75
75
|
privateKey: PrivateKey,
|
|
76
76
|
signOutputs: 'all' | 'none' | 'single' = 'all',
|
|
77
77
|
anyoneCanPay: boolean = false
|
|
78
78
|
): {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>
|
|
80
|
+
estimateLength: () => Promise<108>
|
|
81
|
+
} {
|
|
82
82
|
return {
|
|
83
83
|
sign: async (tx: Transaction, inputIndex: number) => {
|
|
84
84
|
if (typeof privateKey === 'undefined') {
|
|
@@ -131,9 +131,9 @@ export default class RPuzzle implements ScriptTemplate {
|
|
|
131
131
|
])
|
|
132
132
|
},
|
|
133
133
|
estimateLength: async () => {
|
|
134
|
-
// public key (1+33) + signature (1+
|
|
134
|
+
// public key (1+33) + signature (1+73)
|
|
135
135
|
// Note: We add 1 to each element's length because of the associated OP_PUSH
|
|
136
|
-
return
|
|
136
|
+
return 108
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
}
|