package tx import ( "crypto/sha256" "encoding/binary" "errors" ) // Errors the sighash refuses. var ( // ErrSighashType is a signature hash type this wallet does not produce. Only SIGHASH_ALL // is implemented: a swap that could be re-signed with a different type would change what // it commits to, and nothing here needs that. ErrSighashType = errors.New("tx: only SIGHASH_ALL is supported") // ErrInputIndex is an input that is not in the transaction. ErrInputIndex = errors.New("tx: no such input") ) // SighashAll is the only signature hash type the wallet signs. const SighashAll uint32 = 1 // P2WPKHScriptCode is the scriptCode BIP143 uses for a P2WPKH input: the pay-to-pubkey-hash // script the witness program stands for, not the program itself. It is the bare 25-byte // script — the length prefix in the preimage is the codec's, not part of this. func P2WPKHScriptCode(program [20]byte) []byte { out := []byte{0x76, 0xa9, 0x14} out = append(out, program[:]...) return append(out, 0x88, 0xac) } // SighashV0 is the BIP143 digest for input `index` of a segwit spend. // // version | hashPrevouts | hashSequence | outpoint | scriptCode | amount | // sequence | hashOutputs | locktime | sighashType // // The digest commits to every input's outpoint and sequence and to every output, which is what // makes it safe to sign without the other inputs' scripts — the property that lets a wallet // sign an input it did not build. `amount` is the value being spent, and it is in the digest // because BIP143 added it: a signature that did not commit to the amount would let a // counterparty claim a different value. func (t *Tx) SighashV0(index int, scriptCode []byte, amount uint64, sighashType uint32) ([32]byte, error) { if sighashType != SighashAll { return [32]byte{}, ErrSighashType } if index < 0 || index >= len(t.Inputs) { return [32]byte{}, ErrInputIndex } prevouts, err := t.hashPrevouts() if err != nil { return [32]byte{}, err } sequences, err := t.hashSequences() if err != nil { return [32]byte{}, err } outputs, err := t.hashOutputs() if err != nil { return [32]byte{}, err } input := t.Inputs[index] preimage := make([]byte, 0, 156+len(scriptCode)) preimage = binary.LittleEndian.AppendUint32(preimage, t.Version) preimage = append(preimage, prevouts[:]...) preimage = append(preimage, sequences[:]...) preimage = append(preimage, input.Hash[:]...) preimage = binary.LittleEndian.AppendUint32(preimage, input.Index) preimage = appendCompactSize(preimage, len(scriptCode)) preimage = append(preimage, scriptCode...) preimage = binary.LittleEndian.AppendUint64(preimage, amount) preimage = binary.LittleEndian.AppendUint32(preimage, input.Sequence) preimage = append(preimage, outputs[:]...) preimage = binary.LittleEndian.AppendUint32(preimage, t.LockTime) preimage = binary.LittleEndian.AppendUint32(preimage, sighashType) return doubleSHA256(preimage), nil } // hashPrevouts is the double SHA-256 of every input's outpoint. func (t *Tx) hashPrevouts() ([32]byte, error) { if len(t.Inputs) == 0 { return [32]byte{}, ErrNoInputs } raw := make([]byte, 0, 36*len(t.Inputs)) for _, input := range t.Inputs { raw = append(raw, input.Hash[:]...) raw = binary.LittleEndian.AppendUint32(raw, input.Index) } return doubleSHA256(raw), nil } // hashSequences is the double SHA-256 of every input's sequence. func (t *Tx) hashSequences() ([32]byte, error) { if len(t.Inputs) == 0 { return [32]byte{}, ErrNoInputs } raw := make([]byte, 0, 4*len(t.Inputs)) for _, input := range t.Inputs { raw = binary.LittleEndian.AppendUint32(raw, input.Sequence) } return doubleSHA256(raw), nil } // hashOutputs is the double SHA-256 of every output, serialised as the transaction does. func (t *Tx) hashOutputs() ([32]byte, error) { if len(t.Outputs) == 0 { return [32]byte{}, ErrNoOutputs } raw := make([]byte, 0, 34*len(t.Outputs)) for _, output := range t.Outputs { raw = binary.LittleEndian.AppendUint64(raw, output.Value) raw = appendCompactSize(raw, len(output.Script)) raw = append(raw, output.Script...) } return doubleSHA256(raw), nil } // doubleSHA256 is Bitcoin's hash. func doubleSHA256(raw []byte) [32]byte { first := sha256.Sum256(raw) return sha256.Sum256(first[:]) }