diff --git a/wire/blockheader.go b/wire/blockheader.go index ef499ae51..4e9141e57 100644 --- a/wire/blockheader.go +++ b/wire/blockheader.go @@ -66,17 +66,18 @@ func (h *BlockHeader) BlockHash() daghash.Hash { return daghash.DoubleHashH(buf.Bytes()) } -// BestPrevBlock returns the hash of the selected block header. -func (header *BlockHeader) SelectedPrevBlock() *daghash.Hash { - if header.NumPrevBlocks == 0 { +// SelectedPrevBlock returns the hash of the selected block header. +func (h *BlockHeader) SelectedPrevBlock() *daghash.Hash { + if h.NumPrevBlocks == 0 { return nil } - return &header.PrevBlocks[0] + return &h.PrevBlocks[0] } -func (header *BlockHeader) IsGenesis() bool { - return header.NumPrevBlocks == 0 +// IsGenesis returns true iff this block is a genesis block +func (h *BlockHeader) IsGenesis() bool { + return h.NumPrevBlocks == 0 } // BtcDecode decodes r using the bitcoin protocol encoding into the receiver. diff --git a/wire/blockheader_test.go b/wire/blockheader_test.go index 30d4a425e..a82c9edf1 100644 --- a/wire/blockheader_test.go +++ b/wire/blockheader_test.go @@ -315,3 +315,45 @@ func TestBlockHeaderSerializeSize(t *testing.T) { } } } + +func TestIsGenesis(t *testing.T) { + nonce := uint32(123123) // 0x1e0f3 + bits := uint32(0x1d00ffff) + timestamp := time.Unix(0x495fab29, 0) // 2009-01-03 12:15:05 -0600 CST + + baseBlockHdr := &BlockHeader{ + Version: 1, + NumPrevBlocks: 2, + PrevBlocks: []daghash.Hash{mainNetGenesisHash, simNetGenesisHash}, + MerkleRoot: mainNetGenesisMerkleRoot, + Timestamp: timestamp, + Bits: bits, + Nonce: nonce, + } + genesisBlockHdr := &BlockHeader{ + Version: 1, + NumPrevBlocks: 0, + PrevBlocks: []daghash.Hash{}, + MerkleRoot: mainNetGenesisMerkleRoot, + Timestamp: timestamp, + Bits: bits, + Nonce: nonce, + } + + tests := []struct { + in *BlockHeader // Block header to encode + isGenesis bool // Expected result for call of .IsGenesis + }{ + {genesisBlockHdr, true}, + {baseBlockHdr, false}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + isGenesis := test.in.IsGenesis() + if isGenesis != test.isGenesis { + t.Errorf("BlockHeader.IsGenesis: #%d got: %t, want: %t", + i, isGenesis, test.isGenesis) + } + } +} diff --git a/wire/common_test.go b/wire/common_test.go index 2bb078a0c..3d8f15e34 100644 --- a/wire/common_test.go +++ b/wire/common_test.go @@ -334,11 +334,14 @@ func TestVarIntWireErrors(t *testing.T) { // Force errors on discriminant. {0, []byte{0x00}, pver, 0, io.ErrShortWrite, io.EOF}, // Force errors on 2-byte read/write. - {0xfd, []byte{0xfd}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF}, + {0xfd, []byte{0xfd}, pver, 0, io.ErrShortWrite, io.EOF}, // error on writing length + {0xfd, []byte{0xfd}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF}, // error on writing actual data // Force errors on 4-byte read/write. - {0x10000, []byte{0xfe}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF}, + {0x10000, []byte{0xfe}, pver, 0, io.ErrShortWrite, io.EOF}, // error on writing length + {0x10000, []byte{0xfe}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF}, // error on writing actual data // Force errors on 8-byte read/write. - {0x100000000, []byte{0xff}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF}, + {0x100000000, []byte{0xff}, pver, 0, io.ErrShortWrite, io.EOF}, // error on writing length + {0x100000000, []byte{0xff}, pver, 2, io.ErrShortWrite, io.ErrUnexpectedEOF}, // error on writing actual data } t.Logf("Running %d tests", len(tests)) @@ -766,3 +769,51 @@ func TestRandomUint64Errors(t *testing.T) { t.Errorf("Nonce is not 0 [%v]", nonce) } } + +func TestBinaryFreeList(t *testing.T) { + var list binaryFreeList = make(chan []byte, binaryFreeListMaxItems) + + expectedCapacity := 8 + expectedLength := 8 + + first := list.Borrow() + if cap(first) != expectedCapacity { + t.Errorf("MsgTx.TestBinaryFreeList: Expected capacity for first %d, but got %d", + expectedCapacity, cap(first)) + } + if len(first) != expectedLength { + t.Errorf("MsgTx.TestBinaryFreeList: Expected length for first %d, but got %d", + expectedLength, len(first)) + } + list.Return(first) + + // Borrow again, and check that the underlying array is re-used for second + second := list.Borrow() + if cap(second) != expectedCapacity { + t.Errorf("TestBinaryFreeList: Expected capacity for second %d, but got %d", + expectedCapacity, cap(second)) + } + if len(second) != expectedLength { + t.Errorf("TestBinaryFreeList: Expected length for second %d, but got %d", + expectedLength, len(second)) + } + + firstArrayAddress := underlyingArrayAddress(first) + secondArrayAddress := underlyingArrayAddress(second) + + if firstArrayAddress != secondArrayAddress { + t.Errorf("First underlying array is at address %d and second at address %d, "+ + "which means memory was not re-used", firstArrayAddress, secondArrayAddress) + } + + list.Return(second) + + // test there's no crash when channel is full because borrowed too much + buffers := make([][]byte, binaryFreeListMaxItems+1) + for i := 0; i < binaryFreeListMaxItems+1; i++ { + buffers[i] = list.Borrow() + } + for i := 0; i < binaryFreeListMaxItems+1; i++ { + list.Return(buffers[i]) + } +} diff --git a/wire/message_test.go b/wire/message_test.go index 8fe726d39..82ba8fd95 100644 --- a/wire/message_test.go +++ b/wire/message_test.go @@ -60,6 +60,8 @@ func TestMessage(t *testing.T) { msgPing := NewMsgPing(123123) msgPong := NewMsgPong(123123) msgGetHeaders := NewMsgGetHeaders() + msgSendHeaders := NewMsgSendHeaders() + msgFeeFilter := NewMsgFeeFilter(123456) msgHeaders := NewMsgHeaders() msgAlert := NewMsgAlert([]byte("payload"), []byte("signature")) msgMemPool := NewMsgMemPool() @@ -97,6 +99,8 @@ func TestMessage(t *testing.T) { {msgPing, msgPing, pver, MainNet, 32}, {msgPong, msgPong, pver, MainNet, 32}, {msgGetHeaders, msgGetHeaders, pver, MainNet, 61}, + {msgSendHeaders, msgSendHeaders, pver, MainNet, 24}, + {msgFeeFilter, msgFeeFilter, pver, MainNet, 32}, {msgHeaders, msgHeaders, pver, MainNet, 25}, {msgAlert, msgAlert, pver, MainNet, 42}, {msgMemPool, msgMemPool, pver, MainNet, 24}, diff --git a/wire/msgtx.go b/wire/msgtx.go index 30a9394aa..03883c088 100644 --- a/wire/msgtx.go +++ b/wire/msgtx.go @@ -365,13 +365,10 @@ func (msg *MsgTx) BtcDecode(r io.Reader, pver uint32) error { // returns them. returnScriptBuffers := func() { for _, txIn := range msg.TxIn { - if txIn == nil { + if txIn == nil || txIn.SignatureScript == nil { continue } - - if txIn.SignatureScript != nil { - scriptPool.Return(txIn.SignatureScript) - } + scriptPool.Return(txIn.SignatureScript) } for _, txOut := range msg.TxOut { if txOut == nil || txOut.PkScript == nil { diff --git a/wire/msgtx_test.go b/wire/msgtx_test.go index 602c16442..d9c4abd81 100644 --- a/wire/msgtx_test.go +++ b/wire/msgtx_test.go @@ -11,6 +11,7 @@ import ( "math" "reflect" "testing" + "unsafe" "github.com/daglabs/btcd/dagconfig/daghash" "github.com/davecgh/go-spew/spew" @@ -637,6 +638,75 @@ func TestTxSerializeSize(t *testing.T) { } } +func TestScriptFreeList(t *testing.T) { + var list scriptFreeList = make(chan []byte, freeListMaxItems) + + expectedCapacity := 512 + expectedLengthFirst := 12 + expectedLengthSecond := 13 + + first := list.Borrow(uint64(expectedLengthFirst)) + if cap(first) != expectedCapacity { + t.Errorf("MsgTx.TestScriptFreeList: Expected capacity for first %d, but got %d", + expectedCapacity, cap(first)) + } + if len(first) != expectedLengthFirst { + t.Errorf("MsgTx.TestScriptFreeList: Expected length for first %d, but got %d", + expectedLengthFirst, len(first)) + } + list.Return(first) + + // Borrow again, and check that the underlying array is re-used for second + second := list.Borrow(uint64(expectedLengthSecond)) + if cap(second) != expectedCapacity { + t.Errorf("MsgTx.TestScriptFreeList: Expected capacity for second %d, but got %d", + expectedCapacity, cap(second)) + } + if len(second) != expectedLengthSecond { + t.Errorf("MsgTx.TestScriptFreeList: Expected length for second %d, but got %d", + expectedLengthSecond, len(second)) + } + + firstArrayAddress := underlyingArrayAddress(first) + secondArrayAddress := underlyingArrayAddress(second) + + if firstArrayAddress != secondArrayAddress { + t.Errorf("First underlying array is at address %d and second at address %d, "+ + "which means memory was not re-used", firstArrayAddress, secondArrayAddress) + } + + list.Return(second) + + // test for buffers bigger than freeListMaxScriptSize + expectedCapacityBig := freeListMaxScriptSize + 1 + expectedLengthBig := expectedCapacityBig + big := list.Borrow(uint64(expectedCapacityBig)) + + if cap(big) != expectedCapacityBig { + t.Errorf("MsgTx.TestScriptFreeList: Expected capacity for second %d, but got %d", + expectedCapacityBig, cap(big)) + } + if len(big) != expectedLengthBig { + t.Errorf("MsgTx.TestScriptFreeList: Expected length for second %d, but got %d", + expectedLengthBig, len(big)) + } + + list.Return(big) + + // test there's no crash when channel is full because borrowed too much + buffers := make([][]byte, freeListMaxItems+1) + for i := 0; i < freeListMaxItems+1; i++ { + buffers[i] = list.Borrow(1) + } + for i := 0; i < freeListMaxItems+1; i++ { + list.Return(buffers[i]) + } +} + +func underlyingArrayAddress(buf []byte) uint64 { + return uint64((*reflect.SliceHeader)(unsafe.Pointer(&buf)).Data) +} + // multiTx is a MsgTx with an input and output and used in various tests. var multiTx = &MsgTx{ Version: 1,