Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(network/messages): normalize from_block field to be uint or common.Hash #4191

Merged
merged 4 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions dot/network/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/internal/log"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/common/variadic"
libp2pnetwork "github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -121,7 +120,7 @@ func (s *testStreamHandler) readStream(stream libp2pnetwork.Stream,
}
}

var starting, _ = variadic.NewUint32OrHash(uint32(1))
var starting = messages.NewFromBlock(uint(1))

var one = uint32(1)

Expand Down
132 changes: 11 additions & 121 deletions dot/network/message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,125 +5,15 @@ package network

import (
"encoding/hex"
"regexp"
"testing"

"github.com/ChainSafe/gossamer/dot/network/messages"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/common/variadic"

"github.com/stretchr/testify/require"
)

func TestEncodeBlockRequestMessage(t *testing.T) {
jimjbrettj marked this conversation as resolved.
Show resolved Hide resolved
t.Parallel()

expected := common.MustHexToBytes("0x0880808008280130011220dcd1346701ca8396496e52" +
"aa2785b1748deb6db09551b72159dcb3e08991025b")
genesisHash := common.MustHexToBytes("0xdcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b")

var one uint32 = 1
bm := &messages.BlockRequestMessage{
RequestedData: 1,
StartingBlock: *variadic.NewUint32OrHashFromBytes(append([]byte{0}, genesisHash...)),
Direction: 1,
Max: &one,
}

encMsg, err := bm.Encode()
require.NoError(t, err)

require.Equal(t, expected, encMsg)

res := new(messages.BlockRequestMessage)
err = res.Decode(encMsg)
require.NoError(t, err)
require.Equal(t, bm, res)
}

func TestEncodeBlockRequestMessage_BlockHash(t *testing.T) {
t.Parallel()

genesisHash := common.MustHexToBytes("0xdcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b")

var one uint32 = 1
bm := &messages.BlockRequestMessage{
RequestedData: 1,
StartingBlock: *variadic.NewUint32OrHashFromBytes(append([]byte{0}, genesisHash...)),
Direction: 1,
Max: &one,
}

encMsg, err := bm.Encode()
require.NoError(t, err)

res := new(messages.BlockRequestMessage)
err = res.Decode(encMsg)
require.NoError(t, err)
require.Equal(t, bm, res)
}

func TestEncodeBlockRequestMessage_BlockNumber(t *testing.T) {
t.Parallel()

var one uint32 = 1
bm := &messages.BlockRequestMessage{
RequestedData: 1,
StartingBlock: *variadic.NewUint32OrHashFromBytes([]byte{1, 1}),
Direction: 1,
Max: &one,
}

encMsg, err := bm.Encode()
require.NoError(t, err)

res := new(messages.BlockRequestMessage)
err = res.Decode(encMsg)
require.NoError(t, err)
require.Equal(t, bm, res)
}

func TestBlockRequestString(t *testing.T) {
t.Parallel()

genesisHash := common.MustHexToBytes("0xdcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b")

bm := &messages.BlockRequestMessage{
RequestedData: 1,
StartingBlock: *variadic.NewUint32OrHashFromBytes(append([]byte{0}, genesisHash...)),
Direction: 1,
Max: nil,
}

var blockRequestStringRegex = regexp.MustCompile(
`^\ABlockRequestMessage RequestedData=[0-9]* StartingBlock={[\[0-9(\s?)]+\]} Direction=[0-9]* Max=[0-9]*\z$`) //nolint:lll

match := blockRequestStringRegex.MatchString(bm.String())
require.True(t, match)
}

func TestEncodeBlockRequestMessage_NoOptionals(t *testing.T) {
t.Parallel()

genesisHash := common.MustHexToBytes("0xdcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b")

bm := &messages.BlockRequestMessage{
RequestedData: 1,
StartingBlock: *variadic.NewUint32OrHashFromBytes(append([]byte{0}, genesisHash...)),
Direction: 1,
Max: nil,
}

encMsg, err := bm.Encode()
require.NoError(t, err)

res := new(messages.BlockRequestMessage)
err = res.Decode(encMsg)
require.NoError(t, err)
require.Equal(t, bm, res)
}

func TestEncodeBlockResponseMessage_Empty(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -446,7 +336,7 @@ func TestAscendingBlockRequest(t *testing.T) {
expectedBlockRequestMessage: []*messages.BlockRequestMessage{
{
RequestedData: messages.BootstrapRequestData,
StartingBlock: *variadic.MustNewUint32OrHash(uint32(10)),
StartingBlock: *messages.NewFromBlock(uint(10)),
Direction: messages.Ascending,
Max: &one,
},
Expand All @@ -461,7 +351,7 @@ func TestAscendingBlockRequest(t *testing.T) {
expectedBlockRequestMessage: []*messages.BlockRequestMessage{
{
RequestedData: messages.BootstrapRequestData,
StartingBlock: *variadic.MustNewUint32OrHash(uint32(1)),
StartingBlock: *messages.NewFromBlock(uint(1)),
Direction: messages.Ascending,
Max: &maxResponseSize,
},
Expand All @@ -475,25 +365,25 @@ func TestAscendingBlockRequest(t *testing.T) {
expectedBlockRequestMessage: []*messages.BlockRequestMessage{
{
RequestedData: messages.BootstrapRequestData,
StartingBlock: *variadic.MustNewUint32OrHash(uint32(1)),
StartingBlock: *messages.NewFromBlock(uint(1)),
Direction: messages.Ascending,
Max: &maxResponseSize,
},
{
RequestedData: messages.BootstrapRequestData,
StartingBlock: *variadic.MustNewUint32OrHash(uint32(129)),
StartingBlock: *messages.NewFromBlock(uint(129)),
Direction: messages.Ascending,
Max: &maxResponseSize,
},
{
RequestedData: messages.BootstrapRequestData,
StartingBlock: *variadic.MustNewUint32OrHash(uint32(257)),
StartingBlock: *messages.NewFromBlock(uint(257)),
Direction: messages.Ascending,
Max: &maxResponseSize,
},
{
RequestedData: messages.BootstrapRequestData,
StartingBlock: *variadic.MustNewUint32OrHash(uint32(385)),
StartingBlock: *messages.NewFromBlock(uint(385)),
Direction: messages.Ascending,
Max: &maxResponseSize,
},
Expand All @@ -507,31 +397,31 @@ func TestAscendingBlockRequest(t *testing.T) {
expectedBlockRequestMessage: []*messages.BlockRequestMessage{
{
RequestedData: messages.BootstrapRequestData,
StartingBlock: *variadic.MustNewUint32OrHash(uint32(1)),
StartingBlock: *messages.NewFromBlock(uint(1)),
Direction: messages.Ascending,
Max: &maxResponseSize,
},
{
RequestedData: messages.BootstrapRequestData,
StartingBlock: *variadic.MustNewUint32OrHash(uint32(129)),
StartingBlock: *messages.NewFromBlock(uint(129)),
Direction: messages.Ascending,
Max: &maxResponseSize,
},
{
RequestedData: messages.BootstrapRequestData,
StartingBlock: *variadic.MustNewUint32OrHash(uint32(257)),
StartingBlock: *messages.NewFromBlock(uint(257)),
Direction: messages.Ascending,
Max: &maxResponseSize,
},
{
RequestedData: messages.BootstrapRequestData,
StartingBlock: *variadic.MustNewUint32OrHash(uint32(385)),
StartingBlock: *messages.NewFromBlock(uint(385)),
Direction: messages.Ascending,
Max: &maxResponseSize,
},
{
RequestedData: messages.BootstrapRequestData,
StartingBlock: *variadic.MustNewUint32OrHash(uint32(513)),
StartingBlock: *messages.NewFromBlock(uint(513)),
Direction: messages.Ascending,
Max: &three,
},
Expand Down
93 changes: 72 additions & 21 deletions dot/network/messages/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import (
"encoding/binary"
"errors"
"fmt"
"math"

pb "github.com/ChainSafe/gossamer/dot/network/proto"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/common/variadic"
"github.com/ChainSafe/gossamer/pkg/scale"
"google.golang.org/protobuf/proto"
)
Expand All @@ -30,6 +30,17 @@ const (
Descending
)

func (s SyncDirection) String() string {
switch s {
case Ascending:
return "ascending"
case Descending:
return "descending"
default:
return "undefined direction"
}
}

// The following defines the fields that will needs to be
// in the response message
const (
Expand All @@ -50,19 +61,63 @@ var (

var (
errBlockRequestFromNumberInvalid = errors.New("block request message From number is not valid")
errInvalidStartingBlockType = errors.New("invalid StartingBlock in messsage")
ErrNilBlockInResponse = errors.New("nil block in response")
)

type fromBlockType byte

const (
fromBlockNumber fromBlockType = iota
fromBlockHash
)

type FromBlock struct {
value any
}

// NewFromBlock returns a new FromBlock given an uint or Hash
// to be used while issuing a block request or while decoding
// a received block request message
func NewFromBlock[T common.Hash | ~uint](value T) *FromBlock {
return &FromBlock{
value: value,
}
}

// RawValue returns the inner uint or hash value
func (x *FromBlock) RawValue() any {
return x.value
}

// Encode will encode a FromBlock into a 4 bytes representation
func (x *FromBlock) Encode() (fromBlockType, []byte) {
switch rawValue := x.value.(type) {
case uint:
encoded := make([]byte, 4)
if rawValue > uint(math.MaxUint32) {
rawValue = math.MaxUint32
}
binary.LittleEndian.PutUint32(encoded, uint32(rawValue))
return fromBlockNumber, encoded
case common.Hash:
return fromBlockHash, rawValue.ToBytes()
default:
panic(fmt.Sprintf("unsupported FromBlock type: %T", x.value))
}
}

// BlockRequestMessage is sent to request some blocks from a peer
type BlockRequestMessage struct {
RequestedData byte
StartingBlock variadic.Uint32OrHash // first byte 0 = block hash (32 byte), first byte 1 = block number (uint32)
Direction SyncDirection // 0 = ascending, 1 = descending

// starting block represents a protobuf "oneof" data type
// which means that this field can be either a number or hash
StartingBlock FromBlock
Direction SyncDirection // 0 = ascending, 1 = descending
Max *uint32
}

func NewBlockRequest(startingBlock variadic.Uint32OrHash, amount uint32,
func NewBlockRequest(startingBlock FromBlock, amount uint32,
requestedData byte, direction SyncDirection) *BlockRequestMessage {
return &BlockRequestMessage{
RequestedData: requestedData,
Expand All @@ -82,7 +137,7 @@ func NewAscendingBlockRequests(startNumber, targetNumber uint, requestedData byt
// start and end block are the same, just request 1 block
if diff == 0 {
return []*BlockRequestMessage{
NewBlockRequest(*variadic.MustNewUint32OrHash(uint32(startNumber)), 1, requestedData, Ascending),
NewBlockRequest(*NewFromBlock(startNumber), 1, requestedData, Ascending),
}
}

Expand All @@ -107,8 +162,7 @@ func NewAscendingBlockRequests(startNumber, targetNumber uint, requestedData byt
max = uint32(missingBlocks)
}

start := variadic.MustNewUint32OrHash(startNumber)
reqs[i] = NewBlockRequest(*start, max, requestedData, Ascending)
reqs[i] = NewBlockRequest(*NewFromBlock(startNumber), max, requestedData, Ascending)
startNumber += uint(max)
}

Expand Down Expand Up @@ -141,19 +195,16 @@ func (bm *BlockRequestMessage) Encode() ([]byte, error) {
MaxBlocks: max,
}

if bm.StartingBlock.IsHash() {
hash := bm.StartingBlock.Hash()
protoType, encoded := bm.StartingBlock.Encode()
switch protoType {
case fromBlockHash:
msg.FromBlock = &pb.BlockRequest_Hash{
Hash: hash[:],
Hash: encoded,
}
} else if bm.StartingBlock.IsUint32() {
buf := make([]byte, 4)
binary.LittleEndian.PutUint32(buf, bm.StartingBlock.Uint32())
case fromBlockNumber:
msg.FromBlock = &pb.BlockRequest_Number{
Number: buf,
Number: encoded,
}
} else {
return nil, errInvalidStartingBlockType
}

return proto.Marshal(msg)
Expand All @@ -168,20 +219,20 @@ func (bm *BlockRequestMessage) Decode(in []byte) error {
}

var (
startingBlock *variadic.Uint32OrHash
startingBlock *FromBlock
max *uint32
)

switch from := msg.FromBlock.(type) {
case *pb.BlockRequest_Hash:
startingBlock, err = variadic.NewUint32OrHash(common.BytesToHash(from.Hash))
startingBlock = NewFromBlock(common.BytesToHash(from.Hash))
case *pb.BlockRequest_Number:
if len(from.Number) != 4 {
return fmt.Errorf("%w expected 4 bytes, got %d bytes", errBlockRequestFromNumberInvalid, len(from.Number))
}

number := binary.LittleEndian.Uint32(from.Number)
startingBlock, err = variadic.NewUint32OrHash(number)
number := uint(binary.LittleEndian.Uint32(from.Number))
startingBlock = NewFromBlock(number)
default:
err = errors.New("invalid StartingBlock")
}
Expand Down
Loading
Loading