@bsv/templates 1.2.2 → 1.3.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.
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/MultiSigPubkeyHash.js +32 -20
- package/dist/cjs/src/MultiSigPubkeyHash.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/MultiSigPubkeyHash.js +34 -25
- package/dist/esm/src/MultiSigPubkeyHash.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/MultiSigPubkeyHash.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/MultiSigPubkeyHash.ts +70 -60
|
@@ -10,9 +10,23 @@ function concatPubkeys(pubkeys: PublicKey[]): number[] {
|
|
|
10
10
|
return pubkeys.map((p) => p.toDER() as number[]).reduce((a, b) => a.concat(b), [])
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
function numberFromScriptChunk(chunk: ScriptChunk): number {
|
|
14
|
+
let returnNum: number
|
|
15
|
+
if (!chunk.data) {
|
|
16
|
+
returnNum = 1 + (chunk.op as number) - OP.OP_1
|
|
17
|
+
} else {
|
|
18
|
+
const reader = new Utils.Reader(chunk.data)
|
|
19
|
+
const threshold = reader.readInt64LEBn()
|
|
20
|
+
returnNum = threshold.toNumber()
|
|
21
|
+
}
|
|
22
|
+
return returnNum
|
|
23
|
+
}
|
|
24
|
+
|
|
13
25
|
export class MultiSigPubkeyHash implements ScriptTemplate {
|
|
14
26
|
|
|
15
27
|
static address(pubkeys: PublicKey[], threshold: number): string {
|
|
28
|
+
if (threshold < 1 || threshold > pubkeys.length) throw new Error('threshold must be between 1 and the number of pubkeys')
|
|
29
|
+
if (!pubkeys || pubkeys.length < 2 || pubkeys.length < threshold) throw new Error(`at least ${threshold || 2} pubkeys are required`)
|
|
16
30
|
const concat = concatPubkeys(pubkeys)
|
|
17
31
|
const hash = Hash.hash160(concat)
|
|
18
32
|
const writer = new Utils.Writer()
|
|
@@ -51,9 +65,9 @@ export class MultiSigPubkeyHash implements ScriptTemplate {
|
|
|
51
65
|
address?: string,
|
|
52
66
|
pubkeys?: PublicKey[],
|
|
53
67
|
threshold: number = 1,
|
|
54
|
-
): LockingScript {
|
|
68
|
+
): LockingScript {
|
|
55
69
|
let hash: number[]
|
|
56
|
-
let total: number
|
|
70
|
+
let total: number = pubkeys?.length || 0
|
|
57
71
|
if (address) {
|
|
58
72
|
if (typeof address !== 'string') throw new Error('address must be a string')
|
|
59
73
|
const result = MultiSigPubkeyHash.thresholdAndTotalFromAddress(address)
|
|
@@ -61,12 +75,13 @@ export class MultiSigPubkeyHash implements ScriptTemplate {
|
|
|
61
75
|
total = result.total
|
|
62
76
|
threshold = result.threshold
|
|
63
77
|
} else {
|
|
64
|
-
if (!pubkeys ||
|
|
78
|
+
if (!pubkeys || total < 2) throw new Error(`at least 2 pubkeys are required`)
|
|
65
79
|
const concat = concatPubkeys(pubkeys)
|
|
66
80
|
hash = Hash.hash160(concat)
|
|
67
|
-
total = pubkeys.length
|
|
68
81
|
}
|
|
69
|
-
|
|
82
|
+
if (!threshold || threshold < 1 || threshold > total) throw new Error('threshold must be between 1 and the number of pubkeys')
|
|
83
|
+
if (total > 10) throw new Error('total must be less than or equal to 10')
|
|
84
|
+
|
|
70
85
|
const script = new LockingScript();
|
|
71
86
|
for (let i = 0; i < total - 1; i++) {
|
|
72
87
|
script.writeOpCode(OP.OP_CAT)
|
|
@@ -104,53 +119,52 @@ export class MultiSigPubkeyHash implements ScriptTemplate {
|
|
|
104
119
|
return {
|
|
105
120
|
sign: async (tx: Transaction, inputIndex: number) => {
|
|
106
121
|
if (!workingUnlockingScript) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
122
|
+
workingUnlockingScript = new UnlockingScript()
|
|
123
|
+
workingUnlockingScript.writeOpCode(OP.OP_0)
|
|
124
|
+
customInstructions.pubkeys.forEach((pubkey) => {
|
|
125
|
+
workingUnlockingScript!.writeBin(PublicKey.fromString(pubkey).toDER() as number[])
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
let signatureScope = TransactionSignature.SIGHASH_FORKID;
|
|
130
|
+
if (signOutputs === "all") {
|
|
131
|
+
signatureScope |= TransactionSignature.SIGHASH_ALL;
|
|
132
|
+
}
|
|
133
|
+
if (signOutputs === "none") {
|
|
134
|
+
signatureScope |= TransactionSignature.SIGHASH_NONE;
|
|
135
|
+
}
|
|
136
|
+
if (signOutputs === "single") {
|
|
137
|
+
signatureScope |= TransactionSignature.SIGHASH_SINGLE;
|
|
138
|
+
}
|
|
139
|
+
if (anyoneCanPay) {
|
|
140
|
+
signatureScope |= TransactionSignature.SIGHASH_ANYONECANPAY;
|
|
141
|
+
}
|
|
142
|
+
const input = tx.inputs[inputIndex];
|
|
143
|
+
|
|
144
|
+
const otherInputs = tx.inputs.filter(
|
|
145
|
+
(_, index) => index !== inputIndex
|
|
146
|
+
)
|
|
131
147
|
|
|
132
148
|
const sourceTXID = input.sourceTXID
|
|
133
|
-
|
|
134
|
-
|
|
149
|
+
? input.sourceTXID
|
|
150
|
+
: input.sourceTransaction?.id("hex")
|
|
151
|
+
|
|
135
152
|
if (!sourceTXID) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
153
|
+
throw new Error(
|
|
154
|
+
"The input sourceTXID or sourceTransaction is required for transaction signing."
|
|
155
|
+
)
|
|
139
156
|
}
|
|
140
|
-
sourceSatoshis ||=
|
|
141
|
-
input.sourceTransaction?.outputs[input.sourceOutputIndex].satoshis;
|
|
157
|
+
sourceSatoshis ||= input.sourceTransaction?.outputs[input.sourceOutputIndex].satoshis;
|
|
142
158
|
if (!sourceSatoshis) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
159
|
+
throw new Error(
|
|
160
|
+
"The sourceSatoshis or input sourceTransaction is required for transaction signing."
|
|
161
|
+
)
|
|
146
162
|
}
|
|
147
|
-
lockingScript ||=
|
|
148
|
-
input.sourceTransaction?.outputs[input.sourceOutputIndex]
|
|
149
|
-
.lockingScript;
|
|
163
|
+
lockingScript ||= input.sourceTransaction?.outputs[input.sourceOutputIndex].lockingScript;
|
|
150
164
|
if (!lockingScript) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
165
|
+
throw new Error(
|
|
166
|
+
"The lockingScript or input sourceTransaction is required for transaction signing."
|
|
167
|
+
)
|
|
154
168
|
}
|
|
155
169
|
|
|
156
170
|
const preimage = TransactionSignature.format({
|
|
@@ -182,32 +196,28 @@ export class MultiSigPubkeyHash implements ScriptTemplate {
|
|
|
182
196
|
|
|
183
197
|
workingUnlockingScript.writeBin(sigForScript)
|
|
184
198
|
const chunkforSig = workingUnlockingScript.chunks.pop() as ScriptChunk
|
|
185
|
-
// add it to the array
|
|
186
|
-
workingUnlockingScript.chunks.splice(
|
|
199
|
+
// add it to the array before the pubkeys, pushing the other content to the right
|
|
200
|
+
workingUnlockingScript.chunks.splice(workingUnlockingScript.chunks.length - customInstructions.pubkeys.length, 0, chunkforSig)
|
|
187
201
|
return workingUnlockingScript
|
|
188
202
|
},
|
|
189
203
|
|
|
190
204
|
estimateLength: (tx: Transaction, inputIndex: number) => {
|
|
191
205
|
let numberOfPubkeys = 2
|
|
192
206
|
let numberOfSignatures = 1
|
|
193
|
-
const staticLength =
|
|
207
|
+
const staticLength = 1
|
|
194
208
|
const input = tx.inputs[inputIndex];
|
|
195
209
|
const lockingScript = input.sourceTransaction?.outputs[input.sourceOutputIndex].lockingScript;
|
|
196
210
|
if (!lockingScript) {
|
|
197
211
|
return Promise.resolve(1000) // guess
|
|
198
212
|
}
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const threshold = reader.readInt64LEBn()
|
|
208
|
-
numberOfSignatures = threshold.toNumber()
|
|
209
|
-
}
|
|
210
|
-
return Promise.resolve(staticLength + (numberOfPubkeys * 34) + (numberOfSignatures * 73))
|
|
213
|
+
|
|
214
|
+
const totalChunk = lockingScript?.chunks[lockingScript.chunks.length - 2] as { op: number, data: number[] }
|
|
215
|
+
numberOfPubkeys = numberFromScriptChunk(totalChunk)
|
|
216
|
+
|
|
217
|
+
const thresholdPos = lockingScript.chunks.map(chunk => chunk.op === OP.OP_EQUALVERIFY).indexOf(true) + 1
|
|
218
|
+
const thresholdChunk = lockingScript?.chunks[thresholdPos] as { op: number, data: number[] }
|
|
219
|
+
numberOfSignatures = numberFromScriptChunk(thresholdChunk)
|
|
220
|
+
return Promise.resolve(staticLength + (numberOfSignatures * 74) + (numberOfPubkeys * 34))
|
|
211
221
|
}
|
|
212
222
|
}
|
|
213
223
|
}
|