Skip to content

Commit

Permalink
feat(network/messages): normalize from_block field to be uint or …
Browse files Browse the repository at this point in the history
…`common.Hash` (#4191)
  • Loading branch information
EclesioMeloJunior authored Sep 20, 2024
1 parent f1e3ad3 commit 44f1e86
Show file tree
Hide file tree
Showing 13 changed files with 206 additions and 476 deletions.
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) {
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

0 comments on commit 44f1e86

Please sign in to comment.