multi: Separate tx serialization type from version.

Decred's serialized format for transactions split the 32-bit version
field into two 16-bit components such that the upper bits are used to
encode a serialization type and the lower 16 bits are the actual
transaction version.

Unfortunately, when this was done, the in-memory transaction struct was
not also updated to hide this complexity, which means that callers
currently have to understand and take special care when dealing with the
version field of the transaction.

Since the main purpose of the wire package is precisely to hide these
details, this remedies the situation by introducing a new field on the
in-memory transaction struct named SerType which houses the
serialization type and changes the Version field back to having the
desired semantics of actually being the real transaction version.  Also,
since the maximum version can only be a 16-bit value, the Version field
has been changed to a uint16 to properly reflect this.

The serialization and deserialization functions now deal with properly
converting to and from these fields to the actual serialized format as
intended.

Finally, these changes also include a fairly significant amount of
related code cleanup and optimization along with some bug fixes in order
to allow the transaction version to be bumped as intended.

The following is an overview of all changes:
- Introduce new SerType field to MsgTx to specify the serialization type
- Change MsgTx.Version to a uint16 to properly reflect its maximum
  allowed value
- Change the semantics of MsgTx.Version to be the actual transaction
  version as intended
- Update all callers that had special code to deal with the previous
  Version field semantics to use the new semantics
- Switch all of the code that deals with encoding and decoding the
  serialized version field to use more efficient masks and shifts
  instead of binary writes into buffers which cause allocations
- Correct several issues that would prevent producing expected
  serializations for transactions with actual transaction versions that
  are not 1
- Simplify the various serialize functions to use a single func which
  accepts the serialization type to reduce code duplication
- Make serialization type switch usage more consistent with the rest of
  the code base
- Update the utxoview and related code to use uint16s for the
  transaction version as well since it should not care about the
  serialization type due to using its own
- Make code more consistent in how it uses bytes.Buffer
- Clean up several of the comments regarding hashes and add some new
  comments to better describe the serialization types
This commit is contained in:
Dave Collins 2017-08-04 18:43:45 -05:00
parent 961f708116
commit fce24223cd
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2
21 changed files with 265 additions and 307 deletions

View File

@ -1,5 +1,5 @@
// Copyright (c) 2015-2016 The btcsuite developers
// Copyright (c) 2016 The Decred developers
// Copyright (c) 2016-2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@ -264,7 +264,7 @@ type spentTxOut struct {
pkScript []byte // The public key script for the output.
stakeExtra []byte // Extra information for the staking system.
amount int64 // The amount of the output.
txVersion int32 // The txVersion of creating tx.
txVersion uint16 // The version of creating tx.
height uint32 // Height of the the block containing the tx.
index uint32 // Index in the block of the transaction.
scriptVersion uint16 // The version of the scripting language.
@ -393,7 +393,7 @@ func decodeSpentTxOut(serialized []byte, stxo *spentTxOut, amount int64,
"after version")
}
stxo.txVersion = int32(txVersion)
stxo.txVersion = uint16(txVersion)
if stxo.txType == stake.TxTypeSStx {
sz := readDeserializeSizeOfMinimalOutputs(serialized[offset:])
@ -837,7 +837,7 @@ func deserializeUtxoEntry(serialized []byte) (*UtxoEntry, error) {
// Create a new utxo entry with the details deserialized above to house
// all of the utxos.
entry := newUtxoEntry(int32(version), uint32(blockHeight),
entry := newUtxoEntry(uint16(version), uint32(blockHeight),
uint32(blockIndex), isCoinBase, hasExpiry, txType)
// Add sparse output for unspent outputs 0 and 1 as needed based on the

View File

@ -299,6 +299,7 @@ func TestSpendJournalSerialization(t *testing.T) {
stakeExtra: nil,
}},
blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{{
PreviousOutPoint: wire.OutPoint{
@ -351,6 +352,7 @@ func TestSpendJournalSerialization(t *testing.T) {
stakeExtra: nil,
}},
blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{{
PreviousOutPoint: wire.OutPoint{
@ -376,6 +378,7 @@ func TestSpendJournalSerialization(t *testing.T) {
LockTime: 0,
Expiry: 0,
}, {
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{{
PreviousOutPoint: wire.OutPoint{
@ -438,6 +441,7 @@ func TestSpendJournalSerialization(t *testing.T) {
compressed: true,
}},
blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{{
PreviousOutPoint: wire.OutPoint{
@ -542,6 +546,7 @@ func TestSpendJournalErrors(t *testing.T) {
{
name: "Force assertion due to missing stxos",
blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{{
PreviousOutPoint: wire.OutPoint{
@ -559,6 +564,7 @@ func TestSpendJournalErrors(t *testing.T) {
{
name: "Force deserialization error in stxos",
blockTxns: []*wire.MsgTx{{ // Coinbase omitted.
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{{
PreviousOutPoint: wire.OutPoint{

View File

@ -69,6 +69,7 @@ var (
Height: uint32(0),
},
Transactions: []*wire.MsgTx{{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{{
PreviousOutPoint: wire.OutPoint{

View File

@ -1634,6 +1634,7 @@ var sstxTxOut4VerBad = wire.TxOut{
// sstxMsgTx is a valid SStx MsgTx with an input and outputs and is used in various
// tests
var sstxMsgTx = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&sstxTxIn,
@ -1655,6 +1656,7 @@ var sstxMsgTx = &wire.MsgTx{
// sstxMsgTxExtraInputs is an invalid SStx MsgTx with too many inputs
var sstxMsgTxExtraInput = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&sstxTxIn, &sstxTxIn, &sstxTxIn, &sstxTxIn, &sstxTxIn, &sstxTxIn,
@ -1680,6 +1682,7 @@ var sstxMsgTxExtraInput = &wire.MsgTx{
// sstxMsgTxExtraOutputs is an invalid SStx MsgTx with too many outputs
var sstxMsgTxExtraOutputs = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&sstxTxIn,
@ -1721,6 +1724,7 @@ var sstxMsgTxExtraOutputs = &wire.MsgTx{
// sstxMismatchedInsOuts is an invalid SStx MsgTx with too many outputs for the
// number of inputs it has
var sstxMismatchedInsOuts = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&sstxTxIn,
@ -1735,6 +1739,7 @@ var sstxMismatchedInsOuts = &wire.MsgTx{
// sstxBadVersionOut is an invalid SStx MsgTx with an output containing a bad
// version.
var sstxBadVersionOut = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&sstxTxIn,
@ -1757,6 +1762,7 @@ var sstxBadVersionOut = &wire.MsgTx{
// sstxNullDataMissing is an invalid SStx MsgTx with no address push in the second
// output
var sstxNullDataMissing = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&sstxTxIn,
@ -1771,6 +1777,7 @@ var sstxNullDataMissing = &wire.MsgTx{
// sstxNullDataMisplaced is an invalid SStx MsgTx that has the commitment and
// change outputs swapped
var sstxNullDataMisplaced = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&sstxTxIn,
@ -1926,6 +1933,7 @@ var ssgenTxOut3BadVer = wire.TxOut{
// ssgenMsgTx is a valid SSGen MsgTx with an input and outputs and is used in
// various testing scenarios
var ssgenMsgTx = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&ssgenTxIn0,
@ -1943,6 +1951,7 @@ var ssgenMsgTx = &wire.MsgTx{
// ssgenMsgTxExtraInput is an invalid SSGen MsgTx with too many inputs
var ssgenMsgTxExtraInput = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&ssgenTxIn0,
@ -1960,6 +1969,7 @@ var ssgenMsgTxExtraInput = &wire.MsgTx{
// ssgenMsgTxExtraOutputs is an invalid SSGen MsgTx with too many outputs
var ssgenMsgTxExtraOutputs = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&ssgenTxIn0,
@ -1992,6 +2002,7 @@ var ssgenMsgTxExtraOutputs = &wire.MsgTx{
// ssgenMsgTxStakeBaseWrong is an invalid SSGen tx with the stakebase in the wrong
// position
var ssgenMsgTxStakeBaseWrong = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&ssgenTxIn1,
@ -2009,6 +2020,7 @@ var ssgenMsgTxStakeBaseWrong = &wire.MsgTx{
// ssgenMsgTxBadVerOut is an invalid SSGen tx that contains an output with a bad
// version
var ssgenMsgTxBadVerOut = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&ssgenTxIn0,
@ -2027,6 +2039,7 @@ var ssgenMsgTxBadVerOut = &wire.MsgTx{
// ssgenMsgTxWrongZeroethOut is an invalid SSGen tx with the first output being not
// an OP_RETURN push
var ssgenMsgTxWrongZeroethOut = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&ssgenTxIn0,
@ -2044,6 +2057,7 @@ var ssgenMsgTxWrongZeroethOut = &wire.MsgTx{
// ssgenMsgTxWrongFirstOut is an invalid SSGen tx with the second output being not
// an OP_RETURN push
var ssgenMsgTxWrongFirstOut = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&ssgenTxIn0,
@ -2154,6 +2168,7 @@ var ssrtxTxOut2BadVer = wire.TxOut{
// ssrtxMsgTx is a valid SSRtx MsgTx with an input and outputs and is used in
// various testing scenarios
var ssrtxMsgTx = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&ssrtxTxIn,
@ -2169,6 +2184,7 @@ var ssrtxMsgTx = &wire.MsgTx{
// ssrtxMsgTx is a valid SSRtx MsgTx with an input and outputs and is used in
// various testing scenarios
var ssrtxMsgTxTooManyInputs = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&ssrtxTxIn,
@ -2184,6 +2200,7 @@ var ssrtxMsgTxTooManyInputs = &wire.MsgTx{
// ssrtxMsgTx is a valid SSRtx MsgTx with an input and outputs and is used in
// various testing scenarios
var ssrtxMsgTxTooManyOutputs = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&ssrtxTxIn,
@ -2210,6 +2227,7 @@ var ssrtxMsgTxTooManyOutputs = &wire.MsgTx{
}
var ssrtxMsgTxBadVerOut = &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
&ssrtxTxIn,

View File

@ -1106,6 +1106,7 @@ var simNetGenesisMerkleRoot = genesisMerkleRoot
// genesisCoinbaseTx legacy is the coinbase transaction for the genesis blocks for
// the regression test network and test network.
var genesisCoinbaseTxLegacy = wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
{
@ -1153,6 +1154,7 @@ var genesisCoinbaseTxLegacy = wire.MsgTx{
var genesisMerkleRoot = genesisCoinbaseTxLegacy.TxHash()
var regTestGenesisCoinbaseTx = wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
{

View File

@ -1,4 +1,5 @@
// Copyright (c) 2015-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.
@ -91,7 +92,7 @@ type UtxoEntry struct {
sparseOutputs map[uint32]*utxoOutput // Sparse map of unspent outputs.
stakeExtra []byte // Extra data for the staking system.
txVersion int32 // The tx version of this tx.
txVersion uint16 // The tx version of this tx.
height uint32 // Height of block containing tx.
index uint32 // Index of containing tx in block.
txType stake.TxType // The stake type of the transaction.
@ -103,7 +104,7 @@ type UtxoEntry struct {
// TxVersion returns the transaction version of the transaction the
// utxo represents.
func (entry *UtxoEntry) TxVersion() int32 {
func (entry *UtxoEntry) TxVersion() uint16 {
return entry.txVersion
}
@ -260,7 +261,7 @@ func (entry *UtxoEntry) Clone() *UtxoEntry {
// newUtxoEntry returns a new unspent transaction output entry with the provided
// coinbase flag and block height ready to have unspent outputs added.
func newUtxoEntry(txVersion int32, height uint32, index uint32, isCoinBase bool,
func newUtxoEntry(txVersion uint16, height uint32, index uint32, isCoinBase bool,
hasExpiry bool, tt stake.TxType) *UtxoEntry {
return &UtxoEntry{
sparseOutputs: make(map[uint32]*utxoOutput),

View File

@ -2238,6 +2238,7 @@ var simNetGenesisMerkleRoot = genesisMerkleRoot
// genesisCoinbaseTx legacy is the coinbase transaction for the genesis blocks for
// the regression test network and test network.
var genesisCoinbaseTxLegacy = wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
{
@ -2285,6 +2286,7 @@ var genesisCoinbaseTxLegacy = wire.MsgTx{
var genesisMerkleRoot = genesisCoinbaseTxLegacy.TxHash()
var regTestGenesisCoinbaseTx = wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
{

View File

@ -17,6 +17,7 @@ import (
// genesisCoinbaseTx is the coinbase transaction for the genesis blocks for
// the main network.
var genesisCoinbaseTx = wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
{
@ -91,6 +92,7 @@ var genesisHash = genesisBlock.BlockHash()
// genesisCoinbaseTxLegacy is the coinbase transaction for the genesis block for
// the test network.
var genesisCoinbaseTxLegacy = wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
{
@ -162,6 +164,7 @@ var testNet2GenesisHash = testNet2GenesisBlock.BlockHash()
// SimNet -------------------------------------------------------------------------
var regTestGenesisCoinbaseTx = wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
{

View File

@ -350,21 +350,17 @@ func checkTransactionStandard(tx *dcrutil.Tx, txType stake.TxType, height int64,
timeSource blockchain.MedianTimeSource, minRelayTxFee dcrutil.Amount,
maxTxVersion uint16) error {
// The transaction must be a currently supported version.
//
// The version includes the real transaction version in the lower 16
// bits and the transaction serialize type as the upper 16 bits.
// The transaction must be a currently supported version and serialize
// type.
msgTx := tx.MsgTx()
serType := wire.TxSerializeType(uint32(msgTx.Version) >> 16)
txVersion := uint16(uint32(msgTx.Version) & 0xffff)
if serType != wire.TxSerializeFull {
if msgTx.SerType != wire.TxSerializeFull {
str := fmt.Sprintf("transaction is not serialized with all "+
"required data -- type %v", serType)
"required data -- type %v", msgTx.SerType)
return txRuleError(wire.RejectNonstandard, str)
}
if txVersion > maxTxVersion || txVersion < 1 {
if msgTx.Version > maxTxVersion || msgTx.Version < 1 {
str := fmt.Sprintf("transaction version %d is not in the "+
"valid range of %d-%d", txVersion, 1, maxTxVersion)
"valid range of %d-%d", msgTx.Version, 1, maxTxVersion)
return txRuleError(wire.RejectNonstandard, str)
}

View File

@ -1,5 +1,5 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2016 The Decred developers
// Copyright (c) 2016-2017 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@ -332,6 +332,7 @@ func TestCheckTransactionStandard(t *testing.T) {
{
name: "Typical pay-to-pubkey-hash transaction",
tx: wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{&dummyTxIn},
TxOut: []*wire.TxOut{&dummyTxOut},
@ -343,7 +344,8 @@ func TestCheckTransactionStandard(t *testing.T) {
{
name: "Transaction serialize type not full",
tx: wire.MsgTx{
Version: int32(uint32(wire.TxSerializeNoWitness)<<16 | 1),
SerType: wire.TxSerializeNoWitness,
Version: 1,
TxIn: []*wire.TxIn{&dummyTxIn},
TxOut: []*wire.TxOut{&dummyTxOut},
LockTime: 0,
@ -355,6 +357,7 @@ func TestCheckTransactionStandard(t *testing.T) {
{
name: "Transaction version too high",
tx: wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: maxTxVersion + 1,
TxIn: []*wire.TxIn{&dummyTxIn},
TxOut: []*wire.TxOut{&dummyTxOut},
@ -367,6 +370,7 @@ func TestCheckTransactionStandard(t *testing.T) {
{
name: "Transaction is not finalized",
tx: wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{{
PreviousOutPoint: dummyPrevOut,
@ -383,6 +387,7 @@ func TestCheckTransactionStandard(t *testing.T) {
{
name: "Transaction size is too large",
tx: wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{&dummyTxIn},
TxOut: []*wire.TxOut{{
@ -399,6 +404,7 @@ func TestCheckTransactionStandard(t *testing.T) {
{
name: "Signature script size is too large",
tx: wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{{
PreviousOutPoint: dummyPrevOut,
@ -416,6 +422,7 @@ func TestCheckTransactionStandard(t *testing.T) {
{
name: "Signature script that does more than push data",
tx: wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{{
PreviousOutPoint: dummyPrevOut,
@ -433,6 +440,7 @@ func TestCheckTransactionStandard(t *testing.T) {
{
name: "Valid but non standard public key script",
tx: wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{&dummyTxIn},
TxOut: []*wire.TxOut{{
@ -448,6 +456,7 @@ func TestCheckTransactionStandard(t *testing.T) {
{
name: "More than four nulldata outputs",
tx: wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{&dummyTxIn},
TxOut: []*wire.TxOut{{
@ -475,6 +484,7 @@ func TestCheckTransactionStandard(t *testing.T) {
{
name: "Dust output",
tx: wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{&dummyTxIn},
TxOut: []*wire.TxOut{{
@ -490,6 +500,7 @@ func TestCheckTransactionStandard(t *testing.T) {
{
name: "One nulldata output with 0 amount (standard)",
tx: wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{&dummyTxIn},
TxOut: []*wire.TxOut{{

View File

@ -1350,7 +1350,7 @@ func createTxRawResult(chainParams *chaincfg.Params, mtx *wire.MsgTx, txHash str
Txid: txHash,
Vin: createVinList(mtx),
Vout: createVoutList(mtx, chainParams, nil),
Version: mtx.Version,
Version: int32(mtx.Version),
LockTime: mtx.LockTime,
Expiry: mtx.Expiry,
BlockHeight: blkHeight,
@ -1391,7 +1391,7 @@ func handleDecodeRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan
// Create and return the result.
txReply := dcrjson.TxRawDecodeResult{
Txid: mtx.TxHash().String(),
Version: mtx.Version,
Version: int32(mtx.Version),
Locktime: mtx.LockTime,
Expiry: mtx.Expiry,
Vin: createVinList(&mtx),
@ -3961,7 +3961,7 @@ func handleGetTxOut(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (i
// from there, otherwise attempt to fetch from the block database.
var bestBlockHash string
var confirmations int64
var txVersion int32
var txVersion uint16
var value int64
var scriptVersion uint16
var pkScript []byte
@ -4045,7 +4045,7 @@ func handleGetTxOut(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (i
BestBlock: bestBlockHash,
Confirmations: confirmations,
Value: dcrutil.Amount(value).ToUnit(dcrutil.AmountCoin),
Version: txVersion,
Version: int32(txVersion),
ScriptPubKey: dcrjson.ScriptPubKeyResult{
Asm: disbuf,
Hex: hex.EncodeToString(pkScript),
@ -4972,7 +4972,7 @@ func handleSearchRawTransactions(s *rpcServer, cmd interface{}, closeChan <-chan
"Could not create vin list")
}
result.Vout = createVoutList(mtx, chainParams, filterAddrMap)
result.Version = mtx.Version
result.Version = int32(mtx.Version)
result.LockTime = mtx.LockTime
// Transactions grabbed from the mempool aren't yet in a block,

View File

@ -33,6 +33,7 @@ func TestBadPC(t *testing.T) {
}
// tx with almost empty scripts.
tx := &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
{
@ -93,6 +94,7 @@ func TestCheckErrorCondition(t *testing.T) {
// tx with almost empty scripts.
tx := &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
{
@ -185,6 +187,7 @@ func TestInvalidFlagCombinations(t *testing.T) {
// tx with almost empty scripts.
tx := &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
{

View File

@ -512,6 +512,8 @@ func TestIsPushOnlyScript(t *testing.T) {
// TestCalcSignatureHash does some rudimentary testing of msg hash calculation.
func TestCalcSignatureHash(t *testing.T) {
tx := new(wire.MsgTx)
tx.SerType = wire.TxSerializeFull
tx.Version = 1
for i := 0; i < 3; i++ {
txIn := new(wire.TxIn)
txIn.Sequence = 0xFFFFFFFF

View File

@ -169,6 +169,7 @@ func TestSignTxOutput(t *testing.T) {
secSchnorr,
}
tx := &wire.MsgTx{
SerType: wire.TxSerializeFull,
Version: 1,
TxIn: []*wire.TxIn{
{

View File

@ -1,5 +1,5 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2016 The Decred 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.
@ -19,6 +19,7 @@ import (
// genesisCoinbaseTx is the coinbase transaction for the genesis blocks for
// the main network, regression test network, and test network (version 3).
var genesisCoinbaseTx = MsgTx{
SerType: TxSerializeFull,
Version: 1,
TxIn: []*TxIn{
{
@ -83,6 +84,7 @@ var blockOne = MsgBlock{
},
Transactions: []*MsgTx{
{
SerType: TxSerializeFull,
Version: 1,
TxIn: []*TxIn{
{
@ -316,7 +318,7 @@ func BenchmarkReadTxIn(b *testing.B) {
var txIn TxIn
for i := 0; i < b.N; i++ {
r.Seek(0, 0)
readTxInPrefix(r, 0, 0, &txIn)
readTxInPrefix(r, 0, TxSerializeFull, 0, &txIn)
scriptPool.Return(txIn.SignatureScript)
}
}
@ -373,7 +375,8 @@ func BenchmarkDeserializeTxSmall(b *testing.B) {
// deserialize a very large transaction.
func BenchmarkDeserializeTxLarge(b *testing.B) {
bigTx := new(MsgTx)
bigTx.Version = DefaultMsgTxVersion()
bigTx.SerType = TxSerializeFull
bigTx.Version = TxVersion
inputsLen := 1000
outputsLen := 2000
bigTx.TxIn = make([]*TxIn, inputsLen)

View File

@ -37,7 +37,7 @@ type BlockHeader struct {
// Merkle tree reference to hash of all stake transactions for the block.
StakeRoot chainhash.Hash
// Votes on the previous merkleroot and yet undecided parameters. (TODO)
// Votes on the previous merkleroot and yet undecided parameters.
VoteBits uint16
// Final state of the PRNG used for ticket selection in the lottery.
@ -93,9 +93,8 @@ func (h *BlockHeader) BlockHash() chainhash.Hash {
// transactions. Ignore the error returns since there is no way the
// encode could fail except being out of memory which would cause a
// run-time panic.
var buf bytes.Buffer
buf.Grow(MaxBlockHeaderPayload)
_ = writeBlockHeader(&buf, 0, h)
buf := bytes.NewBuffer(make([]byte, 0, MaxBlockHeaderPayload))
_ = writeBlockHeader(buf, 0, h)
return chainhash.HashH(buf.Bytes())
}
@ -145,17 +144,12 @@ func (h *BlockHeader) Serialize(w io.Writer) error {
// Bytes returns a byte slice containing the serialized contents of the block
// header.
func (h *BlockHeader) Bytes() ([]byte, error) {
// Serialize the MsgBlock.
var w bytes.Buffer
w.Grow(MaxBlockHeaderPayload)
err := h.Serialize(&w)
buf := bytes.NewBuffer(make([]byte, 0, MaxBlockHeaderPayload))
err := h.Serialize(buf)
if err != nil {
return nil, err
}
serializedBlockHeader := w.Bytes()
// Cache the serialized bytes and return them.
return serializedBlockHeader, nil
return buf.Bytes(), nil
}
// NewBlockHeader returns a new BlockHeader using the provided previous block

View File

@ -1,5 +1,5 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2016 The Decred 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.
@ -195,9 +195,8 @@ func TestBlockHeaderWire(t *testing.T) {
for i, test := range tests {
// Encode to wire format.
// Former test (doesn't work because of capacity error)
var buf bytes.Buffer
buf.Grow(MaxBlockHeaderPayload)
err := writeBlockHeader(&buf, test.pver, test.in)
buf := bytes.NewBuffer(make([]byte, 0, MaxBlockHeaderPayload))
err := writeBlockHeader(buf, test.pver, test.in)
if err != nil {
t.Errorf("writeBlockHeader #%d error %v", i, err)
continue
@ -209,7 +208,7 @@ func TestBlockHeaderWire(t *testing.T) {
}
buf.Reset()
err = test.in.BtcEncode(&buf, pver)
err = test.in.BtcEncode(buf, pver)
if err != nil {
t.Errorf("BtcEncode #%d error %v", i, err)
continue
@ -320,14 +319,13 @@ func TestBlockHeaderSerialize(t *testing.T) {
}
t.Logf("Running %d tests", len(tests))
var buf bytes.Buffer
buf.Grow(MaxBlockHeaderPayload)
buf := bytes.NewBuffer(make([]byte, 0, MaxBlockHeaderPayload))
for i, test := range tests {
// Clear existing contents.
buf.Reset()
// Serialize the block header.
err := test.in.Serialize(&buf)
err := test.in.Serialize(buf)
if err != nil {
t.Errorf("Serialize #%d error %v", i, err)
continue

View File

@ -107,7 +107,7 @@ func (msg *MsgBlock) BtcDecode(r io.Reader, pver uint32) error {
msg.Transactions = make([]*MsgTx, 0, txCount)
for i := uint64(0); i < txCount; i++ {
tx := MsgTx{}
var tx MsgTx
err := tx.BtcDecode(r, pver)
if err != nil {
return err
@ -131,7 +131,7 @@ func (msg *MsgBlock) BtcDecode(r io.Reader, pver uint32) error {
msg.STransactions = make([]*MsgTx, 0, stakeTxCount)
for i := uint64(0); i < stakeTxCount; i++ {
tx := MsgTx{}
var tx MsgTx
err := tx.BtcDecode(r, pver)
if err != nil {
return err
@ -202,7 +202,7 @@ func (msg *MsgBlock) DeserializeTxLoc(r *bytes.Buffer) ([]TxLoc, []TxLoc, error)
txLocs := make([]TxLoc, txCount)
for i := uint64(0); i < txCount; i++ {
txLocs[i].TxStart = fullLen - r.Len()
tx := MsgTx{}
var tx MsgTx
err := tx.Deserialize(r)
if err != nil {
return nil, nil, err
@ -234,7 +234,7 @@ func (msg *MsgBlock) DeserializeTxLoc(r *bytes.Buffer) ([]TxLoc, []TxLoc, error)
sTxLocs := make([]TxLoc, stakeTxCount)
for i := uint64(0); i < stakeTxCount; i++ {
sTxLocs[i].TxStart = fullLen - r.Len()
tx := MsgTx{}
var tx MsgTx
err := tx.Deserialize(r)
if err != nil {
return nil, nil, err
@ -301,14 +301,12 @@ func (msg *MsgBlock) Serialize(w io.Writer) error {
// Bytes returns the serialized form of the block in bytes.
func (msg *MsgBlock) Bytes() ([]byte, error) {
// Serialize the MsgTx.
var w bytes.Buffer
w.Grow(msg.SerializeSize())
err := msg.Serialize(&w)
buf := bytes.NewBuffer(make([]byte, 0, msg.SerializeSize()))
err := msg.Serialize(buf)
if err != nil {
return nil, err
}
return w.Bytes(), nil
return buf.Bytes(), nil
}
// SerializeSize returns the number of bytes it would take to serialize the

View File

@ -1,5 +1,5 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2016 The Decred 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.
@ -314,9 +314,8 @@ func TestBlockSerialize(t *testing.T) {
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Serialize the block.
var buf bytes.Buffer
buf.Grow(test.in.SerializeSize())
err := test.in.Serialize(&buf)
buf := bytes.NewBuffer(make([]byte, 0, test.in.SerializeSize()))
err := test.in.Serialize(buf)
if err != nil {
t.Errorf("Serialize #%d error %v", i, err)
continue
@ -600,6 +599,7 @@ var testBlock = MsgBlock{
},
Transactions: []*MsgTx{
{
SerType: TxSerializeFull,
Version: 1,
TxIn: []*TxIn{
{
@ -642,6 +642,7 @@ var testBlock = MsgBlock{
},
STransactions: []*MsgTx{
{
SerType: TxSerializeFull,
Version: 1,
TxIn: []*TxIn{
{

View File

@ -7,7 +7,6 @@ package wire
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"strconv"
@ -51,11 +50,11 @@ const (
// inserted into a block yet.
TxTreeUnknown int8 = -1
// TxTreeRegular is the value for a normal transcation tree for a
// 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 transcation tree for a
// TxTreeStake is the value for a stake transaction tree for a
// transaction's location in a block.
TxTreeStake int8 = 1
)
@ -113,66 +112,31 @@ const (
freeListMaxItems = 12500
)
// TxSerializeType is a uint16 representing the serialized type of transaction
// this msgTx is. You can use a bitmask for this too, but Decred just splits
// the int32 version into 2x uint16s so that you have:
// {
// uint16 type
// uint16 version
// }
// TxSerializeType represents the serialized type of a transaction.
type TxSerializeType uint16
// The differente possible values for TxSerializeType.
const (
TxSerializeFull = TxSerializeType(iota)
// 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
// TxSerializeWitnessSigning indicates a transaction be serialized with
// only the witness scripts.
TxSerializeWitnessSigning
// TxSerializeWitnessValueSigning indicates a transaction be serialized
// with only the witness input values and scripts.
TxSerializeWitnessValueSigning
)
// TODO replace all these with predeclared int32 or [4]byte cj
// DefaultMsgTxVersion returns the default version int32 (serialize the tx
// fully, version number 1).
func DefaultMsgTxVersion() int32 {
verBytes := make([]byte, 4, 4)
binary.LittleEndian.PutUint16(verBytes[0:2], TxVersion)
binary.LittleEndian.PutUint16(verBytes[2:4], uint16(TxSerializeFull))
ver := binary.LittleEndian.Uint32(verBytes)
return int32(ver)
}
// NoWitnessMsgTxVersion returns the witness free serializing int32 (serialize
// the tx without witness, version number 1).
func NoWitnessMsgTxVersion() int32 {
verBytes := make([]byte, 4, 4)
binary.LittleEndian.PutUint16(verBytes[0:2], TxVersion)
binary.LittleEndian.PutUint16(verBytes[2:4], uint16(TxSerializeNoWitness))
ver := binary.LittleEndian.Uint32(verBytes)
return int32(ver)
}
// WitnessOnlyMsgTxVersion returns the witness only version int32 (serialize
// the tx witness, version number 1).
func WitnessOnlyMsgTxVersion() int32 {
verBytes := make([]byte, 4, 4)
binary.LittleEndian.PutUint16(verBytes[0:2], TxVersion)
binary.LittleEndian.PutUint16(verBytes[2:4], uint16(TxSerializeOnlyWitness))
ver := binary.LittleEndian.Uint32(verBytes)
return int32(ver)
}
// WitnessSigningMsgTxVersion returns the witness only version int32 (serialize
// the tx witness for signing, version number 1).
func WitnessSigningMsgTxVersion() int32 {
verBytes := make([]byte, 4, 4)
binary.LittleEndian.PutUint16(verBytes[0:2], TxVersion)
binary.LittleEndian.PutUint16(verBytes[2:4], uint16(TxSerializeWitnessSigning))
ver := binary.LittleEndian.Uint32(verBytes)
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
@ -261,17 +225,6 @@ func readScript(r io.Reader, pver uint32, maxAllowed uint32, fieldName string) (
return b, nil
}
// WitnessValueSigningMsgTxVersion returns the witness only version int32
// (serialize the tx witness for signing with value, version number 1).
func WitnessValueSigningMsgTxVersion() int32 {
verBytes := make([]byte, 4, 4)
binary.LittleEndian.PutUint16(verBytes[0:2], TxVersion)
binary.LittleEndian.PutUint16(verBytes[2:4],
uint16(TxSerializeWitnessValueSigning))
ver := binary.LittleEndian.Uint32(verBytes)
return int32(ver)
}
// OutPoint defines a decred data type that is used to track previous
// transaction outputs.
type OutPoint struct {
@ -402,7 +355,8 @@ func NewTxOut(value int64, pkScript []byte) *TxOut {
// inputs and outputs.
type MsgTx struct {
CachedHash *chainhash.Hash
Version int32
SerType TxSerializeType
Version uint16
TxIn []*TxIn
TxOut []*TxOut
LockTime uint32
@ -419,45 +373,44 @@ func (msg *MsgTx) AddTxOut(to *TxOut) {
msg.TxOut = append(msg.TxOut, to)
}
// msgTxVersionToVars converts an int32 version into serialization types and
// actual version.
func msgTxVersionToVars(version int32) (uint16, TxSerializeType) {
mVerBytes := make([]byte, 4, 4)
binary.LittleEndian.PutUint32(mVerBytes[0:4], uint32(version))
mVer := binary.LittleEndian.Uint16(mVerBytes[0:2])
mType := binary.LittleEndian.Uint16(mVerBytes[2:4])
return mVer, TxSerializeType(mType)
}
// shallowCopyForSerializing make a shallow copy of a tx with a new
// version, so that it can be hashed or serialized accordingly.
func (msg *MsgTx) shallowCopyForSerializing(version int32) *MsgTx {
return &MsgTx{
Version: version,
TxIn: msg.TxIn,
TxOut: msg.TxOut,
LockTime: msg.LockTime,
Expiry: msg.Expiry,
}
}
// TxHash generates the Hash name for the transaction prefix.
func (msg *MsgTx) TxHash() chainhash.Hash {
// TxHash should always calculate a non-witnessed hash.
mtxCopy := msg.shallowCopyForSerializing(NoWitnessMsgTxVersion())
// 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 {
panic("MsgTx failed serializing for TxHash")
return nil, err
}
return chainhash.HashH(buf.Bytes())
return buf.Bytes(), nil
}
// CachedTxHash generates the Hash name for the transaction prefix and stores
// it if it does not exist. The cached hash is then returned. It can be
// recalculated later with RecacheTxHash.
// 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()
@ -467,8 +420,9 @@ func (msg *MsgTx) CachedTxHash() *chainhash.Hash {
return msg.CachedHash
}
// RecacheTxHash generates the Hash name for the transaction prefix and stores
// it. The cached hash is then returned.
// 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
@ -476,65 +430,41 @@ func (msg *MsgTx) RecacheTxHash() *chainhash.Hash {
return msg.CachedHash
}
// TxHashWitness generates the Hash name for the transaction witness.
// TxHashWitness generates the hash for the transaction witness.
func (msg *MsgTx) TxHashWitness() chainhash.Hash {
// TxHashWitness should always calculate a witnessed hash.
mtxCopy := msg.shallowCopyForSerializing(WitnessOnlyMsgTxVersion())
buf := bytes.NewBuffer(make([]byte, 0, mtxCopy.SerializeSize()))
err := mtxCopy.Serialize(buf)
if err != nil {
panic("MsgTx failed serializing for TxHashWitness")
}
return chainhash.HashH(buf.Bytes())
return chainhash.HashH(msg.mustSerialize(TxSerializeOnlyWitness))
}
// TxHashWitnessSigning generates the Hash name for the transaction witness with
// the malleable portions (AmountIn, BlockHeight, BlockIndex) removed. These are
// TxHashWitnessSigning generates the hash for the transaction witness with the
// malleable portions (AmountIn, BlockHeight, BlockIndex) removed. These are
// verified and set by the miner instead.
func (msg *MsgTx) TxHashWitnessSigning() chainhash.Hash {
// TxHashWitness should always calculate a witnessed hash.
mtxCopy := msg.shallowCopyForSerializing(WitnessSigningMsgTxVersion())
buf := bytes.NewBuffer(make([]byte, 0, mtxCopy.SerializeSize()))
err := mtxCopy.Serialize(buf)
if err != nil {
panic("MsgTx failed serializing for TxHashWitnessSigning")
}
return chainhash.HashH(buf.Bytes())
return chainhash.HashH(msg.mustSerialize(TxSerializeWitnessSigning))
}
// TxHashWitnessValueSigning generates the Hash name for the transaction witness
// with BlockHeight and BlockIndex removed, allowing the signer to specify the
// TxHashWitnessValueSigning generates the hash for the transaction witness with
// BlockHeight and BlockIndex removed, allowing the signer to specify the
// ValueIn.
func (msg *MsgTx) TxHashWitnessValueSigning() chainhash.Hash {
// TxHashWitness should always calculate a witnessed hash.
mtxCopy := msg.shallowCopyForSerializing(WitnessValueSigningMsgTxVersion())
buf := bytes.NewBuffer(make([]byte, 0, mtxCopy.SerializeSize()))
err := mtxCopy.Serialize(buf)
if err != nil {
panic("MsgTx failed serializing for TxHashWitnessValueSigning")
}
return chainhash.HashH(buf.Bytes())
return chainhash.HashH(msg.mustSerialize(TxSerializeWitnessValueSigning))
}
// TxHashFull generates the Hash name for the transaction prefix || witness. It
// first obtains the hashes for both the transaction prefix and witness, then
// concatenates them and hashes these 64 bytes.
// Note that the inputs to the hashes, serialized prefix and serialized witnesses,
// have different uint32 versions because version is now actually two uint16s,
// with the last 16 bits referring to the serialization type. The first 16 bits
// refer to the actual version, and these must be the same in both serializations.
// 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 {
concat := make([]byte, 64, 64)
// 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:32], prefixHash[:])
copy(concat[32:64], witnessHash[:])
copy(concat[0:], prefixHash[:])
copy(concat[chainhash.HashSize:], witnessHash[:])
return chainhash.HashH(concat)
}
@ -545,6 +475,7 @@ 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)),
@ -616,7 +547,7 @@ func (msg *MsgTx) Copy() *MsgTx {
// 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) {
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
@ -670,7 +601,7 @@ func writeTxScriptsToMsgTx(msg *MsgTx, totalScriptSize uint64, mType TxSerialize
}
// Handle the serialization types accordingly.
switch mType {
switch serType {
case TxSerializeNoWitness:
writeTxOuts()
case TxSerializeOnlyWitness:
@ -711,7 +642,7 @@ func (msg *MsgTx) decodePrefix(r io.Reader, pver uint32) (uint64, error) {
// and needs to be returned to the pool on error.
ti := &txIns[i]
msg.TxIn[i] = ti
err = readTxInPrefix(r, pver, msg.Version, ti)
err = readTxInPrefix(r, pver, msg.SerType, msg.Version, ti)
if err != nil {
return 0, err
}
@ -927,12 +858,15 @@ func (msg *MsgTx) decodeWitnessValueSigning(r io.Reader, pver uint32) (uint64, e
// 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 {
// 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 = int32(version)
_, mType := msgTxVersionToVars(msg.Version)
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
@ -960,40 +894,40 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32) error {
// 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:
switch txSerType := msg.SerType; txSerType {
case TxSerializeNoWitness:
totalScriptSize, err := msg.decodePrefix(r, pver)
if err != nil {
returnScriptBuffers()
return err
}
writeTxScriptsToMsgTx(msg, totalScriptSize, mType)
writeTxScriptsToMsgTx(msg, totalScriptSize, txSerType)
case mType == TxSerializeOnlyWitness:
case TxSerializeOnlyWitness:
totalScriptSize, err := msg.decodeWitness(r, pver, false)
if err != nil {
returnScriptBuffers()
return err
}
writeTxScriptsToMsgTx(msg, totalScriptSize, mType)
writeTxScriptsToMsgTx(msg, totalScriptSize, txSerType)
case mType == TxSerializeWitnessSigning:
case TxSerializeWitnessSigning:
totalScriptSize, err := msg.decodeWitnessSigning(r, pver)
if err != nil {
returnScriptBuffers()
return err
}
writeTxScriptsToMsgTx(msg, totalScriptSize, mType)
writeTxScriptsToMsgTx(msg, totalScriptSize, txSerType)
case mType == TxSerializeWitnessValueSigning:
case TxSerializeWitnessValueSigning:
totalScriptSize, err := msg.decodeWitnessValueSigning(r, pver)
if err != nil {
returnScriptBuffers()
return err
}
writeTxScriptsToMsgTx(msg, totalScriptSize, mType)
writeTxScriptsToMsgTx(msg, totalScriptSize, txSerType)
case mType == TxSerializeFull:
case TxSerializeFull:
totalScriptSizeIns, err := msg.decodePrefix(r, pver)
if err != nil {
returnScriptBuffers()
@ -1004,7 +938,9 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32) error {
returnScriptBuffers()
return err
}
writeTxScriptsToMsgTx(msg, totalScriptSizeIns+totalScriptSizeOuts, mType)
writeTxScriptsToMsgTx(msg, totalScriptSizeIns+
totalScriptSizeOuts, txSerType)
default:
return messageError("MsgTx.BtcDecode", "unsupported transaction type")
}
@ -1131,38 +1067,41 @@ func (msg *MsgTx) encodeWitnessValueSigning(w io.Writer, pver uint32) error {
// 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 {
err := binarySerializer.PutUint32(w, littleEndian, uint32(msg.Version))
// 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
}
_, mType := msgTxVersionToVars(msg.Version)
switch {
case mType == TxSerializeNoWitness:
switch msg.SerType {
case TxSerializeNoWitness:
err := msg.encodePrefix(w, pver)
if err != nil {
return err
}
case mType == TxSerializeOnlyWitness:
case TxSerializeOnlyWitness:
err := msg.encodeWitness(w, pver)
if err != nil {
return err
}
case mType == TxSerializeWitnessSigning:
case TxSerializeWitnessSigning:
err := msg.encodeWitnessSigning(w, pver)
if err != nil {
return err
}
case mType == TxSerializeWitnessValueSigning:
case TxSerializeWitnessValueSigning:
err := msg.encodeWitnessValueSigning(w, pver)
if err != nil {
return err
}
case mType == TxSerializeFull:
case TxSerializeFull:
err := msg.encodePrefix(w, pver)
if err != nil {
return err
@ -1198,51 +1137,31 @@ func (msg *MsgTx) Serialize(w io.Writer) error {
// Bytes returns the serialized form of the transaction in bytes.
func (msg *MsgTx) Bytes() ([]byte, error) {
// Serialize the MsgTx.
var w bytes.Buffer
w.Grow(msg.SerializeSize())
err := msg.Serialize(&w)
buf := bytes.NewBuffer(make([]byte, 0, msg.SerializeSize()))
err := msg.Serialize(buf)
if err != nil {
return nil, err
}
return w.Bytes(), nil
return buf.Bytes(), nil
}
// BytesPrefix returns the serialized form of the transaction prefix in bytes.
func (msg *MsgTx) BytesPrefix() ([]byte, error) {
mtxCopy := msg.shallowCopyForSerializing(NoWitnessMsgTxVersion())
var w bytes.Buffer
w.Grow(msg.SerializeSize())
err := mtxCopy.Serialize(&w)
if err != nil {
return nil, err
}
return w.Bytes(), nil
return msg.serialize(TxSerializeNoWitness)
}
// BytesWitness returns the serialized form of the transaction prefix in bytes.
func (msg *MsgTx) BytesWitness() ([]byte, error) {
mtxCopy := msg.shallowCopyForSerializing(WitnessOnlyMsgTxVersion())
var w bytes.Buffer
w.Grow(msg.SerializeSize())
err := mtxCopy.Serialize(&w)
if err != nil {
return nil, err
}
return w.Bytes(), nil
return msg.serialize(TxSerializeOnlyWitness)
}
// SerializeSize returns the number of bytes it would take to serialize the
// the transaction.
func (msg *MsgTx) SerializeSize() int {
_, mType := msgTxVersionToVars(msg.Version)
// Unknown type return 0.
n := 0
switch {
case mType == TxSerializeNoWitness:
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.
@ -1256,7 +1175,7 @@ func (msg *MsgTx) SerializeSize() int {
n += txOut.SerializeSize()
}
case mType == TxSerializeOnlyWitness:
case TxSerializeOnlyWitness:
// Version 4 bytes + Serialized varint size for the
// number of transaction signatures.
n = 4 + VarIntSerializeSize(uint64(len(msg.TxIn)))
@ -1265,7 +1184,7 @@ func (msg *MsgTx) SerializeSize() int {
n += txIn.SerializeSizeWitness()
}
case mType == TxSerializeWitnessSigning:
case TxSerializeWitnessSigning:
// Version 4 bytes + Serialized varint size for the
// number of transaction signatures.
n = 4 + VarIntSerializeSize(uint64(len(msg.TxIn)))
@ -1274,7 +1193,7 @@ func (msg *MsgTx) SerializeSize() int {
n += txIn.SerializeSizeWitnessSigning()
}
case mType == TxSerializeWitnessValueSigning:
case TxSerializeWitnessValueSigning:
// Version 4 bytes + Serialized varint size for the
// number of transaction signatures.
n = 4 + VarIntSerializeSize(uint64(len(msg.TxIn)))
@ -1283,7 +1202,7 @@ func (msg *MsgTx) SerializeSize() int {
n += txIn.SerializeSizeWitnessValueSigning()
}
case mType == TxSerializeFull:
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
@ -1370,14 +1289,15 @@ func (msg *MsgTx) PkScriptLocs() []int {
// future.
func NewMsgTx() *MsgTx {
return &MsgTx{
Version: DefaultMsgTxVersion(),
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 int32, op *OutPoint) error {
func ReadOutPoint(r io.Reader, pver uint32, version uint16, op *OutPoint) error {
_, err := io.ReadFull(r, op.Hash[:])
if err != nil {
return err
@ -1399,7 +1319,7 @@ func ReadOutPoint(r io.Reader, pver uint32, version int32, op *OutPoint) error {
// WriteOutPoint encodes op to the decred protocol encoding for an OutPoint
// to w.
func WriteOutPoint(w io.Writer, pver uint32, version int32, op *OutPoint) error {
func WriteOutPoint(w io.Writer, pver uint32, version uint16, op *OutPoint) error {
_, err := w.Write(op.Hash[:])
if err != nil {
return err
@ -1415,8 +1335,8 @@ func WriteOutPoint(w io.Writer, pver uint32, version int32, op *OutPoint) error
// 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, version int32, ti *TxIn) error {
if version == WitnessOnlyMsgTxVersion() {
func readTxInPrefix(r io.Reader, pver uint32, serType TxSerializeType, version uint16, ti *TxIn) error {
if serType == TxSerializeOnlyWitness {
return messageError("readTxInPrefix",
"tried to read a prefix input for a witness only tx")
}
@ -1434,7 +1354,7 @@ func readTxInPrefix(r io.Reader, pver uint32, version int32, ti *TxIn) error {
// 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 int32, ti *TxIn) error {
func readTxInWitness(r io.Reader, pver uint32, version uint16, ti *TxIn) error {
// ValueIn.
valueIn, err := binarySerializer.Uint64(r, littleEndian)
if err != nil {
@ -1461,11 +1381,9 @@ func readTxInWitness(r io.Reader, pver uint32, version int32, ti *TxIn) error {
}
// readTxInWitnessSigning reads a TxIn witness for signing.
func readTxInWitnessSigning(r io.Reader, pver uint32, version int32,
ti *TxIn) error {
var err error
func readTxInWitnessSigning(r io.Reader, pver uint32, version uint16, ti *TxIn) error {
// Signature script.
var err error
ti.SignatureScript, err = readScript(r, pver, MaxMessagePayload,
"transaction input signature script")
return err
@ -1473,8 +1391,7 @@ func readTxInWitnessSigning(r io.Reader, pver uint32, version int32,
// readTxInWitnessValueSigning reads a TxIn witness for signing with value
// included.
func readTxInWitnessValueSigning(r io.Reader, pver uint32, version int32,
ti *TxIn) error {
func readTxInWitnessValueSigning(r io.Reader, pver uint32, version uint16, ti *TxIn) error {
// ValueIn.
valueIn, err := binarySerializer.Uint64(r, littleEndian)
if err != nil {
@ -1490,7 +1407,7 @@ func readTxInWitnessValueSigning(r io.Reader, pver uint32, version int32,
// writeTxInPrefixs encodes ti to the decred protocol encoding for a transaction
// input (TxIn) prefix to w.
func writeTxInPrefix(w io.Writer, pver uint32, version int32, ti *TxIn) error {
func writeTxInPrefix(w io.Writer, pver uint32, version uint16, ti *TxIn) error {
err := WriteOutPoint(w, pver, version, &ti.PreviousOutPoint)
if err != nil {
return err
@ -1501,7 +1418,7 @@ func writeTxInPrefix(w io.Writer, pver uint32, version int32, ti *TxIn) error {
// writeTxWitness encodes ti to the decred protocol encoding for a transaction
// input (TxIn) witness to w.
func writeTxInWitness(w io.Writer, pver uint32, version int32, ti *TxIn) error {
func writeTxInWitness(w io.Writer, pver uint32, version uint16, ti *TxIn) error {
// ValueIn.
err := binarySerializer.PutUint64(w, littleEndian, uint64(ti.ValueIn))
if err != nil {
@ -1526,19 +1443,16 @@ func writeTxInWitness(w io.Writer, pver uint32, version int32, ti *TxIn) error {
// writeTxInWitnessSigning encodes ti to the decred protocol encoding for a
// transaction input (TxIn) witness to w for signing.
func writeTxInWitnessSigning(w io.Writer, pver uint32, version int32, ti *TxIn) error {
func writeTxInWitnessSigning(w io.Writer, pver uint32, version uint16, ti *TxIn) error {
// Only write the signature script.
return WriteVarBytes(w, pver, ti.SignatureScript)
}
// writeTxInWitnessValueSigning encodes ti to the decred protocol encoding for a
// transaction input (TxIn) witness to w for signing with value included.
func writeTxInWitnessValueSigning(w io.Writer, pver uint32, version int32,
ti *TxIn) error {
var err error
func writeTxInWitnessValueSigning(w io.Writer, pver uint32, version uint16, ti *TxIn) error {
// ValueIn.
err = binarySerializer.PutUint64(w, littleEndian, uint64(ti.ValueIn))
err := binarySerializer.PutUint64(w, littleEndian, uint64(ti.ValueIn))
if err != nil {
return err
}
@ -1549,7 +1463,7 @@ func writeTxInWitnessValueSigning(w io.Writer, pver uint32, version int32,
// readTxOut reads the next sequence of bytes from r as a transaction output
// (TxOut).
func readTxOut(r io.Reader, pver uint32, version int32, to *TxOut) error {
func readTxOut(r io.Reader, pver uint32, version uint16, to *TxOut) error {
value, err := binarySerializer.Uint64(r, littleEndian)
if err != nil {
return err
@ -1568,7 +1482,7 @@ func readTxOut(r io.Reader, pver uint32, version int32, to *TxOut) error {
// writeTxOut encodes to into the decred protocol encoding for a transaction
// output (TxOut) to w.
func writeTxOut(w io.Writer, pver uint32, version int32, to *TxOut) error {
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

View File

@ -1,5 +1,5 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2016 The Decred 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.
@ -378,9 +378,8 @@ func TestTxSerialize(t *testing.T) {
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Serialize the transaction.
var buf bytes.Buffer
buf.Grow(test.in.SerializeSize())
err := test.in.Serialize(&buf)
buf := bytes.NewBuffer(make([]byte, 0, test.in.SerializeSize()))
err := test.in.Serialize(buf)
if err != nil {
t.Errorf("Serialize #%d error %v", i, err)
continue
@ -437,7 +436,8 @@ func TestTxSerialize(t *testing.T) {
// TestTxSerializePrefix tests MsgTx serialize and deserialize.
func TestTxSerializePrefix(t *testing.T) {
noTx := NewMsgTx()
noTx.Version = 65537
noTx.SerType = TxSerializeNoWitness
noTx.Version = 1
noTxEncoded := []byte{
0x01, 0x00, 0x01, 0x00, // Version
0x00, // Varint for number of input transactions
@ -472,9 +472,8 @@ func TestTxSerializePrefix(t *testing.T) {
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Serialize the transaction.
var buf bytes.Buffer
buf.Grow(test.in.SerializeSize())
err := test.in.Serialize(&buf)
buf := bytes.NewBuffer(make([]byte, 0, test.in.SerializeSize()))
err := test.in.Serialize(buf)
if err != nil {
t.Errorf("Serialize #%d error %v", i, err)
continue
@ -531,7 +530,8 @@ func TestTxSerializePrefix(t *testing.T) {
// TestTxSerializeWitness tests MsgTx serialize and deserialize.
func TestTxSerializeWitness(t *testing.T) {
noTx := NewMsgTx()
noTx.Version = 131073
noTx.SerType = TxSerializeOnlyWitness
noTx.Version = 1
noTxEncoded := []byte{
0x01, 0x00, 0x02, 0x00, // Version
0x00, // Varint for number of input signatures
@ -563,9 +563,8 @@ func TestTxSerializeWitness(t *testing.T) {
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Serialize the transaction.
var buf bytes.Buffer
buf.Grow(test.in.SerializeSize())
err := test.in.Serialize(&buf)
buf := bytes.NewBuffer(make([]byte, 0, test.in.SerializeSize()))
err := test.in.Serialize(buf)
if err != nil {
t.Errorf("Serialize #%d error %v", i, err)
continue
@ -622,7 +621,8 @@ func TestTxSerializeWitness(t *testing.T) {
// TestTxSerializeWitnessSigning tests MsgTx serialize and deserialize.
func TestTxSerializeWitnessSigning(t *testing.T) {
noTx := NewMsgTx()
noTx.Version = 196609
noTx.SerType = TxSerializeWitnessSigning
noTx.Version = 1
noTxEncoded := []byte{
0x01, 0x00, 0x03, 0x00, // Version
0x00, // Varint for number of input signatures
@ -654,9 +654,8 @@ func TestTxSerializeWitnessSigning(t *testing.T) {
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Serialize the transaction.
var buf bytes.Buffer
buf.Grow(test.in.SerializeSize())
err := test.in.Serialize(&buf)
buf := bytes.NewBuffer(make([]byte, 0, test.in.SerializeSize()))
err := test.in.Serialize(buf)
if err != nil {
t.Errorf("Serialize #%d error %v", i, err)
continue
@ -713,7 +712,8 @@ func TestTxSerializeWitnessSigning(t *testing.T) {
// TestTxSerializeWitnessValueSigning tests MsgTx serialize and deserialize.
func TestTxSerializeWitnessValueSigning(t *testing.T) {
noTx := NewMsgTx()
noTx.Version = 262145
noTx.SerType = TxSerializeWitnessValueSigning
noTx.Version = 1
noTxEncoded := []byte{
0x01, 0x00, 0x04, 0x00, // Version
0x00, // Varint for number of input signatures
@ -745,9 +745,8 @@ func TestTxSerializeWitnessValueSigning(t *testing.T) {
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Serialize the transaction.
var buf bytes.Buffer
buf.Grow(test.in.SerializeSize())
err := test.in.Serialize(&buf)
buf := bytes.NewBuffer(make([]byte, 0, test.in.SerializeSize()))
err := test.in.Serialize(buf)
if err != nil {
t.Errorf("Serialize #%d error %v", i, err)
continue
@ -883,7 +882,7 @@ func TestTxOverflowErrors(t *testing.T) {
// here instead of the latest values because the test data is using
// bytes encoded with those versions.
pver := uint32(1)
txVer := DefaultMsgTxVersion()
txVer := int32(1)
tests := []struct {
buf []byte // Wire encoding
@ -1033,6 +1032,7 @@ func TestTxSerializeSize(t *testing.T) {
// multiTx is a MsgTx with an input and output and used in various tests.
var multiTx = &MsgTx{
SerType: TxSerializeFull,
Version: 1,
TxIn: []*TxIn{
{
@ -1091,7 +1091,8 @@ var multiTx = &MsgTx{
// multiTxPrefix is a MsgTx prefix with an input and output and used in various tests.
var multiTxPrefix = &MsgTx{
Version: 65537,
SerType: TxSerializeNoWitness,
Version: 1,
TxIn: []*TxIn{
{
PreviousOutPoint: OutPoint{
@ -1143,7 +1144,8 @@ var multiTxPrefix = &MsgTx{
// multiTxWitness is a MsgTx witness with only input witness.
var multiTxWitness = &MsgTx{
Version: 131073,
SerType: TxSerializeOnlyWitness,
Version: 1,
TxIn: []*TxIn{
{
ValueIn: 0x1212121212121212,
@ -1159,7 +1161,8 @@ var multiTxWitness = &MsgTx{
// multiTxWitnessSigning is a MsgTx witness with only input witness sigscripts.
var multiTxWitnessSigning = &MsgTx{
Version: 196609,
SerType: TxSerializeWitnessSigning,
Version: 1,
TxIn: []*TxIn{
{
SignatureScript: []byte{
@ -1173,7 +1176,8 @@ var multiTxWitnessSigning = &MsgTx{
// multiTxWitnessValueSigning is a MsgTx witness with only input witness
// sigscripts.
var multiTxWitnessValueSigning = &MsgTx{
Version: 262145,
SerType: TxSerializeWitnessValueSigning,
Version: 1,
TxIn: []*TxIn{
{
ValueIn: 0x1212121212121212,