github.com/btcsuite/btcd@v0.24.0/txscript/hashcache.go (about) 1 // Copyright (c) 2016 The btcsuite developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package txscript 6 7 import ( 8 "bytes" 9 "encoding/binary" 10 "math" 11 "sync" 12 13 "github.com/btcsuite/btcd/chaincfg/chainhash" 14 "github.com/btcsuite/btcd/wire" 15 ) 16 17 // calcHashPrevOuts calculates a single hash of all the previous outputs 18 // (txid:index) referenced within the passed transaction. This calculated hash 19 // can be re-used when validating all inputs spending segwit outputs, with a 20 // signature hash type of SigHashAll. This allows validation to re-use previous 21 // hashing computation, reducing the complexity of validating SigHashAll inputs 22 // from O(N^2) to O(N). 23 func calcHashPrevOuts(tx *wire.MsgTx) chainhash.Hash { 24 var b bytes.Buffer 25 for _, in := range tx.TxIn { 26 // First write out the 32-byte transaction ID one of whose 27 // outputs are being referenced by this input. 28 b.Write(in.PreviousOutPoint.Hash[:]) 29 30 // Next, we'll encode the index of the referenced output as a 31 // little endian integer. 32 var buf [4]byte 33 binary.LittleEndian.PutUint32(buf[:], in.PreviousOutPoint.Index) 34 b.Write(buf[:]) 35 } 36 37 return chainhash.HashH(b.Bytes()) 38 } 39 40 // calcHashSequence computes an aggregated hash of each of the sequence numbers 41 // within the inputs of the passed transaction. This single hash can be re-used 42 // when validating all inputs spending segwit outputs, which include signatures 43 // using the SigHashAll sighash type. This allows validation to re-use previous 44 // hashing computation, reducing the complexity of validating SigHashAll inputs 45 // from O(N^2) to O(N). 46 func calcHashSequence(tx *wire.MsgTx) chainhash.Hash { 47 var b bytes.Buffer 48 for _, in := range tx.TxIn { 49 var buf [4]byte 50 binary.LittleEndian.PutUint32(buf[:], in.Sequence) 51 b.Write(buf[:]) 52 } 53 54 return chainhash.HashH(b.Bytes()) 55 } 56 57 // calcHashOutputs computes a hash digest of all outputs created by the 58 // transaction encoded using the wire format. This single hash can be re-used 59 // when validating all inputs spending witness programs, which include 60 // signatures using the SigHashAll sighash type. This allows computation to be 61 // cached, reducing the total hashing complexity from O(N^2) to O(N). 62 func calcHashOutputs(tx *wire.MsgTx) chainhash.Hash { 63 var b bytes.Buffer 64 for _, out := range tx.TxOut { 65 wire.WriteTxOut(&b, 0, 0, out) 66 } 67 68 return chainhash.HashH(b.Bytes()) 69 } 70 71 // PrevOutputFetcher is an interface used to supply the sighash cache with the 72 // previous output information needed to calculate the pre-computed sighash 73 // midstate for taproot transactions. 74 type PrevOutputFetcher interface { 75 // FetchPrevOutput attempts to fetch the previous output referenced by 76 // the passed outpoint. A nil value will be returned if the passed 77 // outpoint doesn't exist. 78 FetchPrevOutput(wire.OutPoint) *wire.TxOut 79 } 80 81 // CannedPrevOutputFetcher is an implementation of PrevOutputFetcher that only 82 // is able to return information for a single previous output. 83 type CannedPrevOutputFetcher struct { 84 pkScript []byte 85 amt int64 86 } 87 88 // NewCannedPrevOutputFetcher returns an instance of a CannedPrevOutputFetcher 89 // that can only return the TxOut defined by the passed script and amount. 90 func NewCannedPrevOutputFetcher(script []byte, amt int64) *CannedPrevOutputFetcher { 91 return &CannedPrevOutputFetcher{ 92 pkScript: script, 93 amt: amt, 94 } 95 } 96 97 // FetchPrevOutput attempts to fetch the previous output referenced by the 98 // passed outpoint. 99 // 100 // NOTE: This is a part of the PrevOutputFetcher interface. 101 func (c *CannedPrevOutputFetcher) FetchPrevOutput(wire.OutPoint) *wire.TxOut { 102 return &wire.TxOut{ 103 PkScript: c.pkScript, 104 Value: c.amt, 105 } 106 } 107 108 // A compile-time assertion to ensure that CannedPrevOutputFetcher matches the 109 // PrevOutputFetcher interface. 110 var _ PrevOutputFetcher = (*CannedPrevOutputFetcher)(nil) 111 112 // MultiPrevOutFetcher is a custom implementation of the PrevOutputFetcher 113 // backed by a key-value map of prevouts to outputs. 114 type MultiPrevOutFetcher struct { 115 prevOuts map[wire.OutPoint]*wire.TxOut 116 } 117 118 // NewMultiPrevOutFetcher returns an instance of a PrevOutputFetcher that's 119 // backed by an optional map which is used as an input source. The 120 func NewMultiPrevOutFetcher(prevOuts map[wire.OutPoint]*wire.TxOut) *MultiPrevOutFetcher { 121 if prevOuts == nil { 122 prevOuts = make(map[wire.OutPoint]*wire.TxOut) 123 } 124 125 return &MultiPrevOutFetcher{ 126 prevOuts: prevOuts, 127 } 128 } 129 130 // FetchPrevOutput attempts to fetch the previous output referenced by the 131 // passed outpoint. 132 // 133 // NOTE: This is a part of the CannedPrevOutputFetcher interface. 134 func (m *MultiPrevOutFetcher) FetchPrevOutput(op wire.OutPoint) *wire.TxOut { 135 return m.prevOuts[op] 136 } 137 138 // AddPrevOut adds a new prev out, tx out pair to the backing map. 139 func (m *MultiPrevOutFetcher) AddPrevOut(op wire.OutPoint, txOut *wire.TxOut) { 140 m.prevOuts[op] = txOut 141 } 142 143 // Merge merges two instances of a MultiPrevOutFetcher into a single source. 144 func (m *MultiPrevOutFetcher) Merge(other *MultiPrevOutFetcher) { 145 for k, v := range other.prevOuts { 146 m.prevOuts[k] = v 147 } 148 } 149 150 // A compile-time assertion to ensure that MultiPrevOutFetcher matches the 151 // PrevOutputFetcher interface. 152 var _ PrevOutputFetcher = (*MultiPrevOutFetcher)(nil) 153 154 // calcHashInputAmounts computes a hash digest of the input amounts of all 155 // inputs referenced in the passed transaction. This hash pre computation is only 156 // used for validating taproot inputs. 157 func calcHashInputAmounts(tx *wire.MsgTx, inputFetcher PrevOutputFetcher) chainhash.Hash { 158 var b bytes.Buffer 159 for _, txIn := range tx.TxIn { 160 prevOut := inputFetcher.FetchPrevOutput(txIn.PreviousOutPoint) 161 162 _ = binary.Write(&b, binary.LittleEndian, prevOut.Value) 163 } 164 165 return chainhash.HashH(b.Bytes()) 166 } 167 168 // calcHashInputAmts computes the hash digest of all the previous input scripts 169 // referenced by the passed transaction. This hash pre computation is only used 170 // for validating taproot inputs. 171 func calcHashInputScripts(tx *wire.MsgTx, inputFetcher PrevOutputFetcher) chainhash.Hash { 172 var b bytes.Buffer 173 for _, txIn := range tx.TxIn { 174 prevOut := inputFetcher.FetchPrevOutput(txIn.PreviousOutPoint) 175 176 _ = wire.WriteVarBytes(&b, 0, prevOut.PkScript) 177 } 178 179 return chainhash.HashH(b.Bytes()) 180 } 181 182 // SegwitSigHashMidstate is the sighash midstate used in the base segwit 183 // sighash calculation as defined in BIP 143. 184 type SegwitSigHashMidstate struct { 185 HashPrevOutsV0 chainhash.Hash 186 HashSequenceV0 chainhash.Hash 187 HashOutputsV0 chainhash.Hash 188 } 189 190 // TaprootSigHashMidState is the sighash midstate used to compute taproot and 191 // tapscript signatures as defined in BIP 341. 192 type TaprootSigHashMidState struct { 193 HashPrevOutsV1 chainhash.Hash 194 HashSequenceV1 chainhash.Hash 195 HashOutputsV1 chainhash.Hash 196 HashInputScriptsV1 chainhash.Hash 197 HashInputAmountsV1 chainhash.Hash 198 } 199 200 // TxSigHashes houses the partial set of sighashes introduced within BIP0143. 201 // This partial set of sighashes may be re-used within each input across a 202 // transaction when validating all inputs. As a result, validation complexity 203 // for SigHashAll can be reduced by a polynomial factor. 204 type TxSigHashes struct { 205 SegwitSigHashMidstate 206 207 TaprootSigHashMidState 208 } 209 210 // NewTxSigHashes computes, and returns the cached sighashes of the given 211 // transaction. 212 func NewTxSigHashes(tx *wire.MsgTx, 213 inputFetcher PrevOutputFetcher) *TxSigHashes { 214 215 var ( 216 sigHashes TxSigHashes 217 zeroHash chainhash.Hash 218 ) 219 220 // Base segwit (witness version v0), and taproot (witness version v1) 221 // differ in how the set of pre-computed cached sighash midstate is 222 // computed. For taproot, the prevouts, sequence, and outputs are 223 // computed as normal, but a single sha256 hash invocation is used. In 224 // addition, the hashes of all the previous input amounts and scripts 225 // are included as well. 226 // 227 // Based on the above distinction, we'll run through all the referenced 228 // inputs to determine what we need to compute. 229 var hasV0Inputs, hasV1Inputs bool 230 for _, txIn := range tx.TxIn { 231 // If this is a coinbase input, then we know that we only need 232 // the v0 midstate (though it won't be used) in this instance. 233 outpoint := txIn.PreviousOutPoint 234 if outpoint.Index == math.MaxUint32 && outpoint.Hash == zeroHash { 235 hasV0Inputs = true 236 continue 237 } 238 239 prevOut := inputFetcher.FetchPrevOutput(outpoint) 240 241 // If this is spending a script that looks like a taproot output, 242 // then we'll need to pre-compute the extra taproot data. 243 if IsPayToTaproot(prevOut.PkScript) { 244 hasV1Inputs = true 245 } else { 246 // Otherwise, we'll assume we need the v0 sighash midstate. 247 hasV0Inputs = true 248 } 249 250 // If the transaction has _both_ v0 and v1 inputs, then we can stop 251 // here. 252 if hasV0Inputs && hasV1Inputs { 253 break 254 } 255 } 256 257 // Now that we know which cached midstate we need to calculate, we can 258 // go ahead and do so. 259 // 260 // First, we can calculate the information that both segwit v0 and v1 261 // need: the prevout, sequence and output hashes. For v1 the only 262 // difference is that this is a single instead of a double hash. 263 // 264 // Both v0 and v1 share this base data computed using a sha256 single 265 // hash. 266 sigHashes.HashPrevOutsV1 = calcHashPrevOuts(tx) 267 sigHashes.HashSequenceV1 = calcHashSequence(tx) 268 sigHashes.HashOutputsV1 = calcHashOutputs(tx) 269 270 // The v0 data is the same as the v1 (newer data) but it uses a double 271 // hash instead. 272 if hasV0Inputs { 273 sigHashes.HashPrevOutsV0 = chainhash.HashH( 274 sigHashes.HashPrevOutsV1[:], 275 ) 276 sigHashes.HashSequenceV0 = chainhash.HashH( 277 sigHashes.HashSequenceV1[:], 278 ) 279 sigHashes.HashOutputsV0 = chainhash.HashH( 280 sigHashes.HashOutputsV1[:], 281 ) 282 } 283 284 // Finally, we'll compute the taproot specific data if needed. 285 if hasV1Inputs { 286 sigHashes.HashInputAmountsV1 = calcHashInputAmounts( 287 tx, inputFetcher, 288 ) 289 sigHashes.HashInputScriptsV1 = calcHashInputScripts( 290 tx, inputFetcher, 291 ) 292 } 293 294 return &sigHashes 295 } 296 297 // HashCache houses a set of partial sighashes keyed by txid. The set of partial 298 // sighashes are those introduced within BIP0143 by the new more efficient 299 // sighash digest calculation algorithm. Using this threadsafe shared cache, 300 // multiple goroutines can safely re-use the pre-computed partial sighashes 301 // speeding up validation time amongst all inputs found within a block. 302 type HashCache struct { 303 sigHashes map[chainhash.Hash]*TxSigHashes 304 305 sync.RWMutex 306 } 307 308 // NewHashCache returns a new instance of the HashCache given a maximum number 309 // of entries which may exist within it at anytime. 310 func NewHashCache(maxSize uint) *HashCache { 311 return &HashCache{ 312 sigHashes: make(map[chainhash.Hash]*TxSigHashes, maxSize), 313 } 314 } 315 316 // AddSigHashes computes, then adds the partial sighashes for the passed 317 // transaction. 318 func (h *HashCache) AddSigHashes(tx *wire.MsgTx, 319 inputFetcher PrevOutputFetcher) { 320 321 h.Lock() 322 h.sigHashes[tx.TxHash()] = NewTxSigHashes(tx, inputFetcher) 323 h.Unlock() 324 } 325 326 // ContainsHashes returns true if the partial sighashes for the passed 327 // transaction currently exist within the HashCache, and false otherwise. 328 func (h *HashCache) ContainsHashes(txid *chainhash.Hash) bool { 329 h.RLock() 330 _, found := h.sigHashes[*txid] 331 h.RUnlock() 332 333 return found 334 } 335 336 // GetSigHashes possibly returns the previously cached partial sighashes for 337 // the passed transaction. This function also returns an additional boolean 338 // value indicating if the sighashes for the passed transaction were found to 339 // be present within the HashCache. 340 func (h *HashCache) GetSigHashes(txid *chainhash.Hash) (*TxSigHashes, bool) { 341 h.RLock() 342 item, found := h.sigHashes[*txid] 343 h.RUnlock() 344 345 return item, found 346 } 347 348 // PurgeSigHashes removes all partial sighashes from the HashCache belonging to 349 // the passed transaction. 350 func (h *HashCache) PurgeSigHashes(txid *chainhash.Hash) { 351 h.Lock() 352 delete(h.sigHashes, *txid) 353 h.Unlock() 354 }