Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

client(consensus/grandpa): implement finality proof logic #3589

Merged
merged 18 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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

// 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
}

// Value will return value from underying VaryingDataType
func (asc *authoritySetChangeID) Value() (val scale.VaryingDataTypeValue, err error) {
vdt := scale.VaryingDataType(*asc)
return vdt.Value()
type authoritySetChangeID interface {
jimjbrettj marked this conversation as resolved.
Show resolved Hide resolved
isAuthoritySetChangeID()
}

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

type latest struct{}
type authoritySetChangeIDLatest struct{}

func (latest) Index() uint {
return 0
}
func (authoritySetChangeIDLatest) isAuthoritySetChangeID() {}

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

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

type unknown struct{}
type authoritySetChangeIDUnknown struct{}

func (unknown) Index() uint {
return 2
}
func (authoritySetChangeIDUnknown) isAuthoritySetChangeID() {}

// 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) {
jimjbrettj marked this conversation as resolved.
Show resolved Hide resolved
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 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 authoritySetChangeIDUnknown{}, nil
}
err = authSetChangeID.Set(set[N]{

return 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 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()
jimjbrettj marked this conversation as resolved.
Show resolved Hide resolved
}
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
58 changes: 28 additions & 30 deletions client/consensus/grandpa/aux_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import (
"errors"
"fmt"

"github.com/ChainSafe/gossamer/client/api"
grandpa "github.com/ChainSafe/gossamer/pkg/finality-grandpa"
finalityGrandpa "github.com/ChainSafe/gossamer/pkg/finality-grandpa"
jimjbrettj marked this conversation as resolved.
Show resolved Hide resolved
"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,12 +49,12 @@ 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) {
genesis := grandpa.HashNumber[H, N]{Hash: genesisHash, Number: genesisNumber}
makeGenesisRound := grandpa.NewRoundState[H, N]
genesis := finalityGrandpa.HashNumber[H, N]{Hash: genesisHash, Number: genesisNumber}
makeGenesisRound := finalityGrandpa.NewRoundState[H, N]

authSet := &AuthoritySet[H, N, ID]{}
err := loadDecoded(store, authoritySetKey, authSet)
Expand Down Expand Up @@ -105,7 +104,7 @@ func loadPersistent[H comparable, N constraints.Unsigned, ID AuthorityID, Sig Au
return nil, err
}

state := grandpa.NewRoundState(grandpa.HashNumber[H, N]{Hash: genesisHash, Number: genesisNumber})
state := finalityGrandpa.NewRoundState(finalityGrandpa.HashNumber[H, N]{Hash: genesisHash, Number: genesisNumber})
base := state.PrevoteGHOST
if base == nil {
panic("state is for completed round; completed rounds must have a prevote ghost; qed.")
Expand All @@ -116,9 +115,9 @@ 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)
Expand All @@ -145,7 +144,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 @@ -155,7 +153,7 @@ func UpdateAuthoritySet[H comparable, N constraints.Unsigned, ID AuthorityID, Si
// we also overwrite the "last completed round" entry with a blank slate
// because from the perspective of the finality gadget, the chain has
// reset.
genesisState := grandpa.HashNumber[H, N]{
genesisState := finalityGrandpa.HashNumber[H, N]{
Hash: newSet.CanonHash,
Number: newSet.CanonNumber,
}
Expand All @@ -169,18 +167,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 +199,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 +223,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 +242,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 +269,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
Loading