Appearance
比特币 script signature 包含了那些东西?
使用 UTXO 需要私钥签名,私钥到底都签了什么东西呢?一直比较好奇. 比特币的私钥签名总共有五中类型,具体见 btcd 代码,如下:
go
// SigHashType represents hash type bits at the end of a signature.
type SigHashType uint32
// Hash type bits from the end of a signature.
const (
SigHashOld SigHashType = 0x0
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
)
SigHashOld 和 SigHashAll
从代码看,两者是一样的.具体签名内容见图.
签名内容计算方式
go
// calcSignatureHash will, given a script and hash type for the current script
// engine instance, calculate the signature hash to be used for signing and
// verification.
//script 是引用的outpoint对应的输出的锁定脚本
//SigHashType 可能是
// SigHashOld SigHashType = 0x0
// SigHashAll SigHashType = 0x1
// SigHashNone SigHashType = 0x2
// SigHashSingle SigHashType = 0x3
// SigHashAnyOneCanPay SigHashType = 0x80
//tx是这整个交易
//idx 是交易中输入部分的编号
func calcSignatureHash(script []parsedOpcode, hashType SigHashType, tx *wire.MsgTx, idx int) []byte {
// 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.
//
// A bug in the original Satoshi client implementation means specifying
// an index that is out of range results in a signature hash of 1 (as a
// uint256 little endian). The original intent appeared to be to
// indicate failure, but unfortunately, it was never checked and thus is
// treated as the actual signature hash. This buggy behavior is now
// part of the consensus and a hard fork would be required to fix it.
//
// Due to this, care must be taken by software that creates transactions
// which make use of SigHashSingle because it can lead to an extremely
// dangerous situation where the invalid inputs will end up signing a
// hash of 1. This in turn presents an opportunity for attackers to
// cleverly construct transactions which can steal those coins provided
// they can reuse signatures.
if hashType&sigHashMask == SigHashSingle && idx >= len(tx.TxOut) {
var hash chainhash.Hash
hash[0] = 0x01
return hash[:]
}
// Remove all instances of OP_CODESEPARATOR from 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 {
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 = nil
}
}
switch hashType & sigHashMask {
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:
// 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 = nil
}
// Sequence on all other inputs is 0, too.
for i := range txCopy.TxIn {
if i != idx {
txCopy.TxIn[i].Sequence = 0
}
}
default:
// Consensus 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]
}
// The final hash is the double sha256 of both the serialized modified
// transaction and the hash type (encoded as a 4-byte little-endian
// value) appended.
wbuf := bytes.NewBuffer(make([]byte, 0, txCopy.SerializeSizeStripped()+4))
txCopy.SerializeNoWitness(wbuf)
binary.Write(wbuf, binary.LittleEndian, hashType)
return chainhash.DoubleHashB(wbuf.Bytes())
}