ecash-lib 1.2.2-rc9 → 1.4.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.
Files changed (125) hide show
  1. package/README.md +2 -1
  2. package/dist/ecc.d.ts +12 -0
  3. package/dist/ecc.d.ts.map +1 -1
  4. package/dist/ecc.js +9 -47
  5. package/dist/ecc.js.map +1 -1
  6. package/dist/ffi/ecash_lib_wasm_bg_browser.wasm +0 -0
  7. package/dist/ffi/ecash_lib_wasm_bg_browser.wasm.d.ts +18 -4
  8. package/dist/ffi/ecash_lib_wasm_bg_nodejs.wasm +0 -0
  9. package/dist/ffi/ecash_lib_wasm_bg_nodejs.wasm.d.ts +18 -4
  10. package/dist/ffi/ecash_lib_wasm_browser.d.ts +95 -4
  11. package/dist/ffi/ecash_lib_wasm_browser.js +245 -14
  12. package/dist/ffi/ecash_lib_wasm_nodejs.d.ts +77 -0
  13. package/dist/ffi/ecash_lib_wasm_nodejs.js +247 -14
  14. package/dist/hash.d.ts +21 -1
  15. package/dist/hash.d.ts.map +1 -1
  16. package/dist/hash.js +16 -10
  17. package/dist/hash.js.map +1 -1
  18. package/dist/hdwallet.d.ts +33 -0
  19. package/dist/hdwallet.d.ts.map +1 -0
  20. package/dist/hdwallet.js +148 -0
  21. package/dist/hdwallet.js.map +1 -0
  22. package/dist/hmac.d.ts +13 -0
  23. package/dist/hmac.d.ts.map +1 -0
  24. package/dist/hmac.js +63 -0
  25. package/dist/hmac.js.map +1 -0
  26. package/dist/index.d.ts +3 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +3 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/initBrowser.d.ts.map +1 -1
  31. package/dist/initBrowser.js +3 -0
  32. package/dist/initBrowser.js.map +1 -1
  33. package/dist/initNodeJs.d.ts.map +1 -1
  34. package/dist/initNodeJs.js +3 -0
  35. package/dist/initNodeJs.js.map +1 -1
  36. package/dist/io/bytes.d.ts +5 -3
  37. package/dist/io/bytes.d.ts.map +1 -1
  38. package/dist/io/bytes.js +15 -7
  39. package/dist/io/bytes.js.map +1 -1
  40. package/dist/io/writer.d.ts +4 -3
  41. package/dist/io/writer.d.ts.map +1 -1
  42. package/dist/io/writerbytes.d.ts +4 -3
  43. package/dist/io/writerbytes.d.ts.map +1 -1
  44. package/dist/io/writerbytes.js +7 -6
  45. package/dist/io/writerbytes.js.map +1 -1
  46. package/dist/io/writerlength.d.ts +4 -3
  47. package/dist/io/writerlength.d.ts.map +1 -1
  48. package/dist/io/writerlength.js +3 -3
  49. package/dist/io/writerlength.js.map +1 -1
  50. package/dist/mnemonic.d.ts +14 -0
  51. package/dist/mnemonic.d.ts.map +1 -0
  52. package/dist/mnemonic.js +123 -0
  53. package/dist/mnemonic.js.map +1 -0
  54. package/dist/pbkdf2.d.ts +11 -0
  55. package/dist/pbkdf2.d.ts.map +1 -0
  56. package/dist/pbkdf2.js +36 -0
  57. package/dist/pbkdf2.js.map +1 -0
  58. package/dist/script.d.ts +4 -0
  59. package/dist/script.d.ts.map +1 -1
  60. package/dist/script.js +6 -0
  61. package/dist/script.js.map +1 -1
  62. package/package.json +1 -1
  63. package/src/address/address.ts +346 -0
  64. package/src/address/legacyaddr.ts +129 -0
  65. package/src/consts.ts +8 -0
  66. package/src/ecc.ts +61 -0
  67. package/src/ffi/ecash_lib_wasm_bg_browser.wasm +0 -0
  68. package/src/ffi/ecash_lib_wasm_bg_browser.wasm.d.ts +32 -0
  69. package/src/ffi/ecash_lib_wasm_bg_nodejs.wasm +0 -0
  70. package/src/ffi/ecash_lib_wasm_bg_nodejs.wasm.d.ts +32 -0
  71. package/src/ffi/ecash_lib_wasm_browser.d.ts +183 -0
  72. package/src/ffi/ecash_lib_wasm_browser.js +571 -0
  73. package/src/ffi/ecash_lib_wasm_nodejs.d.ts +127 -0
  74. package/src/ffi/ecash_lib_wasm_nodejs.js +498 -0
  75. package/src/hash.ts +46 -0
  76. package/src/hdwallet.ts +181 -0
  77. package/src/hmac.ts +74 -0
  78. package/src/index.ts +29 -0
  79. package/src/indexBrowser.ts +6 -0
  80. package/src/indexNodeJs.ts +6 -0
  81. package/src/initBrowser.ts +21 -0
  82. package/src/initNodeJs.ts +20 -0
  83. package/src/io/bytes.ts +80 -0
  84. package/src/io/hex.ts +69 -0
  85. package/src/io/int.ts +6 -0
  86. package/src/io/str.ts +16 -0
  87. package/src/io/varsize.ts +49 -0
  88. package/src/io/writer.ts +20 -0
  89. package/src/io/writerbytes.ts +87 -0
  90. package/src/io/writerlength.ts +44 -0
  91. package/src/mnemonic.ts +153 -0
  92. package/src/op.ts +162 -0
  93. package/src/opcode.ts +154 -0
  94. package/src/pbkdf2.ts +52 -0
  95. package/src/script.ts +195 -0
  96. package/src/sigHashType.ts +190 -0
  97. package/src/test/testRunner.ts +209 -0
  98. package/src/token/alp.ts +146 -0
  99. package/src/token/common.ts +32 -0
  100. package/src/token/empp.ts +29 -0
  101. package/src/token/slp.ts +212 -0
  102. package/src/tx.ts +180 -0
  103. package/src/txBuilder.ts +262 -0
  104. package/src/unsignedTx.ts +359 -0
  105. package/tsconfig.json +2 -1
  106. package/wordlists/english.json +2053 -0
  107. package/.nyc_output/0fc40ca6-d52c-45eb-b31b-2601ce70b887.json +0 -1
  108. package/.nyc_output/ac5be6db-4e40-41f8-8b84-7598d4747e57.json +0 -1
  109. package/.nyc_output/b316d46f-5ea0-4e98-884a-bfbf9cc1d0f8.json +0 -1
  110. package/.nyc_output/f965566b-9422-4874-b45e-9eefda9c769c.json +0 -1
  111. package/.nyc_output/processinfo/0fc40ca6-d52c-45eb-b31b-2601ce70b887.json +0 -1
  112. package/.nyc_output/processinfo/ac5be6db-4e40-41f8-8b84-7598d4747e57.json +0 -1
  113. package/.nyc_output/processinfo/b316d46f-5ea0-4e98-884a-bfbf9cc1d0f8.json +0 -1
  114. package/.nyc_output/processinfo/f965566b-9422-4874-b45e-9eefda9c769c.json +0 -1
  115. package/.nyc_output/processinfo/index.json +0 -1
  116. package/dist/address/cashaddr.d.ts +0 -78
  117. package/dist/address/cashaddr.d.ts.map +0 -1
  118. package/dist/address/cashaddr.js +0 -543
  119. package/dist/address/cashaddr.js.map +0 -1
  120. package/dist/cashaddr/cashaddr.d.ts +0 -23
  121. package/dist/cashaddr/cashaddr.d.ts.map +0 -1
  122. package/dist/cashaddr/cashaddr.js +0 -325
  123. package/dist/cashaddr/cashaddr.js.map +0 -1
  124. package/global.d.ts +0 -64
  125. package/test.log +0 -82
@@ -0,0 +1,262 @@
1
+ // Copyright (c) 2024 The Bitcoin developers
2
+ // Distributed under the MIT software license, see the accompanying
3
+ // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
+
5
+ import { Ecc, EccDummy } from './ecc.js';
6
+ import { sha256d } from './hash.js';
7
+ import { WriterBytes } from './io/writerbytes.js';
8
+ import { pushBytesOp } from './op.js';
9
+ import { Script } from './script.js';
10
+ import { SigHashType, SigHashTypeVariant } from './sigHashType.js';
11
+ import {
12
+ DEFAULT_TX_VERSION,
13
+ Tx,
14
+ TxInput,
15
+ TxOutput,
16
+ copyTxInput,
17
+ copyTxOutput,
18
+ } from './tx.js';
19
+ import { UnsignedTx, UnsignedTxInput } from './unsignedTx.js';
20
+
21
+ /**
22
+ * Function that contains all the required data to sign a given `input` and
23
+ * return the scriptSig.
24
+ *
25
+ * Use it by attaching a `Signatory` to a TxBuilderInput, e.g. like this for a
26
+ * P2PKH input:
27
+ * ```ts
28
+ * new TxBuilder({
29
+ * inputs: [{
30
+ * input: { prevOut: ... },
31
+ * signatory: P2PKHSignatory(sk, pk, ALL_BIP143),
32
+ * }],
33
+ * ...
34
+ * })
35
+ * ```
36
+ **/
37
+ export type Signatory = (ecc: Ecc, input: UnsignedTxInput) => Script;
38
+
39
+ /** Builder input that bundles all the data required to sign a TxInput */
40
+ export interface TxBuilderInput {
41
+ input: TxInput;
42
+ signatory?: Signatory;
43
+ }
44
+
45
+ /**
46
+ * Output that can either be:
47
+ * - `TxOutput`: A full output with a fixed sats amount
48
+ * - `Script`: A Script which will receive the leftover sats after fees.
49
+ * Leftover usually is the change the sender gets back from providing more
50
+ * sats than needed.
51
+ */
52
+ export type TxBuilderOutput = TxOutput | Script;
53
+
54
+ /** Class that can be used to build and sign txs. */
55
+ export class TxBuilder {
56
+ /** nVersion of the resulting Tx */
57
+ public version: number;
58
+ /** Inputs that will be signed by the buider */
59
+ public inputs: TxBuilderInput[];
60
+ /**
61
+ * Outputs of the tx, can specify a single leftover (i.e. change) output as
62
+ * a Script.
63
+ **/
64
+ public outputs: TxBuilderOutput[];
65
+ /** nLockTime of the resulting Tx */
66
+ public locktime: number;
67
+
68
+ public constructor(params?: {
69
+ version?: number;
70
+ inputs?: TxBuilderInput[];
71
+ outputs?: TxBuilderOutput[];
72
+ locktime?: number;
73
+ }) {
74
+ this.version = params?.version ?? DEFAULT_TX_VERSION;
75
+ this.inputs = params?.inputs ?? [];
76
+ this.outputs = params?.outputs ?? [];
77
+ this.locktime = params?.locktime ?? 0;
78
+ }
79
+
80
+ /** Calculte sum of all sats coming in, or `undefined` if some unknown. */
81
+ private inputSum(): bigint | undefined {
82
+ let inputSum = 0n;
83
+ for (const input of this.inputs) {
84
+ if (input.input.signData === undefined) {
85
+ return undefined;
86
+ }
87
+ inputSum += BigInt(input.input.signData.value);
88
+ }
89
+ return inputSum;
90
+ }
91
+
92
+ private prepareOutputs(): {
93
+ fixedOutputSum: bigint;
94
+ leftoverIdx: number | undefined;
95
+ outputs: TxOutput[];
96
+ } {
97
+ let fixedOutputSum = 0n;
98
+ let leftoverIdx: number | undefined = undefined;
99
+ let outputs: TxOutput[] = new Array(this.outputs.length);
100
+ for (let idx = 0; idx < this.outputs.length; ++idx) {
101
+ const builderOutput = this.outputs[idx];
102
+ if ('bytecode' in builderOutput) {
103
+ // If builderOutput instanceof Script
104
+ // Note that the "builderOutput instanceof Script" check may fail due
105
+ // to discrepancies between nodejs and browser environments
106
+ if (leftoverIdx !== undefined) {
107
+ throw 'Multiple leftover outputs, can at most use one';
108
+ }
109
+ leftoverIdx = idx;
110
+ outputs[idx] = {
111
+ value: 0, // placeholder
112
+ script: builderOutput.copy(),
113
+ };
114
+ } else {
115
+ fixedOutputSum += BigInt(builderOutput.value);
116
+ outputs[idx] = copyTxOutput(builderOutput);
117
+ }
118
+ }
119
+ return { fixedOutputSum, leftoverIdx, outputs };
120
+ }
121
+
122
+ /** Sign the tx built by this builder and return a Tx */
123
+ public sign(ecc: Ecc, feePerKb?: number, dustLimit?: number): Tx {
124
+ const { fixedOutputSum, leftoverIdx, outputs } = this.prepareOutputs();
125
+ const inputs = this.inputs.map(input => copyTxInput(input.input));
126
+ const updateSignatories = (ecc: Ecc, unsignedTx: UnsignedTx) => {
127
+ for (let idx = 0; idx < this.inputs.length; ++idx) {
128
+ const signatory = this.inputs[idx].signatory;
129
+ const input = inputs[idx];
130
+ if (signatory !== undefined) {
131
+ input.script = signatory(
132
+ ecc,
133
+ new UnsignedTxInput({
134
+ inputIdx: idx,
135
+ unsignedTx,
136
+ }),
137
+ );
138
+ }
139
+ }
140
+ };
141
+ if (leftoverIdx !== undefined) {
142
+ const inputSum = this.inputSum();
143
+ if (inputSum === undefined) {
144
+ throw new Error(
145
+ 'Using a leftover output requires setting SignData.value for all inputs',
146
+ );
147
+ }
148
+ if (feePerKb === undefined) {
149
+ throw new Error(
150
+ 'Using a leftover output requires setting feePerKb',
151
+ );
152
+ }
153
+ if (!Number.isInteger(feePerKb)) {
154
+ throw new Error('feePerKb must be an integer');
155
+ }
156
+ if (dustLimit === undefined) {
157
+ throw new Error(
158
+ 'Using a leftover output requires setting dustLimit',
159
+ );
160
+ }
161
+ const dummyUnsignedTx = UnsignedTx.dummyFromTx(
162
+ new Tx({
163
+ version: this.version,
164
+ inputs,
165
+ outputs,
166
+ locktime: this.locktime,
167
+ }),
168
+ );
169
+ // Must use dummy here because ECDSA sigs could be too small for fee calc
170
+ updateSignatories(new EccDummy(), dummyUnsignedTx);
171
+ let txSize = dummyUnsignedTx.tx.serSize();
172
+ let txFee = calcTxFee(txSize, feePerKb);
173
+ const leftoverValue = inputSum - (fixedOutputSum + txFee);
174
+ if (leftoverValue < dustLimit) {
175
+ // inputs cannot pay for a dust leftover -> remove & recalc
176
+ outputs.splice(leftoverIdx, 1);
177
+ dummyUnsignedTx.tx.outputs = outputs;
178
+ // Must update signatories again as they might depend on outputs
179
+ updateSignatories(new EccDummy(), dummyUnsignedTx);
180
+ txSize = dummyUnsignedTx.tx.serSize();
181
+ txFee = calcTxFee(txSize, feePerKb);
182
+ } else {
183
+ outputs[leftoverIdx].value = leftoverValue;
184
+ }
185
+ if (inputSum < fixedOutputSum + txFee) {
186
+ throw new Error(
187
+ `Insufficient input value (${inputSum}): Can only pay for ${
188
+ inputSum - fixedOutputSum
189
+ } fees, but ${txFee} required`,
190
+ );
191
+ }
192
+ }
193
+ const unsignedTx = UnsignedTx.fromTx(
194
+ new Tx({
195
+ version: this.version,
196
+ inputs,
197
+ outputs,
198
+ locktime: this.locktime,
199
+ }),
200
+ );
201
+ updateSignatories(ecc, unsignedTx);
202
+ return unsignedTx.tx;
203
+ }
204
+ }
205
+
206
+ /** Calculate the required tx fee for the given txSize and feePerKb,
207
+ * rounding up */
208
+ export function calcTxFee(txSize: number, feePerKb: number): bigint {
209
+ return (BigInt(txSize) * BigInt(feePerKb) + 999n) / 1000n;
210
+ }
211
+
212
+ /** Append the sighash flags to the signature */
213
+ export function flagSignature(
214
+ sig: Uint8Array,
215
+ sigHashFlags: SigHashType,
216
+ ): Uint8Array {
217
+ const writer = new WriterBytes(sig.length + 1);
218
+ writer.putBytes(sig);
219
+ writer.putU8(sigHashFlags.toInt() & 0xff);
220
+ return writer.data;
221
+ }
222
+
223
+ /**
224
+ * Sign the sighash using Schnorr for BIP143 signatures and ECDSA for Legacy
225
+ * signatures, and then flags the signature correctly
226
+ **/
227
+ export function signWithSigHash(
228
+ ecc: Ecc,
229
+ sk: Uint8Array,
230
+ sigHash: Uint8Array,
231
+ sigHashType: SigHashType,
232
+ ): Uint8Array {
233
+ const sig =
234
+ sigHashType.variant == SigHashTypeVariant.LEGACY
235
+ ? ecc.ecdsaSign(sk, sigHash)
236
+ : ecc.schnorrSign(sk, sigHash);
237
+ return flagSignature(sig, sigHashType);
238
+ }
239
+
240
+ /** Signatory for a P2PKH input. Always uses Schnorr signatures */
241
+ export const P2PKHSignatory = (
242
+ sk: Uint8Array,
243
+ pk: Uint8Array,
244
+ sigHashType: SigHashType,
245
+ ) => {
246
+ return (ecc: Ecc, input: UnsignedTxInput): Script => {
247
+ const preimage = input.sigHashPreimage(sigHashType);
248
+ const sighash = sha256d(preimage.bytes);
249
+ const sigFlagged = signWithSigHash(ecc, sk, sighash, sigHashType);
250
+ return Script.p2pkhSpend(pk, sigFlagged);
251
+ };
252
+ };
253
+
254
+ /** Signatory for a P2PK input. Always uses Schnorr signatures */
255
+ export const P2PKSignatory = (sk: Uint8Array, sigHashType: SigHashType) => {
256
+ return (ecc: Ecc, input: UnsignedTxInput): Script => {
257
+ const preimage = input.sigHashPreimage(sigHashType);
258
+ const sighash = sha256d(preimage.bytes);
259
+ const sigFlagged = signWithSigHash(ecc, sk, sighash, sigHashType);
260
+ return Script.fromOps([pushBytesOp(sigFlagged)]);
261
+ };
262
+ };
@@ -0,0 +1,359 @@
1
+ // Copyright (c) 2024 The Bitcoin developers
2
+ // Distributed under the MIT software license, see the accompanying
3
+ // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
+
5
+ import { sha256d } from './hash.js';
6
+ import { Writer } from './io/writer.js';
7
+ import { WriterBytes } from './io/writerbytes.js';
8
+ import { WriterLength } from './io/writerlength.js';
9
+ import { writeVarSize } from './io/varsize.js';
10
+ import { isPushOp, Op } from './op.js';
11
+ import { OP_CODESEPARATOR } from './opcode.js';
12
+ import { Script } from './script.js';
13
+ import {
14
+ SigHashType,
15
+ SigHashTypeInputs,
16
+ SigHashTypeOutputs,
17
+ SigHashTypeVariant,
18
+ } from './sigHashType.js';
19
+ import {
20
+ DEFAULT_SEQUENCE,
21
+ SignData,
22
+ Tx,
23
+ TxInput,
24
+ writeOutPoint,
25
+ writeTxOutput,
26
+ } from './tx.js';
27
+
28
+ /** An unsigned tx, which helps us build the sighash preimage we need to sign */
29
+ export class UnsignedTx {
30
+ tx: Tx;
31
+ prevoutsHash: Uint8Array;
32
+ sequencesHash: Uint8Array;
33
+ outputsHash: Uint8Array;
34
+
35
+ private constructor(params: {
36
+ tx: Tx;
37
+ prevoutsHash: Uint8Array;
38
+ sequencesHash: Uint8Array;
39
+ outputsHash: Uint8Array;
40
+ }) {
41
+ this.tx = params.tx;
42
+ this.prevoutsHash = params.prevoutsHash;
43
+ this.sequencesHash = params.sequencesHash;
44
+ this.outputsHash = params.outputsHash;
45
+ }
46
+
47
+ /**
48
+ * Make an UnsignedTx from a Tx, will precompute the fields required to
49
+ * sign the tx
50
+ **/
51
+ public static fromTx(tx: Tx): UnsignedTx {
52
+ return new UnsignedTx({
53
+ tx,
54
+ prevoutsHash: txWriterHash(tx, writePrevouts),
55
+ sequencesHash: txWriterHash(tx, writeSequences),
56
+ outputsHash: txWriterHash(tx, writeOutputs),
57
+ });
58
+ }
59
+
60
+ /**
61
+ * Make a dummy UnsignedTx from a Tx, will set dummy values for the fields
62
+ * required to sign the tx. Useful for tx size estimation.
63
+ **/
64
+ public static dummyFromTx(tx: Tx): UnsignedTx {
65
+ return new UnsignedTx({
66
+ tx,
67
+ prevoutsHash: new Uint8Array(32),
68
+ sequencesHash: new Uint8Array(32),
69
+ outputsHash: new Uint8Array(32),
70
+ });
71
+ }
72
+
73
+ /** Return the unsigned tx input at the given input index */
74
+ public inputAt(inputIdx: number): UnsignedTxInput {
75
+ return new UnsignedTxInput({ inputIdx, unsignedTx: this });
76
+ }
77
+ }
78
+
79
+ /** A preimage of a sighash for an input's scriptSig ready to be signed */
80
+ export interface SighashPreimage {
81
+ /** Bytes of the serialized sighash preimage */
82
+ bytes: Uint8Array;
83
+ /** Script code of the preimage, with OP_CODESEPARATOR cut out */
84
+ scriptCode: Script;
85
+ /** Redeem script, with no modifications */
86
+ redeemScript: Script;
87
+ }
88
+
89
+ // Write the legacy preimage used pre-UAHF.
90
+ // It's modeled closely after SignatureHash in interpreter.cpp.
91
+ function writeLegacyPreimage(
92
+ writer: Writer,
93
+ tx: Tx,
94
+ scriptCode: Script,
95
+ inputIdx: number,
96
+ sigHashType: SigHashType,
97
+ ) {
98
+ const hasAnyoneCanPay =
99
+ sigHashType.inputType === SigHashTypeInputs.ANYONECANPAY;
100
+
101
+ const writeLegacyScriptCode = () => {
102
+ const ops = scriptCode.ops();
103
+ let nextOp: Op | undefined = undefined;
104
+ const newOps = [];
105
+ // Filter out all code separators
106
+ while ((nextOp = ops.next()) !== undefined) {
107
+ if (isPushOp(nextOp) || nextOp != OP_CODESEPARATOR) {
108
+ newOps.push(nextOp);
109
+ }
110
+ }
111
+ Script.fromOps(newOps).writeWithSize(writer);
112
+ };
113
+
114
+ const writeLegacyInput = (idx: number) => {
115
+ // In case of SIGHASH_ANYONECANPAY, only the input being signed is
116
+ // serialized
117
+ if (hasAnyoneCanPay) {
118
+ idx = inputIdx;
119
+ }
120
+ const input = tx.inputs[idx];
121
+ // Serialize the prevout
122
+ writeOutPoint(input.prevOut, writer);
123
+ // Serialize the script
124
+ if (idx != inputIdx) {
125
+ // Blank out other inputs' signatures
126
+ new Script().writeWithSize(writer);
127
+ } else {
128
+ writeLegacyScriptCode();
129
+ }
130
+ // Serialize the nSequence
131
+ if (
132
+ idx != inputIdx &&
133
+ (sigHashType.outputType === SigHashTypeOutputs.SINGLE ||
134
+ sigHashType.outputType === SigHashTypeOutputs.NONE)
135
+ ) {
136
+ // let the others update at will
137
+ writer.putU32(0);
138
+ } else {
139
+ writer.putU32(input.sequence ?? DEFAULT_SEQUENCE);
140
+ }
141
+ };
142
+
143
+ const writeLegacyOutput = (idx: number) => {
144
+ if (
145
+ sigHashType.outputType === SigHashTypeOutputs.SINGLE &&
146
+ idx != inputIdx
147
+ ) {
148
+ // Do not lock-in the txout payee at other indices as txin
149
+ writeTxOutput({ value: 0, script: new Script() }, writer);
150
+ } else {
151
+ writeTxOutput(tx.outputs[idx], writer);
152
+ }
153
+ };
154
+
155
+ writer.putU32(tx.version);
156
+ const numInputs = hasAnyoneCanPay ? 1 : tx.inputs.length;
157
+ writeVarSize(numInputs, writer);
158
+ for (let inputIdx = 0; inputIdx < numInputs; ++inputIdx) {
159
+ writeLegacyInput(inputIdx);
160
+ }
161
+
162
+ // Serialize vout
163
+ const numOutputs = (() => {
164
+ switch (sigHashType.outputType) {
165
+ case SigHashTypeOutputs.NONE:
166
+ return 0;
167
+ case SigHashTypeOutputs.SINGLE:
168
+ return inputIdx + 1;
169
+ default:
170
+ return tx.outputs.length;
171
+ }
172
+ })();
173
+ writeVarSize(numOutputs, writer);
174
+ for (let outputIdx = 0; outputIdx < numOutputs; outputIdx++) {
175
+ writeLegacyOutput(outputIdx);
176
+ }
177
+
178
+ // Serialize nLockTime
179
+ writer.putU32(tx.locktime);
180
+
181
+ // Serialize sigHashType
182
+ writer.putU32(sigHashType.toInt());
183
+ }
184
+
185
+ /**
186
+ * An unsigned tx input, can be used to build a sighash preimage ready to be
187
+ * signed
188
+ **/
189
+ export class UnsignedTxInput {
190
+ inputIdx: number;
191
+ unsignedTx: UnsignedTx;
192
+
193
+ public constructor(params: { inputIdx: number; unsignedTx: UnsignedTx }) {
194
+ this.inputIdx = params.inputIdx;
195
+ this.unsignedTx = params.unsignedTx;
196
+ }
197
+
198
+ /**
199
+ * Build the sigHashPreimage for this input, with the given sigHashType
200
+ * and OP_CODESEPARATOR index
201
+ **/
202
+ public sigHashPreimage(
203
+ sigHashType: SigHashType,
204
+ nCodesep?: number,
205
+ ): SighashPreimage {
206
+ const tx = this.unsignedTx.tx;
207
+ const input = tx.inputs[this.inputIdx];
208
+ if (input.signData === undefined) {
209
+ throw new Error('Input must have signData set');
210
+ }
211
+ const signData = input.signData;
212
+ const redeemScript = signDataScriptCode(input.signData);
213
+ const scriptCode =
214
+ nCodesep === undefined
215
+ ? redeemScript
216
+ : redeemScript.cutOutCodesep(nCodesep);
217
+
218
+ // Sign LEGACY signatures that don't use SIGHASH_FORKID
219
+ if (sigHashType.variant === SigHashTypeVariant.LEGACY) {
220
+ if (
221
+ sigHashType.outputType == SigHashTypeOutputs.SINGLE &&
222
+ this.inputIdx >= tx.outputs.length
223
+ ) {
224
+ throw new Error(
225
+ 'Invalid usage of SINGLE, input has no corresponding output',
226
+ );
227
+ }
228
+
229
+ const writePreimage = (writer: Writer) => {
230
+ writeLegacyPreimage(
231
+ writer,
232
+ this.unsignedTx.tx,
233
+ scriptCode,
234
+ this.inputIdx,
235
+ sigHashType,
236
+ );
237
+ };
238
+ const preimageWriterLen = new WriterLength();
239
+ writePreimage(preimageWriterLen);
240
+ const preimageWriter = new WriterBytes(preimageWriterLen.length);
241
+ writePreimage(preimageWriter);
242
+
243
+ return {
244
+ bytes: preimageWriter.data,
245
+ scriptCode,
246
+ redeemScript,
247
+ };
248
+ }
249
+
250
+ let hashOutputs: Uint8Array;
251
+ switch (sigHashType.outputType) {
252
+ case SigHashTypeOutputs.ALL:
253
+ hashOutputs = this.unsignedTx.outputsHash;
254
+ break;
255
+ case SigHashTypeOutputs.NONE:
256
+ hashOutputs = new Uint8Array(32);
257
+ break;
258
+ case SigHashTypeOutputs.SINGLE:
259
+ if (this.inputIdx < tx.outputs.length) {
260
+ const output = tx.outputs[this.inputIdx];
261
+ const writerOutputLength = new WriterLength();
262
+ writeTxOutput(output, writerOutputLength);
263
+ const writerOutput = new WriterBytes(
264
+ writerOutputLength.length,
265
+ );
266
+ writeTxOutput(output, writerOutput);
267
+ hashOutputs = sha256d(writerOutput.data);
268
+ } else {
269
+ hashOutputs = new Uint8Array(32);
270
+ }
271
+ break;
272
+ }
273
+
274
+ const writePreimage = (writer: Writer) => {
275
+ writer.putU32(tx.version);
276
+ if (sigHashType.inputType == SigHashTypeInputs.FIXED) {
277
+ writer.putBytes(this.unsignedTx.prevoutsHash);
278
+ } else {
279
+ writer.putBytes(new Uint8Array(32));
280
+ }
281
+ if (
282
+ sigHashType.inputType == SigHashTypeInputs.FIXED &&
283
+ sigHashType.outputType == SigHashTypeOutputs.ALL
284
+ ) {
285
+ writer.putBytes(this.unsignedTx.sequencesHash);
286
+ } else {
287
+ writer.putBytes(new Uint8Array(32));
288
+ }
289
+ writeOutPoint(input.prevOut, writer);
290
+ scriptCode.writeWithSize(writer);
291
+ writer.putU64(signData.value);
292
+ writer.putU32(input.sequence ?? DEFAULT_SEQUENCE);
293
+ writer.putBytes(hashOutputs);
294
+ writer.putU32(tx.locktime);
295
+ writer.putU32(sigHashType.toInt());
296
+ };
297
+
298
+ const preimageWriterLen = new WriterLength();
299
+ writePreimage(preimageWriterLen);
300
+ const preimageWriter = new WriterBytes(preimageWriterLen.length);
301
+ writePreimage(preimageWriter);
302
+
303
+ return {
304
+ bytes: preimageWriter.data,
305
+ scriptCode,
306
+ redeemScript,
307
+ };
308
+ }
309
+
310
+ /** Return the TxInput of this UnsignedTxInput */
311
+ public txInput(): TxInput {
312
+ return this.unsignedTx.tx.inputs[this.inputIdx];
313
+ }
314
+ }
315
+
316
+ /** Find the scriptCode that should be signed */
317
+ function signDataScriptCode(signData: SignData): Script {
318
+ if (signData.outputScript !== undefined) {
319
+ if (signData.outputScript.isP2sh()) {
320
+ throw new Error(
321
+ 'P2SH requires redeemScript to be set, not outputScript',
322
+ );
323
+ }
324
+ return signData.outputScript;
325
+ }
326
+ if (signData.redeemScript === undefined) {
327
+ throw new Error('Must either set outputScript or redeemScript');
328
+ }
329
+ return signData.redeemScript;
330
+ }
331
+
332
+ function txWriterHash(
333
+ tx: Tx,
334
+ fn: (tx: Tx, writer: Writer) => void,
335
+ ): Uint8Array {
336
+ const writerLength = new WriterLength();
337
+ fn(tx, writerLength);
338
+ const writer = new WriterBytes(writerLength.length);
339
+ fn(tx, writer);
340
+ return sha256d(writer.data);
341
+ }
342
+
343
+ function writePrevouts(tx: Tx, writer: Writer) {
344
+ for (const input of tx.inputs) {
345
+ writeOutPoint(input.prevOut, writer);
346
+ }
347
+ }
348
+
349
+ function writeSequences(tx: Tx, writer: Writer) {
350
+ for (const input of tx.inputs) {
351
+ writer.putU32(input.sequence ?? DEFAULT_SEQUENCE);
352
+ }
353
+ }
354
+
355
+ function writeOutputs(tx: Tx, writer: Writer) {
356
+ for (const output of tx.outputs) {
357
+ writeTxOutput(output, writer);
358
+ }
359
+ }
package/tsconfig.json CHANGED
@@ -11,8 +11,9 @@
11
11
  /* Interop Constraints */
12
12
  "esModuleInterop": true,
13
13
  "forceConsistentCasingInFileNames": true,
14
+ "resolveJsonModule": true,
14
15
  /* Type Checking */
15
16
  "strict": true
16
17
  },
17
- "include": ["src/**/*", "tests/**/*", "global.d.ts"]
18
+ "include": ["src/**/*", "tests/**/*"]
18
19
  }