dcrd/dcrutil/tx.go
2017-11-20 18:49:55 -06:00

223 lines
6.0 KiB
Go

// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package dcrutil
import (
"bytes"
"fmt"
"io"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/wire"
)
// assertTransactionImmutability throws a panic when a transaction has been
// mutated.
var assertTransactionImmutability = false
// TxIndexUnknown is the value returned for a transaction index that is unknown.
// This is typically because the transaction has not been inserted into a block
// yet.
const TxIndexUnknown = -1
// Tx defines a transaction that provides easier and more efficient manipulation
// of raw transactions. It also memoizes the hash for the transaction on its
// first access so subsequent accesses don't have to repeat the relatively
// expensive hashing operations.
type Tx struct {
hash chainhash.Hash // Cached transaction hash
msgTx *wire.MsgTx // Underlying MsgTx
txTree int8 // Indicates which tx tree the tx is found in
txIndex int // Position within a block or TxIndexUnknown
}
// MsgTx returns the underlying wire.MsgTx for the transaction.
func (t *Tx) MsgTx() *wire.MsgTx {
// Return the cached transaction.
return t.msgTx
}
// Hash returns the hash of the transaction. This is equivalent to
// calling TxHash on the underlying wire.MsgTx, however it caches the
// result so subsequent calls are more efficient.
func (t *Tx) Hash() *chainhash.Hash {
if assertTransactionImmutability {
hash := t.msgTx.TxHash()
if !hash.IsEqual(&t.hash) {
str := fmt.Sprintf("ASSERT: mutated util.tx detected, old hash %v, "+
"new hash %v",
t.hash,
hash)
panic(str)
}
}
return &t.hash
}
// Index returns the saved index of the transaction within a block. This value
// will be TxIndexUnknown if it hasn't already explicitly been set.
func (t *Tx) Index() int {
return t.txIndex
}
// SetIndex sets the index of the transaction in within a block.
func (t *Tx) SetIndex(index int) {
t.txIndex = index
}
// Tree returns the saved tree of the transaction within a block. This value
// will be TxTreeUnknown if it hasn't already explicitly been set.
func (t *Tx) Tree() int8 {
return t.txTree
}
// SetTree sets the tree of the transaction in within a block.
func (t *Tx) SetTree(tree int8) {
t.txTree = tree
}
// NewTx returns a new instance of a transaction given an underlying
// wire.MsgTx. See Tx.
func NewTx(msgTx *wire.MsgTx) *Tx {
return &Tx{
hash: msgTx.TxHash(),
msgTx: msgTx,
txTree: wire.TxTreeUnknown,
txIndex: TxIndexUnknown,
}
}
// NewTxDeep returns a new instance of a transaction given an underlying
// wire.MsgTx. Until NewTx, it completely copies the data in the msgTx
// so that there are new memory allocations, in case you were to somewhere
// else modify the data assigned to these pointers.
func NewTxDeep(msgTx *wire.MsgTx) *Tx {
txIns := make([]*wire.TxIn, len(msgTx.TxIn))
txOuts := make([]*wire.TxOut, len(msgTx.TxOut))
for i, txin := range msgTx.TxIn {
sigScript := make([]byte, len(txin.SignatureScript))
copy(sigScript[:], txin.SignatureScript[:])
txIns[i] = &wire.TxIn{
PreviousOutPoint: wire.OutPoint{
Hash: txin.PreviousOutPoint.Hash,
Index: txin.PreviousOutPoint.Index,
Tree: txin.PreviousOutPoint.Tree,
},
Sequence: txin.Sequence,
ValueIn: txin.ValueIn,
BlockHeight: txin.BlockHeight,
BlockIndex: txin.BlockIndex,
SignatureScript: sigScript,
}
}
for i, txout := range msgTx.TxOut {
pkScript := make([]byte, len(txout.PkScript))
copy(pkScript[:], txout.PkScript[:])
txOuts[i] = &wire.TxOut{
Value: txout.Value,
Version: txout.Version,
PkScript: pkScript,
}
}
mtx := &wire.MsgTx{
CachedHash: nil,
SerType: msgTx.SerType,
Version: msgTx.Version,
TxIn: txIns,
TxOut: txOuts,
LockTime: msgTx.LockTime,
Expiry: msgTx.Expiry,
}
return &Tx{
hash: mtx.TxHash(),
msgTx: mtx,
txTree: wire.TxTreeUnknown,
txIndex: TxIndexUnknown,
}
}
// NewTxDeepTxIns is used to deep copy a transaction, maintaining the old
// pointers to the TxOuts while replacing the old pointers to the TxIns with
// deep copies. This is to prevent races when the fraud proofs for the
// transactions are set by the miner.
func NewTxDeepTxIns(msgTx *wire.MsgTx) *Tx {
if msgTx == nil {
return nil
}
newMsgTx := new(wire.MsgTx)
// Copy the fixed fields.
newMsgTx.Version = msgTx.Version
newMsgTx.LockTime = msgTx.LockTime
newMsgTx.Expiry = msgTx.Expiry
// Copy the TxIns deeply.
for _, txIn := range msgTx.TxIn {
sigScrLen := len(txIn.SignatureScript)
sigScrCopy := make([]byte, sigScrLen)
txInCopy := new(wire.TxIn)
txInCopy.PreviousOutPoint.Hash = txIn.PreviousOutPoint.Hash
txInCopy.PreviousOutPoint.Index = txIn.PreviousOutPoint.Index
txInCopy.PreviousOutPoint.Tree = txIn.PreviousOutPoint.Tree
txInCopy.Sequence = txIn.Sequence
txInCopy.ValueIn = txIn.ValueIn
txInCopy.BlockHeight = txIn.BlockHeight
txInCopy.BlockIndex = txIn.BlockIndex
txInCopy.SignatureScript = sigScrCopy
newMsgTx.AddTxIn(txIn)
}
// Shallow copy the TxOuts.
for _, txOut := range msgTx.TxOut {
newMsgTx.AddTxOut(txOut)
}
return &Tx{
hash: msgTx.TxHash(),
msgTx: msgTx,
txTree: wire.TxTreeUnknown,
txIndex: TxIndexUnknown,
}
}
// NewTxFromBytes returns a new instance of a transaction given the
// serialized bytes. See Tx.
func NewTxFromBytes(serializedTx []byte) (*Tx, error) {
br := bytes.NewReader(serializedTx)
return NewTxFromReader(br)
}
// NewTxFromReader returns a new instance of a transaction given a
// Reader to deserialize the transaction. See Tx.
func NewTxFromReader(r io.Reader) (*Tx, error) {
// Deserialize the bytes into a MsgTx.
var msgTx wire.MsgTx
err := msgTx.Deserialize(r)
if err != nil {
return nil, err
}
t := Tx{
hash: msgTx.TxHash(),
msgTx: &msgTx,
txTree: wire.TxTreeUnknown,
txIndex: TxIndexUnknown,
}
return &t, nil
}