diff --git a/lib/parachain/collation_protocol_test.go b/lib/parachain/collation_protocol_test.go index 7ac800a431..4c0b3a9695 100644 --- a/lib/parachain/collation_protocol_test.go +++ b/lib/parachain/collation_protocol_test.go @@ -32,7 +32,7 @@ func TestCollationProtocol(t *testing.T) { copy(collatorID[:], tempCollatID) var collatorSignature CollatorSignature - tempSignature := common.MustHexToBytes(testSDMHex["collatorSignature"]) + tempSignature := common.MustHexToBytes(testDataStatement["collatorSignature"]) copy(collatorSignature[:], tempSignature) var validatorSignature ValidatorSignature diff --git a/lib/parachain/statement_distribution_message_test.go b/lib/parachain/statement_distribution_message_test.go index 8721ff15de..8dca860049 100644 --- a/lib/parachain/statement_distribution_message_test.go +++ b/lib/parachain/statement_distribution_message_test.go @@ -11,13 +11,13 @@ import ( "gopkg.in/yaml.v3" ) -//go:embed testdata/statement_distribution_message.yaml -var testSDMHexRaw string +//go:embed testdata/statement.yaml +var testDataStatementRaw string -var testSDMHex map[string]string +var testDataStatement map[string]string func init() { - err := yaml.Unmarshal([]byte(testSDMHexRaw), &testSDMHex) + err := yaml.Unmarshal([]byte(testDataStatementRaw), &testDataStatement) if err != nil { fmt.Printf("Error unmarshaling test data: %s\n", err) return @@ -28,7 +28,7 @@ func TestStatementDistributionMessage(t *testing.T) { t.Parallel() var collatorSignature CollatorSignature - tempSignature := common.MustHexToBytes(testSDMHex["collatorSignature"]) + tempSignature := common.MustHexToBytes(testDataStatement["collatorSignature"]) copy(collatorSignature[:], tempSignature) var validatorSignature ValidatorSignature @@ -166,17 +166,17 @@ func TestStatementDistributionMessage(t *testing.T) { { name: "SignedFullStatement with valid statement", enumValue: signedFullStatementWithValid, - encodingValue: common.MustHexToBytes(testSDMHex["sfsValid"]), + encodingValue: common.MustHexToBytes(testDataStatement["sfsValid"]), }, { name: "SignedFullStatement with Seconded statement", enumValue: signedFullStatementWithSeconded, - encodingValue: common.MustHexToBytes(testSDMHex["sfsSeconded"]), + encodingValue: common.MustHexToBytes(testDataStatement["sfsSeconded"]), }, { name: "Seconded Statement With LargePayload", enumValue: secondedStatementWithLargePayload, - encodingValue: common.MustHexToBytes(testSDMHex["statementWithLargePayload"]), + encodingValue: common.MustHexToBytes(testDataStatement["statementWithLargePayload"]), }, } diff --git a/lib/parachain/statement_fetching.go b/lib/parachain/statement_fetching.go new file mode 100644 index 0000000000..1f9e9332ea --- /dev/null +++ b/lib/parachain/statement_fetching.go @@ -0,0 +1,81 @@ +package parachain + +import ( + "fmt" + + "github.com/ChainSafe/gossamer/lib/common" + "github.com/ChainSafe/gossamer/pkg/scale" +) + +// StatementFetchingRequest represents a request for fetching a large statement via request/response. +type StatementFetchingRequest struct { + // Data needed to locate and identify the needed statement. + RelayParent common.Hash `scale:"1"` + + // Hash of candidate that was used create the `CommitedCandidateRecept`. + CandidateHash CandidateHash `scale:"2"` +} + +// Encode returns the SCALE encoding of the StatementFetchingRequest. +func (s *StatementFetchingRequest) Encode() ([]byte, error) { + return scale.Marshal(*s) +} + +// StatementFetchingResponse represents the statement fetching response is +// sent by nodes to the clients who issued a collation fetching request. +// +// Respond with found full statement. +type StatementFetchingResponse scale.VaryingDataType + +// MissingDataInStatement represents the data missing to reconstruct the full signed statement. +type MissingDataInStatement CommittedCandidateReceipt + +// Index returns the index of varying data type +func (MissingDataInStatement) Index() uint { + return 0 +} + +// NewStatementFetchingResponse returns a new statement fetching response varying data type +func NewStatementFetchingResponse() StatementFetchingResponse { + vdt := scale.MustNewVaryingDataType(MissingDataInStatement{}) + return StatementFetchingResponse(vdt) +} + +// Set will set a value using the underlying varying data type +func (s *StatementFetchingResponse) Set(val scale.VaryingDataTypeValue) (err error) { + vdt := scale.VaryingDataType(*s) + err = vdt.Set(val) + if err != nil { + return fmt.Errorf("setting value to varying data type: %w", err) + } + + *s = StatementFetchingResponse(vdt) + return nil +} + +// Value returns the value from the underlying varying data type +func (s *StatementFetchingResponse) Value() (scale.VaryingDataTypeValue, error) { + vdt := scale.VaryingDataType(*s) + return vdt.Value() +} + +// Encode returns the SCALE encoding of the StatementFetchingResponse. +func (s *StatementFetchingResponse) Encode() ([]byte, error) { + return scale.Marshal(*s) +} + +// Decode returns the SCALE decoding of the StatementFetchingResponse. +func (s *StatementFetchingResponse) Decode(in []byte) (err error) { + return scale.Unmarshal(in, s) +} + +// String formats a StatementFetchingResponse as a string +func (s *StatementFetchingResponse) String() string { + if s == nil { + return "StatementFetchingResponse=nil" + } + + v, _ := s.Value() + missingData := v.(MissingDataInStatement) + return fmt.Sprintf("StatementFetchingResponse MissingDataInStatement=%+v", missingData) +} diff --git a/lib/parachain/statement_fetching_test.go b/lib/parachain/statement_fetching_test.go new file mode 100644 index 0000000000..bb315588a1 --- /dev/null +++ b/lib/parachain/statement_fetching_test.go @@ -0,0 +1,130 @@ +package parachain + +import ( + "testing" + + "github.com/ChainSafe/gossamer/lib/common" + "github.com/stretchr/testify/require" +) + +func TestEncodeStatementFetchingRequest(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + request StatementFetchingRequest + expectedEncode []byte + }{ + { + // expected encoding is generated by running rust test code: + // fn statement_request() { + // let hash4 = Hash::repeat_byte(4); + // let statement_fetching_request = StatementFetchingRequest{ + // relay_parent: hash4, + // candidate_hash: CandidateHash(hash4) + // }; + // println!( + // "statement_fetching_request encode => {:?}\n\n", + // statement_fetching_request.encode() + // ); + // } + name: "all_4_in_hash", + request: StatementFetchingRequest{ + RelayParent: getDummyHash(4), + CandidateHash: CandidateHash{Value: getDummyHash(4)}, + }, + expectedEncode: common.MustHexToBytes(testDataStatement["all4InCommonHash"]), + }, + { + name: "all_7_in_hash", + request: StatementFetchingRequest{ + RelayParent: getDummyHash(7), + CandidateHash: CandidateHash{Value: getDummyHash(7)}, + }, + expectedEncode: common.MustHexToBytes(testDataStatement["all7InCommonHash"]), + }, + { + name: "random_hash", + request: StatementFetchingRequest{ + RelayParent: common.MustHexToHash("0x677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c19"), + CandidateHash: CandidateHash{ + Value: common.MustHexToHash("0x677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c19"), + }, + }, + expectedEncode: common.MustHexToBytes(testDataStatement["hexOfStatementFetchingRequest"]), + }, + } + + for _, c := range testCases { + c := c + t.Run(c.name, func(t *testing.T) { + t.Parallel() + actualEncode, err := c.request.Encode() + require.NoError(t, err) + require.Equal(t, c.expectedEncode, actualEncode) + }) + } +} + +func TestStatementFetchingResponse(t *testing.T) { + t.Parallel() + + testHash := common.MustHexToHash("0x677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c19") + + var collatorID CollatorID + tempCollatID := common.MustHexToBytes("0x48215b9d322601e5b1a95164cea0dc4626f545f98343d07f1551eb9543c4b147") + copy(collatorID[:], tempCollatID) + + var collatorSignature CollatorSignature + tempSignature := common.MustHexToBytes(testDataStatement["collatorSignature"]) + copy(collatorSignature[:], tempSignature) + + missingDataInStatement := MissingDataInStatement{ + Descriptor: CandidateDescriptor{ + ParaID: uint32(1), + RelayParent: testHash, + Collator: collatorID, + PersistedValidationDataHash: testHash, + PovHash: testHash, + ErasureRoot: testHash, + Signature: collatorSignature, + ParaHead: testHash, + ValidationCodeHash: ValidationCodeHash(testHash), + }, + Commitments: CandidateCommitments{ + UpwardMessages: []UpwardMessage{{1, 2, 3}}, + NewValidationCode: &ValidationCode{1, 2, 3}, + HeadData: headData{1, 2, 3}, + ProcessedDownwardMessages: uint32(5), + HrmpWatermark: uint32(0), + }, + } + + encodedValue := common.MustHexToBytes(testDataStatement["hexOfStatementFetchingResponse"]) + + t.Run("encode_statement_fetching_response", func(t *testing.T) { + t.Parallel() + + response := NewStatementFetchingResponse() + err := response.Set(missingDataInStatement) + require.NoError(t, err) + + actualEncode, err := response.Encode() + require.NoError(t, err) + + require.Equal(t, encodedValue, actualEncode) + }) + + t.Run("Decode_statement_fetching_response", func(t *testing.T) { + t.Parallel() + + response := NewStatementFetchingResponse() + err := response.Decode(encodedValue) + require.NoError(t, err) + + actualData, err := response.Value() + require.NoError(t, err) + + require.EqualValues(t, missingDataInStatement, actualData) + }) +} diff --git a/lib/parachain/statement_test.go b/lib/parachain/statement_test.go index ed5f8eb3c1..cde19fd9ff 100644 --- a/lib/parachain/statement_test.go +++ b/lib/parachain/statement_test.go @@ -24,7 +24,7 @@ func TestStatement(t *testing.T) { copy(collatorID[:], tempCollatID) var collatorSignature CollatorSignature - tempSignature := common.MustHexToBytes(testSDMHex["collatorSignature"]) + tempSignature := common.MustHexToBytes(testDataStatement["collatorSignature"]) copy(collatorSignature[:], tempSignature) hash5 := getDummyHash(5) @@ -59,8 +59,7 @@ func TestStatement(t *testing.T) { { name: "Seconded", enumValue: secondedEnumValue, - encodingValue: common.MustHexToBytes(testSDMHex["statementSeconded"]), - // expected Hex stored in statement_distribution_message.yaml + encodingValue: common.MustHexToBytes(testDataStatement["statementSeconded"]), }, { name: "Valid", diff --git a/lib/parachain/testdata/statement_distribution_message.yaml b/lib/parachain/testdata/statement.yaml similarity index 61% rename from lib/parachain/testdata/statement_distribution_message.yaml rename to lib/parachain/testdata/statement.yaml index e43dd59bb8..8da19561cb 100644 --- a/lib/parachain/testdata/statement_distribution_message.yaml +++ b/lib/parachain/testdata/statement.yaml @@ -1,3 +1,7 @@ + +# ==========| statement distribution message |========== + + collatorSignature: "0xc67cb93bf0a36fcee3d29de8a6a69a759659680acf486475e0a2552a5fbed87e45adce5f290698d8596095722b33599227f7461f51af8617c8be74b894cf1b86" # Seconded @@ -10,4 +14,20 @@ sfsValid: "0x0005050505050505050505050505050505050505050505050505050505050505050 sfsSeconded: "0x0005050505050505050505050505050505050505050505050505050505050505050101000000050505050505050505050505050505050505050505050505050505050505050548215b9d322601e5b1a95164cea0dc4626f545f98343d07f1551eb9543c4b147050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505c67cb93bf0a36fcee3d29de8a6a69a759659680acf486475e0a2552a5fbed87e45adce5f290698d8596095722b33599227f7461f51af8617c8be74b894cf1b8605050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505040c01020300010c0102030c010203050000000000000005000000c67cb93bf0a36fcee3d29de8a6a69a759659680acf486475e0a2552a5fbed87e45adce5f290698d8596095722b33599227f7461f51af8617c8be74b894cf1b86" # Seconded Statement With LargePayload -statementWithLargePayload: "0x010505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505000000c67cb93bf0a36fcee3d29de8a6a69a759659680acf486475e0a2552a5fbed87e45adce5f290698d8596095722b33599227f7461f51af8617c8be74b894cf1b86" \ No newline at end of file +statementWithLargePayload: "0x010505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505000000c67cb93bf0a36fcee3d29de8a6a69a759659680acf486475e0a2552a5fbed87e45adce5f290698d8596095722b33599227f7461f51af8617c8be74b894cf1b86" + + + +# ==========| statement fetching request and response |========== + + +# hex of a common.Hash where all 32 bytes are set to 4. +all4InCommonHash: "0x04040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404" + +# hex of a common.Hash where all 32 bytes are set to 7. +all7InCommonHash: "0x07070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707" + +# hex of statement fetching response +hexOfStatementFetchingResponse: "0x0001000000677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c1948215b9d322601e5b1a95164cea0dc4626f545f98343d07f1551eb9543c4b147677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c19677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c19677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c19c67cb93bf0a36fcee3d29de8a6a69a759659680acf486475e0a2552a5fbed87e45adce5f290698d8596095722b33599227f7461f51af8617c8be74b894cf1b86677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c19677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c19040c01020300010c0102030c0102030500000000000000" + +hexOfStatementFetchingRequest: "0x677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c19677811d2f3ded2489685468dbdb2e4fa280a249fba9356acceb2e823820e2c19" \ No newline at end of file