2020-03-10 12:20:34 -07:00
|
|
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
|
|
|
|
package snowman
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"testing"
|
2020-05-03 23:32:10 -07:00
|
|
|
"time"
|
2020-03-10 12:20:34 -07:00
|
|
|
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
|
|
|
|
"github.com/ava-labs/gecko/database/memdb"
|
|
|
|
"github.com/ava-labs/gecko/ids"
|
|
|
|
"github.com/ava-labs/gecko/snow"
|
|
|
|
"github.com/ava-labs/gecko/snow/choices"
|
|
|
|
"github.com/ava-labs/gecko/snow/consensus/snowman"
|
|
|
|
"github.com/ava-labs/gecko/snow/engine/common"
|
|
|
|
"github.com/ava-labs/gecko/snow/engine/common/queue"
|
|
|
|
"github.com/ava-labs/gecko/snow/networking/router"
|
|
|
|
"github.com/ava-labs/gecko/snow/networking/timeout"
|
|
|
|
"github.com/ava-labs/gecko/snow/validators"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
errUnknownBlock = errors.New("unknown block")
|
|
|
|
)
|
|
|
|
|
|
|
|
func newConfig(t *testing.T) (BootstrapConfig, ids.ShortID, *common.SenderTest, *VMTest) {
|
|
|
|
ctx := snow.DefaultContextTest()
|
|
|
|
|
|
|
|
peers := validators.NewSet()
|
|
|
|
db := memdb.New()
|
|
|
|
sender := &common.SenderTest{}
|
|
|
|
vm := &VMTest{}
|
|
|
|
engine := &Transitive{}
|
2020-05-28 20:48:08 -07:00
|
|
|
handler := &router.Handler{}
|
2020-03-10 12:20:34 -07:00
|
|
|
router := &router.ChainRouter{}
|
|
|
|
timeouts := &timeout.Manager{}
|
|
|
|
|
|
|
|
sender.T = t
|
|
|
|
vm.T = t
|
|
|
|
|
|
|
|
sender.Default(true)
|
|
|
|
vm.Default(true)
|
|
|
|
|
|
|
|
sender.CantGetAcceptedFrontier = false
|
|
|
|
|
|
|
|
peer := validators.GenerateRandomValidator(1)
|
|
|
|
peerID := peer.ID()
|
|
|
|
peers.Add(peer)
|
|
|
|
|
2020-06-06 09:23:54 -07:00
|
|
|
handler.Initialize(
|
|
|
|
engine,
|
|
|
|
make(chan common.Message),
|
|
|
|
1,
|
|
|
|
"",
|
|
|
|
prometheus.NewRegistry(),
|
|
|
|
)
|
2020-03-10 12:20:34 -07:00
|
|
|
timeouts.Initialize(0)
|
2020-05-29 15:32:17 -07:00
|
|
|
router.Initialize(ctx.Log, timeouts, time.Hour, time.Second)
|
2020-03-10 12:20:34 -07:00
|
|
|
|
|
|
|
blocker, _ := queue.New(db)
|
|
|
|
|
|
|
|
commonConfig := common.Config{
|
|
|
|
Context: ctx,
|
|
|
|
Validators: peers,
|
|
|
|
Beacons: peers,
|
2020-04-01 18:20:31 -07:00
|
|
|
Alpha: uint64(peers.Len()/2 + 1),
|
2020-03-10 12:20:34 -07:00
|
|
|
Sender: sender,
|
|
|
|
}
|
|
|
|
return BootstrapConfig{
|
|
|
|
Config: commonConfig,
|
|
|
|
Blocked: blocker,
|
|
|
|
VM: vm,
|
|
|
|
}, peerID, sender, vm
|
|
|
|
}
|
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
// Single node in the accepted frontier; no need to fecth parent
|
2020-03-10 12:20:34 -07:00
|
|
|
func TestBootstrapperSingleFrontier(t *testing.T) {
|
2020-06-05 15:03:34 -07:00
|
|
|
config, _, _, vm := newConfig(t)
|
2020-03-10 12:20:34 -07:00
|
|
|
|
|
|
|
blkID0 := ids.Empty.Prefix(0)
|
|
|
|
blkID1 := ids.Empty.Prefix(1)
|
|
|
|
|
|
|
|
blkBytes0 := []byte{0}
|
|
|
|
blkBytes1 := []byte{1}
|
|
|
|
|
|
|
|
blk0 := &Blk{
|
|
|
|
id: blkID0,
|
|
|
|
height: 0,
|
|
|
|
status: choices.Accepted,
|
|
|
|
bytes: blkBytes0,
|
|
|
|
}
|
|
|
|
blk1 := &Blk{
|
|
|
|
parent: blk0,
|
|
|
|
id: blkID1,
|
|
|
|
height: 1,
|
|
|
|
status: choices.Processing,
|
|
|
|
bytes: blkBytes1,
|
|
|
|
}
|
|
|
|
|
|
|
|
bs := bootstrapper{}
|
|
|
|
bs.metrics.Initialize(config.Context.Log, fmt.Sprintf("gecko_%s", config.Context.ChainID), prometheus.NewRegistry())
|
|
|
|
bs.Initialize(config)
|
2020-06-05 15:03:34 -07:00
|
|
|
finished := new(bool)
|
|
|
|
bs.onFinished = func() error { *finished = true; return nil }
|
2020-03-10 12:20:34 -07:00
|
|
|
|
|
|
|
acceptedIDs := ids.Set{}
|
|
|
|
acceptedIDs.Add(blkID1)
|
|
|
|
|
|
|
|
vm.GetBlockF = func(blkID ids.ID) (snowman.Block, error) {
|
|
|
|
switch {
|
|
|
|
case blkID.Equals(blkID1):
|
2020-06-05 15:03:34 -07:00
|
|
|
return blk1, nil
|
|
|
|
case blkID.Equals(blkID0):
|
|
|
|
return blk0, nil
|
2020-03-10 12:20:34 -07:00
|
|
|
default:
|
|
|
|
t.Fatal(errUnknownBlock)
|
|
|
|
panic(errUnknownBlock)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vm.ParseBlockF = func(blkBytes []byte) (snowman.Block, error) {
|
|
|
|
switch {
|
|
|
|
case bytes.Equal(blkBytes, blkBytes1):
|
|
|
|
return blk1, nil
|
2020-06-05 15:03:34 -07:00
|
|
|
case bytes.Equal(blkBytes, blkBytes0):
|
|
|
|
return blk0, nil
|
2020-03-10 12:20:34 -07:00
|
|
|
}
|
|
|
|
t.Fatal(errUnknownBlock)
|
|
|
|
return nil, errUnknownBlock
|
|
|
|
}
|
|
|
|
|
2020-06-06 22:08:53 -07:00
|
|
|
vm.CantBootstrapping = false
|
2020-06-01 20:24:21 -07:00
|
|
|
vm.CantBootstrapped = false
|
2020-03-10 12:20:34 -07:00
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
if err := bs.ForceAccepted(acceptedIDs); err != nil { // should finish
|
|
|
|
t.Fatal(err)
|
|
|
|
} else if !*finished {
|
2020-03-10 12:20:34 -07:00
|
|
|
t.Fatalf("Bootstrapping should have finished")
|
2020-06-05 15:03:34 -07:00
|
|
|
} else if blk1.Status() != choices.Accepted {
|
2020-03-10 12:20:34 -07:00
|
|
|
t.Fatalf("Block should be accepted")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
// Requests the unknown block and gets back a MultiPut with unexpected request ID.
|
|
|
|
// Requests again and gets response from unexpected peer.
|
|
|
|
// Requests again and gets an unexpected block.
|
|
|
|
// Requests again and gets the expected block.
|
2020-03-10 12:20:34 -07:00
|
|
|
func TestBootstrapperUnknownByzantineResponse(t *testing.T) {
|
|
|
|
config, peerID, sender, vm := newConfig(t)
|
|
|
|
|
|
|
|
blkID0 := ids.Empty.Prefix(0)
|
|
|
|
blkID1 := ids.Empty.Prefix(1)
|
|
|
|
blkID2 := ids.Empty.Prefix(2)
|
|
|
|
|
|
|
|
blkBytes0 := []byte{0}
|
|
|
|
blkBytes1 := []byte{1}
|
|
|
|
blkBytes2 := []byte{2}
|
|
|
|
|
|
|
|
blk0 := &Blk{
|
|
|
|
id: blkID0,
|
|
|
|
height: 0,
|
|
|
|
status: choices.Accepted,
|
|
|
|
bytes: blkBytes0,
|
|
|
|
}
|
|
|
|
blk1 := &Blk{
|
|
|
|
parent: blk0,
|
|
|
|
id: blkID1,
|
|
|
|
height: 1,
|
2020-06-05 15:03:34 -07:00
|
|
|
status: choices.Unknown,
|
2020-03-10 12:20:34 -07:00
|
|
|
bytes: blkBytes1,
|
|
|
|
}
|
|
|
|
blk2 := &Blk{
|
|
|
|
parent: blk1,
|
|
|
|
id: blkID2,
|
|
|
|
height: 2,
|
|
|
|
status: choices.Processing,
|
|
|
|
bytes: blkBytes2,
|
|
|
|
}
|
|
|
|
|
|
|
|
bs := bootstrapper{}
|
|
|
|
bs.metrics.Initialize(config.Context.Log, fmt.Sprintf("gecko_%s", config.Context.ChainID), prometheus.NewRegistry())
|
|
|
|
bs.Initialize(config)
|
2020-06-05 15:03:34 -07:00
|
|
|
finished := new(bool)
|
|
|
|
bs.onFinished = func() error { *finished = true; return nil }
|
2020-03-10 12:20:34 -07:00
|
|
|
|
|
|
|
acceptedIDs := ids.Set{}
|
2020-06-05 15:03:34 -07:00
|
|
|
acceptedIDs.Add(blkID2)
|
2020-03-10 12:20:34 -07:00
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
parsedBlk1 := false
|
2020-03-10 12:20:34 -07:00
|
|
|
vm.GetBlockF = func(blkID ids.ID) (snowman.Block, error) {
|
|
|
|
switch {
|
2020-06-05 15:03:34 -07:00
|
|
|
case blkID.Equals(blkID0):
|
|
|
|
return blk0, nil
|
2020-03-10 12:20:34 -07:00
|
|
|
case blkID.Equals(blkID1):
|
2020-06-05 15:03:34 -07:00
|
|
|
if parsedBlk1 {
|
|
|
|
return blk1, nil
|
|
|
|
}
|
2020-03-10 12:20:34 -07:00
|
|
|
return nil, errUnknownBlock
|
2020-06-05 15:03:34 -07:00
|
|
|
case blkID.Equals(blkID2):
|
|
|
|
return blk2, nil
|
2020-03-10 12:20:34 -07:00
|
|
|
default:
|
|
|
|
t.Fatal(errUnknownBlock)
|
|
|
|
panic(errUnknownBlock)
|
|
|
|
}
|
|
|
|
}
|
2020-06-05 15:03:34 -07:00
|
|
|
vm.ParseBlockF = func(blkBytes []byte) (snowman.Block, error) {
|
|
|
|
switch {
|
|
|
|
case bytes.Equal(blkBytes, blkBytes0):
|
|
|
|
return blk0, nil
|
|
|
|
case bytes.Equal(blkBytes, blkBytes1):
|
|
|
|
blk1.status = choices.Processing
|
|
|
|
parsedBlk1 = true
|
|
|
|
return blk1, nil
|
|
|
|
case bytes.Equal(blkBytes, blkBytes2):
|
|
|
|
return blk2, nil
|
|
|
|
}
|
|
|
|
t.Fatal(errUnknownBlock)
|
|
|
|
return nil, errUnknownBlock
|
|
|
|
}
|
2020-03-10 12:20:34 -07:00
|
|
|
|
|
|
|
requestID := new(uint32)
|
2020-06-05 15:03:34 -07:00
|
|
|
sender.GetAncestorsF = func(vdr ids.ShortID, reqID uint32, vtxID ids.ID) {
|
2020-03-10 12:20:34 -07:00
|
|
|
if !vdr.Equals(peerID) {
|
|
|
|
t.Fatalf("Should have requested block from %s, requested from %s", peerID, vdr)
|
|
|
|
}
|
|
|
|
switch {
|
|
|
|
case vtxID.Equals(blkID1):
|
|
|
|
default:
|
2020-06-05 15:03:34 -07:00
|
|
|
t.Fatalf("should have requested blk1")
|
2020-03-10 12:20:34 -07:00
|
|
|
}
|
|
|
|
*requestID = reqID
|
|
|
|
}
|
2020-06-01 20:24:21 -07:00
|
|
|
vm.CantBootstrapping = false
|
2020-03-10 12:20:34 -07:00
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
if err := bs.ForceAccepted(acceptedIDs); err != nil { // should request blk1
|
|
|
|
t.Fatal(err)
|
2020-03-10 12:20:34 -07:00
|
|
|
}
|
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
oldReqID := *requestID
|
|
|
|
if err := bs.MultiPut(peerID, *requestID+1, [][]byte{blkBytes1}); err != nil { // respond with wrong request ID
|
|
|
|
t.Fatal(err)
|
|
|
|
} else if oldReqID != *requestID {
|
|
|
|
t.Fatal("should not have sent new request")
|
|
|
|
}
|
2020-03-10 12:20:34 -07:00
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
if err := bs.MultiPut(ids.NewShortID([20]byte{1, 2, 3}), *requestID, [][]byte{blkBytes1}); err != nil { // respond from wrong peer
|
|
|
|
t.Fatal(err)
|
|
|
|
} else if oldReqID != *requestID {
|
|
|
|
t.Fatal("should not have sent new request")
|
|
|
|
}
|
2020-03-10 12:20:34 -07:00
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
if err := bs.MultiPut(peerID, *requestID, [][]byte{blkBytes0}); err != nil { // respond with wrong block
|
|
|
|
t.Fatal(err)
|
|
|
|
} else if oldReqID == *requestID {
|
|
|
|
t.Fatal("should have sent new request")
|
|
|
|
}
|
2020-03-10 12:20:34 -07:00
|
|
|
|
2020-06-06 22:08:53 -07:00
|
|
|
vm.CantBootstrapped = false
|
2020-03-10 12:20:34 -07:00
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
if err := bs.MultiPut(peerID, *requestID, [][]byte{blkBytes1}); err != nil { // respond with right block
|
|
|
|
t.Fatal(err)
|
|
|
|
} else if !*finished {
|
2020-03-10 12:20:34 -07:00
|
|
|
t.Fatalf("Bootstrapping should have finished")
|
2020-06-05 15:03:34 -07:00
|
|
|
} else if blk0.Status() != choices.Accepted {
|
|
|
|
t.Fatalf("Block should be accepted")
|
|
|
|
} else if blk1.Status() != choices.Accepted {
|
|
|
|
t.Fatalf("Block should be accepted")
|
|
|
|
} else if blk2.Status() != choices.Accepted {
|
2020-03-10 12:20:34 -07:00
|
|
|
t.Fatalf("Block should be accepted")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
// There are multiple needed blocks and MultiPut returns one at a time
|
|
|
|
func TestBootstrapperPartialFetch(t *testing.T) {
|
2020-03-10 12:20:34 -07:00
|
|
|
config, peerID, sender, vm := newConfig(t)
|
|
|
|
|
|
|
|
blkID0 := ids.Empty.Prefix(0)
|
|
|
|
blkID1 := ids.Empty.Prefix(1)
|
|
|
|
blkID2 := ids.Empty.Prefix(2)
|
2020-06-05 15:03:34 -07:00
|
|
|
blkID3 := ids.Empty.Prefix(3)
|
2020-03-10 12:20:34 -07:00
|
|
|
|
|
|
|
blkBytes0 := []byte{0}
|
|
|
|
blkBytes1 := []byte{1}
|
|
|
|
blkBytes2 := []byte{2}
|
2020-06-05 15:03:34 -07:00
|
|
|
blkBytes3 := []byte{3}
|
2020-03-10 12:20:34 -07:00
|
|
|
|
|
|
|
blk0 := &Blk{
|
|
|
|
id: blkID0,
|
|
|
|
height: 0,
|
|
|
|
status: choices.Accepted,
|
|
|
|
bytes: blkBytes0,
|
|
|
|
}
|
|
|
|
blk1 := &Blk{
|
|
|
|
parent: blk0,
|
|
|
|
id: blkID1,
|
|
|
|
height: 1,
|
|
|
|
status: choices.Unknown,
|
|
|
|
bytes: blkBytes1,
|
|
|
|
}
|
|
|
|
blk2 := &Blk{
|
|
|
|
parent: blk1,
|
|
|
|
id: blkID2,
|
|
|
|
height: 2,
|
2020-06-05 15:03:34 -07:00
|
|
|
status: choices.Unknown,
|
2020-03-10 12:20:34 -07:00
|
|
|
bytes: blkBytes2,
|
|
|
|
}
|
2020-06-05 15:03:34 -07:00
|
|
|
blk3 := &Blk{
|
|
|
|
parent: blk2,
|
|
|
|
id: blkID3,
|
|
|
|
height: 3,
|
|
|
|
status: choices.Processing,
|
|
|
|
bytes: blkBytes3,
|
|
|
|
}
|
2020-03-10 12:20:34 -07:00
|
|
|
|
|
|
|
bs := bootstrapper{}
|
|
|
|
bs.metrics.Initialize(config.Context.Log, fmt.Sprintf("gecko_%s", config.Context.ChainID), prometheus.NewRegistry())
|
|
|
|
bs.Initialize(config)
|
2020-06-05 15:03:34 -07:00
|
|
|
finished := new(bool)
|
|
|
|
bs.onFinished = func() error { *finished = true; return nil }
|
2020-03-10 12:20:34 -07:00
|
|
|
|
|
|
|
acceptedIDs := ids.Set{}
|
2020-06-05 15:03:34 -07:00
|
|
|
acceptedIDs.Add(blkID3)
|
2020-03-10 12:20:34 -07:00
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
parsedBlk1 := false
|
|
|
|
parsedBlk2 := false
|
2020-03-10 12:20:34 -07:00
|
|
|
vm.GetBlockF = func(blkID ids.ID) (snowman.Block, error) {
|
|
|
|
switch {
|
2020-06-05 15:03:34 -07:00
|
|
|
case blkID.Equals(blkID0):
|
|
|
|
return blk0, nil
|
|
|
|
case blkID.Equals(blkID1):
|
|
|
|
if parsedBlk1 {
|
|
|
|
return blk1, nil
|
|
|
|
}
|
|
|
|
return nil, errUnknownBlock
|
2020-03-10 12:20:34 -07:00
|
|
|
case blkID.Equals(blkID2):
|
2020-06-05 15:03:34 -07:00
|
|
|
if parsedBlk2 {
|
|
|
|
return blk2, nil
|
|
|
|
}
|
|
|
|
return nil, errUnknownBlock
|
|
|
|
case blkID.Equals(blkID3):
|
|
|
|
return blk3, nil
|
2020-03-10 12:20:34 -07:00
|
|
|
default:
|
2020-06-05 15:03:34 -07:00
|
|
|
t.Fatal(errUnknownBlock)
|
|
|
|
panic(errUnknownBlock)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vm.ParseBlockF = func(blkBytes []byte) (snowman.Block, error) {
|
|
|
|
switch {
|
|
|
|
case bytes.Equal(blkBytes, blkBytes0):
|
|
|
|
return blk0, nil
|
|
|
|
case bytes.Equal(blkBytes, blkBytes1):
|
|
|
|
blk1.status = choices.Processing
|
|
|
|
parsedBlk1 = true
|
|
|
|
return blk1, nil
|
|
|
|
case bytes.Equal(blkBytes, blkBytes2):
|
|
|
|
blk2.status = choices.Processing
|
|
|
|
parsedBlk2 = true
|
|
|
|
return blk2, nil
|
|
|
|
case bytes.Equal(blkBytes, blkBytes3):
|
|
|
|
return blk3, nil
|
2020-03-10 12:20:34 -07:00
|
|
|
}
|
2020-06-05 15:03:34 -07:00
|
|
|
t.Fatal(errUnknownBlock)
|
|
|
|
return nil, errUnknownBlock
|
2020-03-10 12:20:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
requestID := new(uint32)
|
2020-06-05 15:03:34 -07:00
|
|
|
requested := ids.Empty
|
|
|
|
sender.GetAncestorsF = func(vdr ids.ShortID, reqID uint32, vtxID ids.ID) {
|
2020-03-10 12:20:34 -07:00
|
|
|
if !vdr.Equals(peerID) {
|
|
|
|
t.Fatalf("Should have requested block from %s, requested from %s", peerID, vdr)
|
|
|
|
}
|
|
|
|
switch {
|
2020-06-05 15:03:34 -07:00
|
|
|
case vtxID.Equals(blkID1), vtxID.Equals(blkID2):
|
2020-03-10 12:20:34 -07:00
|
|
|
default:
|
2020-06-05 15:03:34 -07:00
|
|
|
t.Fatalf("should have requested blk1 or blk2")
|
2020-03-10 12:20:34 -07:00
|
|
|
}
|
|
|
|
*requestID = reqID
|
2020-06-05 15:03:34 -07:00
|
|
|
requested = vtxID
|
|
|
|
}
|
|
|
|
|
2020-06-01 20:24:21 -07:00
|
|
|
vm.CantBootstrapping = false
|
2020-03-10 12:20:34 -07:00
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
if err := bs.ForceAccepted(acceptedIDs); err != nil { // should request blk2
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := bs.MultiPut(peerID, *requestID, [][]byte{blkBytes2}); err != nil { // respond with blk2
|
|
|
|
t.Fatal(err)
|
|
|
|
} else if !requested.Equals(blkID1) {
|
|
|
|
t.Fatal("should have requested blk1")
|
|
|
|
}
|
|
|
|
|
2020-06-06 22:08:53 -07:00
|
|
|
vm.CantBootstrapped = false
|
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
if err := bs.MultiPut(peerID, *requestID, [][]byte{blkBytes1}); err != nil { // respond with blk1
|
|
|
|
t.Fatal(err)
|
|
|
|
} else if !requested.Equals(blkID1) {
|
|
|
|
t.Fatal("should not have requested another block")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !*finished {
|
|
|
|
t.Fatalf("Bootstrapping should have finished")
|
|
|
|
} else if blk0.Status() != choices.Accepted {
|
|
|
|
t.Fatalf("Block should be accepted")
|
|
|
|
} else if blk1.Status() != choices.Accepted {
|
|
|
|
t.Fatalf("Block should be accepted")
|
|
|
|
} else if blk2.Status() != choices.Accepted {
|
|
|
|
t.Fatalf("Block should be accepted")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// There are multiple needed blocks and MultiPut returns all at once
|
|
|
|
func TestBootstrapperMultiPut(t *testing.T) {
|
|
|
|
config, peerID, sender, vm := newConfig(t)
|
|
|
|
|
|
|
|
blkID0 := ids.Empty.Prefix(0)
|
|
|
|
blkID1 := ids.Empty.Prefix(1)
|
|
|
|
blkID2 := ids.Empty.Prefix(2)
|
|
|
|
blkID3 := ids.Empty.Prefix(3)
|
|
|
|
|
|
|
|
blkBytes0 := []byte{0}
|
|
|
|
blkBytes1 := []byte{1}
|
|
|
|
blkBytes2 := []byte{2}
|
|
|
|
blkBytes3 := []byte{3}
|
|
|
|
|
|
|
|
blk0 := &Blk{
|
|
|
|
id: blkID0,
|
|
|
|
height: 0,
|
|
|
|
status: choices.Accepted,
|
|
|
|
bytes: blkBytes0,
|
|
|
|
}
|
|
|
|
blk1 := &Blk{
|
|
|
|
parent: blk0,
|
|
|
|
id: blkID1,
|
|
|
|
height: 1,
|
|
|
|
status: choices.Unknown,
|
|
|
|
bytes: blkBytes1,
|
|
|
|
}
|
|
|
|
blk2 := &Blk{
|
|
|
|
parent: blk1,
|
|
|
|
id: blkID2,
|
|
|
|
height: 2,
|
|
|
|
status: choices.Unknown,
|
|
|
|
bytes: blkBytes2,
|
|
|
|
}
|
|
|
|
blk3 := &Blk{
|
|
|
|
parent: blk2,
|
|
|
|
id: blkID3,
|
|
|
|
height: 3,
|
|
|
|
status: choices.Processing,
|
|
|
|
bytes: blkBytes3,
|
2020-03-10 12:20:34 -07:00
|
|
|
}
|
2020-06-06 22:08:53 -07:00
|
|
|
vm.CantBootstrapping = false
|
2020-03-10 12:20:34 -07:00
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
bs := bootstrapper{}
|
|
|
|
bs.metrics.Initialize(config.Context.Log, fmt.Sprintf("gecko_%s", config.Context.ChainID), prometheus.NewRegistry())
|
|
|
|
bs.Initialize(config)
|
|
|
|
finished := new(bool)
|
|
|
|
bs.onFinished = func() error { *finished = true; return nil }
|
2020-03-10 12:20:34 -07:00
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
acceptedIDs := ids.Set{}
|
|
|
|
acceptedIDs.Add(blkID3)
|
2020-03-10 12:20:34 -07:00
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
parsedBlk1 := false
|
|
|
|
parsedBlk2 := false
|
|
|
|
vm.GetBlockF = func(blkID ids.ID) (snowman.Block, error) {
|
|
|
|
switch {
|
|
|
|
case blkID.Equals(blkID0):
|
|
|
|
return blk0, nil
|
|
|
|
case blkID.Equals(blkID1):
|
|
|
|
if parsedBlk1 {
|
|
|
|
return blk1, nil
|
|
|
|
}
|
|
|
|
return nil, errUnknownBlock
|
|
|
|
case blkID.Equals(blkID2):
|
|
|
|
if parsedBlk2 {
|
|
|
|
return blk2, nil
|
|
|
|
}
|
|
|
|
return nil, errUnknownBlock
|
|
|
|
case blkID.Equals(blkID3):
|
|
|
|
return blk3, nil
|
|
|
|
default:
|
|
|
|
t.Fatal(errUnknownBlock)
|
|
|
|
panic(errUnknownBlock)
|
|
|
|
}
|
|
|
|
}
|
2020-03-10 12:20:34 -07:00
|
|
|
vm.ParseBlockF = func(blkBytes []byte) (snowman.Block, error) {
|
|
|
|
switch {
|
2020-06-05 15:03:34 -07:00
|
|
|
case bytes.Equal(blkBytes, blkBytes0):
|
|
|
|
return blk0, nil
|
2020-03-10 12:20:34 -07:00
|
|
|
case bytes.Equal(blkBytes, blkBytes1):
|
2020-06-05 15:03:34 -07:00
|
|
|
blk1.status = choices.Processing
|
|
|
|
parsedBlk1 = true
|
2020-03-10 12:20:34 -07:00
|
|
|
return blk1, nil
|
|
|
|
case bytes.Equal(blkBytes, blkBytes2):
|
2020-06-05 15:03:34 -07:00
|
|
|
blk2.status = choices.Processing
|
|
|
|
parsedBlk2 = true
|
2020-03-10 12:20:34 -07:00
|
|
|
return blk2, nil
|
2020-06-05 15:03:34 -07:00
|
|
|
case bytes.Equal(blkBytes, blkBytes3):
|
|
|
|
return blk3, nil
|
2020-03-10 12:20:34 -07:00
|
|
|
}
|
|
|
|
t.Fatal(errUnknownBlock)
|
|
|
|
return nil, errUnknownBlock
|
|
|
|
}
|
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
requestID := new(uint32)
|
|
|
|
requested := ids.Empty
|
|
|
|
sender.GetAncestorsF = func(vdr ids.ShortID, reqID uint32, vtxID ids.ID) {
|
|
|
|
if !vdr.Equals(peerID) {
|
|
|
|
t.Fatalf("Should have requested block from %s, requested from %s", peerID, vdr)
|
|
|
|
}
|
|
|
|
switch {
|
|
|
|
case vtxID.Equals(blkID1), vtxID.Equals(blkID2):
|
|
|
|
default:
|
|
|
|
t.Fatalf("should have requested blk1 or blk2")
|
|
|
|
}
|
|
|
|
*requestID = reqID
|
|
|
|
requested = vtxID
|
|
|
|
}
|
2020-03-10 12:20:34 -07:00
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
if err := bs.ForceAccepted(acceptedIDs); err != nil { // should request blk2
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2020-03-10 12:20:34 -07:00
|
|
|
|
2020-06-01 20:24:21 -07:00
|
|
|
vm.CantBootstrapped = false
|
2020-03-10 12:20:34 -07:00
|
|
|
|
2020-06-05 15:03:34 -07:00
|
|
|
if err := bs.MultiPut(peerID, *requestID, [][]byte{blkBytes2, blkBytes1}); err != nil { // respond with blk2 and blk1
|
|
|
|
t.Fatal(err)
|
|
|
|
} else if !requested.Equals(blkID2) {
|
|
|
|
t.Fatal("should not have requested another block")
|
|
|
|
}
|
2020-03-10 12:20:34 -07:00
|
|
|
|
|
|
|
if !*finished {
|
|
|
|
t.Fatalf("Bootstrapping should have finished")
|
2020-06-05 15:03:34 -07:00
|
|
|
} else if blk0.Status() != choices.Accepted {
|
2020-03-10 12:20:34 -07:00
|
|
|
t.Fatalf("Block should be accepted")
|
2020-06-05 15:03:34 -07:00
|
|
|
} else if blk1.Status() != choices.Accepted {
|
|
|
|
t.Fatalf("Block should be accepted")
|
|
|
|
} else if blk2.Status() != choices.Accepted {
|
2020-03-10 12:20:34 -07:00
|
|
|
t.Fatalf("Block should be accepted")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBootstrapperAcceptedFrontier(t *testing.T) {
|
|
|
|
config, _, _, vm := newConfig(t)
|
|
|
|
|
|
|
|
blkID := GenerateID()
|
|
|
|
|
|
|
|
bs := bootstrapper{}
|
|
|
|
bs.metrics.Initialize(config.Context.Log, fmt.Sprintf("gecko_%s", config.Context.ChainID), prometheus.NewRegistry())
|
|
|
|
bs.Initialize(config)
|
|
|
|
|
|
|
|
vm.LastAcceptedF = func() ids.ID { return blkID }
|
|
|
|
|
|
|
|
accepted := bs.CurrentAcceptedFrontier()
|
|
|
|
|
|
|
|
if accepted.Len() != 1 {
|
|
|
|
t.Fatalf("Only one block should be accepted")
|
|
|
|
}
|
|
|
|
if !accepted.Contains(blkID) {
|
|
|
|
t.Fatalf("Blk should be accepted")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBootstrapperFilterAccepted(t *testing.T) {
|
|
|
|
config, _, _, vm := newConfig(t)
|
|
|
|
|
|
|
|
blkID0 := GenerateID()
|
|
|
|
blkID1 := GenerateID()
|
|
|
|
blkID2 := GenerateID()
|
|
|
|
|
|
|
|
blk0 := &Blk{
|
|
|
|
id: blkID0,
|
|
|
|
status: choices.Accepted,
|
|
|
|
}
|
|
|
|
blk1 := &Blk{
|
|
|
|
id: blkID1,
|
|
|
|
status: choices.Accepted,
|
|
|
|
}
|
|
|
|
|
|
|
|
bs := bootstrapper{}
|
|
|
|
bs.metrics.Initialize(config.Context.Log, fmt.Sprintf("gecko_%s", config.Context.ChainID), prometheus.NewRegistry())
|
|
|
|
bs.Initialize(config)
|
|
|
|
|
|
|
|
blkIDs := ids.Set{}
|
|
|
|
blkIDs.Add(
|
|
|
|
blkID0,
|
|
|
|
blkID1,
|
|
|
|
blkID2,
|
|
|
|
)
|
|
|
|
|
|
|
|
vm.GetBlockF = func(blkID ids.ID) (snowman.Block, error) {
|
|
|
|
switch {
|
|
|
|
case blkID.Equals(blkID0):
|
|
|
|
return blk0, nil
|
|
|
|
case blkID.Equals(blkID1):
|
|
|
|
return blk1, nil
|
|
|
|
case blkID.Equals(blkID2):
|
|
|
|
return nil, errUnknownBlock
|
|
|
|
}
|
|
|
|
t.Fatal(errUnknownBlock)
|
|
|
|
return nil, errUnknownBlock
|
|
|
|
}
|
2020-06-01 20:24:21 -07:00
|
|
|
vm.CantBootstrapping = false
|
2020-03-10 12:20:34 -07:00
|
|
|
|
|
|
|
accepted := bs.FilterAccepted(blkIDs)
|
|
|
|
|
|
|
|
if accepted.Len() != 2 {
|
|
|
|
t.Fatalf("Two blocks should be accepted")
|
|
|
|
}
|
|
|
|
if !accepted.Contains(blkID0) {
|
|
|
|
t.Fatalf("Blk should be accepted")
|
|
|
|
}
|
|
|
|
if !accepted.Contains(blkID1) {
|
|
|
|
t.Fatalf("Blk should be accepted")
|
|
|
|
}
|
|
|
|
if accepted.Contains(blkID2) {
|
|
|
|
t.Fatalf("Blk shouldn't be accepted")
|
|
|
|
}
|
|
|
|
}
|