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