diff --git a/lib/parachain/available_data_fetching.go b/lib/parachain/available_data_fetching.go new file mode 100644 index 0000000000..83ec7c2578 --- /dev/null +++ b/lib/parachain/available_data_fetching.go @@ -0,0 +1,119 @@ +package parachain + +import ( + "fmt" + + "github.com/ChainSafe/gossamer/lib/common" + "github.com/ChainSafe/gossamer/pkg/scale" +) + +// AvailableDataFetchingRequest represents a request to retrieve all available data for a specific candidate. +type AvailableDataFetchingRequest struct { + // Hash of the candidate for which the available data is requested. + CandidateHash CandidateHash +} + +// Encode returns the SCALE encoding of the AvailableDataFetchingRequest +func (a AvailableDataFetchingRequest) Encode() ([]byte, error) { + return scale.Marshal(a) +} + +// AvailableDataFetchingResponse represents the possible responses to an available data fetching request. +type AvailableDataFetchingResponse scale.VaryingDataType + +// NewAvailableDataFetchingResponse returns a new available data fetching response varying data type +func NewAvailableDataFetchingResponse() AvailableDataFetchingResponse { + vdt := scale.MustNewVaryingDataType(AvailableData{}, NoSuchData{}) + return AvailableDataFetchingResponse(vdt) +} + +// Set will set a value using the underlying varying data type +func (a *AvailableDataFetchingResponse) Set(val scale.VaryingDataTypeValue) (err error) { + vdt := scale.VaryingDataType(*a) + err = vdt.Set(val) + if err != nil { + return + } + *a = AvailableDataFetchingResponse(vdt) + return +} + +// Value returns the value from the underlying varying data type +func (a *AvailableDataFetchingResponse) Value() (val scale.VaryingDataTypeValue, err error) { + vdt := scale.VaryingDataType(*a) + return vdt.Value() +} + +// AvailableData represents the data that is kept available for each candidate included in the relay chain. +type AvailableData struct { + // The Proof-of-Validation (PoV) of the candidate + PoV Pov `scale:"1"` + + // The persisted validation data needed for approval checks + ValidationData PersistedValidationData `scale:"2"` +} + +// Pov represents a Proof-of-Validity block (PoV block) or a parachain block. +// It contains the necessary data for the parachain specific state transition logic. +type Pov struct { + BlockData BlockData `scale:"1"` +} + +// BlockData represents parachain block data. +// It contains everything required to validate para-block, may contain block and witness data. +type BlockData []byte + +// Index returns the index of varying data type +func (AvailableData) Index() uint { + return 0 +} + +// PersistedValidationData provides information about how to create the inputs for the validation +// of a candidate by calling the Runtime. +// This information is derived from the parachain state and will vary from parachain to parachain, +// although some of the fields may be the same for every parachain. +type PersistedValidationData struct { + // The parent head-data + ParentHead headData `scale:"1"` + + // The relay-chain block number this is in the context of + RelayParentNumber BlockNumber `scale:"2"` + + // The relay-chain block storage root this is in the context of + RelayParentStorageRoot common.Hash `scale:"3"` + + // The maximum legal size of a POV block, in bytes + MaxPovSize uint32 `scale:"4"` +} + +// NoSuchData indicates that the requested data was not found. +type NoSuchData struct{} + +// Index returns the index of varying data type +func (NoSuchData) Index() uint { + return 1 +} + +// Encode returns the SCALE encoding of the AvailableDataFetchingResponse +func (a *AvailableDataFetchingResponse) Encode() ([]byte, error) { + return scale.Marshal(*a) +} + +// Decode returns the SCALE decoding of the AvailableDataFetchingResponse. +func (a *AvailableDataFetchingResponse) Decode(in []byte) (err error) { + return scale.Unmarshal(in, a) +} + +// String formats a AvailableDataFetchingResponse as a string +func (p *AvailableDataFetchingResponse) String() string { + if p == nil { + return "AvailableDataFetchingResponse=nil" + } + + v, _ := p.Value() + availableData, ok := v.(AvailableData) + if !ok { + return "AvailableDataFetchingResponse=NoSuchData" + } + return fmt.Sprintf("AvailableDataFetchingResponse AvailableData=%+v", availableData) +} diff --git a/lib/parachain/available_data_fetching_test.go b/lib/parachain/available_data_fetching_test.go new file mode 100644 index 0000000000..81e2b16613 --- /dev/null +++ b/lib/parachain/available_data_fetching_test.go @@ -0,0 +1,90 @@ +package parachain + +import ( + "testing" + + "github.com/ChainSafe/gossamer/lib/common" + "github.com/ChainSafe/gossamer/pkg/scale" + "github.com/stretchr/testify/require" +) + +func TestEncodeAvailableDataFetchingRequest(t *testing.T) { + availableDataFetchingRequest := AvailableDataFetchingRequest{ + CandidateHash: CandidateHash{ + common.MustHexToHash("0x677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c19"), + }, + } + + actualEncode, err := availableDataFetchingRequest.Encode() + require.NoError(t, err) + + expextedEncode := common.MustHexToBytes("0x677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c19") + require.Equal(t, expextedEncode, actualEncode) +} + +func TestAvailableDataFetchingResponse(t *testing.T) { + t.Parallel() + + testHash := common.MustHexToHash("0x677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c19") + testBytes := testHash.ToBytes() + availableData := AvailableData{ + PoV: Pov{BlockData: testBytes}, + ValidationData: PersistedValidationData{ + ParentHead: testBytes, + RelayParentNumber: BlockNumber(4), + RelayParentStorageRoot: testHash, + MaxPovSize: 6, + }, + } + + testCases := []struct { + name string + value scale.VaryingDataTypeValue + encodeValue []byte + }{ + { + name: "AvailableData", + value: availableData, + encodeValue: common.MustHexToBytes("0x0080677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c1980677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c1904000000677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c1906000000"), //nolint:lll + }, + { + name: "NoSuchData", + value: NoSuchData{}, + 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() + + availableDataFetchingResponse := NewAvailableDataFetchingResponse() + err := availableDataFetchingResponse.Set(c.value) + require.NoError(t, err) + + actualEncode, err := availableDataFetchingResponse.Encode() + require.NoError(t, err) + + require.Equal(t, c.encodeValue, actualEncode) + }) + + t.Run("decode", func(t *testing.T) { + t.Parallel() + + availableDataFetchingResponse := NewAvailableDataFetchingResponse() + err := availableDataFetchingResponse.Decode(c.encodeValue) + require.NoError(t, err) + + actualData, err := availableDataFetchingResponse.Value() + require.NoError(t, err) + + require.EqualValues(t, c.value, actualData) + }) + + }) + } +}