Skip to content

Commit

Permalink
Implement parachain inherents (#2566)
Browse files Browse the repository at this point in the history
- Added support for parachain inherent and newheads
- Improved inherentsData's encode function
- Made inherent identifers enums
- Common setInherent function
  • Loading branch information
kishansagathiya committed Oct 7, 2022
1 parent 139ad89 commit 14dda74
Show file tree
Hide file tree
Showing 16 changed files with 849 additions and 94 deletions.
6 changes: 3 additions & 3 deletions dot/sync/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ func BuildBlock(t *testing.T, instance runtime.Instance, parent *types.Header, e
err = instance.InitializeBlock(header)
require.NoError(t, err)

idata := types.NewInherentsData()
err = idata.SetInt64Inherent(types.Timstap0, uint64(time.Now().Unix()))
idata := types.NewInherentData()
err = idata.SetInherent(types.Timstap0, uint64(time.Now().Unix()))
require.NoError(t, err)

err = idata.SetInt64Inherent(types.Babeslot, 1)
err = idata.SetInherent(types.Babeslot, uint64(1))
require.NoError(t, err)

ienc, err := idata.Encode()
Expand Down
6 changes: 3 additions & 3 deletions dot/types/babe_digest.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (d *BabePrimaryPreDigest) ToPreRuntimeDigest() (*PreRuntimeDigest, error) {
return toPreRuntimeDigest(*d)
}

// Index Returns VDT index
// Index returns VDT index
func (BabePrimaryPreDigest) Index() uint { return 1 }

// BabeSecondaryPlainPreDigest is included in a block built by a secondary slot authorized producer
Expand All @@ -83,7 +83,7 @@ func (d *BabeSecondaryPlainPreDigest) ToPreRuntimeDigest() (*PreRuntimeDigest, e
return toPreRuntimeDigest(*d)
}

// Index Returns VDT index
// Index returns VDT index
func (BabeSecondaryPlainPreDigest) Index() uint { return 2 }

// BabeSecondaryVRFPreDigest is included in a block built by a secondary slot authorized producer
Expand Down Expand Up @@ -111,7 +111,7 @@ func (d *BabeSecondaryVRFPreDigest) ToPreRuntimeDigest() (*PreRuntimeDigest, err
return toPreRuntimeDigest(*d)
}

// Index Returns VDT index
// Index returns VDT index
func (BabeSecondaryVRFPreDigest) Index() uint { return 3 }

// toPreRuntimeDigest returns the VaryingDataTypeValue as a PreRuntimeDigest
Expand Down
16 changes: 8 additions & 8 deletions dot/types/consensus_digest.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type GrandpaScheduledChange struct {
Delay uint32
}

// Index Returns VDT index
// Index returns VDT index
func (GrandpaScheduledChange) Index() uint { return 1 }

// GrandpaForcedChange represents a GRANDPA forced authority change
Expand All @@ -39,31 +39,31 @@ type GrandpaForcedChange struct {
Delay uint32
}

// Index Returns VDT index
// Index returns VDT index
func (GrandpaForcedChange) Index() uint { return 2 }

// GrandpaOnDisabled represents a GRANDPA authority being disabled
type GrandpaOnDisabled struct {
ID uint64
}

// Index Returns VDT index
// Index returns VDT index
func (GrandpaOnDisabled) Index() uint { return 3 }

// GrandpaPause represents an authority set pause
type GrandpaPause struct {
Delay uint32
}

// Index Returns VDT index
// Index returns VDT index
func (GrandpaPause) Index() uint { return 4 }

// GrandpaResume represents an authority set resume
type GrandpaResume struct {
Delay uint32
}

// Index Returns VDT index
// Index returns VDT index
func (GrandpaResume) Index() uint { return 5 }

// NextEpochData is the digest that contains the data for the upcoming BABE epoch.
Expand All @@ -73,7 +73,7 @@ type NextEpochData struct {
Randomness [RandomnessLength]byte
}

// Index Returns VDT index
// Index returns VDT index
func (NextEpochData) Index() uint { return 1 }

func (d NextEpochData) String() string {
Expand All @@ -98,7 +98,7 @@ type BABEOnDisabled struct {
ID uint32
}

// Index Returns VDT index
// Index returns VDT index
func (BABEOnDisabled) Index() uint { return 2 }

// NextConfigData is the digest that contains changes to the BABE configuration.
Expand All @@ -109,7 +109,7 @@ type NextConfigData struct {
SecondarySlots byte
}

// Index Returns VDT index
// Index returns VDT index
func (NextConfigData) Index() uint { return 3 }

// ToConfigData returns the NextConfigData as ConfigData
Expand Down
8 changes: 4 additions & 4 deletions dot/types/digest.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type ChangesTrieRootDigest struct {
Hash common.Hash
}

// Index Returns VDT index
// Index returns VDT index
func (ChangesTrieRootDigest) Index() uint { return 2 }

// String returns the digest as a string
Expand All @@ -58,7 +58,7 @@ type PreRuntimeDigest struct {
Data []byte
}

// Index Returns VDT index
// Index returns VDT index
func (PreRuntimeDigest) Index() uint { return 6 }

// NewBABEPreRuntimeDigest returns a PreRuntimeDigest with the BABE consensus ID
Expand All @@ -80,7 +80,7 @@ type ConsensusDigest struct {
Data []byte
}

// Index Returns VDT index
// Index returns VDT index
func (ConsensusDigest) Index() uint { return 4 }

// String returns the digest as a string
Expand All @@ -94,7 +94,7 @@ type SealDigest struct {
Data []byte
}

// Index Returns VDT index
// Index returns VDT index
func (SealDigest) Index() uint { return 5 }

// String returns the digest as a string
Expand Down
108 changes: 72 additions & 36 deletions dot/types/inherents.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,68 +5,87 @@ package types

import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"math/big"
"sort"

"github.com/ChainSafe/gossamer/pkg/scale"
)

var (
// Timstap0 is an inherent key.
Timstap0 = []byte("timstap0")
// Babeslot is an inherent key.
Babeslot = []byte("babeslot")
// Uncles00 is an inherent key.
Uncles00 = []byte("uncles00")
// InherentIdentifier is an identifier for an inherent.
type InherentIdentifier uint

const (
// Timstap0 is the identifier for the `timestamp` inherent.
Timstap0 InherentIdentifier = iota
// Babeslot is the BABE inherent identifier.
Babeslot
// Uncles00 is the identifier for the `uncles` inherent.
Uncles00
// Parachn0 is an inherent key for parachains inherent.
Parachn0
// Newheads is an inherent key for new minimally-attested parachain heads.
Newheads
)

// InherentsData contains a mapping of inherent keys to values
// Bytes returns a byte array of given inherent identifier.
func (ii InherentIdentifier) Bytes() [8]byte {

kb := [8]byte{}
switch ii {
case Timstap0:
copy(kb[:], []byte("timstap0"))
case Babeslot:
copy(kb[:], []byte("babeslot"))
case Uncles00:
copy(kb[:], []byte("uncles00"))
case Parachn0:
copy(kb[:], []byte("parachn0"))
case Newheads:
copy(kb[:], []byte("newheads"))
default:
panic("invalid inherent identifier")
}

return kb
}

// InherentData contains a mapping of inherent keys to values
// keys must be 8 bytes, values are a scale-encoded byte array
type InherentsData struct {
data map[[8]byte]([]byte)
type InherentData struct {
Data map[[8]byte][]byte
}

// NewInherentsData returns InherentsData
func NewInherentsData() *InherentsData {
return &InherentsData{
data: make(map[[8]byte]([]byte)),
// NewInherentData returns InherentData
func NewInherentData() *InherentData {
return &InherentData{
Data: make(map[[8]byte][]byte),
}
}

func (d *InherentsData) String() string {
func (d *InherentData) String() string {
str := ""
for k, v := range d.data {
for k, v := range d.Data {
str = str + fmt.Sprintf("key=%v\tvalue=%v\n", k, v)
}
return str
}

// SetInt64Inherent set an inherent of type uint64
func (d *InherentsData) SetInt64Inherent(key []byte, data uint64) error {
if len(key) != 8 {
return errors.New("inherent key must be 8 bytes")
}

val := make([]byte, 8)
binary.LittleEndian.PutUint64(val, data)

venc, err := scale.Marshal(val)
// SetInherent sets a inherent.
func (d *InherentData) SetInherent(inherentIdentifier InherentIdentifier, value any) error {
data, err := scale.Marshal(value)
if err != nil {
return err
}

kb := [8]byte{}
copy(kb[:], key)
d.Data[inherentIdentifier.Bytes()] = data

d.data[kb] = venc
return nil
}

// Encode will encode a given []byte using scale.Encode
func (d *InherentsData) Encode() ([]byte, error) {
length := big.NewInt(int64(len(d.data)))
func (d *InherentData) Encode() ([]byte, error) {
length := big.NewInt(int64(len(d.Data)))
buffer := bytes.Buffer{}

l, err := scale.Marshal(length)
Expand All @@ -79,15 +98,32 @@ func (d *InherentsData) Encode() ([]byte, error) {
return nil, err
}

for k, v := range d.data {
_, err = buffer.Write(k[:])
keys := [][8]byte{}
for key := range d.Data {
keys = append(keys, key)
}

sort.Slice(keys, func(i, j int) bool {
return bytes.Compare(keys[i][:], keys[j][:]) < 0
})

for _, key := range keys {
v := d.Data[key]

_, err = buffer.Write(key[:])
if err != nil {
return nil, err
}
_, err = buffer.Write(v)

venc, err := scale.Marshal(v)
if err != nil {
return nil, fmt.Errorf("scale encoding encoded value: %w", err)
}
_, err = buffer.Write(venc)
if err != nil {
return nil, err
}
}

return buffer.Bytes(), nil
}
45 changes: 45 additions & 0 deletions dot/types/inherents_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2022 ChainSafe Systems (ON)
// SPDX-License-Identifier: LGPL-3.0-only

package types

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestInherentDataMarshal(t *testing.T) {
tests := []struct {
name string
getInherentData func(t *testing.T) *InherentData
want []byte
}{
{
/*
let mut data = InherentData::new();
let timestamp: u64 = 99;
data.put_data(*b"babeslot", &timestamp).unwrap();
data.put_data(*b"timstap0", &timestamp).unwrap();
*/
getInherentData: func(t *testing.T) *InherentData {
id := NewInherentData()
err := id.SetInherent(Babeslot, uint64(99))
require.NoError(t, err)

err = id.SetInherent(Timstap0, uint64(99))
require.NoError(t, err)
return id
},
want: []byte{8, 98, 97, 98, 101, 115, 108, 111, 116, 32, 99, 0, 0, 0, 0, 0, 0, 0, 116, 105, 109, 115, 116, 97, 112, 48, 32, 99, 0, 0, 0, 0, 0, 0, 0}, //nolint:lll
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
idata := tt.getInherentData(t)
got, err := idata.Encode()
require.NoError(t, err)
require.Equal(t, tt.want, got)
})
}
}

0 comments on commit 14dda74

Please sign in to comment.