// TestMessage tests the Read/WriteMessage and Read/WriteMessageN API. func TestMessage(t *testing.T) { pver := wire.ProtocolVersion // Create the various types of messages to test. // MsgVersion. addrYou := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 8333} you, err := wire.NewNetAddress(addrYou, wire.SFNodeNetwork) if err != nil { t.Errorf("NewNetAddress: %v", err) } you.Timestamp = time.Time{} // Version message has zero value timestamp. addrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8333} me, err := wire.NewNetAddress(addrMe, wire.SFNodeNetwork) if err != nil { t.Errorf("NewNetAddress: %v", err) } me.Timestamp = time.Time{} // Version message has zero value timestamp. msgVersion := wire.NewMsgVersion(me, you, 123123, 0) msgVerack := wire.NewMsgVerAck() msgGetAddr := wire.NewMsgGetAddr() msgAddr := wire.NewMsgAddr() msgGetBlocks := wire.NewMsgGetBlocks(&chainhash.Hash{}) msgBlock := &testBlock msgInv := wire.NewMsgInv() msgGetData := wire.NewMsgGetData() msgNotFound := wire.NewMsgNotFound() msgTx := wire.NewMsgTx() msgPing := wire.NewMsgPing(123123) msgPong := wire.NewMsgPong(123123) msgGetHeaders := wire.NewMsgGetHeaders() msgHeaders := wire.NewMsgHeaders() msgAlert := wire.NewMsgAlert([]byte("payload"), []byte("signature")) msgMemPool := wire.NewMsgMemPool() msgFilterAdd := wire.NewMsgFilterAdd([]byte{0x01}) msgFilterClear := wire.NewMsgFilterClear() msgFilterLoad := wire.NewMsgFilterLoad([]byte{0x01}, 10, 0, wire.BloomUpdateNone) bh := wire.NewBlockHeader( int32(0), // Version &chainhash.Hash{}, // PrevHash &chainhash.Hash{}, // MerkleRoot &chainhash.Hash{}, // StakeRoot uint16(0x0000), // VoteBits [6]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // FinalState uint16(0x0000), // Voters uint8(0x00), // FreshStake uint8(0x00), // Revocations uint32(0), // Poolsize uint32(0x00000000), // Bits int64(0x0000000000000000), // Sbits uint32(0), // Height uint32(0), // Size uint32(0x00000000), // Nonce [36]byte{}, // ExtraData ) msgMerkleBlock := wire.NewMsgMerkleBlock(bh) msgReject := wire.NewMsgReject("block", wire.RejectDuplicate, "duplicate block") tests := []struct { in wire.Message // Value to encode out wire.Message // Expected decoded value pver uint32 // Protocol version for wire encoding dcrnet wire.CurrencyNet // Network to use for wire encoding bytes int // Expected num bytes read/written }{ {msgVersion, msgVersion, pver, wire.MainNet, 125}, // [0] {msgVerack, msgVerack, pver, wire.MainNet, 24}, // [1] {msgGetAddr, msgGetAddr, pver, wire.MainNet, 24}, // [2] {msgAddr, msgAddr, pver, wire.MainNet, 25}, // [3] {msgGetBlocks, msgGetBlocks, pver, wire.MainNet, 61}, // [4] {msgBlock, msgBlock, pver, wire.MainNet, 522}, // [5] {msgInv, msgInv, pver, wire.MainNet, 25}, // [6] {msgGetData, msgGetData, pver, wire.MainNet, 25}, // [7] {msgNotFound, msgNotFound, pver, wire.MainNet, 25}, // [8] {msgTx, msgTx, pver, wire.MainNet, 39}, // [9] {msgPing, msgPing, pver, wire.MainNet, 32}, // [10] {msgPong, msgPong, pver, wire.MainNet, 32}, // [11] {msgGetHeaders, msgGetHeaders, pver, wire.MainNet, 61}, // [12] {msgHeaders, msgHeaders, pver, wire.MainNet, 25}, // [13] {msgAlert, msgAlert, pver, wire.MainNet, 42}, // [14] {msgMemPool, msgMemPool, pver, wire.MainNet, 24}, // [15] {msgFilterAdd, msgFilterAdd, pver, wire.MainNet, 26}, // [16] {msgFilterClear, msgFilterClear, pver, wire.MainNet, 24}, // [17] {msgFilterLoad, msgFilterLoad, pver, wire.MainNet, 35}, // [18] {msgMerkleBlock, msgMerkleBlock, pver, wire.MainNet, 215}, // [19] {msgReject, msgReject, pver, wire.MainNet, 79}, // [20] } t.Logf("Running %d tests", len(tests)) for i, test := range tests { // Encode to wire format. var buf bytes.Buffer nw, err := wire.WriteMessageN(&buf, test.in, test.pver, test.dcrnet) 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 := wire.ReadMessageN(rbuf, test.pver, test.dcrnet) 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 := wire.WriteMessage(&buf, test.in, test.pver, test.dcrnet) if err != nil { t.Errorf("WriteMessage #%d error %v", i, err) continue } // Decode from wire format. rbuf := bytes.NewReader(buf.Bytes()) msg, _, err := wire.ReadMessage(rbuf, test.pver, test.dcrnet) 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 := wire.ProtocolVersion dcrnet := wire.MainNet // Ensure message errors are as expected with no function specified. wantErr := "something bad happened" testErr := wire.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 = wire.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. testNet3Bytes := makeHeader(wire.TestNet, "", 0, 0) // Wire encoded bytes for a message that exceeds max overall message // length. mpl := uint32(wire.MaxMessagePayload) exceedMaxPayloadBytes := makeHeader(dcrnet, "getaddr", mpl+1, 0) // Wire encoded bytes for a command which is invalid utf-8. badCommandBytes := makeHeader(dcrnet, "bogus", 0, 0) badCommandBytes[4] = 0x81 // Wire encoded bytes for a command which is valid, but not supported. unsupportedCommandBytes := makeHeader(dcrnet, "bogus", 0, 0) // Wire encoded bytes for a message which exceeds the max payload for // a specific message type. exceedTypePayloadBytes := makeHeader(dcrnet, "getaddr", 1, 0) // Wire encoded bytes for a message which does not deliver the full // payload according to the header length. shortPayloadBytes := makeHeader(dcrnet, "version", 115, 0) // Wire encoded bytes for a message with a bad checksum. badChecksumBytes := makeHeader(dcrnet, "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(dcrnet, "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(dcrnet, "bogus", 15*1024, 0) tests := []struct { buf []byte // Wire encoding pver uint32 // Protocol version for wire encoding dcrnet wire.CurrencyNet // Decred 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. [0] { []byte{}, pver, dcrnet, 0, io.EOF, 0, }, // Wrong network. Want MainNet, but giving TestNet. [1] { testNet3Bytes, pver, dcrnet, len(testNet3Bytes), &wire.MessageError{}, 24, }, // Exceed max overall message payload length. [2] { exceedMaxPayloadBytes, pver, dcrnet, len(exceedMaxPayloadBytes), &wire.MessageError{}, 24, }, // Invalid UTF-8 command. [3] { badCommandBytes, pver, dcrnet, len(badCommandBytes), &wire.MessageError{}, 24, }, // Valid, but unsupported command. [4] { unsupportedCommandBytes, pver, dcrnet, len(unsupportedCommandBytes), &wire.MessageError{}, 24, }, // Exceed max allowed payload for a message of a specific type. [5] { exceedTypePayloadBytes, pver, dcrnet, len(exceedTypePayloadBytes), &wire.MessageError{}, 24, }, // Message with a payload shorter than the header indicates. [6] { shortPayloadBytes, pver, dcrnet, len(shortPayloadBytes), io.EOF, 24, }, // Message with a bad checksum. [7] { badChecksumBytes, pver, dcrnet, len(badChecksumBytes), &wire.MessageError{}, 26, }, // Message with a valid header, but wrong format. [8] { badMessageBytes, pver, dcrnet, len(badMessageBytes), &wire.MessageError{}, 25, }, // 15k bytes of data to discard. [9] { discardBytes, pver, dcrnet, len(discardBytes), &wire.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 := wire.ReadMessageN(r, test.pver, test.dcrnet) 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 wire.MessageError, check // them for equality. if _, ok := err.(*wire.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 } } } }