Skip to content

Commit

Permalink
feat(parachain): Add StatementDistributionMessage varingDataType (#3316)
Browse files Browse the repository at this point in the history
  • Loading branch information
axaysagathiya authored and kishansagathiya committed Aug 22, 2023
1 parent 0411eda commit 42256b3
Show file tree
Hide file tree
Showing 6 changed files with 448 additions and 1 deletion.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ require (
golang.org/x/exp v0.0.0-20230810033253-352e893a4cad
golang.org/x/term v0.11.0
google.golang.org/protobuf v1.31.0
gopkg.in/yaml.v3 v3.0.1
)

require (
Expand Down Expand Up @@ -198,7 +199,6 @@ require (
gonum.org/v1/gonum v0.13.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/blake3 v1.2.1 // indirect
nhooyr.io/websocket v1.8.7 // indirect
)
Expand Down
56 changes: 56 additions & 0 deletions lib/parachain/statement.go
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"`
}
91 changes: 91 additions & 0 deletions lib/parachain/statement_distribution_message.go
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
199 changes: 199 additions & 0 deletions lib/parachain/statement_distribution_message_test.go
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)
})
}
}
Loading

0 comments on commit 42256b3

Please sign in to comment.