@bsv/sdk 1.8.13 → 1.9.1

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.
Files changed (89) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/dist/cjs/src/auth/Peer.js +35 -16
  3. package/dist/cjs/src/auth/Peer.js.map +1 -1
  4. package/dist/cjs/src/kvstore/LocalKVStore.js +7 -7
  5. package/dist/cjs/src/kvstore/LocalKVStore.js.map +1 -1
  6. package/dist/cjs/src/primitives/BigNumber.js +43 -31
  7. package/dist/cjs/src/primitives/BigNumber.js.map +1 -1
  8. package/dist/cjs/src/primitives/Hash.js +11 -5
  9. package/dist/cjs/src/primitives/Hash.js.map +1 -1
  10. package/dist/cjs/src/primitives/SymmetricKey.js +15 -6
  11. package/dist/cjs/src/primitives/SymmetricKey.js.map +1 -1
  12. package/dist/cjs/src/primitives/TransactionSignature.js +60 -18
  13. package/dist/cjs/src/primitives/TransactionSignature.js.map +1 -1
  14. package/dist/cjs/src/primitives/utils.js +74 -28
  15. package/dist/cjs/src/primitives/utils.js.map +1 -1
  16. package/dist/cjs/src/script/Script.js +217 -108
  17. package/dist/cjs/src/script/Script.js.map +1 -1
  18. package/dist/cjs/src/script/Spend.js +5 -2
  19. package/dist/cjs/src/script/Spend.js.map +1 -1
  20. package/dist/cjs/src/transaction/Beef.js +62 -7
  21. package/dist/cjs/src/transaction/Beef.js.map +1 -1
  22. package/dist/cjs/src/transaction/BeefTx.js +1 -1
  23. package/dist/cjs/src/transaction/BeefTx.js.map +1 -1
  24. package/dist/cjs/src/transaction/Transaction.js +67 -35
  25. package/dist/cjs/src/transaction/Transaction.js.map +1 -1
  26. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  27. package/dist/esm/src/auth/Peer.js +36 -16
  28. package/dist/esm/src/auth/Peer.js.map +1 -1
  29. package/dist/esm/src/kvstore/LocalKVStore.js +7 -7
  30. package/dist/esm/src/kvstore/LocalKVStore.js.map +1 -1
  31. package/dist/esm/src/primitives/BigNumber.js +43 -31
  32. package/dist/esm/src/primitives/BigNumber.js.map +1 -1
  33. package/dist/esm/src/primitives/Hash.js +11 -5
  34. package/dist/esm/src/primitives/Hash.js.map +1 -1
  35. package/dist/esm/src/primitives/SymmetricKey.js +15 -6
  36. package/dist/esm/src/primitives/SymmetricKey.js.map +1 -1
  37. package/dist/esm/src/primitives/TransactionSignature.js +60 -18
  38. package/dist/esm/src/primitives/TransactionSignature.js.map +1 -1
  39. package/dist/esm/src/primitives/utils.js +74 -28
  40. package/dist/esm/src/primitives/utils.js.map +1 -1
  41. package/dist/esm/src/script/Script.js +222 -110
  42. package/dist/esm/src/script/Script.js.map +1 -1
  43. package/dist/esm/src/script/Spend.js +6 -2
  44. package/dist/esm/src/script/Spend.js.map +1 -1
  45. package/dist/esm/src/transaction/Beef.js +64 -7
  46. package/dist/esm/src/transaction/Beef.js.map +1 -1
  47. package/dist/esm/src/transaction/BeefTx.js +1 -1
  48. package/dist/esm/src/transaction/BeefTx.js.map +1 -1
  49. package/dist/esm/src/transaction/Transaction.js +69 -35
  50. package/dist/esm/src/transaction/Transaction.js.map +1 -1
  51. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  52. package/dist/types/src/auth/Peer.d.ts +4 -0
  53. package/dist/types/src/auth/Peer.d.ts.map +1 -1
  54. package/dist/types/src/primitives/BigNumber.d.ts.map +1 -1
  55. package/dist/types/src/primitives/Hash.d.ts +10 -10
  56. package/dist/types/src/primitives/Hash.d.ts.map +1 -1
  57. package/dist/types/src/primitives/SymmetricKey.d.ts.map +1 -1
  58. package/dist/types/src/primitives/TransactionSignature.d.ts +34 -13
  59. package/dist/types/src/primitives/TransactionSignature.d.ts.map +1 -1
  60. package/dist/types/src/primitives/utils.d.ts +6 -8
  61. package/dist/types/src/primitives/utils.d.ts.map +1 -1
  62. package/dist/types/src/script/Script.d.ts +18 -9
  63. package/dist/types/src/script/Script.d.ts.map +1 -1
  64. package/dist/types/src/script/Spend.d.ts +1 -0
  65. package/dist/types/src/script/Spend.d.ts.map +1 -1
  66. package/dist/types/src/transaction/Beef.d.ts +9 -0
  67. package/dist/types/src/transaction/Beef.d.ts.map +1 -1
  68. package/dist/types/src/transaction/Transaction.d.ts +7 -0
  69. package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
  70. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  71. package/dist/umd/bundle.js +3 -3
  72. package/dist/umd/bundle.js.map +1 -1
  73. package/docs/reference/primitives.md +87 -37
  74. package/docs/reference/script.md +11 -7
  75. package/docs/reference/transaction.md +2 -0
  76. package/package.json +1 -1
  77. package/src/auth/Peer.ts +44 -18
  78. package/src/kvstore/LocalKVStore.ts +7 -7
  79. package/src/kvstore/__tests/LocalKVStore.test.ts +17 -17
  80. package/src/primitives/BigNumber.ts +44 -23
  81. package/src/primitives/Hash.ts +41 -17
  82. package/src/primitives/SymmetricKey.ts +15 -6
  83. package/src/primitives/TransactionSignature.ts +77 -31
  84. package/src/primitives/utils.ts +80 -30
  85. package/src/script/Script.ts +238 -104
  86. package/src/script/Spend.ts +7 -3
  87. package/src/transaction/Beef.ts +74 -7
  88. package/src/transaction/BeefTx.ts +1 -1
  89. package/src/transaction/Transaction.ts +77 -34
@@ -1,6 +1,19 @@
1
1
  // @ts-nocheck
2
2
  import ReductionContext from './ReductionContext.js'
3
3
 
4
+ const BufferCtor =
5
+ typeof globalThis !== 'undefined' ? (globalThis as any).Buffer : undefined
6
+ const CAN_USE_BUFFER =
7
+ BufferCtor != null && typeof BufferCtor.from === 'function'
8
+ const HEX_CHAR_TO_VALUE = new Int8Array(256).fill(-1)
9
+ for (let i = 0; i < 10; i++) {
10
+ HEX_CHAR_TO_VALUE[48 + i] = i // '0'-'9'
11
+ }
12
+ for (let i = 0; i < 6; i++) {
13
+ HEX_CHAR_TO_VALUE[65 + i] = 10 + i // 'A'-'F'
14
+ HEX_CHAR_TO_VALUE[97 + i] = 10 + i // 'a'-'f'
15
+ }
16
+
4
17
  /**
5
18
  * JavaScript numbers are only precise up to 53 bits. Since Bitcoin relies on
6
19
  * 256-bit cryptography, this BigNumber class enables operations on larger
@@ -1059,31 +1072,30 @@ export default class BigNumber {
1059
1072
  static fromSm (bytes: number[], endian: 'big' | 'little' = 'big'): BigNumber {
1060
1073
  if (bytes.length === 0) return new BigNumber(0n)
1061
1074
 
1075
+ const beBytes = bytes.slice()
1076
+ if (endian === 'little') {
1077
+ beBytes.reverse()
1078
+ }
1062
1079
  let sign: 0 | 1 = 0
1063
- let hex = ''
1080
+ if (beBytes.length > 0 && (beBytes[0] & 0x80) !== 0) {
1081
+ sign = 1
1082
+ beBytes[0] &= 0x7f
1083
+ }
1064
1084
 
1065
- if (endian === 'little') {
1066
- const last = bytes.length - 1
1067
- let firstByte = bytes[last]
1068
- if ((firstByte & 0x80) !== 0) { sign = 1; firstByte &= 0x7f }
1069
- hex += (firstByte < 16 ? '0' : '') + firstByte.toString(16)
1070
- for (let i = last - 1; i >= 0; i--) {
1071
- const b = bytes[i]
1072
- hex += (b < 16 ? '0' : '') + b.toString(16)
1073
- }
1085
+ let magnitude = 0n
1086
+ if (CAN_USE_BUFFER) {
1087
+ const hex = BufferCtor.from(beBytes).toString('hex') as string
1088
+ magnitude = hex.length === 0 ? 0n : BigInt('0x' + hex)
1074
1089
  } else {
1075
- let firstByte = bytes[0]
1076
- if ((firstByte & 0x80) !== 0) { sign = 1; firstByte &= 0x7f }
1077
- hex += (firstByte < 16 ? '0' : '') + firstByte.toString(16)
1078
- for (let i = 1; i < bytes.length; i++) {
1079
- const b = bytes[i]
1080
- hex += (b < 16 ? '0' : '') + b.toString(16)
1090
+ let hex = ''
1091
+ for (const byte of beBytes) {
1092
+ hex += byte < 16 ? '0' + byte.toString(16) : byte.toString(16)
1081
1093
  }
1094
+ magnitude = hex.length === 0 ? 0n : BigInt('0x' + hex)
1082
1095
  }
1083
1096
 
1084
- const mag = hex === '' ? 0n : BigInt('0x' + hex)
1085
1097
  const r = new BigNumber(0n)
1086
- r._initializeState(mag, sign)
1098
+ r._initializeState(magnitude, sign)
1087
1099
  return r
1088
1100
  }
1089
1101
 
@@ -1105,17 +1117,26 @@ export default class BigNumber {
1105
1117
  const byteLen = hex.length / 2
1106
1118
  const bytes = new Array(byteLen)
1107
1119
  for (let i = 0, j = 0; i < hex.length; i += 2) {
1108
- bytes[j++] = parseInt(hex.slice(i, i + 2), 16)
1120
+ const high = HEX_CHAR_TO_VALUE[hex.charCodeAt(i)]
1121
+ const low = HEX_CHAR_TO_VALUE[hex.charCodeAt(i + 1)]
1122
+ bytes[j++] = ((high & 0xf) << 4) | (low & 0xf)
1109
1123
  }
1110
1124
 
1125
+ let result: number[]
1111
1126
  if (this._sign === 1) {
1112
- if ((bytes[0] & 0x80) !== 0) bytes.unshift(0x80)
1113
- else bytes[0] |= 0x80
1127
+ if ((bytes[0] & 0x80) !== 0) {
1128
+ result = [0x80, ...bytes]
1129
+ } else {
1130
+ result = bytes.slice()
1131
+ result[0] |= 0x80
1132
+ }
1114
1133
  } else if ((bytes[0] & 0x80) !== 0) {
1115
- bytes.unshift(0x00)
1134
+ result = [0x00, ...bytes]
1135
+ } else {
1136
+ result = bytes.slice()
1116
1137
  }
1117
1138
 
1118
- return endian === 'little' ? bytes.reverse() : bytes
1139
+ return endian === 'little' ? result.reverse() : result
1119
1140
  }
1120
1141
 
1121
1142
  /**
@@ -613,8 +613,12 @@ export class SHA256 {
613
613
  this.h = new FastSHA256()
614
614
  }
615
615
 
616
- update (msg: number[] | string, enc?: 'hex' | 'utf8'): this {
617
- const data = Uint8Array.from(toArray(msg, enc))
616
+ update (
617
+ msg: Uint8Array | number[] | string,
618
+ enc?: 'hex' | 'utf8'
619
+ ): this {
620
+ const data =
621
+ msg instanceof Uint8Array ? msg : Uint8Array.from(toArray(msg, enc))
618
622
  this.h.update(data)
619
623
  return this
620
624
  }
@@ -778,8 +782,16 @@ export class SHA256HMAC {
778
782
  * @example
779
783
  * const myHMAC = new SHA256HMAC('deadbeef');
780
784
  */
781
- constructor (key: number[] | string) {
782
- const k = Uint8Array.from(toArray(key, 'hex'))
785
+ constructor (key: Uint8Array | number[] | string) {
786
+ const k =
787
+ key instanceof Uint8Array
788
+ ? key
789
+ : Uint8Array.from(
790
+ toArray(
791
+ key,
792
+ typeof key === 'string' ? 'hex' : undefined
793
+ )
794
+ )
783
795
  this.h = new HMAC(sha256Fast, k)
784
796
  }
785
797
 
@@ -794,8 +806,10 @@ export class SHA256HMAC {
794
806
  * @example
795
807
  * myHMAC.update('deadbeef', 'hex');
796
808
  */
797
- update (msg: number[] | string, enc?: 'hex'): SHA256HMAC {
798
- this.h.update(Uint8Array.from(toArray(msg, enc)))
809
+ update (msg: Uint8Array | number[] | string, enc?: 'hex'): SHA256HMAC {
810
+ const data =
811
+ msg instanceof Uint8Array ? msg : Uint8Array.from(toArray(msg, enc))
812
+ this.h.update(data)
799
813
  return this
800
814
  }
801
815
 
@@ -902,8 +916,16 @@ export class SHA512HMAC {
902
916
  * @example
903
917
  * const myHMAC = new SHA512HMAC('deadbeef');
904
918
  */
905
- constructor (key: number[] | string) {
906
- const k = Uint8Array.from(toArray(key, 'hex'))
919
+ constructor (key: Uint8Array | number[] | string) {
920
+ const k =
921
+ key instanceof Uint8Array
922
+ ? key
923
+ : Uint8Array.from(
924
+ toArray(
925
+ key,
926
+ typeof key === 'string' ? 'hex' : undefined
927
+ )
928
+ )
907
929
  this.h = new HMAC(sha512Fast, k)
908
930
  }
909
931
 
@@ -918,8 +940,10 @@ export class SHA512HMAC {
918
940
  * @example
919
941
  * myHMAC.update('deadbeef', 'hex');
920
942
  */
921
- update (msg: number[] | string, enc?: 'hex' | 'utf8'): SHA512HMAC {
922
- this.h.update(Uint8Array.from(toArray(msg, enc)))
943
+ update (msg: Uint8Array | number[] | string, enc?: 'hex' | 'utf8'): SHA512HMAC {
944
+ const data =
945
+ msg instanceof Uint8Array ? msg : Uint8Array.from(toArray(msg, enc))
946
+ this.h.update(data)
923
947
  return this
924
948
  }
925
949
 
@@ -998,7 +1022,7 @@ export const sha1 = (
998
1022
  * const digest = sha256('Hello, world!');
999
1023
  */
1000
1024
  export const sha256 = (
1001
- msg: number[] | string,
1025
+ msg: Uint8Array | number[] | string,
1002
1026
  enc?: 'hex' | 'utf8'
1003
1027
  ): number[] => {
1004
1028
  return new SHA256().update(msg, enc).digest()
@@ -1036,7 +1060,7 @@ export const sha512 = (
1036
1060
  * const doubleHash = hash256('Hello, world!');
1037
1061
  */
1038
1062
  export const hash256 = (
1039
- msg: number[] | string,
1063
+ msg: Uint8Array | number[] | string,
1040
1064
  enc?: 'hex' | 'utf8'
1041
1065
  ): number[] => {
1042
1066
  const first = new SHA256().update(msg, enc).digest()
@@ -1056,7 +1080,7 @@ export const hash256 = (
1056
1080
  * const hash = hash160('Hello, world!');
1057
1081
  */
1058
1082
  export const hash160 = (
1059
- msg: number[] | string,
1083
+ msg: Uint8Array | number[] | string,
1060
1084
  enc?: 'hex' | 'utf8'
1061
1085
  ): number[] => {
1062
1086
  const first = new SHA256().update(msg, enc).digest()
@@ -1076,8 +1100,8 @@ export const hash160 = (
1076
1100
  * const digest = sha256hmac('deadbeef', 'ffff001d');
1077
1101
  */
1078
1102
  export const sha256hmac = (
1079
- key: number[] | string,
1080
- msg: number[] | string,
1103
+ key: Uint8Array | number[] | string,
1104
+ msg: Uint8Array | number[] | string,
1081
1105
  enc?: 'hex'
1082
1106
  ): number[] => {
1083
1107
  return new SHA256HMAC(key).update(msg, enc).digest()
@@ -1096,8 +1120,8 @@ export const sha256hmac = (
1096
1120
  * const digest = sha512hmac('deadbeef', 'ffff001d');
1097
1121
  */
1098
1122
  export const sha512hmac = (
1099
- key: number[] | string,
1100
- msg: number[] | string,
1123
+ key: Uint8Array | number[] | string,
1124
+ msg: Uint8Array | number[] | string,
1101
1125
  enc?: 'hex'
1102
1126
  ): number[] => {
1103
1127
  return new SHA512HMAC(key).update(msg, enc).digest()
@@ -43,13 +43,22 @@ export default class SymmetricKey extends BigNumber {
43
43
  encrypt (msg: number[] | string, enc?: 'hex'): string | number[] {
44
44
  const iv = Random(32)
45
45
  msg = toArray(msg, enc)
46
+ const keyBytes = this.toArray('be', 32)
46
47
  const { result, authenticationTag } = AESGCM(
47
48
  msg,
48
49
  [],
49
50
  iv,
50
- this.toArray('be', 32)
51
+ keyBytes
51
52
  )
52
- return encode([...iv, ...result, ...authenticationTag], enc)
53
+ const totalLength = iv.length + result.length + authenticationTag.length
54
+ const combined = new Array(totalLength)
55
+ let offset = 0
56
+ for (const chunk of [iv, result, authenticationTag]) {
57
+ for (let i = 0; i < chunk.length; i++) {
58
+ combined[offset++] = chunk[i]
59
+ }
60
+ }
61
+ return encode(combined, enc)
53
62
  }
54
63
 
55
64
  /**
@@ -71,15 +80,15 @@ export default class SymmetricKey extends BigNumber {
71
80
  decrypt (msg: number[] | string, enc?: 'hex' | 'utf8'): string | number[] {
72
81
  msg = toArray(msg, enc)
73
82
  const iv = msg.slice(0, 32)
74
- const ciphertextWithTag = msg.slice(32)
75
- const messageTag = ciphertextWithTag.slice(-16)
76
- const ciphertext = ciphertextWithTag.slice(0, -16)
83
+ const tagStart = msg.length - 16
84
+ const ciphertext = msg.slice(32, tagStart)
85
+ const messageTag = msg.slice(tagStart)
77
86
  const result = AESGCMDecrypt(
78
87
  ciphertext,
79
88
  [],
80
89
  iv,
81
90
  messageTag,
82
- this.toArray()
91
+ this.toArray('be', 32)
83
92
  )
84
93
  if (result === null) {
85
94
  throw new Error('Decryption failed!')
@@ -6,6 +6,30 @@ import Script from '../script/Script.js'
6
6
  import TransactionInput from '../transaction/TransactionInput.js'
7
7
  import TransactionOutput from '../transaction/TransactionOutput.js'
8
8
 
9
+ export interface SignatureHashCache {
10
+ hashPrevouts?: number[]
11
+ hashSequence?: number[]
12
+ hashOutputsAll?: number[]
13
+ hashOutputsSingle?: Map<number, number[]>
14
+ }
15
+
16
+ interface TransactionSignatureFormatParams {
17
+ sourceTXID: string
18
+ sourceOutputIndex: number
19
+ sourceSatoshis: number
20
+ transactionVersion: number
21
+ otherInputs: TransactionInput[]
22
+ outputs: TransactionOutput[]
23
+ inputIndex: number
24
+ subscript: Script
25
+ inputSequence: number
26
+ lockTime: number
27
+ scope: number
28
+ cache?: SignatureHashCache
29
+ }
30
+
31
+ const EMPTY_SCRIPT = new Uint8Array(0)
32
+
9
33
  export default class TransactionSignature extends Signature {
10
34
  public static readonly SIGHASH_ALL = 0x00000001
11
35
  public static readonly SIGHASH_NONE = 0x00000002
@@ -15,19 +39,23 @@ export default class TransactionSignature extends Signature {
15
39
 
16
40
  scope: number
17
41
 
18
- static format (params: {
19
- sourceTXID: string
20
- sourceOutputIndex: number
21
- sourceSatoshis: number
22
- transactionVersion: number
23
- otherInputs: TransactionInput[]
24
- outputs: TransactionOutput[]
25
- inputIndex: number
26
- subscript: Script
27
- inputSequence: number
28
- lockTime: number
29
- scope: number
30
- }): number[] {
42
+ /**
43
+ * Formats the SIGHASH preimage for the targeted input, optionally using a cache to skip recomputing shared hash prefixes.
44
+ * @param params - Context for the signing input plus transaction metadata.
45
+ * @param params.cache - Optional cache storing previously computed `hashPrevouts`, `hashSequence`, or `hashOutputs*` values; it will be populated if present.
46
+ */
47
+ static format (params: TransactionSignatureFormatParams): number[] {
48
+ return Array.from(this.formatBytes(params))
49
+ }
50
+
51
+ /**
52
+ * Formats the same SIGHASH preimage bytes as `format`, supporting the optional cache for hash reuse.
53
+ * @param params - Context for the signing operation.
54
+ * @param params.cache - Optional `SignatureHashCache` that may already contain hashed prefixes and is populated during formatting.
55
+ * @returns Bytes for signing.
56
+ */
57
+ static formatBytes (params: TransactionSignatureFormatParams): Uint8Array {
58
+ const cache = params.cache
31
59
  const currentInput = {
32
60
  sourceTXID: params.sourceTXID,
33
61
  sourceOutputIndex: params.sourceOutputIndex,
@@ -51,9 +79,7 @@ export default class TransactionSignature extends Signature {
51
79
  writer.writeUInt32LE(input.sourceOutputIndex)
52
80
  }
53
81
 
54
- const buf = writer.toArray()
55
- const ret = Hash.hash256(buf)
56
- return ret
82
+ return Hash.hash256(writer.toUint8Array())
57
83
  }
58
84
 
59
85
  const getSequenceHash = (): number[] => {
@@ -64,9 +90,7 @@ export default class TransactionSignature extends Signature {
64
90
  writer.writeUInt32LE(sequence)
65
91
  }
66
92
 
67
- const buf = writer.toArray()
68
- const ret = Hash.hash256(buf)
69
- return ret
93
+ return Hash.hash256(writer.toUint8Array())
70
94
  }
71
95
 
72
96
  function getOutputsHash (outputIndex?: number): number[] {
@@ -77,7 +101,7 @@ export default class TransactionSignature extends Signature {
77
101
  const satoshis = output.satoshis ?? 0 // Default to 0 if undefined
78
102
  writer.writeUInt64LE(satoshis)
79
103
 
80
- const script = output.lockingScript?.toBinary() ?? []
104
+ const script = output.lockingScript?.toUint8Array() ?? EMPTY_SCRIPT
81
105
  writer.writeVarIntNum(script.length)
82
106
  writer.write(script)
83
107
  }
@@ -91,14 +115,12 @@ export default class TransactionSignature extends Signature {
91
115
  const satoshis = output.satoshis ?? 0 // Default to 0 if undefined
92
116
  writer.writeUInt64LE(satoshis)
93
117
 
94
- const script = output.lockingScript?.toBinary() ?? []
118
+ const script = output.lockingScript?.toUint8Array() ?? EMPTY_SCRIPT
95
119
  writer.writeVarIntNum(script.length)
96
120
  writer.write(script)
97
121
  }
98
122
 
99
- const buf = writer.toArray()
100
- const ret = Hash.hash256(buf)
101
- return ret
123
+ return Hash.hash256(writer.toUint8Array())
102
124
  }
103
125
 
104
126
  let hashPrevouts = new Array(32).fill(0)
@@ -106,7 +128,12 @@ export default class TransactionSignature extends Signature {
106
128
  let hashOutputs = new Array(32).fill(0)
107
129
 
108
130
  if ((params.scope & TransactionSignature.SIGHASH_ANYONECANPAY) === 0) {
109
- hashPrevouts = getPrevoutHash()
131
+ if (cache?.hashPrevouts != null) {
132
+ hashPrevouts = cache.hashPrevouts
133
+ } else {
134
+ hashPrevouts = getPrevoutHash()
135
+ if (cache != null) cache.hashPrevouts = hashPrevouts
136
+ }
110
137
  }
111
138
 
112
139
  if (
@@ -114,19 +141,39 @@ export default class TransactionSignature extends Signature {
114
141
  (params.scope & 31) !== TransactionSignature.SIGHASH_SINGLE &&
115
142
  (params.scope & 31) !== TransactionSignature.SIGHASH_NONE
116
143
  ) {
117
- hashSequence = getSequenceHash()
144
+ if (cache?.hashSequence != null) {
145
+ hashSequence = cache.hashSequence
146
+ } else {
147
+ hashSequence = getSequenceHash()
148
+ if (cache != null) cache.hashSequence = hashSequence
149
+ }
118
150
  }
119
151
 
120
152
  if (
121
153
  (params.scope & 31) !== TransactionSignature.SIGHASH_SINGLE &&
122
154
  (params.scope & 31) !== TransactionSignature.SIGHASH_NONE
123
155
  ) {
124
- hashOutputs = getOutputsHash()
156
+ if (cache?.hashOutputsAll != null) {
157
+ hashOutputs = cache.hashOutputsAll
158
+ } else {
159
+ hashOutputs = getOutputsHash()
160
+ if (cache != null) cache.hashOutputsAll = hashOutputs
161
+ }
125
162
  } else if (
126
163
  (params.scope & 31) === TransactionSignature.SIGHASH_SINGLE &&
127
164
  params.inputIndex < params.outputs.length
128
165
  ) {
129
- hashOutputs = getOutputsHash(params.inputIndex)
166
+ const key = params.inputIndex
167
+ const cachedSingle = cache?.hashOutputsSingle?.get(key)
168
+ if (cachedSingle != null) {
169
+ hashOutputs = cachedSingle
170
+ } else {
171
+ hashOutputs = getOutputsHash(key)
172
+ if (cache != null) {
173
+ if (cache.hashOutputsSingle == null) cache.hashOutputsSingle = new Map()
174
+ cache.hashOutputsSingle.set(key, hashOutputs)
175
+ }
176
+ }
130
177
  }
131
178
 
132
179
  const writer = new Writer()
@@ -143,7 +190,7 @@ export default class TransactionSignature extends Signature {
143
190
  writer.writeUInt32LE(params.sourceOutputIndex)
144
191
 
145
192
  // scriptCode of the input (serialized as scripts inside CTxOuts)
146
- const subscriptBin = params.subscript.toBinary()
193
+ const subscriptBin = params.subscript.toUint8Array()
147
194
  writer.writeVarIntNum(subscriptBin.length)
148
195
  writer.write(subscriptBin)
149
196
 
@@ -163,8 +210,7 @@ export default class TransactionSignature extends Signature {
163
210
  // sighashType
164
211
  writer.writeUInt32LE(params.scope >>> 0)
165
212
 
166
- const buf = writer.toArray()
167
- return buf
213
+ return writer.toUint8Array()
168
214
  }
169
215
 
170
216
  // The format used in a tx
@@ -1,6 +1,12 @@
1
1
  import BigNumber from './BigNumber.js'
2
2
  import { hash256 } from './Hash.js'
3
3
 
4
+ const BufferCtor =
5
+ typeof globalThis !== 'undefined' ? (globalThis as any).Buffer : undefined
6
+ const CAN_USE_BUFFER =
7
+ BufferCtor != null && typeof BufferCtor.from === 'function'
8
+ const PURE_HEX_REGEX = /^[0-9a-fA-F]+$/
9
+
4
10
  /**
5
11
  * Prepends a '0' to an odd character length word to ensure it has an even number of characters.
6
12
  * @param {string} word - The input word.
@@ -19,12 +25,23 @@ export const zero2 = (word: string): string => {
19
25
  * @param {number[]} msg - The input array of numbers.
20
26
  * @returns {string} - The hexadecimal string representation of the input array.
21
27
  */
28
+ const HEX_DIGITS = '0123456789abcdef'
29
+ const HEX_BYTE_STRINGS: string[] = new Array(256)
30
+ for (let i = 0; i < 256; i++) {
31
+ HEX_BYTE_STRINGS[i] =
32
+ HEX_DIGITS[(i >> 4) & 0xf] + HEX_DIGITS[i & 0xf]
33
+ }
34
+
22
35
  export const toHex = (msg: number[]): string => {
23
- let res = ''
24
- for (const num of msg) {
25
- res += zero2(num.toString(16))
36
+ if (CAN_USE_BUFFER) {
37
+ return BufferCtor.from(msg).toString('hex')
26
38
  }
27
- return res
39
+ if (msg.length === 0) return ''
40
+ const out = new Array(msg.length)
41
+ for (let i = 0; i < msg.length; i++) {
42
+ out[i] = HEX_BYTE_STRINGS[msg[i] & 0xff]
43
+ }
44
+ return out.join('')
28
45
  }
29
46
 
30
47
  /**
@@ -53,13 +70,37 @@ export const toArray = (msg: any, enc?: 'hex' | 'utf8' | 'base64'): any[] => {
53
70
  }
54
71
  }
55
72
 
73
+ const HEX_CHAR_TO_VALUE = new Int8Array(256).fill(-1)
74
+ for (let i = 0; i < 10; i++) {
75
+ HEX_CHAR_TO_VALUE[48 + i] = i // '0'-'9'
76
+ }
77
+ for (let i = 0; i < 6; i++) {
78
+ HEX_CHAR_TO_VALUE[65 + i] = 10 + i // 'A'-'F'
79
+ HEX_CHAR_TO_VALUE[97 + i] = 10 + i // 'a'-'f'
80
+ }
81
+
56
82
  const hexToArray = (msg: string): number[] => {
57
- msg = msg.replace(/[^a-z0-9]+/gi, '')
58
- if (msg.length % 2 !== 0) msg = '0' + msg
59
- const res: number[] = []
60
- for (let i = 0; i < msg.length; i += 2) {
61
- res.push(parseInt(msg[i] + msg[i + 1], 16))
83
+ if (CAN_USE_BUFFER && PURE_HEX_REGEX.test(msg)) {
84
+ const normalized = msg.length % 2 === 0 ? msg : '0' + msg
85
+ return Array.from(BufferCtor.from(normalized, 'hex'))
86
+ }
87
+ const res: number[] = new Array(Math.ceil(msg.length / 2))
88
+ let nibble = -1
89
+ let size = 0
90
+ for (let i = 0; i < msg.length; i++) {
91
+ const value = HEX_CHAR_TO_VALUE[msg.charCodeAt(i)]
92
+ if (value === -1) continue
93
+ if (nibble === -1) {
94
+ nibble = value
95
+ } else {
96
+ res[size++] = (nibble << 4) | value
97
+ nibble = -1
98
+ }
62
99
  }
100
+ if (nibble !== -1) {
101
+ res[size++] = nibble
102
+ }
103
+ if (size !== res.length) res.length = size
63
104
  return res
64
105
  }
65
106
 
@@ -377,11 +418,13 @@ export const fromBase58Check = (
377
418
  return { prefix, data }
378
419
  }
379
420
 
421
+ type WriterChunk = readonly number[] | Uint8Array
422
+
380
423
  export class Writer {
381
- public bufs: number[][]
424
+ public bufs: WriterChunk[]
382
425
  private length: number
383
426
 
384
- constructor (bufs?: number[][]) {
427
+ constructor (bufs?: WriterChunk[]) {
385
428
  this.bufs = bufs !== undefined ? bufs : []
386
429
  this.length = 0
387
430
  for (const b of this.bufs) this.length += b.length
@@ -391,19 +434,36 @@ export class Writer {
391
434
  return this.length
392
435
  }
393
436
 
437
+ toUint8Array (): Uint8Array {
438
+ const out = new Uint8Array(this.length)
439
+ let offset = 0
440
+ for (const buf of this.bufs) {
441
+ out.set(buf, offset)
442
+ offset += buf.length
443
+ }
444
+ return out
445
+ }
446
+
394
447
  toArray (): number[] {
395
448
  const totalLength = this.length
396
449
  const ret = new Array(totalLength)
397
450
  let offset = 0
398
451
  for (const buf of this.bufs) {
399
- for (const value of buf) {
400
- ret[offset++] = value
452
+ if (buf instanceof Uint8Array) {
453
+ for (let i = 0; i < buf.length; i++) {
454
+ ret[offset++] = buf[i]
455
+ }
456
+ } else {
457
+ const arr = buf as number[]
458
+ for (let i = 0; i < arr.length; i++) {
459
+ ret[offset++] = arr[i]
460
+ }
401
461
  }
402
462
  }
403
463
  return ret
404
464
  }
405
465
 
406
- write (buf: number[]): this {
466
+ write (buf: WriterChunk): this {
407
467
  this.bufs.push(buf)
408
468
  this.length += buf.length
409
469
  return this
@@ -414,14 +474,12 @@ export class Writer {
414
474
  for (let i = 0; i < buf2.length; i++) {
415
475
  buf2[i] = buf[buf.length - 1 - i]
416
476
  }
417
- this.bufs.push(buf2)
418
- this.length += buf2.length
419
- return this
477
+ return this.write(buf2)
420
478
  }
421
479
 
422
480
  writeUInt8 (n: number): this {
423
481
  const buf = new Array(1)
424
- buf[0] = n
482
+ buf[0] = n & 0xff
425
483
  this.write(buf)
426
484
  return this
427
485
  }
@@ -438,9 +496,7 @@ export class Writer {
438
496
  (n >> 8) & 0xff, // shift right 8 bits to get the high byte
439
497
  n & 0xff // low byte is just the last 8 bits
440
498
  ]
441
- this.bufs.push(buf)
442
- this.length += 2
443
- return this
499
+ return this.write(buf)
444
500
  }
445
501
 
446
502
  writeInt16BE (n: number): this {
@@ -452,9 +508,7 @@ export class Writer {
452
508
  n & 0xff, // low byte is just the last 8 bits
453
509
  (n >> 8) & 0xff // shift right 8 bits to get the high byte
454
510
  ]
455
- this.bufs.push(buf)
456
- this.length += 2
457
- return this
511
+ return this.write(buf)
458
512
  }
459
513
 
460
514
  writeInt16LE (n: number): this {
@@ -468,9 +522,7 @@ export class Writer {
468
522
  (n >> 8) & 0xff,
469
523
  n & 0xff // lowest byte
470
524
  ]
471
- this.bufs.push(buf)
472
- this.length += 4
473
- return this
525
+ return this.write(buf)
474
526
  }
475
527
 
476
528
  writeInt32BE (n: number): this {
@@ -484,9 +536,7 @@ export class Writer {
484
536
  (n >> 16) & 0xff,
485
537
  (n >> 24) & 0xff // highest byte
486
538
  ]
487
- this.bufs.push(buf)
488
- this.length += 4
489
- return this
539
+ return this.write(buf)
490
540
  }
491
541
 
492
542
  writeInt32LE (n: number): this {