diff --git a/blockchain/chainio.go b/blockchain/chainio.go index 51482acf..a944f743 100644 --- a/blockchain/chainio.go +++ b/blockchain/chainio.go @@ -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 diff --git a/blockchain/chainio_test.go b/blockchain/chainio_test.go index 29e9fd0e..c58d5ea4 100644 --- a/blockchain/chainio_test.go +++ b/blockchain/chainio_test.go @@ -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{ diff --git a/blockchain/fullblocktests/params.go b/blockchain/fullblocktests/params.go index 4644126e..2d872e9e 100644 --- a/blockchain/fullblocktests/params.go +++ b/blockchain/fullblocktests/params.go @@ -69,6 +69,7 @@ var ( Height: uint32(0), }, Transactions: []*wire.MsgTx{{ + SerType: wire.TxSerializeFull, Version: 1, TxIn: []*wire.TxIn{{ PreviousOutPoint: wire.OutPoint{ diff --git a/blockchain/stake/staketx_test.go b/blockchain/stake/staketx_test.go index 62dfd487..149361f2 100644 --- a/blockchain/stake/staketx_test.go +++ b/blockchain/stake/staketx_test.go @@ -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, diff --git a/blockchain/stake/tickets_test.go b/blockchain/stake/tickets_test.go index 52ee96b5..d7bd3750 100644 --- a/blockchain/stake/tickets_test.go +++ b/blockchain/stake/tickets_test.go @@ -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{ { diff --git a/blockchain/utxoviewpoint.go b/blockchain/utxoviewpoint.go index fac7a7a5..9026f0bd 100644 --- a/blockchain/utxoviewpoint.go +++ b/blockchain/utxoviewpoint.go @@ -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), diff --git a/blockchain/validate_test.go b/blockchain/validate_test.go index 90627ea8..877dc457 100644 --- a/blockchain/validate_test.go +++ b/blockchain/validate_test.go @@ -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{ { diff --git a/chaincfg/genesis.go b/chaincfg/genesis.go index 5a3274a1..94ed41e1 100644 --- a/chaincfg/genesis.go +++ b/chaincfg/genesis.go @@ -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{ { diff --git a/mempool/policy.go b/mempool/policy.go index 9ca06387..ad974383 100644 --- a/mempool/policy.go +++ b/mempool/policy.go @@ -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) } diff --git a/mempool/policy_test.go b/mempool/policy_test.go index 84143fd5..da94ad0c 100644 --- a/mempool/policy_test.go +++ b/mempool/policy_test.go @@ -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{{ diff --git a/rpcserver.go b/rpcserver.go index 6d96b07a..64e2befd 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -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, diff --git a/txscript/engine_test.go b/txscript/engine_test.go index 48b83eb8..4c36ee66 100644 --- a/txscript/engine_test.go +++ b/txscript/engine_test.go @@ -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{ { diff --git a/txscript/script_test.go b/txscript/script_test.go index ab4bfeef..7499c2c9 100644 --- a/txscript/script_test.go +++ b/txscript/script_test.go @@ -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 diff --git a/txscript/sign_test.go b/txscript/sign_test.go index e303fda5..1adf7be5 100644 --- a/txscript/sign_test.go +++ b/txscript/sign_test.go @@ -169,6 +169,7 @@ func TestSignTxOutput(t *testing.T) { secSchnorr, } tx := &wire.MsgTx{ + SerType: wire.TxSerializeFull, Version: 1, TxIn: []*wire.TxIn{ { diff --git a/wire/bench_test.go b/wire/bench_test.go index f90e7d48..6a93171a 100644 --- a/wire/bench_test.go +++ b/wire/bench_test.go @@ -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) diff --git a/wire/blockheader.go b/wire/blockheader.go index 508906aa..7b7b2b03 100644 --- a/wire/blockheader.go +++ b/wire/blockheader.go @@ -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 diff --git a/wire/blockheader_test.go b/wire/blockheader_test.go index 3f680726..6e034395 100644 --- a/wire/blockheader_test.go +++ b/wire/blockheader_test.go @@ -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 diff --git a/wire/msgblock.go b/wire/msgblock.go index 7dce15ad..71bbf022 100644 --- a/wire/msgblock.go +++ b/wire/msgblock.go @@ -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 diff --git a/wire/msgblock_test.go b/wire/msgblock_test.go index a6b40bcb..02fe5b12 100644 --- a/wire/msgblock_test.go +++ b/wire/msgblock_test.go @@ -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{ { diff --git a/wire/msgtx.go b/wire/msgtx.go index a0153723..49d69abb 100644 --- a/wire/msgtx.go +++ b/wire/msgtx.go @@ -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 diff --git a/wire/msgtx_test.go b/wire/msgtx_test.go index 123a73a9..58403bb5 100644 --- a/wire/msgtx_test.go +++ b/wire/msgtx_test.go @@ -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,