-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
Copy pathutils.go
480 lines (411 loc) · 15 KB
/
utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
// Copyright (c) 2018 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package psbt
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"sort"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
)
// WriteTxWitness is a utility function due to non-exported witness
// serialization (writeTxWitness encodes the bitcoin protocol encoding for a
// transaction input's witness into w).
func WriteTxWitness(w io.Writer, wit [][]byte) error {
if err := wire.WriteVarInt(w, 0, uint64(len(wit))); err != nil {
return err
}
for _, item := range wit {
err := wire.WriteVarBytes(w, 0, item)
if err != nil {
return err
}
}
return nil
}
// writePKHWitness writes a witness for a p2wkh spending input
func writePKHWitness(sig []byte, pub []byte) ([]byte, error) {
return writeWitness(sig, pub)
}
// writeWitness serializes a witness stack from the given items.
func writeWitness(stackElements ...[]byte) ([]byte, error) {
var (
buf bytes.Buffer
witnessItems = append([][]byte{}, stackElements...)
)
if err := WriteTxWitness(&buf, witnessItems); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// checkIsMultisigScript is a utility function to check whether a given
// redeemscript fits the standard multisig template used in all P2SH based
// multisig, given a set of pubkeys for redemption.
func checkIsMultiSigScript(pubKeys [][]byte, sigs [][]byte,
script []byte) bool {
// First insist that the script type is multisig.
if txscript.GetScriptClass(script) != txscript.MultiSigTy {
return false
}
// Inspect the script to ensure that the number of sigs and pubkeys is
// correct
_, numSigs, err := txscript.CalcMultiSigStats(script)
if err != nil {
return false
}
// If the number of sigs provided, doesn't match the number of required
// pubkeys, then we can't proceed as we're not yet final.
if numSigs != len(pubKeys) || numSigs != len(sigs) {
return false
}
return true
}
// extractKeyOrderFromScript is a utility function to extract an ordered list
// of signatures, given a serialized script (redeemscript or witness script), a
// list of pubkeys and the signatures corresponding to those pubkeys. This
// function is used to ensure that the signatures will be embedded in the final
// scriptSig or scriptWitness in the correct order.
func extractKeyOrderFromScript(script []byte, expectedPubkeys [][]byte,
sigs [][]byte) ([][]byte, error) {
// If this isn't a proper finalized multi-sig script, then we can't
// proceed.
if !checkIsMultiSigScript(expectedPubkeys, sigs, script) {
return nil, ErrUnsupportedScriptType
}
// Arrange the pubkeys and sigs into a slice of format:
// * [[pub,sig], [pub,sig],..]
type sigWithPub struct {
pubKey []byte
sig []byte
}
var pubsSigs []sigWithPub
for i, pub := range expectedPubkeys {
pubsSigs = append(pubsSigs, sigWithPub{
pubKey: pub,
sig: sigs[i],
})
}
// Now that we have the set of (pubkey, sig) pairs, we'll construct a
// position map that we can use to swap the order in the slice above to
// match how things are laid out in the script.
type positionEntry struct {
index int
value sigWithPub
}
var positionMap []positionEntry
// For each pubkey in our pubsSigs slice, we'll now construct a proper
// positionMap entry, based on _where_ in the script the pubkey first
// appears.
for _, p := range pubsSigs {
pos := bytes.Index(script, p.pubKey)
if pos < 0 {
return nil, errors.New("script does not contain pubkeys")
}
positionMap = append(positionMap, positionEntry{
index: pos,
value: p,
})
}
// Now that we have the position map full populated, we'll use the
// index data to properly sort the entries in the map based on where
// they appear in the script.
sort.Slice(positionMap, func(i, j int) bool {
return positionMap[i].index < positionMap[j].index
})
// Finally, we can simply iterate through the position map in order to
// extract the proper signature ordering.
sortedSigs := make([][]byte, 0, len(positionMap))
for _, x := range positionMap {
sortedSigs = append(sortedSigs, x.value.sig)
}
return sortedSigs, nil
}
// getMultisigScriptWitness creates a full psbt serialized Witness field for
// the transaction, given the public keys and signatures to be appended. This
// function will only accept witnessScripts of the type M of N multisig. This
// is used for both p2wsh and nested p2wsh multisig cases.
func getMultisigScriptWitness(witnessScript []byte, pubKeys [][]byte,
sigs [][]byte) ([]byte, error) {
// First using the script as a guide, we'll properly order the sigs
// according to how their corresponding pubkeys appear in the
// witnessScript.
orderedSigs, err := extractKeyOrderFromScript(
witnessScript, pubKeys, sigs,
)
if err != nil {
return nil, err
}
// Now that we know the proper order, we'll append each of the
// signatures into a new witness stack, then top it off with the
// witness script at the end, prepending the nil as we need the extra
// pop..
witnessElements := make(wire.TxWitness, 0, len(sigs)+2)
witnessElements = append(witnessElements, nil)
for _, os := range orderedSigs {
witnessElements = append(witnessElements, os)
}
witnessElements = append(witnessElements, witnessScript)
// Now that we have the full witness stack, we'll serialize it in the
// expected format, and return the final bytes.
var buf bytes.Buffer
if err = WriteTxWitness(&buf, witnessElements); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// checkSigHashFlags compares the sighash flag byte on a signature with the
// value expected according to any PsbtInSighashType field in this section of
// the PSBT, and returns true if they match, false otherwise.
// If no SighashType field exists, it is assumed to be SIGHASH_ALL.
//
// TODO(waxwing): sighash type not restricted to one byte in future?
func checkSigHashFlags(sig []byte, input *PInput) bool {
expectedSighashType := txscript.SigHashAll
if input.SighashType != 0 {
expectedSighashType = input.SighashType
}
return expectedSighashType == txscript.SigHashType(sig[len(sig)-1])
}
// serializeKVpair writes out a kv pair using a varbyte prefix for each.
func serializeKVpair(w io.Writer, key []byte, value []byte) error {
if err := wire.WriteVarBytes(w, 0, key); err != nil {
return err
}
return wire.WriteVarBytes(w, 0, value)
}
// serializeKVPairWithType writes out to the passed writer a type coupled with
// a key.
func serializeKVPairWithType(w io.Writer, kt uint8, keydata []byte,
value []byte) error {
// If the key has no data, then we write a blank slice.
if keydata == nil {
keydata = []byte{}
}
// The final key to be written is: {type} || {keyData}
serializedKey := append([]byte{kt}, keydata...)
return serializeKVpair(w, serializedKey, value)
}
// getKey retrieves a single key - both the key type and the keydata (if
// present) from the stream and returns the key type as an integer, or -1 if
// the key was of zero length. This integer is used to indicate the presence
// of a separator byte which indicates the end of a given key-value pair list,
// and the keydata as a byte slice or nil if none is present.
func getKey(r io.Reader) (int, []byte, error) {
// For the key, we read the varint separately, instead of using the
// available ReadVarBytes, because we have a specific treatment of 0x00
// here:
count, err := wire.ReadVarInt(r, 0)
if err != nil {
return -1, nil, ErrInvalidPsbtFormat
}
if count == 0 {
// A separator indicates end of key-value pair list.
return -1, nil, nil
}
// Check that we don't attempt to decode a dangerously large key.
if count > MaxPsbtKeyLength {
return -1, nil, ErrInvalidKeyData
}
// Next, we ready out the designated number of bytes, which may include
// a type, key, and optional data.
keyTypeReader := io.LimitReader(r, int64(count))
keyType, err := wire.ReadVarInt(keyTypeReader, 0)
if err != nil {
return -1, nil, ErrInvalidPsbtFormat
}
// The maximum value of a compact size int is capped in bitcoind, do the
// same here to mimic the behavior.
if keyType > MaxPsbtKeyValue {
return -1, nil, ErrInvalidPsbtFormat
}
keyData, err := io.ReadAll(keyTypeReader)
if err != nil {
return -1, nil, ErrInvalidPsbtFormat
}
// Note that the second return value will usually be empty, since most
// keys contain no more than the key type byte.
if len(keyData) == 0 {
return int(keyType), nil, nil
}
// Otherwise, we return the key, along with any data that it may
// contain.
return int(keyType), keyData, nil
}
// readTxOut is a limited version of wire.ReadTxOut, because the latter is not
// exported.
func readTxOut(txout []byte) (*wire.TxOut, error) {
if len(txout) < 10 {
return nil, ErrInvalidPsbtFormat
}
valueSer := binary.LittleEndian.Uint64(txout[:8])
scriptPubKey := txout[9:]
return wire.NewTxOut(int64(valueSer), scriptPubKey), nil
}
// SumUtxoInputValues tries to extract the sum of all inputs specified in the
// UTXO fields of the PSBT. An error is returned if an input is specified that
// does not contain any UTXO information.
func SumUtxoInputValues(packet *Packet) (int64, error) {
// We take the TX ins of the unsigned TX as the truth for how many
// inputs there should be, as the fields in the extra data part of the
// PSBT can be empty.
if len(packet.UnsignedTx.TxIn) != len(packet.Inputs) {
return 0, fmt.Errorf("TX input length doesn't match PSBT " +
"input length")
}
inputSum := int64(0)
for idx, in := range packet.Inputs {
switch {
case in.WitnessUtxo != nil:
// Witness UTXOs only need to reference the TxOut.
inputSum += in.WitnessUtxo.Value
case in.NonWitnessUtxo != nil:
// Non-witness UTXOs reference to the whole transaction
// the UTXO resides in.
utxOuts := in.NonWitnessUtxo.TxOut
txIn := packet.UnsignedTx.TxIn[idx]
// Check that utxOuts actually has enough space to
// contain the previous outpoint's index.
opIdx := txIn.PreviousOutPoint.Index
if opIdx >= uint32(len(utxOuts)) {
return 0, fmt.Errorf("input %d has malformed "+
"TxOut field", idx)
}
inputSum += utxOuts[txIn.PreviousOutPoint.Index].Value
default:
return 0, fmt.Errorf("input %d has no UTXO information",
idx)
}
}
return inputSum, nil
}
// TxOutsEqual returns true if two transaction outputs are equal.
func TxOutsEqual(out1, out2 *wire.TxOut) bool {
if out1 == nil || out2 == nil {
return out1 == out2
}
return out1.Value == out2.Value &&
bytes.Equal(out1.PkScript, out2.PkScript)
}
// VerifyOutputsEqual verifies that the two slices of transaction outputs are
// deep equal to each other. We do the length check and manual loop to provide
// better error messages to the user than just returning "not equal".
func VerifyOutputsEqual(outs1, outs2 []*wire.TxOut) error {
if len(outs1) != len(outs2) {
return fmt.Errorf("number of outputs are different")
}
for idx, out := range outs1 {
// There is a byte slice in the output so we can't use the
// equality operator.
if !TxOutsEqual(out, outs2[idx]) {
return fmt.Errorf("output %d is different", idx)
}
}
return nil
}
// VerifyInputPrevOutpointsEqual verifies that the previous outpoints of the
// two slices of transaction inputs are deep equal to each other. We do the
// length check and manual loop to provide better error messages to the user
// than just returning "not equal".
func VerifyInputPrevOutpointsEqual(ins1, ins2 []*wire.TxIn) error {
if len(ins1) != len(ins2) {
return fmt.Errorf("number of inputs are different")
}
for idx, in := range ins1 {
if in.PreviousOutPoint != ins2[idx].PreviousOutPoint {
return fmt.Errorf("previous outpoint of input %d is "+
"different", idx)
}
}
return nil
}
// VerifyInputOutputLen makes sure a packet is non-nil, contains a non-nil wire
// transaction and that the wire input/output lengths match the partial input/
// output lengths. A caller also can specify if they expect any inputs and/or
// outputs to be contained in the packet.
func VerifyInputOutputLen(packet *Packet, needInputs, needOutputs bool) error {
if packet == nil || packet.UnsignedTx == nil {
return fmt.Errorf("PSBT packet cannot be nil")
}
if len(packet.UnsignedTx.TxIn) != len(packet.Inputs) {
return fmt.Errorf("invalid PSBT, wire inputs don't match " +
"partial inputs")
}
if len(packet.UnsignedTx.TxOut) != len(packet.Outputs) {
return fmt.Errorf("invalid PSBT, wire outputs don't match " +
"partial outputs")
}
if needInputs && len(packet.UnsignedTx.TxIn) == 0 {
return fmt.Errorf("PSBT packet must contain at least one " +
"input")
}
if needOutputs && len(packet.UnsignedTx.TxOut) == 0 {
return fmt.Errorf("PSBT packet must contain at least one " +
"output")
}
return nil
}
// InputsReadyToSign makes sure that all input data have the previous output
// specified meaning that either nonwitness UTXO or the witness UTXO data is
// specified in the psbt package. This check is necessary because of 2 reasons.
// The sighash calculation is now different for witnessV0 and witnessV1 inputs
// this means we need to check the previous output pkScript for the specific
// type and the second reason is that the sighash calculation for taproot inputs
// include the previous output pkscripts.
func InputsReadyToSign(packet *Packet) error {
err := VerifyInputOutputLen(packet, true, true)
if err != nil {
return err
}
for i := range packet.UnsignedTx.TxIn {
input := packet.Inputs[i]
if input.NonWitnessUtxo == nil && input.WitnessUtxo == nil {
return fmt.Errorf("invalid PSBT, input with index %d "+
"missing utxo information", i)
}
}
return nil
}
// NewFromSignedTx is a utility function to create a packet from an
// already-signed transaction. Returned are: an unsigned transaction
// serialization, a list of scriptSigs, one per input, and a list of witnesses,
// one per input.
func NewFromSignedTx(tx *wire.MsgTx) (*Packet, [][]byte,
[]wire.TxWitness, error) {
scriptSigs := make([][]byte, 0, len(tx.TxIn))
witnesses := make([]wire.TxWitness, 0, len(tx.TxIn))
tx2 := tx.Copy()
// Blank out signature info in inputs
for i, tin := range tx2.TxIn {
tin.SignatureScript = nil
scriptSigs = append(scriptSigs, tx.TxIn[i].SignatureScript)
tin.Witness = nil
witnesses = append(witnesses, tx.TxIn[i].Witness)
}
// Outputs always contain: (value, scriptPubkey) so don't need
// amending. Now tx2 is tx with all signing data stripped out
unsignedPsbt, err := NewFromUnsignedTx(tx2)
if err != nil {
return nil, nil, nil, err
}
return unsignedPsbt, scriptSigs, witnesses, nil
}
// FindLeafScript attempts to locate the leaf script of a given target Tap Leaf
// hash in the list of leaf scripts of the given input.
func FindLeafScript(pInput *PInput,
targetLeafHash []byte) (*TaprootTapLeafScript, error) {
for _, leaf := range pInput.TaprootLeafScript {
leafHash := txscript.TapLeaf{
LeafVersion: leaf.LeafVersion,
Script: leaf.Script,
}.TapHash()
if bytes.Equal(targetLeafHash, leafHash[:]) {
return leaf, nil
}
}
return nil, fmt.Errorf("leaf script for target leaf hash %x not "+
"found in input", targetLeafHash)
}