mirror of
https://github.com/FlipsideCrypto/dcrd.git
synced 2026-02-06 10:56:47 +00:00
Merge in btcd commit '6229e3583505a82d4514b1efa86f910b78693825'
Merges in btcd commit 6229e35835.
This commit is contained in:
commit
615c018baf
@ -286,6 +286,7 @@ func BenchmarkReadTxOut(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
r.Seek(0, 0)
|
||||
readTxOut(r, 0, 0, &txOut)
|
||||
scriptPool.Return(txOut.PkScript)
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,6 +317,7 @@ func BenchmarkReadTxIn(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
r.Seek(0, 0)
|
||||
readTxInPrefix(r, 0, 0, &txIn)
|
||||
scriptPool.Return(txIn.SignatureScript)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
387
wire/msgtx.go
387
wire/msgtx.go
@ -47,14 +47,14 @@ const (
|
||||
DefaultPkScriptVersion uint16 = 0x0000
|
||||
)
|
||||
|
||||
// defaultTxInOutAlloc is the default size used for the backing array for
|
||||
// transaction inputs and outputs. The array will dynamically grow as needed,
|
||||
// but this figure is intended to provide enough space for the number of
|
||||
// inputs and outputs in a typical transaction without needing to grow the
|
||||
// backing array multiple times.
|
||||
const defaultTxInOutAlloc = 15
|
||||
|
||||
const (
|
||||
// defaultTxInOutAlloc is the default size used for the backing array
|
||||
// for transaction inputs and outputs. The array will dynamically grow
|
||||
// as needed, but this figure is intended to provide enough space for
|
||||
// the number of inputs and outputs in a typical transaction without
|
||||
// needing to grow the backing array multiple times.
|
||||
defaultTxInOutAlloc = 15
|
||||
|
||||
// minTxInPayload is the minimum payload size for a transaction input.
|
||||
// PreviousOutPoint.Hash + PreviousOutPoint.Index 4 bytes +
|
||||
// PreviousOutPoint.Tree 1 byte + Varint for SignatureScript length 1
|
||||
@ -83,6 +83,21 @@ const (
|
||||
// of transaction signatures + LockTime 4 bytes + Expiry 4 bytes + min
|
||||
// input payload + min output payload.
|
||||
minTxPayload = 4 + 1 + 1 + 1 + 4 + 4
|
||||
|
||||
// freeListMaxScriptSize is the size of each buffer in the free list
|
||||
// that is used for deserializing scripts from the wire before they are
|
||||
// concatenated into a single contiguous buffers. This value was chosen
|
||||
// because it is slightly more than twice the size of the vast majority
|
||||
// of all "standard" scripts. Larger scripts are still deserialized
|
||||
// properly as the free list will simply be bypassed for them.
|
||||
freeListMaxScriptSize = 512
|
||||
|
||||
// freeListMaxItems is the number of buffers to keep in the free list
|
||||
// to use for script deserialization. This value allows up to 100
|
||||
// scripts per transaction being simultaneously deserialized by 125
|
||||
// peers. Thus, the peak usage of the free list is 12,500 * 512 =
|
||||
// 6,400,000 bytes.
|
||||
freeListMaxItems = 12500
|
||||
)
|
||||
|
||||
// TxSerializeType is a uint16 representing the serialized type of transaction
|
||||
@ -145,6 +160,94 @@ func WitnessSigningMsgTxVersion() int32 {
|
||||
return int32(ver)
|
||||
}
|
||||
|
||||
// scriptFreeList defines a free list of byte slices (up to the maximum number
|
||||
// defined by the freeListMaxItems constant) that have a cap according to the
|
||||
// freeListMaxScriptSize constant. It is used to provide temporary buffers for
|
||||
// deserializing scripts in order to greatly reduce the number of allocations
|
||||
// required.
|
||||
//
|
||||
// The caller can obtain a buffer from the free list by calling the Borrow
|
||||
// function and should return it via the Return function when done using it.
|
||||
type scriptFreeList chan []byte
|
||||
|
||||
// Borrow returns a byte slice from the free list with a length according the
|
||||
// provided size. A new buffer is allocated if there are any items available.
|
||||
//
|
||||
// When the size is larger than the max size allowed for items on the free list
|
||||
// a new buffer of the appropriate size is allocated and returned. It is safe
|
||||
// to attempt to return said buffer via the Return function as it will be
|
||||
// ignored and allowed to go the garbage collector.
|
||||
func (c scriptFreeList) Borrow(size uint64) []byte {
|
||||
if size > freeListMaxScriptSize {
|
||||
return make([]byte, size, size)
|
||||
}
|
||||
|
||||
var buf []byte
|
||||
select {
|
||||
case buf = <-c:
|
||||
default:
|
||||
buf = make([]byte, freeListMaxScriptSize)
|
||||
}
|
||||
return buf[:size]
|
||||
}
|
||||
|
||||
// Return puts the provided byte slice back on the free list when it has a cap
|
||||
// of the expected length. The buffer is expected to have been obtained via
|
||||
// the Borrow function. Any slices that are not of the appropriate size, such
|
||||
// as those whose size is greater than the largest allowed free list item size
|
||||
// are simply ignored so they can go to the garbage collector.
|
||||
func (c scriptFreeList) Return(buf []byte) {
|
||||
// Ignore any buffers returned that aren't the expected size for the
|
||||
// free list.
|
||||
if cap(buf) != freeListMaxScriptSize {
|
||||
return
|
||||
}
|
||||
|
||||
// Return the buffer to the free list when it's not full. Otherwise let
|
||||
// it be garbage collected.
|
||||
select {
|
||||
case c <- buf:
|
||||
default:
|
||||
// Let it go to the garbage collector.
|
||||
}
|
||||
}
|
||||
|
||||
// Create the concurrent safe free list to use for script deserialization. As
|
||||
// previously described, this free list is maintained to significantly reduce
|
||||
// the number of allocations.
|
||||
var scriptPool scriptFreeList = make(chan []byte, freeListMaxItems)
|
||||
|
||||
// readScript reads a variable length byte array that represents a transaction
|
||||
// script. It is encoded as a varInt containing the length of the array
|
||||
// followed by the bytes themselves. An error is returned if the length is
|
||||
// greater than the passed maxAllowed parameter which helps protect against
|
||||
// memory exhuastion attacks and forced panics thorugh malformed messages. The
|
||||
// fieldName parameter is only used for the error message so it provides more
|
||||
// context in the error.
|
||||
func readScript(r io.Reader, pver uint32, maxAllowed uint32, fieldName string) ([]byte, error) {
|
||||
count, err := ReadVarInt(r, pver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Prevent byte array larger than the max message size. It would
|
||||
// be possible to cause memory exhaustion and panics without a sane
|
||||
// upper bound on this count.
|
||||
if count > uint64(maxAllowed) {
|
||||
str := fmt.Sprintf("%s is larger than the max allowed size "+
|
||||
"[count %d, max %d]", fieldName, count, maxAllowed)
|
||||
return nil, messageError("readScript", str)
|
||||
}
|
||||
|
||||
b := scriptPool.Borrow(count)
|
||||
_, err = io.ReadFull(r, b)
|
||||
if err != nil {
|
||||
scriptPool.Return(b)
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// WitnessValueSigningMsgTxVersion returns the witness only version int32
|
||||
// (serialize the tx witness for signing with value, version number 1).
|
||||
func WitnessValueSigningMsgTxVersion() int32 {
|
||||
@ -554,12 +657,90 @@ func (msg *MsgTx) Copy() *MsgTx {
|
||||
return &newTx
|
||||
}
|
||||
|
||||
// writeTxScriptsToMsgTx allocates the memory for variable length fields in a
|
||||
// MsgTx TxIns, TxOuts, or both as a contiguous chunk of memory, then fills
|
||||
// in these fields for the MsgTx by copying to a contiguous piece of memory
|
||||
// and setting the pointer.
|
||||
//
|
||||
// NOTE: It is no longer valid to return any previously borrowed script
|
||||
// buffers after this function has run because it is already done and the
|
||||
// scripts in the transaction inputs and outputs no longer point to the
|
||||
// buffers.
|
||||
func writeTxScriptsToMsgTx(msg *MsgTx, totalScriptSize uint64, mType TxSerializeType) {
|
||||
// Create a single allocation to house all of the scripts and set each
|
||||
// input signature scripts and output public key scripts to the
|
||||
// appropriate subslice of the overall contiguous buffer. Then, return
|
||||
// each individual script buffer back to the pool so they can be reused
|
||||
// for future deserializations. This is done because it significantly
|
||||
// reduces the number of allocations the garbage collector needs to track,
|
||||
// which in turn improves performance and drastically reduces the amount
|
||||
// of runtime overhead that would otherwise be needed to keep track of
|
||||
// millions of small allocations.
|
||||
//
|
||||
// Closures around writing the TxIn and TxOut scripts are used in Decred
|
||||
// because, depending on the serialization type desired, only input or
|
||||
// output scripts may be required.
|
||||
var offset uint64
|
||||
scripts := make([]byte, totalScriptSize)
|
||||
writeTxIns := func() {
|
||||
for i := 0; i < len(msg.TxIn); i++ {
|
||||
// Copy the signature script into the contiguous buffer at the
|
||||
// appropriate offset.
|
||||
signatureScript := msg.TxIn[i].SignatureScript
|
||||
copy(scripts[offset:], signatureScript)
|
||||
|
||||
// Reset the signature script of the transaction input to the
|
||||
// slice of the contiguous buffer where the script lives.
|
||||
scriptSize := uint64(len(signatureScript))
|
||||
end := offset + scriptSize
|
||||
msg.TxIn[i].SignatureScript = scripts[offset:end:end]
|
||||
offset += scriptSize
|
||||
|
||||
// Return the temporary script buffer to the pool.
|
||||
scriptPool.Return(signatureScript)
|
||||
}
|
||||
}
|
||||
writeTxOuts := func() {
|
||||
for i := 0; i < len(msg.TxOut); i++ {
|
||||
// Copy the public key script into the contiguous buffer at the
|
||||
// appropriate offset.
|
||||
pkScript := msg.TxOut[i].PkScript
|
||||
copy(scripts[offset:], pkScript)
|
||||
|
||||
// Reset the public key script of the transaction output to the
|
||||
// slice of the contiguous buffer where the script lives.
|
||||
scriptSize := uint64(len(pkScript))
|
||||
end := offset + scriptSize
|
||||
msg.TxOut[i].PkScript = scripts[offset:end:end]
|
||||
offset += scriptSize
|
||||
|
||||
// Return the temporary script buffer to the pool.
|
||||
scriptPool.Return(pkScript)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the serialization types accordingly.
|
||||
switch mType {
|
||||
case TxSerializeNoWitness:
|
||||
writeTxOuts()
|
||||
case TxSerializeOnlyWitness:
|
||||
fallthrough
|
||||
case TxSerializeWitnessSigning:
|
||||
fallthrough
|
||||
case TxSerializeWitnessValueSigning:
|
||||
writeTxIns()
|
||||
case TxSerializeFull:
|
||||
writeTxIns()
|
||||
writeTxOuts()
|
||||
}
|
||||
}
|
||||
|
||||
// decodePrefix decodes a transaction prefix and stores the contents
|
||||
// in the embedded msgTx.
|
||||
func (msg *MsgTx) decodePrefix(r io.Reader, pver uint32) error {
|
||||
func (msg *MsgTx) decodePrefix(r io.Reader, pver uint32) (uint64, error) {
|
||||
count, err := ReadVarInt(r, pver)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Prevent more input transactions than could possibly fit into a
|
||||
@ -569,24 +750,26 @@ func (msg *MsgTx) decodePrefix(r io.Reader, pver uint32) error {
|
||||
str := fmt.Sprintf("too many input transactions to fit into "+
|
||||
"max message size [count %d, max %d]", count,
|
||||
maxTxInPerMessage)
|
||||
return messageError("MsgTx.decodePrefix", str)
|
||||
return 0, messageError("MsgTx.decodePrefix", str)
|
||||
}
|
||||
|
||||
// TxIns.
|
||||
txIns := make([]TxIn, count)
|
||||
msg.TxIn = make([]*TxIn, count)
|
||||
for i := uint64(0); i < count; i++ {
|
||||
// The pointer is set now in case a script buffer is borrowed
|
||||
// and needs to be returned to the pool on error.
|
||||
ti := &txIns[i]
|
||||
msg.TxIn[i] = ti
|
||||
err = readTxInPrefix(r, pver, msg.Version, ti)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
msg.TxIn[i] = ti
|
||||
}
|
||||
|
||||
count, err = ReadVarInt(r, pver)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Prevent more output transactions than could possibly fit into a
|
||||
@ -596,42 +779,47 @@ func (msg *MsgTx) decodePrefix(r io.Reader, pver uint32) error {
|
||||
str := fmt.Sprintf("too many output transactions to fit into "+
|
||||
"max message size [count %d, max %d]", count,
|
||||
maxTxOutPerMessage)
|
||||
return messageError("MsgTx.decodePrefix", str)
|
||||
return 0, messageError("MsgTx.decodePrefix", str)
|
||||
}
|
||||
|
||||
// TxOuts.
|
||||
var totalScriptSize uint64
|
||||
txOuts := make([]TxOut, count)
|
||||
msg.TxOut = make([]*TxOut, count)
|
||||
for i := uint64(0); i < count; i++ {
|
||||
// The pointer is set now in case a script buffer is borrowed
|
||||
// and needs to be returned to the pool on error.
|
||||
to := &txOuts[i]
|
||||
msg.TxOut[i] = to
|
||||
err = readTxOut(r, pver, msg.Version, to)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
msg.TxOut[i] = to
|
||||
totalScriptSize += uint64(len(to.PkScript))
|
||||
}
|
||||
|
||||
// Locktime and expiry.
|
||||
msg.LockTime, err = binarySerializer.Uint32(r, littleEndian)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
msg.Expiry, err = binarySerializer.Uint32(r, littleEndian)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return nil
|
||||
return totalScriptSize, nil
|
||||
}
|
||||
|
||||
func (msg *MsgTx) decodeWitness(r io.Reader, pver uint32, isFull bool) error {
|
||||
func (msg *MsgTx) decodeWitness(r io.Reader, pver uint32, isFull bool) (uint64, error) {
|
||||
// Witness only; generate the TxIn list and fill out only the
|
||||
// sigScripts.
|
||||
var totalScriptSize uint64
|
||||
if !isFull {
|
||||
count, err := ReadVarInt(r, pver)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Prevent more input transactions than could possibly fit into a
|
||||
@ -641,18 +829,21 @@ func (msg *MsgTx) decodeWitness(r io.Reader, pver uint32, isFull bool) error {
|
||||
str := fmt.Sprintf("too many input transactions to fit into "+
|
||||
"max message size [count %d, max %d]", count,
|
||||
maxTxInPerMessage)
|
||||
return messageError("MsgTx.decodeWitness", str)
|
||||
return 0, messageError("MsgTx.decodeWitness", str)
|
||||
}
|
||||
|
||||
txIns := make([]TxIn, count)
|
||||
msg.TxIn = make([]*TxIn, count)
|
||||
for i := uint64(0); i < count; i++ {
|
||||
// The pointer is set now in case a script buffer is borrowed
|
||||
// and needs to be returned to the pool on error.
|
||||
ti := &txIns[i]
|
||||
msg.TxIn[i] = ti
|
||||
err = readTxInWitness(r, pver, msg.Version, ti)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
msg.TxIn[i] = ti
|
||||
totalScriptSize += uint64(len(ti.SignatureScript))
|
||||
}
|
||||
msg.TxOut = make([]*TxOut, 0)
|
||||
} else {
|
||||
@ -662,7 +853,7 @@ func (msg *MsgTx) decodeWitness(r io.Reader, pver uint32, isFull bool) error {
|
||||
// the signature scripts.
|
||||
count, err := ReadVarInt(r, pver)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Don't allow the deserializer to panic by accessing memory
|
||||
@ -671,7 +862,7 @@ func (msg *MsgTx) decodeWitness(r io.Reader, pver uint32, isFull bool) error {
|
||||
str := fmt.Sprintf("non equal witness and prefix txin quantities "+
|
||||
"(witness %v, prefix %v)", count,
|
||||
len(msg.TxIn))
|
||||
return messageError("MsgTx.decodeWitness", str)
|
||||
return 0, messageError("MsgTx.decodeWitness", str)
|
||||
}
|
||||
|
||||
// Prevent more input transactions than could possibly fit into a
|
||||
@ -681,7 +872,7 @@ func (msg *MsgTx) decodeWitness(r io.Reader, pver uint32, isFull bool) error {
|
||||
str := fmt.Sprintf("too many input transactions to fit into "+
|
||||
"max message size [count %d, max %d]", count,
|
||||
maxTxInPerMessage)
|
||||
return messageError("MsgTx.decodeWitness", str)
|
||||
return 0, messageError("MsgTx.decodeWitness", str)
|
||||
}
|
||||
|
||||
// Read in the witnesses, and copy them into the already generated
|
||||
@ -691,8 +882,9 @@ func (msg *MsgTx) decodeWitness(r io.Reader, pver uint32, isFull bool) error {
|
||||
ti := &txIns[i]
|
||||
err = readTxInWitness(r, pver, msg.Version, ti)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
totalScriptSize += uint64(len(ti.SignatureScript))
|
||||
|
||||
msg.TxIn[i].ValueIn = ti.ValueIn
|
||||
msg.TxIn[i].BlockHeight = ti.BlockHeight
|
||||
@ -701,16 +893,16 @@ func (msg *MsgTx) decodeWitness(r io.Reader, pver uint32, isFull bool) error {
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return totalScriptSize, nil
|
||||
}
|
||||
|
||||
// decodeWitnessSigning decodes a witness for signing.
|
||||
func (msg *MsgTx) decodeWitnessSigning(r io.Reader, pver uint32) error {
|
||||
func (msg *MsgTx) decodeWitnessSigning(r io.Reader, pver uint32) (uint64, error) {
|
||||
// Witness only for signing; generate the TxIn list and fill out only the
|
||||
// sigScripts.
|
||||
count, err := ReadVarInt(r, pver)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Prevent more input transactions than could possibly fit into a
|
||||
@ -720,31 +912,35 @@ func (msg *MsgTx) decodeWitnessSigning(r io.Reader, pver uint32) error {
|
||||
str := fmt.Sprintf("too many input transactions to fit into "+
|
||||
"max message size [count %d, max %d]", count,
|
||||
maxTxInPerMessage)
|
||||
return messageError("MsgTx.decodeWitness", str)
|
||||
return 0, messageError("MsgTx.decodeWitness", str)
|
||||
}
|
||||
|
||||
var totalScriptSize uint64
|
||||
txIns := make([]TxIn, count)
|
||||
msg.TxIn = make([]*TxIn, count)
|
||||
for i := uint64(0); i < count; i++ {
|
||||
// The pointer is set now in case a script buffer is borrowed
|
||||
// and needs to be returned to the pool on error.
|
||||
ti := &txIns[i]
|
||||
msg.TxIn[i] = ti
|
||||
err = readTxInWitnessSigning(r, pver, msg.Version, ti)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
msg.TxIn[i] = ti
|
||||
totalScriptSize += uint64(len(ti.SignatureScript))
|
||||
}
|
||||
msg.TxOut = make([]*TxOut, 0)
|
||||
|
||||
return nil
|
||||
return totalScriptSize, nil
|
||||
}
|
||||
|
||||
// decodeWitnessValueSigning decodes a witness for signing with value.
|
||||
func (msg *MsgTx) decodeWitnessValueSigning(r io.Reader, pver uint32) error {
|
||||
func (msg *MsgTx) decodeWitnessValueSigning(r io.Reader, pver uint32) (uint64, error) {
|
||||
// Witness only for signing; generate the TxIn list and fill out only the
|
||||
// sigScripts.
|
||||
count, err := ReadVarInt(r, pver)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Prevent more input transactions than could possibly fit into a
|
||||
@ -754,22 +950,26 @@ func (msg *MsgTx) decodeWitnessValueSigning(r io.Reader, pver uint32) error {
|
||||
str := fmt.Sprintf("too many input transactions to fit into "+
|
||||
"max message size [count %d, max %d]", count,
|
||||
maxTxInPerMessage)
|
||||
return messageError("MsgTx.decodeWitness", str)
|
||||
return 0, messageError("MsgTx.decodeWitness", str)
|
||||
}
|
||||
|
||||
var totalScriptSize uint64
|
||||
txIns := make([]TxIn, count)
|
||||
msg.TxIn = make([]*TxIn, count)
|
||||
for i := uint64(0); i < count; i++ {
|
||||
// The pointer is set now in case a script buffer is borrowed
|
||||
// and needs to be returned to the pool on error.
|
||||
ti := &txIns[i]
|
||||
msg.TxIn[i] = ti
|
||||
err = readTxInWitnessValueSigning(r, pver, msg.Version, ti)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
msg.TxIn[i] = ti
|
||||
totalScriptSize += uint64(len(ti.SignatureScript))
|
||||
}
|
||||
msg.TxOut = make([]*TxOut, 0)
|
||||
|
||||
return nil
|
||||
return totalScriptSize, nil
|
||||
}
|
||||
|
||||
// BtcDecode decodes r using the decred protocol encoding into the receiver.
|
||||
@ -784,40 +984,77 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32) error {
|
||||
msg.Version = int32(version)
|
||||
_, mType := msgTxVersionToVars(msg.Version)
|
||||
|
||||
// returnScriptBuffers is a closure that returns any script buffers that
|
||||
// were borrowed from the pool when there are any deserialization
|
||||
// errors. This is only valid to call before the final step which
|
||||
// replaces the scripts with the location in a contiguous buffer and
|
||||
// returns them.
|
||||
returnScriptBuffers := func() {
|
||||
for _, txIn := range msg.TxIn {
|
||||
if txIn == nil || txIn.SignatureScript == nil {
|
||||
continue
|
||||
}
|
||||
scriptPool.Return(txIn.SignatureScript)
|
||||
}
|
||||
for _, txOut := range msg.TxOut {
|
||||
if txOut == nil || txOut.PkScript == nil {
|
||||
continue
|
||||
}
|
||||
scriptPool.Return(txOut.PkScript)
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize the transactions depending on their serialization
|
||||
// types. Write the transaction scripts at the end of each
|
||||
// serialization procedure using the more efficient contiguous
|
||||
// memory allocations, which reduces the amount of memory that
|
||||
// must be handled by the GC tremendously. If any of these
|
||||
// serializations fail, free the relevant memory.
|
||||
switch {
|
||||
case mType == TxSerializeNoWitness:
|
||||
err := msg.decodePrefix(r, pver)
|
||||
totalScriptSize, err := msg.decodePrefix(r, pver)
|
||||
if err != nil {
|
||||
returnScriptBuffers()
|
||||
return err
|
||||
}
|
||||
writeTxScriptsToMsgTx(msg, totalScriptSize, mType)
|
||||
|
||||
case mType == TxSerializeOnlyWitness:
|
||||
err := msg.decodeWitness(r, pver, false)
|
||||
totalScriptSize, err := msg.decodeWitness(r, pver, false)
|
||||
if err != nil {
|
||||
returnScriptBuffers()
|
||||
return err
|
||||
}
|
||||
writeTxScriptsToMsgTx(msg, totalScriptSize, mType)
|
||||
|
||||
case mType == TxSerializeWitnessSigning:
|
||||
err := msg.decodeWitnessSigning(r, pver)
|
||||
totalScriptSize, err := msg.decodeWitnessSigning(r, pver)
|
||||
if err != nil {
|
||||
returnScriptBuffers()
|
||||
return err
|
||||
}
|
||||
writeTxScriptsToMsgTx(msg, totalScriptSize, mType)
|
||||
|
||||
case mType == TxSerializeWitnessValueSigning:
|
||||
err := msg.decodeWitnessValueSigning(r, pver)
|
||||
totalScriptSize, err := msg.decodeWitnessValueSigning(r, pver)
|
||||
if err != nil {
|
||||
returnScriptBuffers()
|
||||
return err
|
||||
}
|
||||
writeTxScriptsToMsgTx(msg, totalScriptSize, mType)
|
||||
|
||||
case mType == TxSerializeFull:
|
||||
err := msg.decodePrefix(r, pver)
|
||||
totalScriptSizeIns, err := msg.decodePrefix(r, pver)
|
||||
if err != nil {
|
||||
returnScriptBuffers()
|
||||
return err
|
||||
}
|
||||
err = msg.decodeWitness(r, pver, true)
|
||||
totalScriptSizeOuts, err := msg.decodeWitness(r, pver, true)
|
||||
if err != nil {
|
||||
returnScriptBuffers()
|
||||
return err
|
||||
}
|
||||
writeTxScriptsToMsgTx(msg, totalScriptSizeIns+totalScriptSizeOuts, mType)
|
||||
default:
|
||||
return messageError("MsgTx.BtcDecode", "unsupported transaction type")
|
||||
}
|
||||
@ -849,20 +1086,46 @@ func (msg *MsgTx) LegacyBtcDecode(r io.Reader, pver uint32) error {
|
||||
return messageError("MsgTx.BtcDecode", str)
|
||||
}
|
||||
|
||||
// returnScriptBuffers is a closure that returns any script buffers that
|
||||
// were borrowed from the pool when there are any deserialization
|
||||
// errors. This is only valid to call before the final step which
|
||||
// replaces the scripts with the location in a contiguous buffer and
|
||||
// returns them.
|
||||
returnScriptBuffers := func() {
|
||||
for _, txIn := range msg.TxIn {
|
||||
if txIn == nil || txIn.SignatureScript == nil {
|
||||
continue
|
||||
}
|
||||
scriptPool.Return(txIn.SignatureScript)
|
||||
}
|
||||
for _, txOut := range msg.TxOut {
|
||||
if txOut == nil || txOut.PkScript == nil {
|
||||
continue
|
||||
}
|
||||
scriptPool.Return(txOut.PkScript)
|
||||
}
|
||||
}
|
||||
|
||||
// Deserialize the inputs.
|
||||
var totalScriptSize uint64
|
||||
txIns := make([]TxIn, count)
|
||||
msg.TxIn = make([]*TxIn, count)
|
||||
for i := uint64(0); i < count; i++ {
|
||||
// The pointer is set now in case a script buffer is borrowed
|
||||
// and needs to be returned to the pool on error.
|
||||
ti := &txIns[i]
|
||||
msg.TxIn[i] = ti
|
||||
err = legacyReadTxIn(r, pver, msg.Version, ti)
|
||||
if err != nil {
|
||||
returnScriptBuffers()
|
||||
return err
|
||||
}
|
||||
msg.TxIn[i] = ti
|
||||
totalScriptSize += uint64(len(ti.SignatureScript))
|
||||
}
|
||||
|
||||
count, err = ReadVarInt(r, pver)
|
||||
if err != nil {
|
||||
returnScriptBuffers()
|
||||
return err
|
||||
}
|
||||
|
||||
@ -870,6 +1133,7 @@ func (msg *MsgTx) LegacyBtcDecode(r io.Reader, pver uint32) error {
|
||||
// message. It would be possible to cause memory exhaustion and panics
|
||||
// without a sane upper bound on this count.
|
||||
if count > uint64(maxTxOutPerMessage) {
|
||||
returnScriptBuffers()
|
||||
str := fmt.Sprintf("too many output transactions to fit into "+
|
||||
"max message size [count %d, max %d]", count,
|
||||
maxTxOutPerMessage)
|
||||
@ -880,16 +1144,21 @@ func (msg *MsgTx) LegacyBtcDecode(r io.Reader, pver uint32) error {
|
||||
txOuts := make([]TxOut, count)
|
||||
msg.TxOut = make([]*TxOut, count)
|
||||
for i := uint64(0); i < count; i++ {
|
||||
// The pointer is set now in case a script buffer is borrowed
|
||||
// and needs to be returned to the pool on error.
|
||||
to := &txOuts[i]
|
||||
err = legacyReadTxOut(r, pver, msg.Version, to)
|
||||
msg.TxOut[i] = to
|
||||
if err != nil {
|
||||
returnScriptBuffers()
|
||||
return err
|
||||
}
|
||||
msg.TxOut[i] = to
|
||||
totalScriptSize += uint64(len(to.PkScript))
|
||||
}
|
||||
|
||||
msg.LockTime, err = binarySerializer.Uint32(r, littleEndian)
|
||||
if err != nil {
|
||||
returnScriptBuffers()
|
||||
return err
|
||||
}
|
||||
|
||||
@ -1458,12 +1727,10 @@ func readTxInPrefix(r io.Reader, pver uint32, version int32, ti *TxIn) error {
|
||||
}
|
||||
|
||||
// Outpoint.
|
||||
var op OutPoint
|
||||
err := ReadOutPoint(r, pver, version, &op)
|
||||
err := ReadOutPoint(r, pver, version, &ti.PreviousOutPoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ti.PreviousOutPoint = op
|
||||
|
||||
// Sequence.
|
||||
ti.Sequence, err = binarySerializer.Uint32(r, littleEndian)
|
||||
@ -1497,7 +1764,7 @@ func readTxInWitness(r io.Reader, pver uint32, version int32, ti *TxIn) error {
|
||||
}
|
||||
|
||||
// Signature script.
|
||||
ti.SignatureScript, err = ReadVarBytes(r, pver, MaxMessagePayload,
|
||||
ti.SignatureScript, err = readScript(r, pver, MaxMessagePayload,
|
||||
"transaction input signature script")
|
||||
if err != nil {
|
||||
return err
|
||||
@ -1512,7 +1779,7 @@ func readTxInWitnessSigning(r io.Reader, pver uint32, version int32,
|
||||
var err error
|
||||
|
||||
// Signature script.
|
||||
ti.SignatureScript, err = ReadVarBytes(r, pver, MaxMessagePayload,
|
||||
ti.SignatureScript, err = readScript(r, pver, MaxMessagePayload,
|
||||
"transaction input signature script")
|
||||
if err != nil {
|
||||
return err
|
||||
@ -1533,7 +1800,7 @@ func readTxInWitnessValueSigning(r io.Reader, pver uint32, version int32,
|
||||
ti.ValueIn = int64(valueIn)
|
||||
|
||||
// Signature script.
|
||||
ti.SignatureScript, err = ReadVarBytes(r, pver, MaxMessagePayload,
|
||||
ti.SignatureScript, err = readScript(r, pver, MaxMessagePayload,
|
||||
"transaction input signature script")
|
||||
if err != nil {
|
||||
return err
|
||||
@ -1545,14 +1812,12 @@ func readTxInWitnessValueSigning(r io.Reader, pver uint32, version int32,
|
||||
// readTxInPrefix reads the next sequence of bytes from r as a transaction input
|
||||
// (TxIn) in the transaction prefix.
|
||||
func legacyReadTxIn(r io.Reader, pver uint32, version int32, ti *TxIn) error {
|
||||
var op OutPoint
|
||||
err := legacyReadOutPoint(r, pver, version, &op)
|
||||
err := legacyReadOutPoint(r, pver, version, &ti.PreviousOutPoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ti.PreviousOutPoint = op
|
||||
|
||||
ti.SignatureScript, err = ReadVarBytes(r, pver, MaxMessagePayload,
|
||||
ti.SignatureScript, err = readScript(r, pver, MaxMessagePayload,
|
||||
"transaction input signature script")
|
||||
if err != nil {
|
||||
return err
|
||||
@ -1683,7 +1948,7 @@ func readTxOut(r io.Reader, pver uint32, version int32, to *TxOut) error {
|
||||
return err
|
||||
}
|
||||
|
||||
to.PkScript, err = ReadVarBytes(r, pver, MaxMessagePayload,
|
||||
to.PkScript, err = readScript(r, pver, MaxMessagePayload,
|
||||
"transaction output public key script")
|
||||
if err != nil {
|
||||
return err
|
||||
@ -1721,7 +1986,7 @@ func legacyReadTxOut(r io.Reader, pver uint32, version int32, to *TxOut) error {
|
||||
}
|
||||
to.Value = int64(value)
|
||||
|
||||
to.PkScript, err = ReadVarBytes(r, pver, MaxMessagePayload,
|
||||
to.PkScript, err = readScript(r, pver, MaxMessagePayload,
|
||||
"transaction output public key script")
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
Loading…
Reference in New Issue
Block a user