1package txscript
2
3import (
4	"crypto/sha256"
5	"errors"
6	"fmt"
7
8	"github.com/btcsuite/btcd/btcec"
9	"github.com/btcsuite/btcd/chaincfg"
10	"github.com/btcsuite/btcd/wire"
11	"github.com/btcsuite/btcutil"
12	"golang.org/x/crypto/ripemd160"
13)
14
15const (
16	// minPubKeyHashSigScriptLen is the minimum length of a signature script
17	// that spends a P2PKH output. The length is composed of the following:
18	//   Signature length (1 byte)
19	//   Signature (min 8 bytes)
20	//   Signature hash type (1 byte)
21	//   Public key length (1 byte)
22	//   Public key (33 byte)
23	minPubKeyHashSigScriptLen = 1 + btcec.MinSigLen + 1 + 1 + 33
24
25	// maxPubKeyHashSigScriptLen is the maximum length of a signature script
26	// that spends a P2PKH output. The length is composed of the following:
27	//   Signature length (1 byte)
28	//   Signature (max 72 bytes)
29	//   Signature hash type (1 byte)
30	//   Public key length (1 byte)
31	//   Public key (33 byte)
32	maxPubKeyHashSigScriptLen = 1 + 72 + 1 + 1 + 33
33
34	// compressedPubKeyLen is the length in bytes of a compressed public
35	// key.
36	compressedPubKeyLen = 33
37
38	// pubKeyHashLen is the length of a P2PKH script.
39	pubKeyHashLen = 25
40
41	// witnessV0PubKeyHashLen is the length of a P2WPKH script.
42	witnessV0PubKeyHashLen = 22
43
44	// scriptHashLen is the length of a P2SH script.
45	scriptHashLen = 23
46
47	// witnessV0ScriptHashLen is the length of a P2WSH script.
48	witnessV0ScriptHashLen = 34
49
50	// maxLen is the maximum script length supported by ParsePkScript.
51	maxLen = witnessV0ScriptHashLen
52)
53
54var (
55	// ErrUnsupportedScriptType is an error returned when we attempt to
56	// parse/re-compute an output script into a PkScript struct.
57	ErrUnsupportedScriptType = errors.New("unsupported script type")
58)
59
60// PkScript is a wrapper struct around a byte array, allowing it to be used
61// as a map index.
62type PkScript struct {
63	// class is the type of the script encoded within the byte array. This
64	// is used to determine the correct length of the script within the byte
65	// array.
66	class ScriptClass
67
68	// script is the script contained within a byte array. If the script is
69	// smaller than the length of the byte array, it will be padded with 0s
70	// at the end.
71	script [maxLen]byte
72}
73
74// ParsePkScript parses an output script into the PkScript struct.
75// ErrUnsupportedScriptType is returned when attempting to parse an unsupported
76// script type.
77func ParsePkScript(pkScript []byte) (PkScript, error) {
78	var outputScript PkScript
79	scriptClass, _, _, err := ExtractPkScriptAddrs(
80		pkScript, &chaincfg.MainNetParams,
81	)
82	if err != nil {
83		return outputScript, fmt.Errorf("unable to parse script type: "+
84			"%v", err)
85	}
86
87	if !isSupportedScriptType(scriptClass) {
88		return outputScript, ErrUnsupportedScriptType
89	}
90
91	outputScript.class = scriptClass
92	copy(outputScript.script[:], pkScript)
93
94	return outputScript, nil
95}
96
97// isSupportedScriptType determines whether the script type is supported by the
98// PkScript struct.
99func isSupportedScriptType(class ScriptClass) bool {
100	switch class {
101	case PubKeyHashTy, WitnessV0PubKeyHashTy, ScriptHashTy,
102		WitnessV0ScriptHashTy:
103		return true
104	default:
105		return false
106	}
107}
108
109// Class returns the script type.
110func (s PkScript) Class() ScriptClass {
111	return s.class
112}
113
114// Script returns the script as a byte slice without any padding.
115func (s PkScript) Script() []byte {
116	var script []byte
117
118	switch s.class {
119	case PubKeyHashTy:
120		script = make([]byte, pubKeyHashLen)
121		copy(script, s.script[:pubKeyHashLen])
122
123	case WitnessV0PubKeyHashTy:
124		script = make([]byte, witnessV0PubKeyHashLen)
125		copy(script, s.script[:witnessV0PubKeyHashLen])
126
127	case ScriptHashTy:
128		script = make([]byte, scriptHashLen)
129		copy(script, s.script[:scriptHashLen])
130
131	case WitnessV0ScriptHashTy:
132		script = make([]byte, witnessV0ScriptHashLen)
133		copy(script, s.script[:witnessV0ScriptHashLen])
134
135	default:
136		// Unsupported script type.
137		return nil
138	}
139
140	return script
141}
142
143// Address encodes the script into an address for the given chain.
144func (s PkScript) Address(chainParams *chaincfg.Params) (btcutil.Address, error) {
145	_, addrs, _, err := ExtractPkScriptAddrs(s.Script(), chainParams)
146	if err != nil {
147		return nil, fmt.Errorf("unable to parse address: %v", err)
148	}
149
150	return addrs[0], nil
151}
152
153// String returns a hex-encoded string representation of the script.
154func (s PkScript) String() string {
155	str, _ := DisasmString(s.Script())
156	return str
157}
158
159// ComputePkScript computes the script of an output by looking at the spending
160// input's signature script or witness.
161//
162// NOTE: Only P2PKH, P2SH, P2WSH, and P2WPKH redeem scripts are supported.
163func ComputePkScript(sigScript []byte, witness wire.TxWitness) (PkScript, error) {
164	switch {
165	case len(sigScript) > 0:
166		return computeNonWitnessPkScript(sigScript)
167	case len(witness) > 0:
168		return computeWitnessPkScript(witness)
169	default:
170		return PkScript{}, ErrUnsupportedScriptType
171	}
172}
173
174// computeNonWitnessPkScript computes the script of an output by looking at the
175// spending input's signature script.
176func computeNonWitnessPkScript(sigScript []byte) (PkScript, error) {
177	switch {
178	// Since we only support P2PKH and P2SH scripts as the only non-witness
179	// script types, we should expect to see a push only script.
180	case !IsPushOnlyScript(sigScript):
181		return PkScript{}, ErrUnsupportedScriptType
182
183	// If a signature script is provided with a length long enough to
184	// represent a P2PKH script, then we'll attempt to parse the compressed
185	// public key from it.
186	case len(sigScript) >= minPubKeyHashSigScriptLen &&
187		len(sigScript) <= maxPubKeyHashSigScriptLen:
188
189		// The public key should be found as the last part of the
190		// signature script. We'll attempt to parse it to ensure this is
191		// a P2PKH redeem script.
192		pubKey := sigScript[len(sigScript)-compressedPubKeyLen:]
193		if btcec.IsCompressedPubKey(pubKey) {
194			pubKeyHash := hash160(pubKey)
195			script, err := payToPubKeyHashScript(pubKeyHash)
196			if err != nil {
197				return PkScript{}, err
198			}
199
200			pkScript := PkScript{class: PubKeyHashTy}
201			copy(pkScript.script[:], script)
202			return pkScript, nil
203		}
204
205		fallthrough
206
207	// If we failed to parse a compressed public key from the script in the
208	// case above, or if the script length is not that of a P2PKH one, we
209	// can assume it's a P2SH signature script.
210	default:
211		// The redeem script will always be the last data push of the
212		// signature script, so we'll parse the script into opcodes to
213		// obtain it.
214		parsedOpcodes, err := parseScript(sigScript)
215		if err != nil {
216			return PkScript{}, err
217		}
218		redeemScript := parsedOpcodes[len(parsedOpcodes)-1].data
219
220		scriptHash := hash160(redeemScript)
221		script, err := payToScriptHashScript(scriptHash)
222		if err != nil {
223			return PkScript{}, err
224		}
225
226		pkScript := PkScript{class: ScriptHashTy}
227		copy(pkScript.script[:], script)
228		return pkScript, nil
229	}
230}
231
232// computeWitnessPkScript computes the script of an output by looking at the
233// spending input's witness.
234func computeWitnessPkScript(witness wire.TxWitness) (PkScript, error) {
235	// We'll use the last item of the witness stack to determine the proper
236	// witness type.
237	lastWitnessItem := witness[len(witness)-1]
238
239	var pkScript PkScript
240	switch {
241	// If the witness stack has a size of 2 and its last item is a
242	// compressed public key, then this is a P2WPKH witness.
243	case len(witness) == 2 && len(lastWitnessItem) == compressedPubKeyLen:
244		pubKeyHash := hash160(lastWitnessItem)
245		script, err := payToWitnessPubKeyHashScript(pubKeyHash)
246		if err != nil {
247			return pkScript, err
248		}
249
250		pkScript.class = WitnessV0PubKeyHashTy
251		copy(pkScript.script[:], script)
252
253	// For any other witnesses, we'll assume it's a P2WSH witness.
254	default:
255		scriptHash := sha256.Sum256(lastWitnessItem)
256		script, err := payToWitnessScriptHashScript(scriptHash[:])
257		if err != nil {
258			return pkScript, err
259		}
260
261		pkScript.class = WitnessV0ScriptHashTy
262		copy(pkScript.script[:], script)
263	}
264
265	return pkScript, nil
266}
267
268// hash160 returns the RIPEMD160 hash of the SHA-256 HASH of the given data.
269func hash160(data []byte) []byte {
270	h := sha256.Sum256(data)
271	return ripemd160h(h[:])
272}
273
274// ripemd160h returns the RIPEMD160 hash of the given data.
275func ripemd160h(data []byte) []byte {
276	h := ripemd160.New()
277	h.Write(data)
278	return h.Sum(nil)
279}
280