dcrd/wire/msgtx.go
Marco Peereboom 80f5feb1db
multi: Add decentralized treasury support.
This is based on https://proposals.decred.org/proposals/c96290a but was
modified in order to deal with realities that were unknown at the time
of the specification draft.

It is large and could not really be broken apart due to the pervasive
use of the isTreasuryEnabled flag. It was primarily authored by
* Marco Peereboom <marco@peereboom.us>
* Dave Collins <davec@conformal.com>
* Matheus Degiovani <opensource@matheusd.com>

With additional contributions from
* Donald Adu-Poku <donald.adu@gmail.com>
* Jamie Holdstock <jholdstock@decred.org>

Major changes:
* Add decentralized treasury agenda, as specified in DCP0006, to all supported
  nets.
* Add functions to determine if the decentralized treasury agenda is active at
  given block.
* Add new opcode OP_TADD that is a nop in txscript but is used to tag scripts
  that credit the treasury account. This opcode is overloaded for treasurybase
  and for normal transactions.
* Add new opcode OP_TSPEND that is a nop in txscript but is used to tag scripts
  that debit the treasury account.
* Add new opcode OP_TGEN that is a nop in txscript but is used to tag P2PKH and
  P2SH outputs in a TSpend transaction.
* Add functions that detect if a transaction is a valid TAdd, TSpend
  or treasurybase transaction.
* Add error codes that return specific treasurybase/TAdd/TSpend consensus
  violations.
* Modify countSpentOutputs to deal with treasury opcodes accordingly.
* Modify indexBlock to skip treasury transactions that do not have inputs.
* Add IsTreasuryEnabled call to ChainQueryer interface.
* Add treasury logger for debugging and logging the decentralized treasury
  subsystem.
* Add IsTreasuryActive flag to BlockConnectedNtfnsData and
  BlockDisconnectedNtfnsData.
* Modify OP_SSGEN to allow an optional output that contains votes for a TSpend
  transaction hash.
* Add function that returns TSpend votes from an SSGen transaction.
* Modify CalcStakeVoteSubsidy so that treasurybase, unlike coinbase, is always
  awarded the full percentage of the assigned block reward.
* Add helper functions to do all TSpend math so that callers don't roll their
  own.
* Modify IsCoinBaseTx to not mistake a TSpend transaction as a coinbase.
* Add checkTreasuryBase function that verifies that a treasurybase is properly
  constructed and pays the right amount to the treasury account.
* Add functions to calculate treasury balance for the provided block hash/node.
* Add function that verifies if a TSpend has a valid signature.
* Add functions to determine if a TSpend is not overspending.
* Add function to determine if a TSpend has been mined on the provided chain.
* Add functions that count and verifies treasury spend votes.
* Modify connectTransaction and disconnectTransactions to deal with the various
  treasury transactions.
* Split CheckTransactionSanity in two functions
  checkTransactionSanityContextFree and checkTransactionSanityContextual. This
  is done in order to keep the decentralized treasury, which is always
  contextual, from infecting the context free checks.
* Modify checkTransactionSanityContextual to recognize and verify treasury
  transactions.
* Modify CheckTransactionSanity to deal with treasury transactions.
* Split checkBlockSanity in two functions checkBlockSanityContextFree and
  checkBlockSanityContextual. This is done in order to keep the decentralized
  treasury, which is always contextual, from infecting the context free checks.
* Modify checkBlockSanityContextual to enforce treasurybase and TAdd consensus
  checks.
* Modify checkBlockPositional by unindenting it and adding TSpend consensus
  enforcement.
* Modify checkCoinbaseUniqueHeightWithAddress to deal with the removal of the
  project subsidy from output 0.
* Add checkCoinbaseUniqueHeightWithTreasuryBase that verifies coinbase and
  treasurybase in the provided block.
* Unindent checkBlockContext.
* Modify checkTicketRedeemerCommitments and checkVoteInputs to deal with
  potential tspend votes.
* Modify CheckTransactionInputs to skip treasurybase transactions.
* Modify CheckTransactionInputs to deal with TSpend transactions. Ensure the
  provided Pi key is valid and that the signature is valid for the transaction.
  Ensure that treasury TAdd and TSpend transaction utxo can only be spent after
  coinbase maturity.
* Modify CountSigOps to deal with treasury transactions.
* Modify CountP2SHSigOps to deal with treasury transactions.
* Modify getStakeTreeFees to skip treasury transactions. Modify
  totalOutputs to subtract ValueIn 0 for TSpend and treasurybase transactions.
* Modify checkTransactionsAndConnect to deal with modified amounts.
* Add tspendChecks function that verifies an entire TSpend transaction
  validity at the point of the provided block. It ensures a TSpend is on a TVI.
  It ensures the TSpend is in the valid window. It verifies that a TSpend In
  and Out amounts match. It ensures a TSpend has the ValueIn amount encoded in
  the OP_RETURN in Out 0. It ensures a TSpend has not been mined before on this
  chain. It ensures a TSpend has the requisite votes. It ensures a TSpend is
  not overspending.
* Modify checkConnectBlock to call checkTreasuryBase and tspendChecks when
  treasury agenda is active.
* Add two tables to the database. Table "treasury" records the balance as of
  this block and balance changes that occurred in this block which will become
  active in CoinbaseMaturity blocks. Table "tspend" records all block hashes
  where a TSpend has been mined this is to detect forks and prevent a Tspend
  from being mined more than once.
* Modify handleBlockchainNotification to communicate if the treasury agenda is
  active and skip treasurybase transaction when needed.
* Add various Treasury parameters to chaincfg params.
* Add hardcoded Tspend signatures in dcr_tmux_simnet_setup.sh.
* Add notifytspend and stoptspend calls to the RPC server. notifytspend
  notifies the mempool when a TSpend transaction arrives.
* Modify commit filters V2 to recognize TAdd and TSpend transactions. It was
  possible to modify V2 instead of introducing V3 because nothing changes from
  the viewpoint of the wallet and treasury opcodes are disallowed prior to
  agenda activation.
* Modify AddMemPoolTransaction to skip TSpend transactions that would throw the
  fee estimator off.
* Add IsTreasuryAgendaActive, OnTSpendReceived and TSpendMinedOnAncestor to
  mempool.Config in order to reject/accept TSpends in the mempool.
* Modify checkPoolDoubleSpend to ignore treasurybase.
* Modify mempool.maybeAcceptTransaction to enforce treasury standardness rules.
  Don't allow TSpend transactions prior to stake validation height. Skip
  treasurybase and tspend transactions in the orphan test. Ensure a tspend is
  in a valid window. Ensure not more than 7 TSpends are active in the mempool.
  Ensure TSpend has a well-known Pi key. Ensure The provided Pi key was used to
  sign the transaction. Ensure TSpend was not mined in an ancestor block.
  Notify subscribers that a valid TSpend was received.
* Add standardCoinbaseOpReturn and standardTreasurybaseOpReturn to create an
  OP_RETURN followed by a data push that little endian encodes the height of
  the block. Then there are a number of random bytes to ensure that the
  transaction hash is always random.
* Modify createCoinbaseTx to create a coinbase that is valid when treasury is
  enabled or not. Additionally, alter the transaction version if treasury is
  enabled.
* Add createTreasuryBaseTx that creates a standard treasurybase.
* Modify maybeInsertStakeTx to recognize treasurybase and TSpend transactions.
* Modify handleTooFewVoters to call createTreasuryBaseTx when the treasury
  agenda is active. Skip copying treasurybase.
* Modify NewBlockTemplate to recognize and deal with treasury transactions.
  Skip TSpend transaction if block is not a TVI. Skip TSpend transaction if it
  is not in the proper window. Skip TSpend transaction if a TSpend does not
  have enough yes votes. Skip TSpend transaction if it overspends the treasury
  account. Skip TAdd if there are more than 20 TAdds in the block. Create
  treasurybase if required. Insert valid TAdd/TSpend transactions into stake
  tree.
* Add TreasuryBalance and IsTreasuryAgendaActive to rpcserver Chain interface.
* Add gettreasurybalance, sendfromtreasury and sendtotreasury calls to RPC
  server.
* Add notifytspend and stopnotifytspend to RPC websocket commands.
* Add simnet miner to generate large number of blocks during rpctests without
  triggering PoW difficulty increases. This is used to verify various treasury
  and tspend conditions during CI/CT.
* Modify RPC voting wallet to also vote on TSpends.
* Add json tests to verify all new opcodes and corner cases in the script
  engine.
* Modify isStakeOpcode to recognize treasury opcodes.
* Modify countSigOpsV0 to count TSpends.
* Modify handleStakeOutSign to deal with TSpends.
* Modify SignTxOutput to recognize TSpends.
* Add TSpendSignatureScript that signs a TSpend transaction.
* Add TreasuryAddTy and TreasurySpendTy types to the standard scripts.
* Add isTreasuryAddScript and isTreasurySpendScript functions that recognize
  a form of TAdd and TSpend transactions.
* Modify ExtractPkScriptAddrs to deal with TAdd and TSpend outputs.
* Add TxVersionSeqLock = 2 and TxVersionTreasury = 3 to wire. This is
  used to discriminate between treasury and non-treasury scripts.
* Rig up all functions that need the isTreasuryEnabledflag directly or
  indirectly.
* Shuffle various functions around and export them when they were needed to be
  called from other packages.
* Added and modified numerous tests to verify (hopefully) all corner cases that
  the decentralized treasury agenda has added.
2020-09-21 12:15:31 -05:00

1280 lines
41 KiB
Go

// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2020 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"fmt"
"io"
"strconv"
"github.com/decred/dcrd/chaincfg/chainhash"
)
const (
// TxVersion is the initial transaction version.
TxVersion uint16 = 1
// TxVersionSeqLock is the transaction version that enables sequence
// locks.
TxVersionSeqLock uint16 = 2
// TxVersionTreasury is the transaction version that enables the
// decentralized treasury features.
TxVersionTreasury uint16 = 3
// MaxTxInSequenceNum is the maximum sequence number the sequence field
// of a transaction input can be.
MaxTxInSequenceNum uint32 = 0xffffffff
// MaxPrevOutIndex is the maximum index the index field of a previous
// outpoint can be.
MaxPrevOutIndex uint32 = 0xffffffff
// NoExpiryValue is the value of expiry that indicates the transaction
// has no expiry.
NoExpiryValue uint32 = 0
// NullValueIn is a null value for an input witness.
NullValueIn int64 = -1
// NullBlockHeight is the null value for an input witness. It references
// the genesis block.
NullBlockHeight uint32 = 0x00000000
// NullBlockIndex is the null transaction index in a block for an input
// witness.
NullBlockIndex uint32 = 0xffffffff
// DefaultPkScriptVersion is the default pkScript version, referring to
// extended Decred script.
DefaultPkScriptVersion uint16 = 0x0000
// TxTreeUnknown is the value returned for a transaction tree that is
// unknown. This is typically because the transaction has not been
// inserted into a block yet.
TxTreeUnknown int8 = -1
// TxTreeRegular is the value for a normal transaction tree for a
// transaction's location in a block.
TxTreeRegular int8 = 0
// TxTreeStake is the value for a stake transaction tree for a
// transaction's location in a block.
TxTreeStake int8 = 1
// SequenceLockTimeDisabled is a flag that if set on a transaction
// input's sequence number, the sequence number will not be interpreted
// as a relative locktime.
SequenceLockTimeDisabled = 1 << 31
// SequenceLockTimeIsSeconds is a flag that if set on a transaction
// input's sequence number, the relative locktime has units of 512
// seconds.
SequenceLockTimeIsSeconds = 1 << 22
// SequenceLockTimeMask is a mask that extracts the relative locktime
// when masked against the transaction input sequence number.
SequenceLockTimeMask = 0x0000ffff
// SequenceLockTimeGranularity is the defined time based granularity
// for seconds-based relative time locks. When converting from seconds
// to a sequence number, the value is right shifted by this amount,
// therefore the granularity of relative time locks in 512 or 2^9
// seconds. Enforced relative lock times are multiples of 512 seconds.
SequenceLockTimeGranularity = 9
)
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
// byte + Sequence 4 bytes.
minTxInPayload = 11 + chainhash.HashSize
// maxTxInPerMessage is the maximum number of transactions inputs that
// a transaction which fits into a message could possibly have.
maxTxInPerMessage = (MaxMessagePayload / minTxInPayload) + 1
// minTxOutPayload is the minimum payload size for a transaction output.
// Value 8 bytes + Varint for PkScript length 1 byte.
minTxOutPayload = 9
// maxTxOutPerMessage is the maximum number of transactions outputs that
// a transaction which fits into a message could possibly have.
maxTxOutPerMessage = (MaxMessagePayload / minTxOutPayload) + 1
// minTxPayload is the minimum payload size for any full encoded
// (prefix and witness transaction). Note that any realistically
// usable transaction must have at least one input or output, but
// that is a rule enforced at a higher layer, so it is intentionally
// not included here.
// Version 4 bytes + Varint number of transaction inputs 1 byte + Varint
// number of transaction outputs 1 byte + Varint representing the number
// 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 buffer. 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 represents the serialized type of a transaction.
type TxSerializeType uint16
const (
// TxSerializeFull indicates a transaction be serialized with the prefix
// and all witness data.
TxSerializeFull TxSerializeType = iota
// TxSerializeNoWitness indicates a transaction be serialized with only
// the prefix.
TxSerializeNoWitness
// TxSerializeOnlyWitness indicates a transaction be serialized with
// only the witness data.
TxSerializeOnlyWitness
)
// 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)
}
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 exhaustion attacks and forced panics through 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) {
const op = "readScript"
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) {
msg := fmt.Sprintf("%s is larger than the max allowed size "+
"[count %d, max %d]", fieldName, count, maxAllowed)
return nil, messageError(op, ErrVarBytesTooLong, msg)
}
b := scriptPool.Borrow(count)
_, err = io.ReadFull(r, b)
if err != nil {
scriptPool.Return(b)
return nil, err
}
return b, nil
}
// OutPoint defines a Decred data type that is used to track previous
// transaction outputs.
type OutPoint struct {
Hash chainhash.Hash
Index uint32
Tree int8
}
// NewOutPoint returns a new Decred transaction outpoint point with the
// provided hash and index.
func NewOutPoint(hash *chainhash.Hash, index uint32, tree int8) *OutPoint {
return &OutPoint{
Hash: *hash,
Index: index,
Tree: tree,
}
}
// String returns the OutPoint in the human-readable form "hash:index".
func (o OutPoint) String() string {
// Allocate enough for hash string, colon, and 10 digits. Although
// at the time of writing, the number of digits can be no greater than
// the length of the decimal representation of maxTxOutPerMessage, the
// maximum message payload may increase in the future and this
// optimization may go unnoticed, so allocate space for 10 decimal
// digits, which will fit any uint32.
buf := make([]byte, 2*chainhash.HashSize+1, 2*chainhash.HashSize+1+10)
copy(buf, o.Hash.String())
buf[2*chainhash.HashSize] = ':'
buf = strconv.AppendUint(buf, uint64(o.Index), 10)
return string(buf)
}
// TxIn defines a Decred transaction input.
type TxIn struct {
// Non-witness
PreviousOutPoint OutPoint
Sequence uint32
// Witness
ValueIn int64
BlockHeight uint32
BlockIndex uint32
SignatureScript []byte
}
// SerializeSizePrefix returns the number of bytes it would take to serialize
// the transaction input for a prefix.
func (t *TxIn) SerializeSizePrefix() int {
// Outpoint Hash 32 bytes + Outpoint Index 4 bytes + Outpoint Tree 1 byte +
// Sequence 4 bytes.
return 41
}
// SerializeSizeWitness returns the number of bytes it would take to serialize the
// transaction input for a witness.
func (t *TxIn) SerializeSizeWitness() int {
// ValueIn (8 bytes) + BlockHeight (4 bytes) + BlockIndex (4 bytes) +
// serialized varint size for the length of SignatureScript +
// SignatureScript bytes.
return 8 + 4 + 4 + VarIntSerializeSize(uint64(len(t.SignatureScript))) +
len(t.SignatureScript)
}
// NewTxIn returns a new Decred transaction input with the provided
// previous outpoint point and signature script with a default sequence of
// MaxTxInSequenceNum.
func NewTxIn(prevOut *OutPoint, valueIn int64, signatureScript []byte) *TxIn {
return &TxIn{
PreviousOutPoint: *prevOut,
Sequence: MaxTxInSequenceNum,
SignatureScript: signatureScript,
ValueIn: valueIn,
BlockHeight: NullBlockHeight,
BlockIndex: NullBlockIndex,
}
}
// TxOut defines a Decred transaction output.
type TxOut struct {
Value int64
Version uint16
PkScript []byte
}
// SerializeSize returns the number of bytes it would take to serialize the
// the transaction output.
func (t *TxOut) SerializeSize() int {
// Value 8 bytes + Version 2 bytes + serialized varint size for
// the length of PkScript + PkScript bytes.
return 8 + 2 + VarIntSerializeSize(uint64(len(t.PkScript))) + len(t.PkScript)
}
// NewTxOut returns a new Decred transaction output with the provided
// transaction value and public key script.
func NewTxOut(value int64, pkScript []byte) *TxOut {
return &TxOut{
Value: value,
Version: DefaultPkScriptVersion,
PkScript: pkScript,
}
}
// MsgTx implements the Message interface and represents a Decred tx message.
// It is used to deliver transaction information in response to a getdata
// message (MsgGetData) for a given transaction.
//
// Use the AddTxIn and AddTxOut functions to build up the list of transaction
// inputs and outputs.
type MsgTx struct {
CachedHash *chainhash.Hash
SerType TxSerializeType
Version uint16
TxIn []*TxIn
TxOut []*TxOut
LockTime uint32
Expiry uint32 // In blocks
}
// AddTxIn adds a transaction input to the message.
func (msg *MsgTx) AddTxIn(ti *TxIn) {
msg.TxIn = append(msg.TxIn, ti)
}
// AddTxOut adds a transaction output to the message.
func (msg *MsgTx) AddTxOut(to *TxOut) {
msg.TxOut = append(msg.TxOut, to)
}
// serialize returns the serialization of the transaction for the provided
// serialization type without modifying the original transaction.
func (msg *MsgTx) serialize(serType TxSerializeType) ([]byte, error) {
// Shallow copy so the serialization type can be changed without
// modifying the original transaction.
mtxCopy := *msg
mtxCopy.SerType = serType
buf := bytes.NewBuffer(make([]byte, 0, mtxCopy.SerializeSize()))
err := mtxCopy.Serialize(buf)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// mustSerialize returns the serialization of the transaction for the provided
// serialization type without modifying the original transaction. It will panic
// if any errors occur.
func (msg *MsgTx) mustSerialize(serType TxSerializeType) []byte {
serialized, err := msg.serialize(serType)
if err != nil {
panic(fmt.Sprintf("MsgTx failed serializing for type %v",
serType))
}
return serialized
}
// TxHash generates the hash for the transaction prefix. Since it does not
// contain any witness data, it is not malleable and therefore is stable for
// use in unconfirmed transaction chains.
func (msg *MsgTx) TxHash() chainhash.Hash {
// TxHash should always calculate a non-witnessed hash.
return chainhash.HashH(msg.mustSerialize(TxSerializeNoWitness))
}
// CachedTxHash is equivalent to calling TxHash, however it caches the result so
// subsequent calls do not have to recalculate the hash. It can be recalculated
// later with RecacheTxHash.
func (msg *MsgTx) CachedTxHash() *chainhash.Hash {
if msg.CachedHash == nil {
h := msg.TxHash()
msg.CachedHash = &h
}
return msg.CachedHash
}
// RecacheTxHash is equivalent to calling TxHash, however it replaces the cached
// result so future calls to CachedTxHash will return this newly calculated
// hash.
func (msg *MsgTx) RecacheTxHash() *chainhash.Hash {
h := msg.TxHash()
msg.CachedHash = &h
return msg.CachedHash
}
// TxHashWitness generates the hash for the transaction witness.
func (msg *MsgTx) TxHashWitness() chainhash.Hash {
// TxHashWitness should always calculate a witnessed hash.
return chainhash.HashH(msg.mustSerialize(TxSerializeOnlyWitness))
}
// TxHashFull generates the hash for the transaction prefix || witness. It first
// obtains the hashes for both the transaction prefix and witness, then
// concatenates them and hashes the result.
func (msg *MsgTx) TxHashFull() chainhash.Hash {
// Note that the inputs to the hashes, the serialized prefix and
// witness, have different serialized versions because the serialized
// encoding of the version includes the real transaction version in the
// lower 16 bits and the transaction serialization type in the upper 16
// bits. The real transaction version (lower 16 bits) will be the same
// in both serializations.
concat := make([]byte, chainhash.HashSize*2)
prefixHash := msg.TxHash()
witnessHash := msg.TxHashWitness()
copy(concat[0:], prefixHash[:])
copy(concat[chainhash.HashSize:], witnessHash[:])
return chainhash.HashH(concat)
}
// Copy creates a deep copy of a transaction so that the original does not get
// modified when the copy is manipulated.
func (msg *MsgTx) Copy() *MsgTx {
// Create new tx and start by copying primitive values and making space
// for the transaction inputs and outputs.
newTx := MsgTx{
SerType: msg.SerType,
Version: msg.Version,
TxIn: make([]*TxIn, 0, len(msg.TxIn)),
TxOut: make([]*TxOut, 0, len(msg.TxOut)),
LockTime: msg.LockTime,
Expiry: msg.Expiry,
}
// Deep copy the old TxIn data.
for _, oldTxIn := range msg.TxIn {
// Deep copy the old previous outpoint.
oldOutPoint := oldTxIn.PreviousOutPoint
newOutPoint := OutPoint{}
newOutPoint.Hash.SetBytes(oldOutPoint.Hash[:])
newOutPoint.Index = oldOutPoint.Index
newOutPoint.Tree = oldOutPoint.Tree
// Deep copy the old signature script.
var newScript []byte
oldScript := oldTxIn.SignatureScript
oldScriptLen := len(oldScript)
if oldScriptLen > 0 {
newScript = make([]byte, oldScriptLen)
copy(newScript, oldScript[:oldScriptLen])
}
// Create new txIn with the deep copied data and append it to
// new Tx.
newTxIn := TxIn{
PreviousOutPoint: newOutPoint,
Sequence: oldTxIn.Sequence,
ValueIn: oldTxIn.ValueIn,
BlockHeight: oldTxIn.BlockHeight,
BlockIndex: oldTxIn.BlockIndex,
SignatureScript: newScript,
}
newTx.TxIn = append(newTx.TxIn, &newTxIn)
}
// Deep copy the old TxOut data.
for _, oldTxOut := range msg.TxOut {
// Deep copy the old PkScript
var newScript []byte
oldScript := oldTxOut.PkScript
oldScriptLen := len(oldScript)
if oldScriptLen > 0 {
newScript = make([]byte, oldScriptLen)
copy(newScript, oldScript[:oldScriptLen])
}
// Create new txOut with the deep copied data and append it to
// new Tx.
newTxOut := TxOut{
Value: oldTxOut.Value,
Version: oldTxOut.Version,
PkScript: newScript,
}
newTx.TxOut = append(newTx.TxOut, &newTxOut)
}
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, serType 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 serType {
case TxSerializeNoWitness:
writeTxOuts()
case TxSerializeOnlyWitness:
fallthrough
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) (uint64, error) {
const op = "MsgTx.decodePrefix"
count, err := ReadVarInt(r, pver)
if err != nil {
return 0, err
}
// Prevent more input transactions than could possibly fit into a
// message. It would be possible to cause memory exhaustion and panics
// without a sane upper bound on this count.
if count > uint64(maxTxInPerMessage) {
msg := fmt.Sprintf("too many input transactions to fit into max "+
"message size [count %d, max %d]", count, maxTxInPerMessage)
return 0, messageError(op, ErrTooManyTxs, msg)
}
// 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.SerType, msg.Version, ti)
if err != nil {
return 0, err
}
}
count, err = ReadVarInt(r, pver)
if err != nil {
return 0, err
}
// Prevent more output transactions than could possibly fit into a
// message. It would be possible to cause memory exhaustion and panics
// without a sane upper bound on this count.
if count > uint64(maxTxOutPerMessage) {
msg := fmt.Sprintf("too many output transactions to fit into "+
"max message size [count %d, max %d]", count, maxTxOutPerMessage)
return 0, messageError(op, ErrTooManyTxs, msg)
}
// 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 0, err
}
totalScriptSize += uint64(len(to.PkScript))
}
// Locktime and expiry.
msg.LockTime, err = binarySerializer.Uint32(r, littleEndian)
if err != nil {
return 0, err
}
msg.Expiry, err = binarySerializer.Uint32(r, littleEndian)
if err != nil {
return 0, err
}
return totalScriptSize, nil
}
func (msg *MsgTx) decodeWitness(r io.Reader, pver uint32, isFull bool) (uint64, error) {
const op = "MsgTx.decodeWitness"
// 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 0, err
}
// Prevent more input transactions than could possibly fit into a
// message. It would be possible to cause memory exhaustion and panics
// without a sane upper bound on this count.
if count > uint64(maxTxInPerMessage) {
str := fmt.Sprintf("too many input transactions to fit into "+
"max message size [count %d, max %d]", count, maxTxInPerMessage)
return 0, messageError(op, ErrTooManyTxs, 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 0, err
}
totalScriptSize += uint64(len(ti.SignatureScript))
}
msg.TxOut = make([]*TxOut, 0)
} else {
// We're decoding witnesses from a full transaction, so read in
// the number of signature scripts, check to make sure it's the
// same as the number of TxIns we currently have, then fill in
// the signature scripts.
count, err := ReadVarInt(r, pver)
if err != nil {
return 0, err
}
// Don't allow the deserializer to panic by accessing memory
// that doesn't exist.
if int(count) != len(msg.TxIn) {
msg := fmt.Sprintf("non equal witness and prefix txin quantities "+
"(witness %v, prefix %v)", count, len(msg.TxIn))
return 0, messageError(op, ErrMismatchedWitnessCount, msg)
}
// Prevent more input transactions than could possibly fit into a
// message. It would be possible to cause memory exhaustion and panics
// without a sane upper bound on this count.
if count > uint64(maxTxInPerMessage) {
msg := fmt.Sprintf("too many input transactions to fit into "+
"max message size [count %d, max %d]", count, maxTxInPerMessage)
return 0, messageError(op, ErrTooManyTxs, msg)
}
// Read in the witnesses, and copy them into the already generated
// by decodePrefix TxIns.
txIns := make([]TxIn, count)
for i := uint64(0); i < count; i++ {
ti := &txIns[i]
err = readTxInWitness(r, pver, msg.Version, ti)
if err != nil {
return 0, err
}
totalScriptSize += uint64(len(ti.SignatureScript))
msg.TxIn[i].ValueIn = ti.ValueIn
msg.TxIn[i].BlockHeight = ti.BlockHeight
msg.TxIn[i].BlockIndex = ti.BlockIndex
msg.TxIn[i].SignatureScript = ti.SignatureScript
}
}
return totalScriptSize, nil
}
// BtcDecode decodes r using the Decred protocol encoding into the receiver.
// This is part of the Message interface implementation.
// See Deserialize for decoding transactions stored to disk, such as in a
// database, as opposed to decoding transactions from the wire.
func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32) error {
const op = "MsgTx.BtcDecode"
// The serialized encoding of the version includes the real transaction
// version in the lower 16 bits and the transaction serialization type
// in the upper 16 bits.
version, err := binarySerializer.Uint32(r, littleEndian)
if err != nil {
return err
}
msg.Version = uint16(version & 0xffff)
msg.SerType = TxSerializeType(version >> 16)
// 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 txSerType := msg.SerType; txSerType {
case TxSerializeNoWitness:
totalScriptSize, err := msg.decodePrefix(r, pver)
if err != nil {
returnScriptBuffers()
return err
}
writeTxScriptsToMsgTx(msg, totalScriptSize, txSerType)
case TxSerializeOnlyWitness:
totalScriptSize, err := msg.decodeWitness(r, pver, false)
if err != nil {
returnScriptBuffers()
return err
}
writeTxScriptsToMsgTx(msg, totalScriptSize, txSerType)
case TxSerializeFull:
totalScriptSizeIns, err := msg.decodePrefix(r, pver)
if err != nil {
returnScriptBuffers()
return err
}
totalScriptSizeOuts, err := msg.decodeWitness(r, pver, true)
if err != nil {
returnScriptBuffers()
return err
}
writeTxScriptsToMsgTx(msg, totalScriptSizeIns+
totalScriptSizeOuts, txSerType)
default:
return messageError(op, ErrUnknownTxType, "unsupported transaction type")
}
return nil
}
// Deserialize decodes a transaction from r into the receiver using a format
// that is suitable for long-term storage such as a database while respecting
// the Version field in the transaction. This function differs from BtcDecode
// in that BtcDecode decodes from the Decred wire protocol as it was sent
// across the network. The wire encoding can technically differ depending on
// the protocol version and doesn't even really need to match the format of a
// stored transaction at all. As of the time this comment was written, the
// encoded transaction is the same in both instances, but there is a distinct
// difference and separating the two allows the API to be flexible enough to
// deal with changes.
func (msg *MsgTx) Deserialize(r io.Reader) error {
// At the current time, there is no difference between the wire encoding
// at protocol version 0 and the stable long-term storage format. As
// a result, make use of BtcDecode.
return msg.BtcDecode(r, 0)
}
// FromBytes deserializes a transaction byte slice.
func (msg *MsgTx) FromBytes(b []byte) error {
r := bytes.NewReader(b)
return msg.Deserialize(r)
}
// encodePrefix encodes a transaction prefix into a writer.
func (msg *MsgTx) encodePrefix(w io.Writer, pver uint32) error {
count := uint64(len(msg.TxIn))
err := WriteVarInt(w, pver, count)
if err != nil {
return err
}
for _, ti := range msg.TxIn {
err = writeTxInPrefix(w, pver, msg.Version, ti)
if err != nil {
return err
}
}
count = uint64(len(msg.TxOut))
err = WriteVarInt(w, pver, count)
if err != nil {
return err
}
for _, to := range msg.TxOut {
err = writeTxOut(w, pver, msg.Version, to)
if err != nil {
return err
}
}
err = binarySerializer.PutUint32(w, littleEndian, msg.LockTime)
if err != nil {
return err
}
return binarySerializer.PutUint32(w, littleEndian, msg.Expiry)
}
// encodeWitness encodes a transaction witness into a writer.
func (msg *MsgTx) encodeWitness(w io.Writer, pver uint32) error {
count := uint64(len(msg.TxIn))
err := WriteVarInt(w, pver, count)
if err != nil {
return err
}
for _, ti := range msg.TxIn {
err = writeTxInWitness(w, pver, msg.Version, ti)
if err != nil {
return err
}
}
return nil
}
// BtcEncode encodes the receiver to w using the Decred protocol encoding.
// This is part of the Message interface implementation.
// See Serialize for encoding transactions to be stored to disk, such as in a
// database, as opposed to encoding transactions for the wire.
func (msg *MsgTx) BtcEncode(w io.Writer, pver uint32) error {
// The serialized encoding of the version includes the real transaction
// version in the lower 16 bits and the transaction serialization type
// in the upper 16 bits.
serializedVersion := uint32(msg.Version) | uint32(msg.SerType)<<16
err := binarySerializer.PutUint32(w, littleEndian, serializedVersion)
if err != nil {
return err
}
switch msg.SerType {
case TxSerializeNoWitness:
err := msg.encodePrefix(w, pver)
if err != nil {
return err
}
case TxSerializeOnlyWitness:
err := msg.encodeWitness(w, pver)
if err != nil {
return err
}
case TxSerializeFull:
err := msg.encodePrefix(w, pver)
if err != nil {
return err
}
err = msg.encodeWitness(w, pver)
if err != nil {
return err
}
default:
return messageError("MsgTx.BtcEncode", ErrUnknownTxType,
"unsupported transaction type")
}
return nil
}
// Serialize encodes the transaction to w using a format that suitable for
// long-term storage such as a database while respecting the Version field in
// the transaction. This function differs from BtcEncode in that BtcEncode
// encodes the transaction to the Decred wire protocol in order to be sent
// across the network. The wire encoding can technically differ depending on
// the protocol version and doesn't even really need to match the format of a
// stored transaction at all. As of the time this comment was written, the
// encoded transaction is the same in both instances, but there is a distinct
// difference and separating the two allows the API to be flexible enough to
// deal with changes.
func (msg *MsgTx) Serialize(w io.Writer) error {
// At the current time, there is no difference between the wire encoding
// at protocol version 0 and the stable long-term storage format. As
// a result, make use of BtcEncode.
return msg.BtcEncode(w, 0)
}
// Bytes returns the serialized form of the transaction in bytes.
func (msg *MsgTx) Bytes() ([]byte, error) {
buf := bytes.NewBuffer(make([]byte, 0, msg.SerializeSize()))
err := msg.Serialize(buf)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// BytesPrefix returns the serialized form of the transaction prefix in bytes.
func (msg *MsgTx) BytesPrefix() ([]byte, error) {
return msg.serialize(TxSerializeNoWitness)
}
// BytesWitness returns the serialized form of the transaction prefix in bytes.
func (msg *MsgTx) BytesWitness() ([]byte, error) {
return msg.serialize(TxSerializeOnlyWitness)
}
// SerializeSize returns the number of bytes it would take to serialize the
// the transaction.
func (msg *MsgTx) SerializeSize() int {
// Unknown type return 0.
n := 0
switch msg.SerType {
case TxSerializeNoWitness:
// Version 4 bytes + LockTime 4 bytes + Expiry 4 bytes +
// Serialized varint size for the number of transaction
// inputs and outputs.
n = 12 + VarIntSerializeSize(uint64(len(msg.TxIn))) +
VarIntSerializeSize(uint64(len(msg.TxOut)))
for _, txIn := range msg.TxIn {
n += txIn.SerializeSizePrefix()
}
for _, txOut := range msg.TxOut {
n += txOut.SerializeSize()
}
case TxSerializeOnlyWitness:
// Version 4 bytes + Serialized varint size for the
// number of transaction signatures.
n = 4 + VarIntSerializeSize(uint64(len(msg.TxIn)))
for _, txIn := range msg.TxIn {
n += txIn.SerializeSizeWitness()
}
case TxSerializeFull:
// Version 4 bytes + LockTime 4 bytes + Expiry 4 bytes + Serialized
// varint size for the number of transaction inputs (x2) and
// outputs. The number of inputs is added twice because it's
// encoded once in both the witness and the prefix.
n = 12 + VarIntSerializeSize(uint64(len(msg.TxIn))) +
VarIntSerializeSize(uint64(len(msg.TxIn))) +
VarIntSerializeSize(uint64(len(msg.TxOut)))
for _, txIn := range msg.TxIn {
n += txIn.SerializeSizePrefix()
}
for _, txIn := range msg.TxIn {
n += txIn.SerializeSizeWitness()
}
for _, txOut := range msg.TxOut {
n += txOut.SerializeSize()
}
}
return n
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgTx) Command() string {
return CmdTx
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgTx) MaxPayloadLength(pver uint32) uint32 {
// Protocol version 3 and lower have a different max block payload.
if pver <= 3 {
return MaxBlockPayloadV3
}
return MaxBlockPayload
}
// PkScriptLocs returns a slice containing the start of each public key script
// within the raw serialized transaction. The caller can easily obtain the
// length of each script by using len on the script available via the
// appropriate transaction output entry.
// TODO: Make this work for all serialization types, not just the full
// serialization type.
func (msg *MsgTx) PkScriptLocs() []int {
// Return nil for witness-only tx.
numTxOut := len(msg.TxOut)
if numTxOut == 0 {
return nil
}
// The starting offset in the serialized transaction of the first
// transaction output is:
//
// Version 4 bytes + serialized varint size for the number of
// transaction inputs and outputs + serialized size of each transaction
// input.
n := 4 + VarIntSerializeSize(uint64(len(msg.TxIn))) +
VarIntSerializeSize(uint64(numTxOut))
for _, txIn := range msg.TxIn {
n += txIn.SerializeSizePrefix()
}
// Calculate and set the appropriate offset for each public key script.
pkScriptLocs := make([]int, numTxOut)
for i, txOut := range msg.TxOut {
// The offset of the script in the transaction output is:
//
// Value 8 bytes + version 2 bytes + serialized varint size
// for the length of PkScript.
n += 8 + 2 + VarIntSerializeSize(uint64(len(txOut.PkScript)))
pkScriptLocs[i] = n
n += len(txOut.PkScript)
}
return pkScriptLocs
}
// NewMsgTx returns a new Decred tx message that conforms to the Message
// interface. The return instance has a default version of TxVersion and there
// are no transaction inputs or outputs. Also, the lock time is set to zero
// to indicate the transaction is valid immediately as opposed to some time in
// future.
func NewMsgTx() *MsgTx {
return &MsgTx{
SerType: TxSerializeFull,
Version: TxVersion,
TxIn: make([]*TxIn, 0, defaultTxInOutAlloc),
TxOut: make([]*TxOut, 0, defaultTxInOutAlloc),
}
}
// ReadOutPoint reads the next sequence of bytes from r as an OutPoint.
func ReadOutPoint(r io.Reader, pver uint32, version uint16, op *OutPoint) error {
_, err := io.ReadFull(r, op.Hash[:])
if err != nil {
return err
}
op.Index, err = binarySerializer.Uint32(r, littleEndian)
if err != nil {
return err
}
tree, err := binarySerializer.Uint8(r)
if err != nil {
return err
}
op.Tree = int8(tree)
return nil
}
// WriteOutPoint encodes op to the Decred protocol encoding for an OutPoint
// to w.
func WriteOutPoint(w io.Writer, pver uint32, version uint16, op *OutPoint) error {
_, err := w.Write(op.Hash[:])
if err != nil {
return err
}
err = binarySerializer.PutUint32(w, littleEndian, op.Index)
if err != nil {
return err
}
return binarySerializer.PutUint8(w, uint8(op.Tree))
}
// readTxInPrefix reads the next sequence of bytes from r as a transaction input
// (TxIn) in the transaction prefix.
func readTxInPrefix(r io.Reader, pver uint32, serType TxSerializeType, version uint16, ti *TxIn) error {
if serType == TxSerializeOnlyWitness {
return messageError("readTxInPrefix", ErrReadInPrefixFromWitnessOnlyTx,
"tried to read a prefix input for a witness only tx")
}
// Outpoint.
err := ReadOutPoint(r, pver, version, &ti.PreviousOutPoint)
if err != nil {
return err
}
// Sequence.
ti.Sequence, err = binarySerializer.Uint32(r, littleEndian)
return err
}
// readTxInWitness reads the next sequence of bytes from r as a transaction input
// (TxIn) in the transaction witness.
func readTxInWitness(r io.Reader, pver uint32, version uint16, ti *TxIn) error {
// ValueIn.
valueIn, err := binarySerializer.Uint64(r, littleEndian)
if err != nil {
return err
}
ti.ValueIn = int64(valueIn)
// BlockHeight.
ti.BlockHeight, err = binarySerializer.Uint32(r, littleEndian)
if err != nil {
return err
}
// BlockIndex.
ti.BlockIndex, err = binarySerializer.Uint32(r, littleEndian)
if err != nil {
return err
}
// Signature script.
ti.SignatureScript, err = readScript(r, pver, MaxMessagePayload,
"transaction input signature script")
return err
}
// writeTxInPrefixs encodes ti to the Decred protocol encoding for a transaction
// input (TxIn) prefix to w.
func writeTxInPrefix(w io.Writer, pver uint32, version uint16, ti *TxIn) error {
err := WriteOutPoint(w, pver, version, &ti.PreviousOutPoint)
if err != nil {
return err
}
return binarySerializer.PutUint32(w, littleEndian, ti.Sequence)
}
// writeTxWitness encodes ti to the Decred protocol encoding for a transaction
// input (TxIn) witness to w.
func writeTxInWitness(w io.Writer, pver uint32, version uint16, ti *TxIn) error {
// ValueIn.
err := binarySerializer.PutUint64(w, littleEndian, uint64(ti.ValueIn))
if err != nil {
return err
}
// BlockHeight.
err = binarySerializer.PutUint32(w, littleEndian, ti.BlockHeight)
if err != nil {
return err
}
// BlockIndex.
err = binarySerializer.PutUint32(w, littleEndian, ti.BlockIndex)
if err != nil {
return err
}
// Write the signature script.
return WriteVarBytes(w, pver, ti.SignatureScript)
}
// readTxOut reads the next sequence of bytes from r as a transaction output
// (TxOut).
func readTxOut(r io.Reader, pver uint32, version uint16, to *TxOut) error {
value, err := binarySerializer.Uint64(r, littleEndian)
if err != nil {
return err
}
to.Value = int64(value)
to.Version, err = binarySerializer.Uint16(r, littleEndian)
if err != nil {
return err
}
to.PkScript, err = readScript(r, pver, MaxMessagePayload,
"transaction output public key script")
return err
}
// writeTxOut encodes to into the Decred protocol encoding for a transaction
// output (TxOut) to w.
func writeTxOut(w io.Writer, pver uint32, version uint16, to *TxOut) error {
err := binarySerializer.PutUint64(w, littleEndian, uint64(to.Value))
if err != nil {
return err
}
err = binarySerializer.PutUint16(w, littleEndian, to.Version)
if err != nil {
return err
}
return WriteVarBytes(w, pver, to.PkScript)
}