From 9d8d0ea756f6ef7e045c7f0425b548478e988e63 Mon Sep 17 00:00:00 2001 From: Axay Sagathiya Date: Fri, 7 Jul 2023 12:00:55 +0530 Subject: [PATCH] feat(lib/parachain): Implement request and response message for /req_chunk/1 protocol (#3362) - Added `ChunkFetchingRequest` and `ChunkFetchingResponse` types. - implemented network.Message interface in `ChunkFetchingRequest` and 'network.ResponseMessage' interface in `ChunkFetchingResponse` --- lib/parachain/chunk_fetching.go | 93 ++++++++++++++++++++++++++++ lib/parachain/chunk_fetching_test.go | 83 +++++++++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 lib/parachain/chunk_fetching.go create mode 100644 lib/parachain/chunk_fetching_test.go diff --git a/lib/parachain/chunk_fetching.go b/lib/parachain/chunk_fetching.go new file mode 100644 index 00000000000..e27f52a1d29 --- /dev/null +++ b/lib/parachain/chunk_fetching.go @@ -0,0 +1,93 @@ +package parachain + +import ( + "fmt" + + "github.com/ChainSafe/gossamer/pkg/scale" +) + +// ChunkFetchingRequest represents a request to retrieve chunks of a parachain candidate +type ChunkFetchingRequest struct { + // Hash of candidate we want a chunk for. + CandidateHash CandidateHash `scale:"1"` + + // The index of the chunk to fetch. + Index ValidatorIndex `scale:"2"` +} + +// Encode returns the SCALE encoding of the ChunkFetchingRequest +func (c ChunkFetchingRequest) Encode() ([]byte, error) { + return scale.Marshal(c) +} + +// ChunkFetchingResponse represents the response for a requested erasure chunk +type ChunkFetchingResponse scale.VaryingDataType + +// NewChunkFetchingResponse returns a new chunk fetching response varying data type +func NewChunkFetchingResponse() ChunkFetchingResponse { + vdt := scale.MustNewVaryingDataType(ChunkResponse{}, NoSuchChunk{}) + return ChunkFetchingResponse(vdt) +} + +// Set will set a value using the underlying varying data type +func (c *ChunkFetchingResponse) Set(val scale.VaryingDataTypeValue) (err error) { + vdt := scale.VaryingDataType(*c) + err = vdt.Set(val) + if err != nil { + return + } + *c = ChunkFetchingResponse(vdt) + return +} + +// Value returns the value from the underlying varying data type +func (c *ChunkFetchingResponse) Value() (val scale.VaryingDataTypeValue, err error) { + vdt := scale.VaryingDataType(*c) + return vdt.Value() +} + +// ChunkResponse represents the requested chunk data +type ChunkResponse struct { + // The erasure-encoded chunk of data belonging to the candidate block + Chunk []byte `scale:"1"` + + // Proof for this chunk's branch in the Merkle tree + Proof [][]byte `scale:"2"` +} + +// Index returns the index of varying data type +func (ChunkResponse) Index() uint { + return 0 +} + +// NoSuchChunk indicates that the requested chunk was not found +type NoSuchChunk struct{} + +// Index returns the index of varying data type +func (NoSuchChunk) Index() uint { + return 1 +} + +// Encode returns the SCALE encoding of the ChunkFetchingResponse +func (c *ChunkFetchingResponse) Encode() ([]byte, error) { + return scale.Marshal(*c) +} + +// Decode returns the SCALE decoding of the ChunkFetchingResponse. +func (c *ChunkFetchingResponse) Decode(in []byte) (err error) { + return scale.Unmarshal(in, c) +} + +// String formats a ChunkFetchingResponse as a string +func (c *ChunkFetchingResponse) String() string { + if c == nil { + return "ChunkFetchingResponse=nil" + } + + v, _ := c.Value() + chunkRes, ok := v.(ChunkResponse) + if !ok { + return "ChunkFetchingResponse=NoSuchChunk" + } + return fmt.Sprintf("ChunkFetchingResponse ChunkResponse=%+v", chunkRes) +} diff --git a/lib/parachain/chunk_fetching_test.go b/lib/parachain/chunk_fetching_test.go new file mode 100644 index 00000000000..a666da95b7f --- /dev/null +++ b/lib/parachain/chunk_fetching_test.go @@ -0,0 +1,83 @@ +package parachain + +import ( + "testing" + + "github.com/ChainSafe/gossamer/lib/common" + "github.com/ChainSafe/gossamer/pkg/scale" + "github.com/stretchr/testify/require" +) + +func TestEncodeChunkFetchingRequest(t *testing.T) { + chunkFetchingRequest := ChunkFetchingRequest{ + CandidateHash: CandidateHash{ + common.MustHexToHash("0x677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c19"), + }, + Index: ValidatorIndex(8), + } + + actualEncode, err := chunkFetchingRequest.Encode() + require.NoError(t, err) + + expextedEncode := common.MustHexToBytes("0x677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c1908000000") + require.Equal(t, expextedEncode, actualEncode) +} + +func TestChunkFetchingResponse(t *testing.T) { + t.Parallel() + + testBytes := common.MustHexToBytes("0x677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c19") + testCases := []struct { + name string + value scale.VaryingDataTypeValue + encodeValue []byte + }{ + { + name: "chunkResponse", + value: ChunkResponse{ + Chunk: testBytes, + Proof: [][]byte{testBytes}, + }, + encodeValue: common.MustHexToBytes("0x0080677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c190480677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c19"), //nolint:lll + }, + { + name: "NoSuchChunk", + value: NoSuchChunk{}, + encodeValue: []byte{1}, + }, + } + + for _, c := range testCases { + c := c + t.Run(c.name, func(t *testing.T) { + t.Parallel() + + t.Run("encode", func(t *testing.T) { + t.Parallel() + + chunkFetchingResponse := NewChunkFetchingResponse() + err := chunkFetchingResponse.Set(c.value) + require.NoError(t, err) + + actualEncode, err := chunkFetchingResponse.Encode() + require.NoError(t, err) + + require.Equal(t, c.encodeValue, actualEncode) + }) + + t.Run("decode", func(t *testing.T) { + t.Parallel() + + chunkFetchingResponse := NewChunkFetchingResponse() + err := chunkFetchingResponse.Decode(c.encodeValue) + require.NoError(t, err) + + actualData, err := chunkFetchingResponse.Value() + require.NoError(t, err) + + require.EqualValues(t, c.value, actualData) + }) + + }) + } +}