mirror of
				https://github.com/kaspanet/kaspad.git
				synced 2025-10-14 00:59:33 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			451 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			451 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) 2013-2016 The btcsuite developers
 | |
| // Use of this source code is governed by an ISC
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| package wire
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"encoding/binary"
 | |
| 	"io"
 | |
| 	"net"
 | |
| 	"reflect"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/davecgh/go-spew/spew"
 | |
| 	"github.com/kaspanet/kaspad/util/daghash"
 | |
| )
 | |
| 
 | |
| // makeHeader is a convenience function to make a message header in the form of
 | |
| // a byte slice.  It is used to force errors when reading messages.
 | |
| func makeHeader(btcnet BitcoinNet, command string,
 | |
| 	payloadLen uint32, checksum uint32) []byte {
 | |
| 
 | |
| 	// The length of a bitcoin message header is 24 bytes.
 | |
| 	// 4 byte magic number of the bitcoin network + 12 byte command + 4 byte
 | |
| 	// payload length + 4 byte checksum.
 | |
| 	buf := make([]byte, 24)
 | |
| 	binary.LittleEndian.PutUint32(buf, uint32(btcnet))
 | |
| 	copy(buf[4:], []byte(command))
 | |
| 	binary.LittleEndian.PutUint32(buf[16:], payloadLen)
 | |
| 	binary.LittleEndian.PutUint32(buf[20:], checksum)
 | |
| 	return buf
 | |
| }
 | |
| 
 | |
| // TestMessage tests the Read/WriteMessage and Read/WriteMessageN API.
 | |
| func TestMessage(t *testing.T) {
 | |
| 	pver := ProtocolVersion
 | |
| 
 | |
| 	// Create the various types of messages to test.
 | |
| 
 | |
| 	// MsgVersion.
 | |
| 	addrYou := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333}
 | |
| 	you := NewNetAddress(addrYou, SFNodeNetwork)
 | |
| 	you.Timestamp = time.Time{} // Version message has zero value timestamp.
 | |
| 	addrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333}
 | |
| 	me := NewNetAddress(addrMe, SFNodeNetwork)
 | |
| 	me.Timestamp = time.Time{} // Version message has zero value timestamp.
 | |
| 	msgVersion := NewMsgVersion(me, you, 123123, &daghash.ZeroHash, nil)
 | |
| 
 | |
| 	msgVerack := NewMsgVerAck()
 | |
| 	msgGetAddr := NewMsgGetAddr(false, nil)
 | |
| 	msgAddr := NewMsgAddr(false, nil)
 | |
| 	msgGetBlockInvs := NewMsgGetBlockInvs(&daghash.Hash{}, &daghash.Hash{})
 | |
| 	msgBlock := &blockOne
 | |
| 	msgInv := NewMsgInv()
 | |
| 	msgGetData := NewMsgGetData()
 | |
| 	msgNotFound := NewMsgNotFound()
 | |
| 	msgTx := NewNativeMsgTx(1, nil, nil)
 | |
| 	msgPing := NewMsgPing(123123)
 | |
| 	msgPong := NewMsgPong(123123)
 | |
| 	msgGetHeaders := NewMsgGetHeaders(&daghash.Hash{}, &daghash.Hash{})
 | |
| 	msgGetBlockLocator := NewMsgGetBlockLocator(&daghash.ZeroHash, &daghash.ZeroHash)
 | |
| 	msgBlockLocator := NewMsgBlockLocator()
 | |
| 	msgSendHeaders := NewMsgSendHeaders()
 | |
| 	msgFeeFilter := NewMsgFeeFilter(123456)
 | |
| 	msgHeaders := NewMsgHeaders()
 | |
| 	msgFilterAdd := NewMsgFilterAdd([]byte{0x01})
 | |
| 	msgFilterClear := NewMsgFilterClear()
 | |
| 	msgFilterLoad := NewMsgFilterLoad([]byte{0x01}, 10, 0, BloomUpdateNone)
 | |
| 	bh := NewBlockHeader(1, []*daghash.Hash{mainNetGenesisHash, simNetGenesisHash}, &daghash.Hash{}, &daghash.Hash{}, &daghash.Hash{}, 0, 0)
 | |
| 	msgMerkleBlock := NewMsgMerkleBlock(bh)
 | |
| 	msgReject := NewMsgReject("block", RejectDuplicate, "duplicate block")
 | |
| 
 | |
| 	tests := []struct {
 | |
| 		in     Message    // Value to encode
 | |
| 		out    Message    // Expected decoded value
 | |
| 		pver   uint32     // Protocol version for wire encoding
 | |
| 		btcnet BitcoinNet // Network to use for wire encoding
 | |
| 		bytes  int        // Expected num bytes read/written
 | |
| 	}{
 | |
| 		{msgVersion, msgVersion, pver, MainNet, 154},
 | |
| 		{msgVerack, msgVerack, pver, MainNet, 24},
 | |
| 		{msgGetAddr, msgGetAddr, pver, MainNet, 26},
 | |
| 		{msgAddr, msgAddr, pver, MainNet, 27},
 | |
| 		{msgGetBlockInvs, msgGetBlockInvs, pver, MainNet, 88},
 | |
| 		{msgBlock, msgBlock, pver, MainNet, 372},
 | |
| 		{msgInv, msgInv, pver, MainNet, 25},
 | |
| 		{msgGetData, msgGetData, pver, MainNet, 25},
 | |
| 		{msgNotFound, msgNotFound, pver, MainNet, 25},
 | |
| 		{msgTx, msgTx, pver, MainNet, 58},
 | |
| 		{msgPing, msgPing, pver, MainNet, 32},
 | |
| 		{msgPong, msgPong, pver, MainNet, 32},
 | |
| 		{msgGetHeaders, msgGetHeaders, pver, MainNet, 88},
 | |
| 		{msgGetBlockLocator, msgGetBlockLocator, pver, MainNet, 88},
 | |
| 		{msgBlockLocator, msgBlockLocator, pver, MainNet, 25},
 | |
| 		{msgSendHeaders, msgSendHeaders, pver, MainNet, 24},
 | |
| 		{msgFeeFilter, msgFeeFilter, pver, MainNet, 32},
 | |
| 		{msgHeaders, msgHeaders, pver, MainNet, 25},
 | |
| 		{msgFilterAdd, msgFilterAdd, pver, MainNet, 26},
 | |
| 		{msgFilterClear, msgFilterClear, pver, MainNet, 24},
 | |
| 		{msgFilterLoad, msgFilterLoad, pver, MainNet, 35},
 | |
| 		{msgMerkleBlock, msgMerkleBlock, pver, MainNet, 215},
 | |
| 		{msgReject, msgReject, pver, MainNet, 79},
 | |
| 	}
 | |
| 
 | |
| 	t.Logf("Running %d tests", len(tests))
 | |
| 	for i, test := range tests {
 | |
| 		// Encode to wire format.
 | |
| 		var buf bytes.Buffer
 | |
| 		nw, err := WriteMessageN(&buf, test.in, test.pver, test.btcnet)
 | |
| 		if err != nil {
 | |
| 			t.Errorf("WriteMessage #%d error %v", i, err)
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		// Ensure the number of bytes written match the expected value.
 | |
| 		if nw != test.bytes {
 | |
| 			t.Errorf("WriteMessage #%d unexpected num bytes "+
 | |
| 				"written - got %d, want %d", i, nw, test.bytes)
 | |
| 		}
 | |
| 
 | |
| 		// Decode from wire format.
 | |
| 		rbuf := bytes.NewReader(buf.Bytes())
 | |
| 		nr, msg, _, err := ReadMessageN(rbuf, test.pver, test.btcnet)
 | |
| 		if err != nil {
 | |
| 			t.Errorf("ReadMessage #%d error %v, msg %v", i, err,
 | |
| 				spew.Sdump(msg))
 | |
| 			continue
 | |
| 		}
 | |
| 		if !reflect.DeepEqual(msg, test.out) {
 | |
| 			t.Errorf("ReadMessage #%d\n got: %v want: %v", i,
 | |
| 				spew.Sdump(msg), spew.Sdump(test.out))
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		// Ensure the number of bytes read match the expected value.
 | |
| 		if nr != test.bytes {
 | |
| 			t.Errorf("ReadMessage #%d unexpected num bytes read - "+
 | |
| 				"got %d, want %d", i, nr, test.bytes)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Do the same thing for Read/WriteMessage, but ignore the bytes since
 | |
| 	// they don't return them.
 | |
| 	t.Logf("Running %d tests", len(tests))
 | |
| 	for i, test := range tests {
 | |
| 		// Encode to wire format.
 | |
| 		var buf bytes.Buffer
 | |
| 		err := WriteMessage(&buf, test.in, test.pver, test.btcnet)
 | |
| 		if err != nil {
 | |
| 			t.Errorf("WriteMessage #%d error %v", i, err)
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		// Decode from wire format.
 | |
| 		rbuf := bytes.NewReader(buf.Bytes())
 | |
| 		msg, _, err := ReadMessage(rbuf, test.pver, test.btcnet)
 | |
| 		if err != nil {
 | |
| 			t.Errorf("ReadMessage #%d error %v, msg %v", i, err,
 | |
| 				spew.Sdump(msg))
 | |
| 			continue
 | |
| 		}
 | |
| 		if !reflect.DeepEqual(msg, test.out) {
 | |
| 			t.Errorf("ReadMessage #%d\n got: %v want: %v", i,
 | |
| 				spew.Sdump(msg), spew.Sdump(test.out))
 | |
| 			continue
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TestReadMessageWireErrors performs negative tests against wire decoding into
 | |
| // concrete messages to confirm error paths work correctly.
 | |
| func TestReadMessageWireErrors(t *testing.T) {
 | |
| 	pver := ProtocolVersion
 | |
| 	btcnet := MainNet
 | |
| 
 | |
| 	// Ensure message errors are as expected with no function specified.
 | |
| 	wantErr := "something bad happened"
 | |
| 	testErr := MessageError{Description: wantErr}
 | |
| 	if testErr.Error() != wantErr {
 | |
| 		t.Errorf("MessageError: wrong error - got %v, want %v",
 | |
| 			testErr.Error(), wantErr)
 | |
| 	}
 | |
| 
 | |
| 	// Ensure message errors are as expected with a function specified.
 | |
| 	wantFunc := "foo"
 | |
| 	testErr = MessageError{Func: wantFunc, Description: wantErr}
 | |
| 	if testErr.Error() != wantFunc+": "+wantErr {
 | |
| 		t.Errorf("MessageError: wrong error - got %v, want %v",
 | |
| 			testErr.Error(), wantErr)
 | |
| 	}
 | |
| 
 | |
| 	// Wire encoded bytes for main and testnet networks magic identifiers.
 | |
| 	testNetBytes := makeHeader(TestNet, "", 0, 0)
 | |
| 
 | |
| 	// Wire encoded bytes for a message that exceeds max overall message
 | |
| 	// length.
 | |
| 	mpl := uint32(MaxMessagePayload)
 | |
| 	exceedMaxPayloadBytes := makeHeader(btcnet, "getaddr", mpl+1, 0)
 | |
| 
 | |
| 	// Wire encoded bytes for a command which is invalid utf-8.
 | |
| 	badCommandBytes := makeHeader(btcnet, "bogus", 0, 0)
 | |
| 	badCommandBytes[4] = 0x81
 | |
| 
 | |
| 	// Wire encoded bytes for a command which is valid, but not supported.
 | |
| 	unsupportedCommandBytes := makeHeader(btcnet, "bogus", 0, 0)
 | |
| 
 | |
| 	// Wire encoded bytes for a message which exceeds the max payload for
 | |
| 	// a specific message type.
 | |
| 	exceedTypePayloadBytes := makeHeader(btcnet, "getaddr", 23, 0)
 | |
| 
 | |
| 	// Wire encoded bytes for a message which does not deliver the full
 | |
| 	// payload according to the header length.
 | |
| 	shortPayloadBytes := makeHeader(btcnet, "version", 115, 0)
 | |
| 
 | |
| 	// Wire encoded bytes for a message with a bad checksum.
 | |
| 	badChecksumBytes := makeHeader(btcnet, "version", 2, 0xbeef)
 | |
| 	badChecksumBytes = append(badChecksumBytes, []byte{0x0, 0x0}...)
 | |
| 
 | |
| 	// Wire encoded bytes for a message which has a valid header, but is
 | |
| 	// the wrong format.  An addr starts with a varint of the number of
 | |
| 	// contained in the message.  Claim there is two, but don't provide
 | |
| 	// them.  At the same time, forge the header fields so the message is
 | |
| 	// otherwise accurate.
 | |
| 	badMessageBytes := makeHeader(btcnet, "addr", 1, 0xeaadc31c)
 | |
| 	badMessageBytes = append(badMessageBytes, 0x2)
 | |
| 
 | |
| 	// Wire encoded bytes for a message which the header claims has 15k
 | |
| 	// bytes of data to discard.
 | |
| 	discardBytes := makeHeader(btcnet, "bogus", 15*1024, 0)
 | |
| 
 | |
| 	tests := []struct {
 | |
| 		buf     []byte     // Wire encoding
 | |
| 		pver    uint32     // Protocol version for wire encoding
 | |
| 		btcnet  BitcoinNet // Bitcoin network for wire encoding
 | |
| 		max     int        // Max size of fixed buffer to induce errors
 | |
| 		readErr error      // Expected read error
 | |
| 		bytes   int        // Expected num bytes read
 | |
| 	}{
 | |
| 		// Latest protocol version with intentional read errors.
 | |
| 
 | |
| 		// Short header.
 | |
| 		{
 | |
| 			[]byte{},
 | |
| 			pver,
 | |
| 			btcnet,
 | |
| 			0,
 | |
| 			io.EOF,
 | |
| 			0,
 | |
| 		},
 | |
| 
 | |
| 		// Wrong network.  Want MainNet, but giving TestNet.
 | |
| 		{
 | |
| 			testNetBytes,
 | |
| 			pver,
 | |
| 			btcnet,
 | |
| 			len(testNetBytes),
 | |
| 			&MessageError{},
 | |
| 			24,
 | |
| 		},
 | |
| 
 | |
| 		// Exceed max overall message payload length.
 | |
| 		{
 | |
| 			exceedMaxPayloadBytes,
 | |
| 			pver,
 | |
| 			btcnet,
 | |
| 			len(exceedMaxPayloadBytes),
 | |
| 			&MessageError{},
 | |
| 			24,
 | |
| 		},
 | |
| 
 | |
| 		// Invalid UTF-8 command.
 | |
| 		{
 | |
| 			badCommandBytes,
 | |
| 			pver,
 | |
| 			btcnet,
 | |
| 			len(badCommandBytes),
 | |
| 			&MessageError{},
 | |
| 			24,
 | |
| 		},
 | |
| 
 | |
| 		// Valid, but unsupported command.
 | |
| 		{
 | |
| 			unsupportedCommandBytes,
 | |
| 			pver,
 | |
| 			btcnet,
 | |
| 			len(unsupportedCommandBytes),
 | |
| 			&MessageError{},
 | |
| 			24,
 | |
| 		},
 | |
| 
 | |
| 		// Exceed max allowed payload for a message of a specific type.
 | |
| 		{
 | |
| 			exceedTypePayloadBytes,
 | |
| 			pver,
 | |
| 			btcnet,
 | |
| 			len(exceedTypePayloadBytes),
 | |
| 			&MessageError{},
 | |
| 			24,
 | |
| 		},
 | |
| 
 | |
| 		// Message with a payload shorter than the header indicates.
 | |
| 		{
 | |
| 			shortPayloadBytes,
 | |
| 			pver,
 | |
| 			btcnet,
 | |
| 			len(shortPayloadBytes),
 | |
| 			io.EOF,
 | |
| 			24,
 | |
| 		},
 | |
| 
 | |
| 		// Message with a bad checksum.
 | |
| 		{
 | |
| 			badChecksumBytes,
 | |
| 			pver,
 | |
| 			btcnet,
 | |
| 			len(badChecksumBytes),
 | |
| 			&MessageError{},
 | |
| 			26,
 | |
| 		},
 | |
| 
 | |
| 		// Message with a valid header, but wrong format.
 | |
| 		{
 | |
| 			badMessageBytes,
 | |
| 			pver,
 | |
| 			btcnet,
 | |
| 			len(badMessageBytes),
 | |
| 			io.EOF,
 | |
| 			25,
 | |
| 		},
 | |
| 
 | |
| 		// 15k bytes of data to discard.
 | |
| 		{
 | |
| 			discardBytes,
 | |
| 			pver,
 | |
| 			btcnet,
 | |
| 			len(discardBytes),
 | |
| 			&MessageError{},
 | |
| 			24,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	t.Logf("Running %d tests", len(tests))
 | |
| 	for i, test := range tests {
 | |
| 		// Decode from wire format.
 | |
| 		r := newFixedReader(test.max, test.buf)
 | |
| 		nr, _, _, err := ReadMessageN(r, test.pver, test.btcnet)
 | |
| 		if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
 | |
| 			t.Errorf("ReadMessage #%d wrong error got: %v <%T>, "+
 | |
| 				"want: %T", i, err, err, test.readErr)
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		// Ensure the number of bytes written match the expected value.
 | |
| 		if nr != test.bytes {
 | |
| 			t.Errorf("ReadMessage #%d unexpected num bytes read - "+
 | |
| 				"got %d, want %d", i, nr, test.bytes)
 | |
| 		}
 | |
| 
 | |
| 		// For errors which are not of type MessageError, check them for
 | |
| 		// equality.
 | |
| 		if _, ok := err.(*MessageError); !ok {
 | |
| 			if err != test.readErr {
 | |
| 				t.Errorf("ReadMessage #%d wrong error got: %v <%T>, "+
 | |
| 					"want: %v <%T>", i, err, err,
 | |
| 					test.readErr, test.readErr)
 | |
| 				continue
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TestWriteMessageWireErrors performs negative tests against wire encoding from
 | |
| // concrete messages to confirm error paths work correctly.
 | |
| func TestWriteMessageWireErrors(t *testing.T) {
 | |
| 	pver := ProtocolVersion
 | |
| 	btcnet := MainNet
 | |
| 	wireErr := &MessageError{}
 | |
| 
 | |
| 	// Fake message with a command that is too long.
 | |
| 	badCommandMsg := &fakeMessage{command: "somethingtoolong"}
 | |
| 
 | |
| 	// Fake message with a problem during encoding
 | |
| 	encodeErrMsg := &fakeMessage{forceEncodeErr: true}
 | |
| 
 | |
| 	// Fake message that has payload which exceeds max overall message size.
 | |
| 	exceedOverallPayload := make([]byte, MaxMessagePayload+1)
 | |
| 	exceedOverallPayloadErrMsg := &fakeMessage{payload: exceedOverallPayload}
 | |
| 
 | |
| 	// Fake message that has payload which exceeds max allowed per message.
 | |
| 	exceedPayload := make([]byte, 1)
 | |
| 	exceedPayloadErrMsg := &fakeMessage{payload: exceedPayload, forceLenErr: true}
 | |
| 
 | |
| 	// Fake message that is used to force errors in the header and payload
 | |
| 	// writes.
 | |
| 	bogusPayload := []byte{0x01, 0x02, 0x03, 0x04}
 | |
| 	bogusMsg := &fakeMessage{command: "bogus", payload: bogusPayload}
 | |
| 
 | |
| 	tests := []struct {
 | |
| 		msg    Message    // Message to encode
 | |
| 		pver   uint32     // Protocol version for wire encoding
 | |
| 		btcnet BitcoinNet // Bitcoin network for wire encoding
 | |
| 		max    int        // Max size of fixed buffer to induce errors
 | |
| 		err    error      // Expected error
 | |
| 		bytes  int        // Expected num bytes written
 | |
| 	}{
 | |
| 		// Command too long.
 | |
| 		{badCommandMsg, pver, btcnet, 0, wireErr, 0},
 | |
| 		// Force error in payload encode.
 | |
| 		{encodeErrMsg, pver, btcnet, 0, wireErr, 0},
 | |
| 		// Force error due to exceeding max overall message payload size.
 | |
| 		{exceedOverallPayloadErrMsg, pver, btcnet, 0, wireErr, 0},
 | |
| 		// Force error due to exceeding max payload for message type.
 | |
| 		{exceedPayloadErrMsg, pver, btcnet, 0, wireErr, 0},
 | |
| 		// Force error in header write.
 | |
| 		{bogusMsg, pver, btcnet, 0, io.ErrShortWrite, 0},
 | |
| 		// Force error in payload write.
 | |
| 		{bogusMsg, pver, btcnet, 24, io.ErrShortWrite, 24},
 | |
| 	}
 | |
| 
 | |
| 	t.Logf("Running %d tests", len(tests))
 | |
| 	for i, test := range tests {
 | |
| 		// Encode wire format.
 | |
| 		w := newFixedWriter(test.max)
 | |
| 		nw, err := WriteMessageN(w, test.msg, test.pver, test.btcnet)
 | |
| 		if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
 | |
| 			t.Errorf("WriteMessage #%d wrong error got: %v <%T>, "+
 | |
| 				"want: %T", i, err, err, test.err)
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		// Ensure the number of bytes written match the expected value.
 | |
| 		if nw != test.bytes {
 | |
| 			t.Errorf("WriteMessage #%d unexpected num bytes "+
 | |
| 				"written - got %d, want %d", i, nw, test.bytes)
 | |
| 		}
 | |
| 
 | |
| 		// For errors which are not of type MessageError, check them for
 | |
| 		// equality.
 | |
| 		if _, ok := err.(*MessageError); !ok {
 | |
| 			if err != test.err {
 | |
| 				t.Errorf("ReadMessage #%d wrong error got: %v <%T>, "+
 | |
| 					"want: %v <%T>", i, err, err,
 | |
| 					test.err, test.err)
 | |
| 				continue
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | 
