kaspad/wire/msggetblocks_test.go
stasatdaglabs 4773f87875
[NOD-1125] Implement the IBD flow (#800)
* [NOD-1125] Write a skeleton for starting IBD.

* [NOD-1125] Add WaitForIBDStart to Peer.

* [NOD-1125] Move functions around.

* [NOD-1125] Fix merge errors.

* [NOD-1125] Fix a comment.

* [NOD-1125] Implement sendGetBlockLocator.

* [NOD-1125] Begin implementing findIBDLowHash.

* [NOD-1125] Finish implementing findIBDLowHash.

* [NOD-1125] Rename findIBDLowHash to findHighestSharedBlockHash.

* [NOD-1125] Implement downloadBlocks.

* [NOD-1125] Implement msgIBDBlock.

* [NOD-1125] Implement msgIBDBlock.

* [NOD-1125] Fix message types for HandleIBD.

* [NOD-1125] Write a skeleton for requesting selected tip hashes.

* [NOD-1125] Write a skeleton for the rest of the IBD requests.

* [NOD-1125] Implement HandleGetBlockLocator.

* [NOD-1125] Fix wrong timeout.

* [NOD-1125] Fix compilation error.

* [NOD-1125] Implement HandleGetBlocks.

* [NOD-1125] Fix compilation errors.

* [NOD-1125] Fix merge errors.

* [NOD-1125] Implement selectPeerForIBD.

* [NOD-1125] Implement RequestSelectedTip.

* [NOD-1125] Implement HandleGetSelectedTip.

* [NOD-1125] Make go lint happy.

* [NOD-1125] Add minGetSelectedTipInterval.

* [NOD-1125] Call StartIBDIfRequired where needed.

* [NOD-1125] Fix merge errors.

* [NOD-1125] Remove a redundant line.

* [NOD-1125] Rename shouldContinue to shouldStop.

* [NOD-1125] Lowercasify an error message.

* [NOD-1125] Shuffle statements around in findHighestSharedBlockHash.

* [NOD-1125] Rename hasRecentlyReceivedBlock to isDAGTimeCurrent.

* [NOD-1125] Scope minGetSelectedTipInterval.

* [NOD-1125] Handle an unhandled error.

* [NOD-1125] Use AddUint32 instead of LoadUint32 + StoreUint32.

* [NOD-1125] Use AddUint32 instead of LoadUint32 + StoreUint32.

* [NOD-1125] Use SwapUint32 instead of AddUint32.

* [NOD-1125] Remove error from requestSelectedTips.

* [NOD-1125] Actually stop IBD when it should stop.

* [NOD-1125] Actually stop RequestSelectedTip when it should stop.

* [NOD-1125] Don't ban peers that send us delayed blocks during IBD.

* [NOD-1125] Make unexpected message type messages nicer.

* [NOD-1125] Remove Peer.ready and make HandleHandshake return it to guarantee we never operate on a non-initialized peer.

* [NOD-1125] Remove errors associated with Peer.ready.

* [NOD-1125] Extract maxHashesInMsgIBDBlocks to a const.

* [NOD-1125] Move the ibd package into flows.

* [NOD-1125] Start IBD if required after getting an unknown block inv.

* [NOD-1125] Don't request blocks during relay if we're in the middle of IBD.

* [NOD-1125] Remove AddBlockLocatorHash.

* [NOD-1125] Extract runIBD to a seperate function.

* [NOD-1125] Extract runSelectedTipRequest to a seperate function.

* [NOD-1125] Remove EnqueueWithTimeout.

* [NOD-1125] Increase the capacity of the outgoingRoute.

* [NOD-1125] Fix some bad names.

* [NOD-1125] Fix a comment.

* [NOD-1125] Simplify a comment.

* [NOD-1125] Move WaitFor... functions into their respective run... functions.

* [NOD-1125] Return default values in case of error.

* [NOD-1125] Use CmdXXX in error messages.

* [NOD-1125] Use MaxInvPerMsg in outgoingRouteMaxMessages instead of MaxBlockLocatorsPerMsg.

* [NOD-1125] Fix a comment.

* [NOD-1125] Disconnect a peer that sends us a delayed block during IBD.

* [NOD-1125] Use StoreUint32 instead of SwapUint32.

* [NOD-1125] Add a comment.

* [NOD-1125] Don't ban peers that send us delayed blocks.
2020-07-20 12:52:23 +03:00

242 lines
7.5 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"
"github.com/pkg/errors"
"io"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/kaspanet/kaspad/util/daghash"
)
// TestGetBlocks tests the MsgGetBlocks API.
func TestGetBlocks(t *testing.T) {
pver := ProtocolVersion
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
lowHash, err := daghash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
highHash, err := daghash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// Ensure we get the same data back out.
msg := NewMsgGetBlocks(lowHash, highHash)
if !msg.HighHash.IsEqual(highHash) {
t.Errorf("NewMsgGetBlocks: wrong high hash - got %v, want %v",
msg.HighHash, highHash)
}
// Ensure the command is expected value.
wantCmd := MessageCommand(4)
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgGetBlocks: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is low hash (32 bytes) + high hash (32 bytes).
wantPayload := uint32(64)
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)
}
}
// TestGetBlocksWire tests the MsgGetBlocks wire encode and decode for various
// numbers of block locator hashes and protocol versions.
func TestGetBlocksWire(t *testing.T) {
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
lowHash, err := daghash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
highHash, err := daghash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// MsgGetBlocks message with no start or high hash.
noStartOrStop := NewMsgGetBlocks(&daghash.Hash{}, &daghash.Hash{})
noStartOrStopEncoded := []byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Low hash
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // High hash
}
// MsgGetBlocks message with a low hash and a high hash.
withLowAndHighHash := NewMsgGetBlocks(lowHash, highHash)
withLowAndHighHashEncoded := []byte{
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Low hash
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // High hash
}
tests := []struct {
in *MsgGetBlocks // Message to encode
out *MsgGetBlocks // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version with no block locators.
{
noStartOrStop,
noStartOrStop,
noStartOrStopEncoded,
ProtocolVersion,
},
// Latest protocol version with multiple block locators.
{
withLowAndHighHash,
withLowAndHighHash,
withLowAndHighHashEncoded,
ProtocolVersion,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.KaspaEncode(&buf, test.pver)
if err != nil {
t.Errorf("KaspaEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("KaspaEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgGetBlocks
rbuf := bytes.NewReader(test.buf)
err = msg.KaspaDecode(rbuf, test.pver)
if err != nil {
t.Errorf("KaspaDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("KaspaDecode #%d\n got: %s want: %s", i,
spew.Sdump(&msg), spew.Sdump(test.out))
continue
}
}
}
// TestGetBlocksWireErrors performs negative tests against wire encode and
// decode of MsgGetBlocks to confirm error paths work correctly.
func TestGetBlocksWireErrors(t *testing.T) {
// Set protocol inside getheaders message.
pver := ProtocolVersion
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
lowHash, err := daghash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
highHash, err := daghash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
}
// MsgGetBlocks message with multiple block locators and a high hash.
baseGetBlocks := NewMsgGetBlocks(lowHash, highHash)
baseGetBlocksEncoded := []byte{
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Low hash
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // High hash
}
tests := []struct {
in *MsgGetBlocks // 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 low hash.
{baseGetBlocks, baseGetBlocksEncoded, pver, 0, io.ErrShortWrite, io.EOF},
// Force error in high hash.
{baseGetBlocks, baseGetBlocksEncoded, pver, 32, io.ErrShortWrite, io.EOF},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode to wire format.
w := newFixedWriter(test.max)
err := test.in.KaspaEncode(w, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// For errors which are not of type MessageError, check them for
// equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.writeErr {
t.Errorf("KaspaEncode #%d wrong error got: %v, "+
"want: %v", i, err, test.writeErr)
continue
}
}
// Decode from wire format.
var msg MsgGetBlocks
r := newFixedReader(test.max, test.buf)
err = msg.KaspaDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type MessageError, check them for
// equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.readErr {
t.Errorf("KaspaDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr)
continue
}
}
}
}