// 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)
}
