This adds the GetInitState and InitState messages along with appropriate tests. It also adds a new protocol version (version 8) and bumps the current wire version to it. This set of messages will be used to allow peers to exchange ephemeral startup information in a more general manner than the current existing GetMiningState/MiningState pair. In particular, peers will be able to selectively request only the specific startup info they're insterested in so that different types of nodes (such as SPV clients) may only request relevant startup information. Compared to the existing MiningState message, the InitState message already offers a new type of data: tspend hashes, that nodes may use to fetch tspend transactions for inspecting, generating templates and voting on their approval.
238 lines
7.1 KiB
Go
238 lines
7.1 KiB
Go
// Copyright (c) 2020 The Decred developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package wire
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"io"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/davecgh/go-spew/spew"
|
|
)
|
|
|
|
// TestGetInitState tests the MsgGetInitState API.
|
|
func TestGetInitState(t *testing.T) {
|
|
pver := ProtocolVersion
|
|
|
|
// Ensure Command() returns the expected value.
|
|
wantCmd := "getinitstate"
|
|
msg := NewMsgGetInitState()
|
|
if cmd := msg.Command(); cmd != wantCmd {
|
|
t.Errorf("NewMsgInitMsg: wrong command - got %v, want %v",
|
|
cmd, wantCmd)
|
|
}
|
|
|
|
// Ensure max payload returns the expected value for latest protocol
|
|
// version. Num types (varInt) 1 byte + n var strings with max len.
|
|
wantPayload := uint32(1 + 32*(32+1))
|
|
maxPayload := msg.MaxPayloadLength(pver)
|
|
if maxPayload != wantPayload {
|
|
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
|
"protocol version %d - got %v, want %v", pver,
|
|
maxPayload, wantPayload)
|
|
}
|
|
|
|
// Ensure max payload length is not more than MaxMessagePayload.
|
|
if maxPayload > MaxMessagePayload {
|
|
t.Fatalf("MaxPayloadLength: payload length (%v) for protocol "+
|
|
"version %d exceeds MaxMessagePayload (%v).", maxPayload, pver,
|
|
MaxMessagePayload)
|
|
}
|
|
|
|
// Ensure AddType returns an error when trying to add an invalid type.
|
|
longType := strings.Repeat("x", 33)
|
|
if err := msg.AddType(longType); !errors.Is(err, ErrInitStateTypeTooLong) {
|
|
t.Fatalf("AddType: unexpeted error - got=%v, want %v",
|
|
err, ErrInitStateTypeTooLong)
|
|
}
|
|
nonAscii := "á"
|
|
if err := msg.AddType(nonAscii); !errors.Is(err, ErrMalformedStrictString) {
|
|
t.Fatalf("AddType: unexpeted error - got=%v, want=%v",
|
|
err, ErrMalformedStrictString)
|
|
}
|
|
|
|
// Ensure AddType adds up to the maximum allowed.
|
|
for i := 0; i < MaxInitStateTypes; i++ {
|
|
if err := msg.AddType(""); err != nil {
|
|
t.Fatalf("AddType: unable to add max number of entries: %v", err)
|
|
}
|
|
}
|
|
|
|
// Ensure AddType does _not_ add one more than the maximum allowed.
|
|
if err := msg.AddType(""); !errors.Is(err, ErrTooManyInitStateTypes) {
|
|
t.Fatalf("AddType: unexpected error - got=%v, want=%v",
|
|
err, ErrTooManyInitStateTypes)
|
|
}
|
|
}
|
|
|
|
// TestGetInitStateWire tests the MsgGetInitState wire encode and decode for
|
|
// various numbers of types.
|
|
func TestGetInitStateWire(t *testing.T) {
|
|
pver := ProtocolVersion
|
|
|
|
// MsgGetInitState message with no types.
|
|
noTypes := NewMsgGetInitState()
|
|
noTypesEncoded := []byte{
|
|
0x00, // Varint for number of types
|
|
}
|
|
|
|
// MsgGetInitState message with multiple types.
|
|
multiTypes := NewMsgGetInitState()
|
|
multiTypes.AddType("first")
|
|
multiTypes.AddType("second")
|
|
multiTypes.AddType("third")
|
|
multiTypesEncoded := []byte{
|
|
0x03, // Varint for number of types
|
|
0x05, // Varint for first type
|
|
'f', 'i', 'r', 's', 't',
|
|
0x06, // Varint for second type
|
|
's', 'e', 'c', 'o', 'n', 'd',
|
|
0x05, // Varint for third type
|
|
't', 'h', 'i', 'r', 'd',
|
|
}
|
|
|
|
tests := []struct {
|
|
in *MsgGetInitState // Message to encode
|
|
out *MsgGetInitState // Expected decoded message
|
|
buf []byte // Wire encoding
|
|
pver uint32 // Protocol version for wire encoding
|
|
}{{
|
|
in: noTypes,
|
|
out: noTypes,
|
|
buf: noTypesEncoded,
|
|
pver: pver,
|
|
}, {
|
|
in: multiTypes,
|
|
out: multiTypes,
|
|
buf: multiTypesEncoded,
|
|
pver: pver,
|
|
}}
|
|
|
|
for i, test := range tests {
|
|
// Encode the message to wire format.
|
|
var buf bytes.Buffer
|
|
err := test.in.BtcEncode(&buf, test.pver)
|
|
if err != nil {
|
|
t.Errorf("BtcEncode #%d error %v", i, err)
|
|
continue
|
|
}
|
|
if !bytes.Equal(buf.Bytes(), test.buf) {
|
|
t.Errorf("BtcEncode #%d - got %s, want: %s", i,
|
|
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
|
|
continue
|
|
}
|
|
|
|
// Decode the message from wire format.
|
|
var msg MsgGetInitState
|
|
rbuf := bytes.NewReader(test.buf)
|
|
err = msg.BtcDecode(rbuf, test.pver)
|
|
if err != nil {
|
|
t.Errorf("BtcDecode #%d error %v", i, err)
|
|
continue
|
|
}
|
|
if !reflect.DeepEqual(&msg, test.out) {
|
|
t.Errorf("BtcDecode #%d - got %s, want: %s", i,
|
|
spew.Sdump(&msg), spew.Sdump(test.out))
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestGetInitState performs negative tests against wire encode and decode of
|
|
// MsgGetInitState to confirm error paths work correctly.
|
|
func TestGetInitStateWireErrors(t *testing.T) {
|
|
pver := ProtocolVersion
|
|
|
|
baseMsg := NewMsgGetInitState()
|
|
baseMsg.AddType("first")
|
|
baseMsg.AddType("second")
|
|
baseMsg.AddType("third")
|
|
baseMsgEncoded := []byte{
|
|
0x03, // Varint for number of types
|
|
0x05, // Varint for first type
|
|
'f', 'i', 'r', 's', 't',
|
|
0x06, // Varint for second type
|
|
's', 'e', 'c', 'o', 'n', 'd',
|
|
0x05, // Varint for third type
|
|
't', 'h', 'i', 'r', 'd',
|
|
}
|
|
|
|
// Message that forces an error by having more than the max allowed
|
|
// number of types.
|
|
maxTypes := NewMsgGetInitState()
|
|
maxTypes.Types = make([]string, MaxInitStateTypes+1)
|
|
maxTypesEncoded := []byte{
|
|
0xfc, // Varint for number of types
|
|
}
|
|
|
|
// Message that forces an error by trying to encode a longer than
|
|
// allowed type.
|
|
longType := NewMsgGetInitState()
|
|
longType.Types = []string{strings.Repeat("a", MaxInitStateTypeLen+1)}
|
|
longTypeEncoded := []byte{
|
|
0x01, // Varint for number of types
|
|
0xfc, // Varint for first type
|
|
}
|
|
|
|
// Message that forces an error by trying to encode a type with invalid
|
|
// characters.
|
|
nonAsciiType := NewMsgGetInitState()
|
|
nonAsciiType.Types = []string{"á"}
|
|
nonAsciiTypeEncoded := []byte{
|
|
0x01, // Varint for number of types
|
|
0x01, // Varint for first type
|
|
'á',
|
|
}
|
|
|
|
tests := []struct {
|
|
in *MsgGetInitState // Value to encode
|
|
buf []byte // Wire encoding
|
|
pver uint32 // Protocol version for wire encoding
|
|
max int // Max size of fixed buffer to induce errors
|
|
writeErr error // Expected write error
|
|
readErr error // Expected read error
|
|
}{
|
|
// Force error in number of types varint.
|
|
{baseMsg, baseMsgEncoded, pver, 0, io.ErrShortWrite, io.EOF},
|
|
// Force error in first var string len.
|
|
{baseMsg, baseMsgEncoded, pver, 1, io.ErrShortWrite, io.EOF},
|
|
// Force error in first string varint.
|
|
{baseMsg, baseMsgEncoded, pver, 3, io.ErrShortWrite, io.ErrUnexpectedEOF},
|
|
// Force error in first string.
|
|
{baseMsg, baseMsgEncoded, pver, 4, io.ErrShortWrite, io.ErrUnexpectedEOF},
|
|
// Force error with greater than allowed number of types.
|
|
{maxTypes, maxTypesEncoded, pver, 2, ErrTooManyInitStateTypes, ErrTooManyInitStateTypes},
|
|
// Force error with longer than allowed type.
|
|
{longType, longTypeEncoded, pver, 3, ErrInitStateTypeTooLong, ErrVarStringTooLong},
|
|
// Force error with non-ascii type.
|
|
{nonAsciiType, nonAsciiTypeEncoded, pver, 3, ErrMalformedStrictString, ErrMalformedStrictString},
|
|
}
|
|
|
|
t.Logf("Running %d tests", len(tests))
|
|
for i, test := range tests {
|
|
// Encode to wire format.
|
|
w := newFixedWriter(test.max)
|
|
err := test.in.BtcEncode(w, test.pver)
|
|
if !errors.Is(err, test.writeErr) {
|
|
t.Errorf("BtcEncode #%d wrong error - got %v, want: %v", i, err,
|
|
test.writeErr)
|
|
continue
|
|
}
|
|
|
|
// Decode from wire format.
|
|
var msg MsgGetInitState
|
|
r := newFixedReader(test.max, test.buf)
|
|
err = msg.BtcDecode(r, test.pver)
|
|
if !errors.Is(err, test.readErr) {
|
|
t.Errorf("BtcDecode #%d wrong error - got %v, want: %v", i, err,
|
|
test.readErr)
|
|
continue
|
|
}
|
|
}
|
|
}
|