mirror of
https://github.com/btcsuite/btcd.git
synced 2025-02-24 06:47:59 +01:00
txscript: use DoubleHashRaw to write directly crypto.Hash for segwit sighash
In this commit, we optimize the sighash calc further by writing directly into the buffer used for serialization by the sha256.New() instance rather than to an intermediate buffer, which is then write to the hash buffer.
This commit is contained in:
parent
c3c3545f9b
commit
adfb641a36
1 changed files with 98 additions and 96 deletions
|
@ -205,103 +205,105 @@ func calcWitnessSignatureHashRaw(subScript []byte, sigHashes *TxSigHashes,
|
||||||
return nil, fmt.Errorf("idx %d but %d txins", idx, len(tx.TxIn))
|
return nil, fmt.Errorf("idx %d but %d txins", idx, len(tx.TxIn))
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll utilize this buffer throughout to incrementally calculate
|
sigHashBytes := chainhash.DoubleHashRaw(func(w io.Writer) error {
|
||||||
// the signature hash for this transaction.
|
// First write out, then encode the transaction's version
|
||||||
var sigHash bytes.Buffer
|
// number.
|
||||||
|
|
||||||
// First write out, then encode the transaction's version number.
|
|
||||||
var bVersion [4]byte
|
var bVersion [4]byte
|
||||||
binary.LittleEndian.PutUint32(bVersion[:], uint32(tx.Version))
|
binary.LittleEndian.PutUint32(bVersion[:], uint32(tx.Version))
|
||||||
sigHash.Write(bVersion[:])
|
w.Write(bVersion[:])
|
||||||
|
|
||||||
// Next write out the possibly pre-calculated hashes for the sequence
|
// Next write out the possibly pre-calculated hashes for the
|
||||||
// numbers of all inputs, and the hashes of the previous outs for all
|
// sequence numbers of all inputs, and the hashes of the
|
||||||
// outputs.
|
// previous outs for all outputs.
|
||||||
var zeroHash chainhash.Hash
|
var zeroHash chainhash.Hash
|
||||||
|
|
||||||
// If anyone can pay isn't active, then we can use the cached
|
// If anyone can pay isn't active, then we can use the cached
|
||||||
// hashPrevOuts, otherwise we just write zeroes for the prev outs.
|
// hashPrevOuts, otherwise we just write zeroes for the prev
|
||||||
|
// outs.
|
||||||
if hashType&SigHashAnyOneCanPay == 0 {
|
if hashType&SigHashAnyOneCanPay == 0 {
|
||||||
sigHash.Write(sigHashes.HashPrevOutsV0[:])
|
w.Write(sigHashes.HashPrevOutsV0[:])
|
||||||
} else {
|
} else {
|
||||||
sigHash.Write(zeroHash[:])
|
w.Write(zeroHash[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the sighash isn't anyone can pay, single, or none, the use the
|
// If the sighash isn't anyone can pay, single, or none, the
|
||||||
// cached hash sequences, otherwise write all zeroes for the
|
// use the cached hash sequences, otherwise write all zeroes
|
||||||
// hashSequence.
|
// for the hashSequence.
|
||||||
if hashType&SigHashAnyOneCanPay == 0 &&
|
if hashType&SigHashAnyOneCanPay == 0 &&
|
||||||
hashType&sigHashMask != SigHashSingle &&
|
hashType&sigHashMask != SigHashSingle &&
|
||||||
hashType&sigHashMask != SigHashNone {
|
hashType&sigHashMask != SigHashNone {
|
||||||
sigHash.Write(sigHashes.HashSequenceV0[:])
|
|
||||||
|
w.Write(sigHashes.HashSequenceV0[:])
|
||||||
} else {
|
} else {
|
||||||
sigHash.Write(zeroHash[:])
|
w.Write(zeroHash[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
txIn := tx.TxIn[idx]
|
txIn := tx.TxIn[idx]
|
||||||
|
|
||||||
// Next, write the outpoint being spent.
|
// Next, write the outpoint being spent.
|
||||||
sigHash.Write(txIn.PreviousOutPoint.Hash[:])
|
w.Write(txIn.PreviousOutPoint.Hash[:])
|
||||||
var bIndex [4]byte
|
var bIndex [4]byte
|
||||||
binary.LittleEndian.PutUint32(bIndex[:], txIn.PreviousOutPoint.Index)
|
binary.LittleEndian.PutUint32(
|
||||||
sigHash.Write(bIndex[:])
|
bIndex[:], txIn.PreviousOutPoint.Index,
|
||||||
|
)
|
||||||
|
w.Write(bIndex[:])
|
||||||
|
|
||||||
if isWitnessPubKeyHashScript(subScript) {
|
if isWitnessPubKeyHashScript(subScript) {
|
||||||
// The script code for a p2wkh is a length prefix varint for
|
// The script code for a p2wkh is a length prefix
|
||||||
// the next 25 bytes, followed by a re-creation of the original
|
// varint for the next 25 bytes, followed by a
|
||||||
// p2pkh pk script.
|
// re-creation of the original p2pkh pk script.
|
||||||
sigHash.Write([]byte{0x19})
|
w.Write([]byte{0x19})
|
||||||
sigHash.Write([]byte{OP_DUP})
|
w.Write([]byte{OP_DUP})
|
||||||
sigHash.Write([]byte{OP_HASH160})
|
w.Write([]byte{OP_HASH160})
|
||||||
sigHash.Write([]byte{OP_DATA_20})
|
w.Write([]byte{OP_DATA_20})
|
||||||
sigHash.Write(extractWitnessPubKeyHash(subScript))
|
w.Write(extractWitnessPubKeyHash(subScript))
|
||||||
sigHash.Write([]byte{OP_EQUALVERIFY})
|
w.Write([]byte{OP_EQUALVERIFY})
|
||||||
sigHash.Write([]byte{OP_CHECKSIG})
|
w.Write([]byte{OP_CHECKSIG})
|
||||||
} else {
|
} else {
|
||||||
// For p2wsh outputs, and future outputs, the script code is
|
// For p2wsh outputs, and future outputs, the script
|
||||||
// the original script, with all code separators removed,
|
// code is the original script, with all code
|
||||||
// serialized with a var int length prefix.
|
// separators removed, serialized with a var int length
|
||||||
wire.WriteVarBytes(&sigHash, 0, subScript)
|
// prefix.
|
||||||
|
wire.WriteVarBytes(w, 0, subScript)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next, add the input amount, and sequence number of the input being
|
// Next, add the input amount, and sequence number of the input
|
||||||
// signed.
|
// being signed.
|
||||||
var bAmount [8]byte
|
var bAmount [8]byte
|
||||||
binary.LittleEndian.PutUint64(bAmount[:], uint64(amt))
|
binary.LittleEndian.PutUint64(bAmount[:], uint64(amt))
|
||||||
sigHash.Write(bAmount[:])
|
w.Write(bAmount[:])
|
||||||
var bSequence [4]byte
|
var bSequence [4]byte
|
||||||
binary.LittleEndian.PutUint32(bSequence[:], txIn.Sequence)
|
binary.LittleEndian.PutUint32(bSequence[:], txIn.Sequence)
|
||||||
sigHash.Write(bSequence[:])
|
w.Write(bSequence[:])
|
||||||
|
|
||||||
// If the current signature mode isn't single, or none, then we can
|
// If the current signature mode isn't single, or none, then we
|
||||||
// re-use the pre-generated hashoutputs sighash fragment. Otherwise,
|
// can re-use the pre-generated hashoutputs sighash fragment.
|
||||||
// we'll serialize and add only the target output index to the signature
|
// Otherwise, we'll serialize and add only the target output
|
||||||
// pre-image.
|
// index to the signature pre-image.
|
||||||
if hashType&sigHashMask != SigHashSingle &&
|
if hashType&sigHashMask != SigHashSingle &&
|
||||||
hashType&sigHashMask != SigHashNone {
|
hashType&sigHashMask != SigHashNone {
|
||||||
sigHash.Write(sigHashes.HashOutputsV0[:])
|
|
||||||
} else if hashType&sigHashMask == SigHashSingle && idx < len(tx.TxOut) {
|
w.Write(sigHashes.HashOutputsV0[:])
|
||||||
|
} else if hashType&sigHashMask == SigHashSingle &&
|
||||||
|
idx < len(tx.TxOut) {
|
||||||
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
wire.WriteTxOut(&b, 0, 0, tx.TxOut[idx])
|
wire.WriteTxOut(&b, 0, 0, tx.TxOut[idx])
|
||||||
sigHash.Write(chainhash.DoubleHashB(b.Bytes()))
|
w.Write(chainhash.DoubleHashB(b.Bytes()))
|
||||||
} else {
|
} else {
|
||||||
sigHash.Write(zeroHash[:])
|
w.Write(zeroHash[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, write out the transaction's locktime, and the sig hash
|
// Finally, write out the transaction's locktime, and the sig
|
||||||
// type.
|
// hash type.
|
||||||
var bLockTime [4]byte
|
var bLockTime [4]byte
|
||||||
binary.LittleEndian.PutUint32(bLockTime[:], tx.LockTime)
|
binary.LittleEndian.PutUint32(bLockTime[:], tx.LockTime)
|
||||||
sigHash.Write(bLockTime[:])
|
w.Write(bLockTime[:])
|
||||||
var bHashType [4]byte
|
var bHashType [4]byte
|
||||||
binary.LittleEndian.PutUint32(bHashType[:], uint32(hashType))
|
binary.LittleEndian.PutUint32(bHashType[:], uint32(hashType))
|
||||||
sigHash.Write(bHashType[:])
|
w.Write(bHashType[:])
|
||||||
|
|
||||||
sigHashBytes := chainhash.DoubleHashRaw(func(w io.Writer) error {
|
return nil
|
||||||
// TODO(rosabeef): put entire calc func into this? then no
|
|
||||||
// intermediate buffer
|
|
||||||
_, err := sigHash.WriteTo(w)
|
|
||||||
return err
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return sigHashBytes[:], nil
|
return sigHashBytes[:], nil
|
||||||
|
|
Loading…
Add table
Reference in a new issue