Skip to content

Commit

Permalink
feat(parachain): Create struct for Approval Distribution Message (#3326)
Browse files Browse the repository at this point in the history
  • Loading branch information
edwardmack authored and kishansagathiya committed Jul 12, 2023
1 parent cf5a9a0 commit 5e99a1f
Show file tree
Hide file tree
Showing 2 changed files with 381 additions and 0 deletions.
179 changes: 179 additions & 0 deletions lib/parachain/approval_distribution_message.go
Original file line number Diff line number Diff line change
@@ -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)
}
202 changes: 202 additions & 0 deletions lib/parachain/approval_distribution_message_test.go
Original file line number Diff line number Diff line change
@@ -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,
},
},
}
}

0 comments on commit 5e99a1f

Please sign in to comment.