Skip to content

Commit

Permalink
client(consensus/grandpa): implement finality proof logic (#3589)
Browse files Browse the repository at this point in the history
Co-authored-by: Timothy Wu <tim.wu@chainsafe.io>
  • Loading branch information
jimjbrettj and timwu20 authored Dec 13, 2023
1 parent 87df026 commit 8f28c20
Show file tree
Hide file tree
Showing 16 changed files with 1,813 additions and 247 deletions.
26 changes: 0 additions & 26 deletions client/api/backend.go

This file was deleted.

75 changes: 16 additions & 59 deletions client/consensus/grandpa/authorities.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"fmt"
"sync"

"github.com/ChainSafe/gossamer/pkg/scale"
"golang.org/x/exp/constraints"
"golang.org/x/exp/slices"
)
Expand Down Expand Up @@ -705,64 +704,33 @@ func (asc *AuthoritySetChanges[N]) append(setID uint64, blockNumber N) {
})
}

type authoritySetChangeID scale.VaryingDataType
type authoritySetChangeID any

// Set will set a VaryingDataTypeValue using the underlying VaryingDataType
func (asc *authoritySetChangeID) Set(val scale.VaryingDataTypeValue) (err error) {
vdt := scale.VaryingDataType(*asc)
err = vdt.Set(val)
if err != nil {
return
}
*asc = authoritySetChangeID(vdt)
return
type authoritySetChangeIDs[N constraints.Unsigned] interface {
authoritySetChangeIDLatest | authoritySetChangeIDSet[N] | authoritySetChangeIDUnknown
}

// Value will return value from underying VaryingDataType
func (asc *authoritySetChangeID) Value() (val scale.VaryingDataTypeValue, err error) {
vdt := scale.VaryingDataType(*asc)
return vdt.Value()
func newAuthoritySetID[N constraints.Unsigned, ID authoritySetChangeIDs[N]](authSetChangeID ID) authoritySetChangeID {
return authoritySetChangeID(authSetChangeID)
}

func newAuthoritySetChangeID[N constraints.Unsigned]() authoritySetChangeID {
vdt := scale.MustNewVaryingDataType(latest{}, set[N]{}, unknown{})
return authoritySetChangeID(vdt)
}

type latest struct{}

func (latest) Index() uint {
return 0
}
type authoritySetChangeIDLatest struct{}

type set[N constraints.Unsigned] struct {
type authoritySetChangeIDSet[N constraints.Unsigned] struct {
inner setIDNumber[N]
}

func (set[N]) Index() uint {
return 1
}

type unknown struct{}

func (unknown) Index() uint {
return 2
}
type authoritySetChangeIDUnknown struct{}

// Three states that can be returned: Latest, Set (tuple), Unknown
func (asc *AuthoritySetChanges[N]) getSetID(blockNumber N) (authSetChangeID authoritySetChangeID, err error) {
func (asc *AuthoritySetChanges[N]) getSetID(blockNumber N) (authoritySetChangeID, error) {
if asc == nil {
return authSetChangeID, fmt.Errorf("getSetID: authSetChanges is nil")
return nil, fmt.Errorf("getSetID: authSetChanges is nil")
}
authSetChangeID = newAuthoritySetChangeID[N]()
authSet := *asc
last := authSet[len(authSet)-1]
if last.BlockNumber < blockNumber {
err = authSetChangeID.Set(latest{})
if err != nil {
return authSetChangeID, err
}
return authSetChangeID, nil
return newAuthoritySetID[N](authoritySetChangeIDLatest{}), nil
}

idx, _ := slices.BinarySearchFunc(
Expand All @@ -786,26 +754,15 @@ func (asc *AuthoritySetChanges[N]) getSetID(blockNumber N) (authSetChangeID auth

// if this is the first index but not the first set id then we are missing data.
if idx == 0 && authChange.SetID != 0 {
err = authSetChangeID.Set(unknown{})
if err != nil {
return authSetChangeID, err
}
return authSetChangeID, nil
return newAuthoritySetID[N](authoritySetChangeIDUnknown{}), nil
}
err = authSetChangeID.Set(set[N]{

return newAuthoritySetID[N](authoritySetChangeIDSet[N]{
authChange,
})
if err != nil {
return authSetChangeID, err
}
return authSetChangeID, nil
}), nil
}

err = authSetChangeID.Set(unknown{})
if err != nil {
return authSetChangeID, err
}
return authSetChangeID, nil
return newAuthoritySetID[N](authoritySetChangeIDUnknown{}), nil
}

func (asc *AuthoritySetChanges[N]) insert(blockNumber N) {
Expand Down
22 changes: 7 additions & 15 deletions client/consensus/grandpa/authorities_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
package grandpa

import (
"fmt"
"strings"
"testing"

Expand Down Expand Up @@ -1205,36 +1204,29 @@ func TestCleanUpStaleForcedChangesWhenApplyingStandardChangeAlternateCase(t *tes

func assertExpectedSet(t *testing.T, authSetID authoritySetChangeID, expected setIDNumber[uint]) {
t.Helper()
authSetVal, err := authSetID.Value()
require.NoError(t, err)
switch val := authSetVal.(type) {
case set[uint]:
switch val := authSetID.(type) {
case authoritySetChangeIDSet[uint]:
require.Equal(t, expected, val.inner)
default:
err = fmt.Errorf("invalid authSetID type")
t.FailNow()
}
require.NoError(t, err)
}

func assertUnknown(t *testing.T, authSetID authoritySetChangeID) {
t.Helper()
authSetVal, err := authSetID.Value()
require.NoError(t, err)
isUnknown := false
switch authSetVal.(type) {
case unknown:
switch authSetID.(type) {
case authoritySetChangeIDUnknown:
isUnknown = true
}
require.True(t, isUnknown)
}

func assertLatest(t *testing.T, authSetID authoritySetChangeID) {
t.Helper()
authSetVal, err := authSetID.Value()
require.NoError(t, err)
isLatest := false
switch authSetVal.(type) {
case latest:
switch authSetID.(type) {
case authoritySetChangeIDLatest:
isLatest = true
}
require.True(t, isLatest)
Expand Down
64 changes: 29 additions & 35 deletions client/consensus/grandpa/aux_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"errors"
"fmt"

"github.com/ChainSafe/gossamer/client/api"
grandpa "github.com/ChainSafe/gossamer/pkg/finality-grandpa"
"github.com/ChainSafe/gossamer/pkg/scale"
"golang.org/x/exp/constraints"
Expand All @@ -22,7 +21,7 @@ var (
errValueNotFound = errors.New("value not found")
)

type writeAux func(insertions []api.KeyValue) error
type writeAux func(insertions []KeyValue) error

type getGenesisAuthorities[ID AuthorityID] func() ([]Authority[ID], error)

Expand All @@ -31,7 +30,7 @@ type persistentData[H comparable, N constraints.Unsigned, ID AuthorityID, Sig Au
setState SharedVoterSetState[H, N, ID, Sig]
}

func loadDecoded(store api.AuxStore, key []byte, destination any) error {
func loadDecoded(store AuxStore, key []byte, destination any) error {
encodedValue, err := store.Get(key)
if err != nil {
return err
Expand All @@ -50,7 +49,7 @@ func loadDecoded(store api.AuxStore, key []byte, destination any) error {
}

func loadPersistent[H comparable, N constraints.Unsigned, ID AuthorityID, Sig AuthoritySignature](
store api.AuxStore,
store AuxStore,
genesisHash H,
genesisNumber N,
genesisAuths getGenesisAuthorities[ID]) (*persistentData[H, N, ID, Sig], error) {
Expand Down Expand Up @@ -85,13 +84,11 @@ func loadPersistent[H comparable, N constraints.Unsigned, ID AuthorityID, Sig Au
}
}

newSharedVoterSetState := sharedVoterSetState[H, N, ID, Sig]{
Inner: setState,
}

return &persistentData[H, N, ID, Sig]{
authoritySet: SharedAuthoritySet[H, N, ID]{inner: *authSet},
setState: SharedVoterSetState[H, N, ID, Sig]{Inner: newSharedVoterSetState}, //nolint
setState: SharedVoterSetState[H, N, ID, Sig]{Inner: sharedVoterSetState[H, N, ID, Sig]{
Inner: setState,
}},
}, nil
}

Expand All @@ -116,23 +113,21 @@ func loadPersistent[H comparable, N constraints.Unsigned, ID AuthorityID, Sig Au
return nil, err
}

insert := []api.KeyValue{
{authoritySetKey, scale.MustMarshal(*genesisSet)}, //nolint
{setStateKey, scale.MustMarshal(genesisState)}, //nolint
insert := []KeyValue{
{authoritySetKey, scale.MustMarshal(*genesisSet)},
{setStateKey, scale.MustMarshal(genesisState)},
}

err = store.Insert(insert, nil)
if err != nil {
return nil, err
}

newSharedVoterSetState := sharedVoterSetState[H, N, ID, Sig]{
Inner: genesisState,
}

return &persistentData[H, N, ID, Sig]{
authoritySet: SharedAuthoritySet[H, N, ID]{inner: *genesisSet},
setState: SharedVoterSetState[H, N, ID, Sig]{Inner: newSharedVoterSetState}, //nolint
setState: SharedVoterSetState[H, N, ID, Sig]{Inner: sharedVoterSetState[H, N, ID, Sig]{
Inner: genesisState,
}},
}, nil
}

Expand All @@ -145,7 +140,6 @@ func UpdateAuthoritySet[H comparable, N constraints.Unsigned, ID AuthorityID, Si
set AuthoritySet[H, N, ID],
newSet *NewAuthoritySetStruct[H, N, ID],
write writeAux) error {
// TODO make sure that Insert has affect of both insert and update depending on use case
encodedAuthSet, err := scale.Marshal(set)
if err != nil {
return err
Expand All @@ -169,18 +163,18 @@ func UpdateAuthoritySet[H comparable, N constraints.Unsigned, ID AuthorityID, Si
return err
}

insert := []api.KeyValue{
{authoritySetKey, encodedAuthSet}, //nolint
{setStateKey, encodedVoterSet}, //nolint
insert := []KeyValue{
{authoritySetKey, encodedAuthSet},
{setStateKey, encodedVoterSet},
}
err = write(insert)
if err != nil {
return err
}

} else {
insert := []api.KeyValue{
{authoritySetKey, encodedAuthSet}, //nolint
insert := []KeyValue{
{authoritySetKey, encodedAuthSet},
}

err = write(insert)
Expand All @@ -201,16 +195,16 @@ func updateBestJustification[
N constraints.Unsigned,
S comparable,
ID AuthorityID,
H Header[Hash, N]](
justification Justification[Hash, N, S, ID, H],
](
justification GrandpaJustification[Hash, N, S, ID],
write writeAux) error {
encodedJustificaiton, err := scale.Marshal(justification)
if err != nil {
return fmt.Errorf("marshalling: %w", err)
}

insert := []api.KeyValue{
{bestJustification, encodedJustificaiton}, //nolint
insert := []KeyValue{
{bestJustification, encodedJustificaiton},
}
err = write(insert)
if err != nil {
Expand All @@ -225,15 +219,15 @@ func BestJustification[
N constraints.Unsigned,
S comparable,
ID AuthorityID,
H Header[Hash, N]](
store api.AuxStore) (*Justification[Hash, N, S, ID, H], error) {
justification := Justification[Hash, N, S, ID, H]{}
H Header[Hash, N],
](store AuxStore) (*GrandpaJustification[Hash, N, S, ID], error) {
justification := decodeGrandpaJustification[Hash, N, S, ID, H]{}
err := loadDecoded(store, bestJustification, &justification)
if err != nil {
return nil, err
}

return &justification, nil
return justification.GrandpaJustification(), nil
}

// WriteVoterSetState Write voter set state.
Expand All @@ -244,8 +238,8 @@ func WriteVoterSetState[H comparable, N constraints.Unsigned, ID AuthorityID, Si
if err != nil {
return err
}
insert := []api.KeyValue{
{setStateKey, encodedVoterSet}, //nolint
insert := []KeyValue{
{setStateKey, encodedVoterSet},
}
err = write(insert)
if err != nil {
Expand All @@ -271,8 +265,8 @@ func WriteConcludedRound[H comparable, N constraints.Unsigned, ID AuthorityID, S
return err
}

insert := []api.KeyValue{
{key, encRoundData}, //nolint
insert := []KeyValue{
{key, encRoundData},
}
err = write(insert)
if err != nil {
Expand Down
Loading

0 comments on commit 8f28c20

Please sign in to comment.