mirror of
https://github.com/FlipsideCrypto/dcrd.git
synced 2026-02-06 10:56:47 +00:00
This breaks the dependency on chaincfg.SigHashOptimization which is no longer available in v2 of the chaincfg module. The constant is set to false to ensure the same semantics are kept and an additional comment has been added regarding the status.
459 lines
17 KiB
Go
459 lines
17 KiB
Go
// 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
|
|
// substitions, 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)
|
|
}
|