/
taproot.go
150 lines (125 loc) · 4.72 KB
/
taproot.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
package input
import (
"fmt"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcwallet/waddrmgr"
)
const (
// PubKeyFormatCompressedOdd is the identifier prefix byte for a public
// key whose Y coordinate is odd when serialized in the compressed
// format per section 2.3.4 of
// [SEC1](https://secg.org/sec1-v2.pdf#subsubsection.2.3.4).
// This is copied from the github.com/decred/dcrd/dcrec/secp256k1/v4 to
// avoid needing to directly reference (and by accident pull in
// incompatible crypto primitives) the package.
PubKeyFormatCompressedOdd byte = 0x03
)
// NewTxSigHashesV0Only returns a new txscript.TxSigHashes instance that will
// only calculate the sighash midstate values for segwit v0 inputs and can
// therefore never be used for transactions that want to spend segwit v1
// (taproot) inputs.
func NewTxSigHashesV0Only(tx *wire.MsgTx) *txscript.TxSigHashes {
// The canned output fetcher returns a wire.TxOut instance with the
// given pk script and amount. We can get away with nil since the first
// thing the TxSigHashes constructor checks is the length of the pk
// script and whether it matches taproot output script length. If the
// length doesn't match it assumes v0 inputs only.
nilFetcher := txscript.NewCannedPrevOutputFetcher(nil, 0)
return txscript.NewTxSigHashes(tx, nilFetcher)
}
// MultiPrevOutFetcher returns a txscript.MultiPrevOutFetcher for the given set
// of inputs.
func MultiPrevOutFetcher(inputs []Input) (*txscript.MultiPrevOutFetcher, error) {
fetcher := txscript.NewMultiPrevOutFetcher(nil)
for _, inp := range inputs {
op := inp.OutPoint()
desc := inp.SignDesc()
if op == nil {
return nil, fmt.Errorf("missing input outpoint")
}
if desc == nil || desc.Output == nil {
return nil, fmt.Errorf("missing input utxo information")
}
fetcher.AddPrevOut(*op, desc.Output)
}
return fetcher, nil
}
// TapscriptFullTree creates a waddrmgr.Tapscript for the given internal key and
// tree leaves.
func TapscriptFullTree(internalKey *btcec.PublicKey,
allTreeLeaves ...txscript.TapLeaf) *waddrmgr.Tapscript {
tree := txscript.AssembleTaprootScriptTree(allTreeLeaves...)
rootHash := tree.RootNode.TapHash()
tapKey := txscript.ComputeTaprootOutputKey(internalKey, rootHash[:])
var outputKeyYIsOdd bool
if tapKey.SerializeCompressed()[0] == PubKeyFormatCompressedOdd {
outputKeyYIsOdd = true
}
return &waddrmgr.Tapscript{
Type: waddrmgr.TapscriptTypeFullTree,
ControlBlock: &txscript.ControlBlock{
InternalKey: internalKey,
OutputKeyYIsOdd: outputKeyYIsOdd,
LeafVersion: txscript.BaseLeafVersion,
},
Leaves: allTreeLeaves,
}
}
// TapscriptPartialReveal creates a waddrmgr.Tapscript for the given internal
// key and revealed script.
func TapscriptPartialReveal(internalKey *btcec.PublicKey,
revealedLeaf txscript.TapLeaf,
inclusionProof []byte) *waddrmgr.Tapscript {
controlBlock := &txscript.ControlBlock{
InternalKey: internalKey,
LeafVersion: txscript.BaseLeafVersion,
InclusionProof: inclusionProof,
}
rootHash := controlBlock.RootHash(revealedLeaf.Script)
tapKey := txscript.ComputeTaprootOutputKey(internalKey, rootHash)
if tapKey.SerializeCompressed()[0] == PubKeyFormatCompressedOdd {
controlBlock.OutputKeyYIsOdd = true
}
return &waddrmgr.Tapscript{
Type: waddrmgr.TapscriptTypePartialReveal,
ControlBlock: controlBlock,
RevealedScript: revealedLeaf.Script,
}
}
// TapscriptRootHashOnly creates a waddrmgr.Tapscript for the given internal key
// and root hash.
func TapscriptRootHashOnly(internalKey *btcec.PublicKey,
rootHash []byte) *waddrmgr.Tapscript {
controlBlock := &txscript.ControlBlock{
InternalKey: internalKey,
}
tapKey := txscript.ComputeTaprootOutputKey(internalKey, rootHash)
if tapKey.SerializeCompressed()[0] == PubKeyFormatCompressedOdd {
controlBlock.OutputKeyYIsOdd = true
}
return &waddrmgr.Tapscript{
Type: waddrmgr.TaprootKeySpendRootHash,
ControlBlock: controlBlock,
RootHash: rootHash,
}
}
// TapscriptFullKeyOnly creates a waddrmgr.Tapscript for the given full Taproot
// key.
func TapscriptFullKeyOnly(taprootKey *btcec.PublicKey) *waddrmgr.Tapscript {
return &waddrmgr.Tapscript{
Type: waddrmgr.TaprootFullKeyOnly,
FullOutputKey: taprootKey,
}
}
// PayToTaprootScript creates a new script to pay to a version 1 (taproot)
// witness program. The passed public key will be serialized as an x-only key
// to create the witness program.
func PayToTaprootScript(taprootKey *btcec.PublicKey) ([]byte, error) {
builder := txscript.NewScriptBuilder()
builder.AddOp(txscript.OP_1)
builder.AddData(schnorr.SerializePubKey(taprootKey))
return builder.Script()
}