dcrd/txscript/sighash.go

459 lines
17 KiB
Go
Raw Permalink Normal View History

// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package txscript
import (
"encoding/binary"
"fmt"
"math"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/wire"
)
// SigHashType represents hash type bits at the end of a signature.
type SigHashType byte
// Hash type bits from the end of a signature.
const (
SigHashAll SigHashType = 0x1
SigHashNone SigHashType = 0x2
SigHashSingle SigHashType = 0x3
SigHashAnyOneCanPay SigHashType = 0x80
// sigHashMask defines the number of bits of the hash type which is used
// to identify which outputs are signed.
sigHashMask = 0x1f
)
// SigHashSerType represents the serialization type used when calculating
// signature hashes.
//
// NOTE: These values were originally a part of transaction serialization which
// is why there is a gap and they are not zero based. The logic for calculating
// signature hashes has since been decoupled from transaction serialization
// logic, but these specific values are still required by consensus, so they
// must remain unchanged.
type SigHashSerType uint16
const (
// SigHashSerializePrefix indicates the serialization does not include
// any witness data.
SigHashSerializePrefix = 1
// SigHashSerializeWitness indicates the serialization only contains
// witness data.
SigHashSerializeWitness = 3
)
// -----------------------------------------------------------------------------
// A variable length integer (varint) is an encoding for integers up to a max
// value of 2^64-1 that uses a variable number of bytes depending on the value
// being encoded. It produces fewer bytes for smaller numbers as opposed to a
// fixed-size encoding and is used within the signature hash algorithm to
// specify the number of items or bytes that follow it.
//
// The encoding is as follows:
//
// Value Len Format
// ----- --- ------
// < 0xfd 1 val as uint8
// <= 0xffff 3 0xfd followed by val as little-endian uint16
// <= 0xffffffff 5 0xfe followed by val as little-endian uint32
// <= 0xffffffffffffffff 9 0xff followed by val as little-endian uint64
//
// Example encodings:
// 0 -> [0x00]
// 252 -> [0xfc] * Max 1-byte encoded value
// 253 -> [0xfdfd00] * Min 3-byte encoded value
// 254 -> [0xfdfe00]
// 256 -> [0xfd0001]
// 65535 -> [0xfdffff] * Max 3-byte encoded value
// 65536 -> [0xfe00000100] * Min 5-byte encoded value
// 131071 -> [0xfeffff0100]
// 4294967295 -> [0xfeffffffff] * Max 5-byte encoded value
// 4294967296 -> [0xff0000000001000000] * Min 9-byte encoded value
// 2^64-1 -> [0xffffffffffffffffff] * Max allowed value
// -----------------------------------------------------------------------------
// varIntSerializeSize returns the number of bytes it would take to serialize
// the provided value as a variable length integer according to the format
// described above.
func varIntSerializeSize(val uint64) int {
// The value is small enough to be represented by itself.
if val < 0xfd {
return 1
}
// Discriminant 1 byte plus 2 bytes for the uint16.
if val <= math.MaxUint16 {
return 3
}
// Discriminant 1 byte plus 4 bytes for the uint32.
if val <= math.MaxUint32 {
return 5
}
// Discriminant 1 byte plus 8 bytes for the uint64.
return 9
}
// putVarInt serializes the provided number to a variable-length integer and
// according to the format described above returns the number of bytes of the
// encoded value. The result is placed directly into the passed byte slice
// which must be at least large enough to handle the number of bytes returned by
// the varIntSerializeSize function or it will panic.
func putVarInt(buf []byte, val uint64) int {
if val < 0xfd {
buf[0] = uint8(val)
return 1
}
if val <= math.MaxUint16 {
buf[0] = 0xfd
binary.LittleEndian.PutUint16(buf[1:], uint16(val))
return 3
}
if val <= math.MaxUint32 {
buf[0] = 0xfe
binary.LittleEndian.PutUint32(buf[1:], uint32(val))
return 5
}
buf[0] = 0xff
binary.LittleEndian.PutUint64(buf[1:], val)
return 9
}
// putByte writes the passed byte to the provided slice and returns 1 to signify
// the number of bytes written. The target byte slice must be at least large
// enough to handle the write or it will panic.
func putByte(buf []byte, val byte) int {
buf[0] = val
return 1
}
// putUint16LE writes the provided uint16 as little endian to the provided slice
// and returns 2 to signify the number of bytes written. The target byte slice
// must be at least large enough to handle the write or it will panic.
func putUint16LE(buf []byte, val uint16) int {
binary.LittleEndian.PutUint16(buf, val)
return 2
}
// putUint32LE writes the provided uint32 as little endian to the provided slice
// and returns 4 to signify the number of bytes written. The target byte slice
// must be at least large enough to handle the write or it will panic.
func putUint32LE(buf []byte, val uint32) int {
binary.LittleEndian.PutUint32(buf, val)
return 4
}
// putUint64LE writes the provided uint64 as little endian to the provided slice
// and returns 8 to signify the number of bytes written. The target byte slice
// must be at least large enough to handle the write or it will panic.
func putUint64LE(buf []byte, val uint64) int {
binary.LittleEndian.PutUint64(buf, val)
return 8
}
// sigHashPrefixSerializeSize returns the number of bytes the passed parameters
// would take when encoded with the format used by the prefix hash portion of
// the overall signature hash.
func sigHashPrefixSerializeSize(hashType SigHashType, txIns []*wire.TxIn, txOuts []*wire.TxOut, signIdx int) int {
// 1) 4 bytes version/serialization type
// 2) number of inputs varint
// 3) per input:
// a) 32 bytes prevout hash
// b) 4 bytes prevout index
// c) 1 byte prevout tree
// d) 4 bytes sequence
// 4) number of outputs varint
// 5) per output:
// a) 8 bytes amount
// b) 2 bytes script version
// c) pkscript len varint (1 byte if not SigHashSingle output)
// d) N bytes pkscript (0 bytes if not SigHashSingle output)
// 6) 4 bytes lock time
// 7) 4 bytes expiry
numTxIns := len(txIns)
numTxOuts := len(txOuts)
size := 4 + varIntSerializeSize(uint64(numTxIns)) +
numTxIns*(chainhash.HashSize+4+1+4) +
varIntSerializeSize(uint64(numTxOuts)) +
numTxOuts*(8+2) + 4 + 4
for txOutIdx, txOut := range txOuts {
pkScript := txOut.PkScript
if hashType&sigHashMask == SigHashSingle && txOutIdx != signIdx {
pkScript = nil
}
size += varIntSerializeSize(uint64(len(pkScript)))
size += len(pkScript)
}
return size
}
// sigHashWitnessSerializeSize returns the number of bytes the passed parameters
// would take when encoded with the format used by the witness hash portion of
// the overall signature hash.
func sigHashWitnessSerializeSize(txIns []*wire.TxIn, signScript []byte) int {
// 1) 4 bytes version/serialization type
// 2) number of inputs varint
// 3) per input:
// a) prevout pkscript varint (1 byte if not input being signed)
// b) N bytes prevout pkscript (0 bytes if not input being signed)
//
// NOTE: The prevout pkscript is replaced by nil for all inputs except
// the input being signed. Thus, all other inputs (aka numTxIns-1) commit
// to a nil script which gets encoded as a single 0x00 byte. This is
// because encoding 0 as a varint results in 0x00 and there is no script
// to write. So, rather than looping through all inputs and manually
// calculating the size per input, use (numTxIns - 1) as an
// optimization.
numTxIns := len(txIns)
return 4 + varIntSerializeSize(uint64(numTxIns)) + (numTxIns - 1) +
varIntSerializeSize(uint64(len(signScript))) +
len(signScript)
}
// calcSignatureHash computes the signature hash for the specified input of the
// target transaction observing the desired signature hash type. The cached
// prefix parameter allows the caller to optimize the calculation by providing
// the prefix hash to be reused in the case of SigHashAll without the
// SigHashAnyOneCanPay flag set.
func calcSignatureHash(signScript []byte, hashType SigHashType, tx *wire.MsgTx, idx int, cachedPrefix *chainhash.Hash) ([]byte, error) {
// The SigHashSingle signature type signs only the corresponding input
// and output (the output with the same index number as the input).
//
// Since transactions can have more inputs than outputs, this means it
// is improper to use SigHashSingle on input indices that don't have a
// corresponding output.
if hashType&sigHashMask == SigHashSingle && idx >= len(tx.TxOut) {
str := fmt.Sprintf("attempt to sign single input at index %d "+
">= %d outputs", idx, len(tx.TxOut))
return nil, scriptError(ErrInvalidSigHashSingleIndex, str)
}
// Choose the inputs that will be committed to based on the signature
// hash type.
//
// The SigHashAnyOneCanPay flag specifies that the signature will only
// commit to the input being signed. Otherwise, it will commit to all
// inputs.
txIns := tx.TxIn
signTxInIdx := idx
if hashType&SigHashAnyOneCanPay != 0 {
txIns = tx.TxIn[idx : idx+1]
signTxInIdx = 0
}
// The prefix hash commits to the non-witness data depending on the
// signature hash type. In particular, the specific inputs and output
// semantics which are committed to are modified depending on the
// signature hash type as follows:
//
// SigHashAll (and undefined signature hash types):
// Commits to all outputs.
// SigHashNone:
// Commits to no outputs with all input sequences except the input
// being signed replaced with 0.
// SigHashSingle:
// Commits to a single output at the same index as the input being
// signed. All outputs before that index are cleared by setting the
// value to -1 and pkscript to nil and all outputs after that index
// are removed. Like SigHashNone, all input sequences except the
// input being signed are replaced by 0.
// SigHashAnyOneCanPay:
// Commits to only the input being signed. Bit flag that can be
// combined with the other signature hash types. Without this flag
// set, commits to all inputs.
//
// With the relevant inputs and outputs selected and the aforementioned
// substitutions, the prefix hash consists of the hash of the
// serialization of the following fields:
//
// 1) txversion|(SigHashSerializePrefix<<16) (as little-endian uint32)
// 2) number of inputs (as varint)
// 3) per input:
// a) prevout hash (as little-endian uint256)
// b) prevout index (as little-endian uint32)
// c) prevout tree (as single byte)
// d) sequence (as little-endian uint32)
// 4) number of outputs (as varint)
// 5) per output:
// a) output amount (as little-endian uint64)
// b) pkscript version (as little-endian uint16)
// c) pkscript length (as varint)
// d) pkscript (as unmodified bytes)
// 6) transaction lock time (as little-endian uint32)
// 7) transaction expiry (as little-endian uint32)
//
// In addition, an optimization for SigHashAll is provided when the
// SigHashAnyOneCanPay flag is not set. In that case, the prefix hash
// can be reused because only the witness data has been modified, so
// the wasteful extra O(N^2) hash can be avoided.
var prefixHash chainhash.Hash
if optimizeSigVerification && cachedPrefix != nil &&
hashType&sigHashMask == SigHashAll &&
hashType&SigHashAnyOneCanPay == 0 {
prefixHash = *cachedPrefix
} else {
// Choose the outputs to commit to based on the signature hash
// type.
//
// As the names imply, SigHashNone commits to no outputs and
// SigHashSingle commits to the single output that corresponds
// to the input being signed. However, SigHashSingle is also a
// bit special in that it commits to cleared out variants of all
// outputs prior to the one being signed. This is required by
// consensus due to legacy reasons.
//
// All other signature hash types, such as SighHashAll commit to
// all outputs. Note that this includes undefined hash types as well.
txOuts := tx.TxOut
switch hashType & sigHashMask {
case SigHashNone:
txOuts = nil
case SigHashSingle:
txOuts = tx.TxOut[:idx+1]
default:
fallthrough
case SigHashAll:
// Nothing special here.
}
size := sigHashPrefixSerializeSize(hashType, txIns, txOuts, idx)
prefixBuf := make([]byte, size)
// Commit to the version and hash serialization type.
version := uint32(tx.Version) | uint32(SigHashSerializePrefix)<<16
offset := putUint32LE(prefixBuf, version)
// Commit to the relevant transaction inputs.
offset += putVarInt(prefixBuf[offset:], uint64(len(txIns)))
for txInIdx, txIn := range txIns {
// Commit to the outpoint being spent.
prevOut := &txIn.PreviousOutPoint
offset += copy(prefixBuf[offset:], prevOut.Hash[:])
offset += putUint32LE(prefixBuf[offset:], prevOut.Index)
offset += putByte(prefixBuf[offset:], byte(prevOut.Tree))
// Commit to the sequence. In the case of SigHashNone
// and SigHashSingle, commit to 0 for everything that is
// not the input being signed instead.
sequence := txIn.Sequence
if (hashType&sigHashMask == SigHashNone ||
hashType&sigHashMask == SigHashSingle) &&
txInIdx != signTxInIdx {
sequence = 0
}
offset += putUint32LE(prefixBuf[offset:], sequence)
}
// Commit to the relevant transaction outputs.
offset += putVarInt(prefixBuf[offset:], uint64(len(txOuts)))
for txOutIdx, txOut := range txOuts {
// Commit to the output amount, script version, and
// public key script. In the case of SigHashSingle,
// commit to an output amount of -1 and a nil public
// key script for everything that is not the output
// corresponding to the input being signed instead.
value := txOut.Value
pkScript := txOut.PkScript
if hashType&sigHashMask == SigHashSingle && txOutIdx != idx {
value = -1
pkScript = nil
}
offset += putUint64LE(prefixBuf[offset:], uint64(value))
offset += putUint16LE(prefixBuf[offset:], txOut.Version)
offset += putVarInt(prefixBuf[offset:], uint64(len(pkScript)))
offset += copy(prefixBuf[offset:], pkScript)
}
// Commit to the lock time and expiry.
offset += putUint32LE(prefixBuf[offset:], tx.LockTime)
putUint32LE(prefixBuf[offset:], tx.Expiry)
prefixHash = chainhash.HashH(prefixBuf)
}
// The witness hash commits to the input witness data depending on
// whether or not the signature hash type has the SigHashAnyOneCanPay
// flag set. In particular the semantics are as follows:
//
// SigHashAnyOneCanPay:
// Commits to only the input being signed. Without this flag set,
// commits to all inputs with the reference scripts cleared by setting
// them to nil.
//
// With the relevant inputs selected, and the aforementioned substitutions,
// the witness hash consists of the hash of the serialization of the
// following fields:
//
// 1) txversion|(SigHashSerializeWitness<<16) (as little-endian uint32)
// 2) number of inputs (as varint)
// 3) per input:
// a) length of prevout pkscript (as varint)
// b) prevout pkscript (as unmodified bytes)
size := sigHashWitnessSerializeSize(txIns, signScript)
witnessBuf := make([]byte, size)
// Commit to the version and hash serialization type.
version := uint32(tx.Version) | uint32(SigHashSerializeWitness)<<16
offset := putUint32LE(witnessBuf, version)
// Commit to the relevant transaction inputs.
offset += putVarInt(witnessBuf[offset:], uint64(len(txIns)))
for txInIdx := range txIns {
// Commit to the input script at the index corresponding to the
// input index being signed. Otherwise, commit to a nil script
// instead.
commitScript := signScript
if txInIdx != signTxInIdx {
commitScript = nil
}
offset += putVarInt(witnessBuf[offset:], uint64(len(commitScript)))
offset += copy(witnessBuf[offset:], commitScript)
}
witnessHash := chainhash.HashH(witnessBuf)
// The final signature hash (message to sign) is the hash of the
// serialization of the following fields:
//
// 1) the hash type (as little-endian uint32)
// 2) prefix hash (as produced by hash function)
// 3) witness hash (as produced by hash function)
sigHashBuf := make([]byte, chainhash.HashSize*2+4)
offset = putUint32LE(sigHashBuf, uint32(hashType))
offset += copy(sigHashBuf[offset:], prefixHash[:])
copy(sigHashBuf[offset:], witnessHash[:])
return chainhash.HashB(sigHashBuf), nil
}
// CalcSignatureHash computes the signature hash for the specified input of
// the target transaction observing the desired signature hash type. The
// cached prefix parameter allows the caller to optimize the calculation by
// providing the prefix hash to be reused in the case of SigHashAll without the
// SigHashAnyOneCanPay flag set.
//
// NOTE: This function is only valid for version 0 scripts. Since the function
// does not accept a script version, the results are undefined for other script
// versions.
func CalcSignatureHash(script []byte, hashType SigHashType, tx *wire.MsgTx, idx int, cachedPrefix *chainhash.Hash) ([]byte, error) {
const scriptVersion = 0
if err := checkScriptParses(scriptVersion, script); err != nil {
return nil, err
}
return calcSignatureHash(script, hashType, tx, idx, cachedPrefix)
}