-
Notifications
You must be signed in to change notification settings - Fork 129
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(parachain): Add StatementDistributionMessage varingDataType (#3316)
- Loading branch information
1 parent
0411eda
commit 42256b3
Showing
6 changed files
with
448 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package parachain | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/ChainSafe/gossamer/lib/common" | ||
"github.com/ChainSafe/gossamer/pkg/scale" | ||
) | ||
|
||
// Statement is a result of candidate validation. It could be either `Valid` or `Seconded`. | ||
type Statement scale.VaryingDataType | ||
|
||
// NewStatement returns a new Statement VaryingDataType | ||
func NewStatement() Statement { | ||
vdt := scale.MustNewVaryingDataType(Seconded{}, Valid{}) | ||
return Statement(vdt) | ||
} | ||
|
||
// Set will set a VaryingDataTypeValue using the underlying VaryingDataType | ||
func (s *Statement) 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 = Statement(vdt) | ||
return nil | ||
} | ||
|
||
// Value returns the value from the underlying VaryingDataType | ||
func (s *Statement) Value() (scale.VaryingDataTypeValue, error) { | ||
vdt := scale.VaryingDataType(*s) | ||
return vdt.Value() | ||
} | ||
|
||
// Seconded represents a statement that a validator seconds a candidate. | ||
type Seconded CommittedCandidateReceipt | ||
|
||
// Index returns the VaryingDataType Index | ||
func (s Seconded) Index() uint { | ||
return 1 | ||
} | ||
|
||
// Valid represents a statement that a validator has deemed a candidate valid. | ||
type Valid CandidateHash | ||
|
||
// Index returns the VaryingDataType Index | ||
func (v Valid) Index() uint { | ||
return 2 | ||
} | ||
|
||
// CandidateHash makes it easy to enforce that a hash is a candidate hash on the type level. | ||
type CandidateHash struct { | ||
Value common.Hash `scale:"1"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package parachain | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/ChainSafe/gossamer/lib/common" | ||
"github.com/ChainSafe/gossamer/pkg/scale" | ||
) | ||
|
||
// StatementDistributionMessage represents network messages used by the statement distribution subsystem | ||
type StatementDistributionMessage scale.VaryingDataType | ||
|
||
// NewStatementDistributionMessage returns a new StatementDistributionMessage VaryingDataType | ||
func NewStatementDistributionMessage() StatementDistributionMessage { | ||
vdt := scale.MustNewVaryingDataType(SignedFullStatement{}, SecondedStatementWithLargePayload{}) | ||
return StatementDistributionMessage(vdt) | ||
} | ||
|
||
// Set will set a VaryingDataTypeValue using the underlying VaryingDataType | ||
func (sdm *StatementDistributionMessage) Set(val scale.VaryingDataTypeValue) (err error) { | ||
vdt := scale.VaryingDataType(*sdm) | ||
err = vdt.Set(val) | ||
if err != nil { | ||
return fmt.Errorf("setting value to varying data type: %w", err) | ||
} | ||
|
||
*sdm = StatementDistributionMessage(vdt) | ||
return nil | ||
} | ||
|
||
// Value returns the value from the underlying VaryingDataType | ||
func (sdm *StatementDistributionMessage) Value() (scale.VaryingDataTypeValue, error) { | ||
vdt := scale.VaryingDataType(*sdm) | ||
return vdt.Value() | ||
} | ||
|
||
// SignedFullStatement represents a signed full statement under a given relay-parent. | ||
type SignedFullStatement struct { | ||
Hash common.Hash `scale:"1"` | ||
UncheckedSignedFullStatement UncheckedSignedFullStatement `scale:"2"` | ||
} | ||
|
||
// Index returns the VaryingDataType Index | ||
func (s SignedFullStatement) Index() uint { | ||
return 0 | ||
} | ||
|
||
// Seconded statement with large payload (e.g. containing a runtime upgrade). | ||
// | ||
// We only gossip the hash in that case, actual payloads can be fetched from sending node | ||
// via request/response. | ||
type SecondedStatementWithLargePayload StatementMetadata | ||
|
||
// Index returns the VaryingDataType Index | ||
func (l SecondedStatementWithLargePayload) Index() uint { | ||
return 1 | ||
} | ||
|
||
// UncheckedSignedFullStatement is a Variant of `SignedFullStatement` where the signature has not yet been verified. | ||
type UncheckedSignedFullStatement struct { | ||
// The payload is part of the signed data. The rest is the signing context, | ||
// which is known both at signing and at validation. | ||
Payload Statement `scale:"1"` | ||
|
||
// The index of the validator signing this statement. | ||
ValidatorIndex ValidatorIndex `scale:"2"` | ||
|
||
// The signature by the validator of the signed payload. | ||
Signature ValidatorSignature `scale:"3"` | ||
} | ||
|
||
// StatementMetadata represents the data that makes a statement unique. | ||
type StatementMetadata struct { | ||
// Relay parent this statement is relevant under. | ||
RelayParent common.Hash `scale:"1"` | ||
|
||
// Hash of the candidate that got validated. | ||
CandidateHash CandidateHash `scale:"2"` | ||
|
||
// Validator that attested the validity. | ||
SignedBy ValidatorIndex `scale:"3"` | ||
|
||
// Signature of seconding validator. | ||
Signature ValidatorSignature `scale:"4"` | ||
} | ||
|
||
// ValidatorSignature represents the signature with which parachain validators sign blocks. | ||
type ValidatorSignature Signature | ||
|
||
// Signature represents a cryptographic signature. | ||
type Signature [64]byte |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
package parachain | ||
|
||
import ( | ||
_ "embed" | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/ChainSafe/gossamer/lib/common" | ||
"github.com/ChainSafe/gossamer/pkg/scale" | ||
"github.com/stretchr/testify/require" | ||
"gopkg.in/yaml.v3" | ||
) | ||
|
||
//go:embed testdata/statement_distribution_message.yaml | ||
var testSDMHexRaw string | ||
|
||
var testSDMHex map[string]string | ||
|
||
func init() { | ||
err := yaml.Unmarshal([]byte(testSDMHexRaw), &testSDMHex) | ||
if err != nil { | ||
fmt.Printf("Error unmarshaling test data: %s\n", err) | ||
return | ||
} | ||
} | ||
|
||
func TestStatementDistributionMessage(t *testing.T) { | ||
t.Parallel() | ||
|
||
var collatorSignature CollatorSignature | ||
tempSignature := common.MustHexToBytes(testSDMHex["collatorSignature"]) | ||
copy(collatorSignature[:], tempSignature) | ||
|
||
var validatorSignature ValidatorSignature | ||
copy(validatorSignature[:], tempSignature) | ||
|
||
var collatorID CollatorID | ||
tempCollatID := common.MustHexToBytes("0x48215b9d322601e5b1a95164cea0dc4626f545f98343d07f1551eb9543c4b147") | ||
copy(collatorID[:], tempCollatID) | ||
|
||
hash5 := getDummyHash(5) | ||
|
||
statementWithValid := NewStatement() | ||
err := statementWithValid.Set(Valid{hash5}) | ||
require.NoError(t, err) | ||
|
||
secondedEnumValue := Seconded{ | ||
Descriptor: CandidateDescriptor{ | ||
ParaID: uint32(1), | ||
RelayParent: hash5, | ||
Collator: collatorID, | ||
PersistedValidationDataHash: hash5, | ||
PovHash: hash5, | ||
ErasureRoot: hash5, | ||
Signature: collatorSignature, | ||
ParaHead: hash5, | ||
ValidationCodeHash: ValidationCodeHash(hash5), | ||
}, | ||
Commitments: CandidateCommitments{ | ||
UpwardMessages: []UpwardMessage{{1, 2, 3}}, | ||
HorizontalMessages: []OutboundHrmpMessage{}, | ||
NewValidationCode: &ValidationCode{1, 2, 3}, | ||
HeadData: headData{1, 2, 3}, | ||
ProcessedDownwardMessages: uint32(5), | ||
HrmpWatermark: uint32(0), | ||
}, | ||
} | ||
|
||
statementWithSeconded := NewStatement() | ||
err = statementWithSeconded.Set(secondedEnumValue) | ||
require.NoError(t, err) | ||
|
||
signedFullStatementWithValid := SignedFullStatement{ | ||
Hash: hash5, | ||
UncheckedSignedFullStatement: UncheckedSignedFullStatement{ | ||
Payload: statementWithValid, | ||
ValidatorIndex: ValidatorIndex(5), | ||
Signature: validatorSignature, | ||
}, | ||
} | ||
|
||
signedFullStatementWithSeconded := SignedFullStatement{ | ||
Hash: hash5, | ||
UncheckedSignedFullStatement: UncheckedSignedFullStatement{ | ||
Payload: statementWithSeconded, | ||
ValidatorIndex: ValidatorIndex(5), | ||
Signature: validatorSignature, | ||
}, | ||
} | ||
|
||
secondedStatementWithLargePayload := SecondedStatementWithLargePayload{ | ||
RelayParent: hash5, | ||
CandidateHash: CandidateHash{hash5}, | ||
SignedBy: ValidatorIndex(5), | ||
Signature: validatorSignature, | ||
} | ||
|
||
testCases := []struct { | ||
name string | ||
enumValue scale.VaryingDataTypeValue | ||
encodingValue []byte | ||
}{ | ||
// expected encoding is generated by running rust test code: | ||
// fn statement_distribution_message_encode() { | ||
// let hash1 = Hash::repeat_byte(5); | ||
// let candidate_hash = CandidateHash(hash1); | ||
// let statement_valid = Statement::Valid(candidate_hash); | ||
// let val_sign = ValidatorSignature::from( | ||
// sr25519::Signature([198, 124, 185, 59, 240, 163, 111, 206, 227, | ||
// 210, 157, 232, 166, 166, 154, 117, 150, 89, 104, 10, 207, 72, 100, 117, 224, 162, 85, 42, 95, 190, | ||
// 216, 126, 69, 173, 206, 95, 41, 6, 152, 216, 89, 96, 149, 114, 43, 51, 89, 146, 39, 247, 70, 31, | ||
// 81, 175, 134, 23, 200, 190, 116, 184, 148, 207, 27, 134])); | ||
// let unchecked_signed_full_statement_valid = UncheckedSignedFullStatement::new( | ||
// statement_valid, ValidatorIndex(5), val_sign.clone()); | ||
// let sdm_statement_valid = StatementDistributionMessage::Statement( | ||
// hash1, unchecked_signed_full_statement_valid); | ||
// println!("encode SignedFullStatement with valid statement => {:?}\n\n", sdm_statement_valid.encode()); | ||
|
||
// let collator_result = sr25519::Public::from_string( | ||
// "0x48215b9d322601e5b1a95164cea0dc4626f545f98343d07f1551eb9543c4b147"); | ||
// let collator = collator_result.unwrap(); | ||
// let collsign = CollatorSignature::from(sr25519::Signature( | ||
// [198, 124, 185, 59, 240, 163, 111, 206, 227, 210, 157, 232, | ||
// 166, 166, 154, 117, 150, 89, 104, 10, 207, 72, 100, 117, 224, 162, 85, 42, 95, 190, 216, 126, 69, 173, | ||
// 206, 95, 41, 6, 152, 216, 89, 96, 149, 114, 43, 51, 89, 146, 39, 247, 70, 31, 81, 175, 134, 23, 200, | ||
// 190, 116, 184, 148, 207, 27, 134])); | ||
// let candidate_descriptor = CandidateDescriptor{ | ||
// para_id: 1.into(), | ||
// relay_parent: hash1, | ||
// collator: CollatorId::from(collator), | ||
// persisted_validation_data_hash: hash1, | ||
// pov_hash: hash1, | ||
// erasure_root: hash1, | ||
// signature: collsign, | ||
// para_head: hash1, | ||
// validation_code_hash: ValidationCodeHash::from(hash1) | ||
// }; | ||
// let commitments_new = CandidateCommitments{ | ||
// upward_messages: vec![vec![1, 2, 3]].try_into().expect("error - upward_messages"), | ||
// horizontal_messages: vec![].try_into().expect("error - horizontal_messages"), | ||
// head_data: HeadData(vec![1, 2, 3]), | ||
// hrmp_watermark: 0_u32, | ||
// new_validation_code: ValidationCode(vec![1, 2, 3]).try_into().expect("error - new_validation_code"), | ||
// processed_downward_messages: 5 | ||
// }; | ||
// let committed_candidate_receipt = CommittedCandidateReceipt{ | ||
// descriptor: candidate_descriptor, | ||
// commitments : commitments_new | ||
// }; | ||
// let statement_second = Statement::Seconded(committed_candidate_receipt); | ||
// let unchecked_signed_full_statement_second = UncheckedSignedFullStatement::new( | ||
// statement_second, ValidatorIndex(5), val_sign.clone()); | ||
// let sdm_statement_second = StatementDistributionMessage::Statement( | ||
// hash1, unchecked_signed_full_statement_second); | ||
// println!("encode SignedFullStatement with Seconded statement => {:?}\n\n", sdm_statement_second.encode()); | ||
|
||
// let sdm_large_statement = StatementDistributionMessage::LargeStatement(StatementMetadata{ | ||
// relay_parent: hash1, | ||
// candidate_hash: CandidateHash(hash1), | ||
// signed_by: ValidatorIndex(5_u32), | ||
// signature: val_sign.clone(), | ||
// }); | ||
// println!("encode SecondedStatementWithLargePayload => {:?}\n\n", sdm_large_statement.encode()); | ||
// } | ||
|
||
{ | ||
name: "SignedFullStatement with valid statement", | ||
enumValue: signedFullStatementWithValid, | ||
encodingValue: common.MustHexToBytes(testSDMHex["sfsValid"]), | ||
}, | ||
{ | ||
name: "SignedFullStatement with Seconded statement", | ||
enumValue: signedFullStatementWithSeconded, | ||
encodingValue: common.MustHexToBytes(testSDMHex["sfsSeconded"]), | ||
}, | ||
{ | ||
name: "Seconded Statement With LargePayload", | ||
enumValue: secondedStatementWithLargePayload, | ||
encodingValue: common.MustHexToBytes(testSDMHex["statementWithLargePayload"]), | ||
}, | ||
} | ||
|
||
for _, c := range testCases { | ||
c := c | ||
t.Run(c.name, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
vtd := NewStatementDistributionMessage() | ||
|
||
err := vtd.Set(c.enumValue) | ||
require.NoError(t, err) | ||
|
||
bytes, err := scale.Marshal(vtd) | ||
require.NoError(t, err) | ||
|
||
require.Equal(t, c.encodingValue, bytes) | ||
}) | ||
} | ||
} |
Oops, something went wrong.