From 5e99a1f4b52656fc32f604b017c1ca185937b027 Mon Sep 17 00:00:00 2001 From: Edward Mack Date: Wed, 21 Jun 2023 11:13:48 -0400 Subject: [PATCH] feat(parachain): Create struct for Approval Distribution Message (#3326) --- .../approval_distribution_message.go | 179 ++++++++++++++++ .../approval_distribution_message_test.go | 202 ++++++++++++++++++ 2 files changed, 381 insertions(+) create mode 100644 lib/parachain/approval_distribution_message.go create mode 100644 lib/parachain/approval_distribution_message_test.go diff --git a/lib/parachain/approval_distribution_message.go b/lib/parachain/approval_distribution_message.go new file mode 100644 index 00000000000..ebb6b8ef394 --- /dev/null +++ b/lib/parachain/approval_distribution_message.go @@ -0,0 +1,179 @@ +// Copyright 2021 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package parachain + +import ( + "fmt" + + "github.com/ChainSafe/gossamer/lib/common" + "github.com/ChainSafe/gossamer/lib/crypto/sr25519" + "github.com/ChainSafe/gossamer/pkg/scale" +) + +// AssignmentCertKind different kinds of input or criteria that can prove a validator's assignment +// to check a particular parachain. +type AssignmentCertKind scale.VaryingDataType + +// New will enable scale to create new instance when needed +func (ack AssignmentCertKind) New() AssignmentCertKind { + return NewAssignmentCertKindVDT() +} + +// Set will set VaryingDataTypeValue using undurlying VaryingDataType +func (ack *AssignmentCertKind) Set(val scale.VaryingDataTypeValue) (err error) { + vdt := scale.VaryingDataType(*ack) + err = vdt.Set(val) + if err != nil { + return fmt.Errorf("setting value te varying data type: %w", err) + } + *ack = AssignmentCertKind(vdt) + return nil +} + +// Value returns the value from the underlying VaryingDataType +func (ack *AssignmentCertKind) Value() (scale.VaryingDataTypeValue, error) { + vdt := scale.VaryingDataType(*ack) + return vdt.Value() +} + +// NewAssignmentCertKindVDT constructor for AssignmentCertKind +func NewAssignmentCertKindVDT() AssignmentCertKind { + vdt, err := scale.NewVaryingDataType(NewRelayVRFModulo(), NewVRFDelay()) + if err != nil { + panic(err) + } + return AssignmentCertKind(vdt) +} + +// RelayVRFModulo an assignment story based on the VRF that authorized the relay-chain block where the +// candidate was included combined with a sample number. +type RelayVRFModulo struct { + // Sample the sample number used in this cert. + Sample uint32 +} + +// NewRelayVRFModulo constructor for RelayVRFModulo +func NewRelayVRFModulo() RelayVRFModulo { + return RelayVRFModulo{} +} + +// Index returns varying data type index +func (rvm RelayVRFModulo) Index() uint { + return 0 +} + +// RelayVRFDelay an assignment story based on the VRF that authorized the relay-chain block where the +// candidate was included combined with the index of a particular core. +type RelayVRFDelay struct { + // CoreIndex the unique (during session) index of a core. + CoreIndex uint32 +} + +// NewVRFDelay constructor for RelayVRFDelay +func NewVRFDelay() RelayVRFDelay { + return RelayVRFDelay{} +} + +// Index returns varying data type index +func (rvd RelayVRFDelay) Index() uint { + return 1 +} + +// VrfSignature represents VRF signature, which itself consists of a VRF pre-output and DLEQ proof +type VrfSignature struct { + // Output VRF output + Output [sr25519.VRFOutputLength]byte `scale:"1"` + // Proof VRF proof + Proof [sr25519.VRFProofLength]byte `scale:"2"` +} + +// AssignmentCert is a certification of assignment +type AssignmentCert struct { + // Kind the criterion which is claimed to be met by this cert. + Kind AssignmentCertKind `scale:"1"` + // Vrf the VRF signature showing the criterion is met. + Vrf VrfSignature `scale:"2"` +} + +// IndirectAssignmentCert is an assignment criterion which refers to the candidate under which the assignment is +// relevant by block hash. +type IndirectAssignmentCert struct { + // BlockHash a block hash where the canidate appears. + BlockHash common.Hash `scale:"1"` + // Validator the validator index. + Validator ValidatorIndex `scale:"2"` + // Cert the cert itself. + Cert AssignmentCert `scale:"3"` +} + +// CandidateIndex represents the index of the candidate in the list of candidates fully included as-of the block. +type CandidateIndex uint32 + +// Assignment holds indirect assignment cert and candidate index +type Assignment struct { + IndirectAssignmentCert IndirectAssignmentCert `scale:"1"` + CandidateIndex CandidateIndex `scale:"2"` +} + +// Assignments for candidates in recent, unfinalized blocks. +type Assignments []Assignment + +// Index returns varying data type index +func (a Assignments) Index() uint { + return 0 +} + +// IndirectSignedApprovalVote represents a signed approval vote which references the candidate indirectly via the block. +type IndirectSignedApprovalVote struct { + // BlockHash a block hash where the candidate appears. + BlockHash common.Hash `scale:"1"` + // CandidateIndex the index of the candidate in the list of candidates fully included as-of the block. + CandidateIndex CandidateIndex `scale:"2"` + // ValidatorIndex the validator index. + ValidatorIndex ValidatorIndex `scale:"3"` + // Signature the signature of the validator. + Signature ValidatorSignature `scale:"4"` +} + +// Approvals for candidates in some recent, unfinalized block. +type Approvals []IndirectSignedApprovalVote + +// Index returns varying data type index +func (ap Approvals) Index() uint { + return 1 +} + +// ApprovalDistributionMessage network messages used by approval distribution subsystem. +type ApprovalDistributionMessage scale.VaryingDataType + +// Set will set a VoryingDataTypeValue using the underlying VaryingDataType +func (adm *ApprovalDistributionMessage) Set(val scale.VaryingDataTypeValue) (err error) { + vdt := scale.VaryingDataType(*adm) + err = vdt.Set(val) + if err != nil { + return fmt.Errorf("setting value to varying data type: %w", err) + } + *adm = ApprovalDistributionMessage(vdt) + return nil +} + +// Value returns the value from the underlying VaryingDataType +func (adm *ApprovalDistributionMessage) Value() (scale.VaryingDataTypeValue, error) { + vdt := scale.VaryingDataType(*adm) + return vdt.Value() +} + +// New returns new ApprovalDistributionMessage VDT +func (adm ApprovalDistributionMessage) New() ApprovalDistributionMessage { + return NewApprovalDistributionMessageVDT() +} + +// NewApprovalDistributionMessageVDT ruturns a new ApprovalDistributionMessage VaryingDataType +func NewApprovalDistributionMessageVDT() ApprovalDistributionMessage { + vdt, err := scale.NewVaryingDataType(Assignments{}, Approvals{}) + if err != nil { + panic(err) + } + return ApprovalDistributionMessage(vdt) +} diff --git a/lib/parachain/approval_distribution_message_test.go b/lib/parachain/approval_distribution_message_test.go new file mode 100644 index 00000000000..122af759b01 --- /dev/null +++ b/lib/parachain/approval_distribution_message_test.go @@ -0,0 +1,202 @@ +// Copyright 2021 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + +package parachain + +import ( + "testing" + + "github.com/ChainSafe/gossamer/lib/common" + "github.com/ChainSafe/gossamer/pkg/scale" + "github.com/stretchr/testify/require" +) + +var hash = common.MustHexToHash("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + +func TestEncodeApprovalDistributionMessageAssignmentModulo(t *testing.T) { + approvalDistributionMessage := NewApprovalDistributionMessageVDT() + // expected encoding is generated by running rust test code: + // fn try_msg_assignments_encode() { + // let hash = Hash::repeat_byte(0xAA); + // + // let validator_index = ValidatorIndex(1); + // let cert = fake_assignment_cert(hash, validator_index); + // let assignments = vec![(cert.clone(), 4u32)]; + // let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + // + // let emsg = msg.encode(); + // println!("encode: {:?}", emsg); + //} + expectedEncoding := []byte{0, 4, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, + 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 1, 0, 0, 0, 0, 2, 0, 0, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 4, 0, 0, 0} + + approvalDistributionMessage.Set(Assignments{ + Assignment{ + IndirectAssignmentCert: fakeAssignmentCert(hash, ValidatorIndex(1), false), + CandidateIndex: 4, + }, + }) + + encodedMessage, err := scale.Marshal(approvalDistributionMessage) + require.NoError(t, err) + + require.Equal(t, expectedEncoding, encodedMessage) + + approvalDistributionMessageDecodedTest := NewApprovalDistributionMessageVDT() + scale.Unmarshal(encodedMessage, &approvalDistributionMessageDecodedTest) + require.Equal(t, approvalDistributionMessage, approvalDistributionMessageDecodedTest) +} + +func TestEncodeApprovalDistributionMessageAssignmentDelay(t *testing.T) { + approvalDistributionMessage := NewApprovalDistributionMessageVDT() + + expectedEncoding := []byte{0, 4, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, + 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 2, 0, 0, 0, 1, 1, 0, 0, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 2, 0, 0, 0} + + approvalDistributionMessage.Set(Assignments{ + Assignment{ + IndirectAssignmentCert: fakeAssignmentCert(hash, ValidatorIndex(2), true), + CandidateIndex: 2, + }, + }) + + encodedMessage, err := scale.Marshal(approvalDistributionMessage) + require.NoError(t, err) + + require.Equal(t, expectedEncoding, encodedMessage) +} + +func TestEncodeAssignmentCertKindModulo(t *testing.T) { + assignmentCertKind := NewAssignmentCertKindVDT() + assignmentCertKind.Set(RelayVRFModulo{Sample: 4}) + expectedEncoding := []byte{0, 4, 0, 0, 0} + encodedAssignmentCertKind, err := scale.Marshal(assignmentCertKind) + require.NoError(t, err) + require.Equal(t, expectedEncoding, encodedAssignmentCertKind) + + assignmentCertTest := NewAssignmentCertKindVDT() + err = scale.Unmarshal(encodedAssignmentCertKind, &assignmentCertTest) + require.NoError(t, err) + require.Equal(t, assignmentCertKind, assignmentCertTest) +} + +func TestEncodeAssignmentCertKindDelay(t *testing.T) { + assignmentCertKind := NewAssignmentCertKindVDT() + assignmentCertKind.Set(RelayVRFDelay{CoreIndex: 5}) + expectedEncoding := []byte{1, 5, 0, 0, 0} + encodedAssignmentCertKind, err := scale.Marshal(assignmentCertKind) + require.NoError(t, err) + require.Equal(t, expectedEncoding, encodedAssignmentCertKind) + + assignmentCertTest := NewAssignmentCertKindVDT() + err = scale.Unmarshal(encodedAssignmentCertKind, &assignmentCertTest) + require.NoError(t, err) + require.Equal(t, assignmentCertKind, assignmentCertTest) +} + +func TestEncodeApprovalDistributionMessageApprovals(t *testing.T) { + approvalDistributionMessage := NewApprovalDistributionMessageVDT() + + expectedEncoding := []byte{1, 4, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, + 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 2, 0, 0, 0, 3, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + + approvalDistributionMessage.Set(Approvals{ + IndirectSignedApprovalVote{ + BlockHash: hash, + CandidateIndex: CandidateIndex(2), + ValidatorIndex: ValidatorIndex(3), + Signature: ValidatorSignature{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1}, + }, + }) + + encodedMessage, err := scale.Marshal(approvalDistributionMessage) + require.NoError(t, err) + require.Equal(t, expectedEncoding, encodedMessage) +} + +func TestDecodeApprovalDistributionMessageAssignmentModulo(t *testing.T) { + encoding := []byte{0, 4, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, + 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 2, 0, 0, 0, 0, 2, 0, 0, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 4, 0, 0, 0} + approvalDistributionMessage := NewApprovalDistributionMessageVDT() + err := scale.Unmarshal(encoding, &approvalDistributionMessage) + require.NoError(t, err) + + expectedApprovalDistributionMessage := NewApprovalDistributionMessageVDT() + expectedApprovalDistributionMessage.Set(Assignments{ + Assignment{ + IndirectAssignmentCert: fakeAssignmentCert(hash, ValidatorIndex(2), false), + CandidateIndex: 4, + }, + }) + + approvalValue, err := approvalDistributionMessage.Value() + require.NoError(t, err) + expectedValue, err := expectedApprovalDistributionMessage.Value() + require.NoError(t, err) + require.Equal(t, expectedValue, approvalValue) +} + +func TestDecodeApprovalDistributionMessageApprovals(t *testing.T) { + encoding := []byte{1, 4, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, + 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 2, 0, 0, 0, 3, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + expectedApprovalDistributionMessage := NewApprovalDistributionMessageVDT() + expectedApprovalDistributionMessage.Set(Approvals{ + IndirectSignedApprovalVote{ + BlockHash: hash, + CandidateIndex: CandidateIndex(2), + ValidatorIndex: ValidatorIndex(3), + Signature: ValidatorSignature{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1}, + }, + }) + + approvalDistributionMessage := NewApprovalDistributionMessageVDT() + err := scale.Unmarshal(encoding, &approvalDistributionMessage) + require.NoError(t, err) + require.Equal(t, expectedApprovalDistributionMessage, approvalDistributionMessage) +} + +func fakeAssignmentCert(blockHash common.Hash, validator ValidatorIndex, useDelay bool) IndirectAssignmentCert { + output := [32]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32} + proof := [64]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64} + assignmentCertKind := NewAssignmentCertKindVDT() + if useDelay { + assignmentCertKind.Set(RelayVRFDelay{CoreIndex: 1}) + } else { + assignmentCertKind.Set(RelayVRFModulo{Sample: 2}) + } + + return IndirectAssignmentCert{ + BlockHash: blockHash, + Validator: validator, + Cert: AssignmentCert{ + Kind: assignmentCertKind, + Vrf: VrfSignature{ + Output: output, + Proof: proof, + }, + }, + } +}