// Copyright (c) 2016 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package mining import ( "encoding/hex" "testing" "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) // newHashFromStr converts the passed big-endian hex string into a // chainhash.Hash. It only differs from the one available in chainhash in that // it panics on an error since it will only (and must only) be called with // hard-coded, and therefore known good, hashes. func newHashFromStr(hexStr string) *chainhash.Hash { hash, err := chainhash.NewHashFromStr(hexStr) if err != nil { panic("invalid hash in source file: " + hexStr) } return hash } // hexToBytes converts the passed hex string into bytes and will panic if there // is an error. This is only provided for the hard-coded constants so errors in // the source code can be detected. It will only (and must only) be called with // hard-coded values. func hexToBytes(s string) []byte { b, err := hex.DecodeString(s) if err != nil { panic("invalid hex in source file: " + s) } return b } // newUtxoViewpoint returns a new utxo view populated with outputs of the // provided source transactions as if there were available at the respective // block height specified in the heights slice. The length of the source txns // and source tx heights must match or it will panic. func newUtxoViewpoint(sourceTxns []*wire.MsgTx, sourceTxHeights []int32) *blockchain.UtxoViewpoint { if len(sourceTxns) != len(sourceTxHeights) { panic("each transaction must have its block height specified") } view := blockchain.NewUtxoViewpoint() for i, tx := range sourceTxns { view.AddTxOuts(btcutil.NewTx(tx), sourceTxHeights[i]) } return view } // TestCalcPriority ensures the priority calculations work as intended. func TestCalcPriority(t *testing.T) { // commonSourceTx1 is a valid transaction used in the tests below as an // input to transactions that are having their priority calculated. // // From block 7 in main blockchain. // tx 0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9 commonSourceTx1 := &wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{{ PreviousOutPoint: wire.OutPoint{ Hash: chainhash.Hash{}, Index: wire.MaxPrevOutIndex, }, SignatureScript: hexToBytes("04ffff001d0134"), Sequence: 0xffffffff, }}, TxOut: []*wire.TxOut{{ Value: 5000000000, PkScript: hexToBytes("410411db93e1dcdb8a016b49840f8c5" + "3bc1eb68a382e97b1482ecad7b148a6909a5cb2e0ead" + "dfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8" + "643f656b412a3ac"), }}, LockTime: 0, } // commonRedeemTx1 is a valid transaction used in the tests below as the // transaction to calculate the priority for. // // It originally came from block 170 in main blockchain. commonRedeemTx1 := &wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{{ PreviousOutPoint: wire.OutPoint{ Hash: *newHashFromStr("0437cd7f8525ceed232435" + "9c2d0ba26006d92d856a9c20fa0241106ee5" + "a597c9"), Index: 0, }, SignatureScript: hexToBytes("47304402204e45e16932b8af" + "514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5f" + "b8cd410220181522ec8eca07de4860a4acdd12909d83" + "1cc56cbbac4622082221a8768d1d0901"), Sequence: 0xffffffff, }}, TxOut: []*wire.TxOut{{ Value: 1000000000, PkScript: hexToBytes("4104ae1a62fe09c5f51b13905f07f06" + "b99a2f7159b2225f374cd378d71302fa28414e7aab37" + "397f554a7df5f142c21c1b7303b8a0626f1baded5c72" + "a704f7e6cd84cac"), }, { Value: 4000000000, PkScript: hexToBytes("410411db93e1dcdb8a016b49840f8c5" + "3bc1eb68a382e97b1482ecad7b148a6909a5cb2e0ead" + "dfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8" + "643f656b412a3ac"), }}, LockTime: 0, } tests := []struct { name string // test description tx *wire.MsgTx // tx to calc priority for utxoView *blockchain.UtxoViewpoint // inputs to tx nextHeight int32 // height for priority calc want float64 // expected priority }{ { name: "one height 7 input, prio tx height 169", tx: commonRedeemTx1, utxoView: newUtxoViewpoint([]*wire.MsgTx{commonSourceTx1}, []int32{7}), nextHeight: 169, want: 5e9, }, { name: "one height 100 input, prio tx height 169", tx: commonRedeemTx1, utxoView: newUtxoViewpoint([]*wire.MsgTx{commonSourceTx1}, []int32{100}), nextHeight: 169, want: 2129629629.6296296, }, { name: "one height 7 input, prio tx height 100000", tx: commonRedeemTx1, utxoView: newUtxoViewpoint([]*wire.MsgTx{commonSourceTx1}, []int32{7}), nextHeight: 100000, want: 3086203703703.7036, }, { name: "one height 100 input, prio tx height 100000", tx: commonRedeemTx1, utxoView: newUtxoViewpoint([]*wire.MsgTx{commonSourceTx1}, []int32{100}), nextHeight: 100000, want: 3083333333333.3335, }, } for i, test := range tests { got := CalcPriority(test.tx, test.utxoView, test.nextHeight) if got != test.want { t.Errorf("CalcPriority #%d (%q): unexpected priority "+ "got %v want %v", i, test.name, got, test.want) continue } } }