// calcScriptHash will, given the a script and hashtype for the current // scriptmachine, calculate the doubleSha256 hash of the transaction and // script to be used for signature signing and verification. func calcScriptHash(script []parsedOpcode, hashType byte, tx *btcwire.MsgTx, idx int) []byte { // remove all instances of OP_CODESEPARATOR still left in the script script = removeOpcode(script, OP_CODESEPARATOR) // Make a deep copy of the transaction, zeroing out the script // for all inputs that are not currently being processed. txCopy := tx.Copy() for i := range txCopy.TxIn { var txIn btcwire.TxIn txIn = *txCopy.TxIn[i] txCopy.TxIn[i] = &txIn if i == idx { // unparseScript cannot fail here, because removeOpcode // above only returns a valid script. sigscript, _ := unparseScript(script) txCopy.TxIn[idx].SignatureScript = sigscript } else { txCopy.TxIn[i].SignatureScript = []byte{} } } // Default behaviour has all outputs set up. for i := range txCopy.TxOut { var txOut btcwire.TxOut txOut = *txCopy.TxOut[i] txCopy.TxOut[i] = &txOut } switch hashType & 31 { case SigHashNone: txCopy.TxOut = txCopy.TxOut[0:0] // empty slice for i := range txCopy.TxIn { if i != idx { txCopy.TxIn[i].Sequence = 0 } } case SigHashSingle: if idx >= len(txCopy.TxOut) { // This was created by a buggy implementation. // In this case we do the same as bitcoind and bitcoinj // and return 1 (as a uint256 little endian) as an // error. Unfortunately this was not checked anywhere // and thus is treated as the actual // hash. hash := make([]byte, 32) hash[0] = 0x01 return hash } // Resize output array to up to and including requested index. txCopy.TxOut = txCopy.TxOut[:idx+1] // all but current output get zeroed out for i := 0; i < idx; i++ { txCopy.TxOut[i].Value = -1 txCopy.TxOut[i].PkScript = []byte{} } // Sequence on all other inputs is 0, too. for i := range txCopy.TxIn { if i != idx { txCopy.TxIn[i].Sequence = 0 } } default: // XXX bitcoind treats undefined hashtypes like normal // SigHashAll for purposes of hash generation. fallthrough case SigHashOld: fallthrough case SigHashAll: // nothing special here } if hashType&SigHashAnyOneCanPay != 0 { txCopy.TxIn = txCopy.TxIn[idx : idx+1] idx = 0 } var wbuf bytes.Buffer txCopy.Serialize(&wbuf) // Append LE 4 bytes hash type binary.Write(&wbuf, binary.LittleEndian, uint32(hashType)) return btcwire.DoubleSha256(wbuf.Bytes()) }