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

fix(lib/grandpa): on verifying block justification, compare given block hash with finalised hash #3081

Merged
merged 42 commits into from
Feb 10, 2023
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
a86dcb3
Fixing descendant search
kishansagathiya Jan 23, 2023
b20a8e2
fix lint
kishansagathiya Jan 23, 2023
9e7be4d
parent -> hash
kishansagathiya Jan 23, 2023
d0aa5f8
different approach for getting descendants
kishansagathiya Jan 24, 2023
32db55a
different approach for getting descendants
kishansagathiya Jan 25, 2023
532db0e
Merge branch 'development' into kishan/fix/syncing
kishansagathiya Jan 25, 2023
0f76155
fixed the use of bs.GetAllDescendants
kishansagathiya Jan 26, 2023
b7f0a8a
Merge branch 'development' into kishan/fix/syncing
kishansagathiya Jan 26, 2023
03f1ecb
temp
kishansagathiya Jan 30, 2023
3529a36
temp
kishansagathiya Jan 30, 2023
66fb9d1
Merge branch 'kishan/fix/syncing' into kishan/fix/getting-runtime-ins…
kishansagathiya Jan 30, 2023
57f5eb8
temo
kishansagathiya Jan 30, 2023
1aa8c74
Merge branch 'kishan/fix/syncing' into kishan/fix/getting-runtime-ins…
kishansagathiya Jan 30, 2023
90a2992
check hash on verifying block justification
kishansagathiya Jan 30, 2023
a8d2af7
some cleanup
kishansagathiya Jan 30, 2023
7d775fa
more clean up
kishansagathiya Jan 30, 2023
0f625fd
more cleanup
kishansagathiya Jan 30, 2023
d0ce09a
more cleanup
kishansagathiya Jan 30, 2023
533a1cf
Merge branch 'development' into kishan/fix/getting-runtime-instance
kishansagathiya Jan 30, 2023
75fc709
clean up
kishansagathiya Jan 31, 2023
b7490a9
Merge branch 'kishan/fix/getting-runtime-instance' of github.com:Chai…
kishansagathiya Jan 31, 2023
664136c
use get finalised hash
kishansagathiya Feb 2, 2023
94af433
added test
kishansagathiya Feb 2, 2023
919b1ce
Merge branch 'development' into kishan/fix/getting-runtime-instance
kishansagathiya Feb 2, 2023
1c9d9e2
fix lint
kishansagathiya Feb 2, 2023
6017fdd
Merge branch 'development' into kishan/fix/getting-runtime-instance
kishansagathiya Feb 2, 2023
fb0b5b3
remove returning justification from VerifyJustification
kishansagathiya Feb 3, 2023
5577798
Merge branch 'development' into kishan/fix/getting-runtime-instance
kishansagathiya Feb 3, 2023
fb5c8b7
fixing a few more things
kishansagathiya Feb 3, 2023
99c9d4b
Merge branch 'kishan/fix/getting-runtime-instance' of github.com:Chai…
kishansagathiya Feb 3, 2023
df39d57
Merge branch 'development' into kishan/fix/getting-runtime-instance
kishansagathiya Feb 6, 2023
25134e5
fixing a test
kishansagathiya Feb 6, 2023
df0f311
no need to continue in case of storedFinalisedHash == hash
kishansagathiya Feb 7, 2023
9df1a6b
Merge branch 'development' into kishan/fix/getting-runtime-instance
kishansagathiya Feb 7, 2023
feb0d43
Merge branch 'development' into kishan/fix/getting-runtime-instance
kishansagathiya Feb 8, 2023
94befd7
Merge branch 'development' into kishan/fix/getting-runtime-instance
kishansagathiya Feb 8, 2023
3c10fe3
fix tests, removed some t.Parallel
kishansagathiya Feb 9, 2023
c1daf15
Merge branch 'kishan/fix/getting-runtime-instance' of github.com:Chai…
kishansagathiya Feb 9, 2023
ca43b2a
Merge branch 'development' into kishan/fix/getting-runtime-instance
kishansagathiya Feb 9, 2023
c4e9108
fix lint`
kishansagathiya Feb 9, 2023
e11ba97
try without t.Parallel
kishansagathiya Feb 9, 2023
aad958b
Merge branch 'development' into kishan/fix/getting-runtime-instance
kishansagathiya Feb 10, 2023
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
2 changes: 1 addition & 1 deletion dot/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type ServiceRegisterer interface {

// BlockJustificationVerifier has a verification method for block justifications.
type BlockJustificationVerifier interface {
VerifyBlockJustification(common.Hash, []byte) ([]byte, error)
VerifyBlockJustification(common.Hash, []byte) error
}

// Telemetry is the telemetry client to send telemetry messages.
Expand Down
4 changes: 2 additions & 2 deletions dot/sync/chain_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,12 +284,12 @@ func (s *chainProcessor) handleJustification(header *types.Header, justification
logger.Debugf("handling justification for block %d...", header.Number)

headerHash := header.Hash()
returnedJustification, err := s.finalityGadget.VerifyBlockJustification(headerHash, justification)
err = s.finalityGadget.VerifyBlockJustification(headerHash, justification)
if err != nil {
return fmt.Errorf("verifying block number %d justification: %w", header.Number, err)
}

err = s.blockState.SetJustification(headerHash, returnedJustification)
err = s.blockState.SetJustification(headerHash, justification)
if err != nil {
return fmt.Errorf("setting justification for block number %d: %w", header.Number, err)
}
Expand Down
14 changes: 7 additions & 7 deletions dot/sync/chain_processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ func Test_chainProcessor_handleJustification(t *testing.T) {
chainProcessorBuilder: func(ctrl *gomock.Controller) chainProcessor {
mockFinalityGadget := NewMockFinalityGadget(ctrl)
mockFinalityGadget.EXPECT().VerifyBlockJustification(headerHash,
[]byte(`x`)).Return(nil, errTest)
[]byte(`x`)).Return(errTest)
return chainProcessor{
finalityGadget: mockFinalityGadget,
}
Expand All @@ -313,7 +313,7 @@ func Test_chainProcessor_handleJustification(t *testing.T) {
mockBlockState := NewMockBlockState(ctrl)
mockBlockState.EXPECT().SetJustification(headerHash, []byte(`xx`)).Return(errTest)
mockFinalityGadget := NewMockFinalityGadget(ctrl)
mockFinalityGadget.EXPECT().VerifyBlockJustification(headerHash, []byte(`xx`)).Return([]byte(`xx`), nil)
mockFinalityGadget.EXPECT().VerifyBlockJustification(headerHash, []byte(`xx`)).Return(nil)
return chainProcessor{
blockState: mockBlockState,
finalityGadget: mockFinalityGadget,
Expand All @@ -331,7 +331,7 @@ func Test_chainProcessor_handleJustification(t *testing.T) {
mockBlockState := NewMockBlockState(ctrl)
mockBlockState.EXPECT().SetJustification(headerHash, []byte(`1234`)).Return(nil)
mockFinalityGadget := NewMockFinalityGadget(ctrl)
mockFinalityGadget.EXPECT().VerifyBlockJustification(headerHash, []byte(`1234`)).Return([]byte(`1234`), nil)
mockFinalityGadget.EXPECT().VerifyBlockJustification(headerHash, []byte(`1234`)).Return(nil)
return chainProcessor{
blockState: mockBlockState,
finalityGadget: mockFinalityGadget,
Expand Down Expand Up @@ -430,7 +430,7 @@ func Test_chainProcessor_processBlockData(t *testing.T) {
mockFinalityGadget := NewMockFinalityGadget(ctrl)
mockFinalityGadget.EXPECT().VerifyBlockJustification(common.MustHexToHash(
"0x6443a0b46e0412e626363028115a9f2cf963eeed526b8b33e5316f08b50d0dc3"), []byte{1, 2,
3}).Return([]byte{1, 2, 3}, nil)
3}).Return(nil)
mockStorageState := NewMockStorageState(ctrl)
mockStorageState.EXPECT().TrieState(&common.Hash{}).Return(nil, nil)

Expand Down Expand Up @@ -490,7 +490,7 @@ func Test_chainProcessor_processBlockData(t *testing.T) {
expectedBlockDataHeaderHash := expectedBlockDataHeader.Hash()
finalityGadget.EXPECT().
VerifyBlockJustification(expectedBlockDataHeaderHash, []byte{1, 2, 3}).
Return(nil, mockError)
Return(mockError)

mockChainSync := NewMockChainSync(ctrl)
mockChainSync.EXPECT().syncState().Return(bootstrap)
Expand Down Expand Up @@ -564,7 +564,7 @@ func Test_chainProcessor_processBlockData(t *testing.T) {
mockFinalityGadget := NewMockFinalityGadget(ctrl)
mockFinalityGadget.EXPECT().VerifyBlockJustification(
common.MustHexToHash("0xdcdd89927d8a348e00257e1ecc8617f45edb5118efff3ea2f9961b2ad9b7690a"),
[]byte{1, 2, 3}).Return([]byte{1, 2, 3}, nil)
[]byte{1, 2, 3}).Return(nil)
return chainProcessor{
chainSync: mockChainSync,
blockState: mockBlockState,
Expand Down Expand Up @@ -660,7 +660,7 @@ func Test_chainProcessor_processBlockDataWithStateHeaderAndBody(t *testing.T) {
finalityGadget := NewMockFinalityGadget(ctrl)
finalityGadget.EXPECT().
VerifyBlockJustification(blockHeaderHash, []byte{3}).
Return(nil, errTest)
Return(errTest)

return chainProcessor{
blockState: blockState,
Expand Down
2 changes: 1 addition & 1 deletion dot/sync/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ type BabeVerifier interface {

// FinalityGadget implements justification verification functionality
type FinalityGadget interface {
VerifyBlockJustification(common.Hash, []byte) ([]byte, error)
VerifyBlockJustification(common.Hash, []byte) error
}

// BlockImportHandler is the interface for the handler of newly imported blocks
Expand Down
7 changes: 3 additions & 4 deletions dot/sync/mocks_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions dot/sync/syncer_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ func newTestSyncer(t *testing.T) *Service {
cfg.LogLvl = log.Trace
mockFinalityGadget := NewMockFinalityGadget(ctrl)
mockFinalityGadget.EXPECT().VerifyBlockJustification(gomock.AssignableToTypeOf(common.Hash{}),
gomock.AssignableToTypeOf([]byte{})).DoAndReturn(func(hash common.Hash, justification []byte) ([]byte, error) {
return justification, nil
gomock.AssignableToTypeOf([]byte{})).DoAndReturn(func(hash common.Hash, justification []byte) error {
return nil
}).AnyTimes()

cfg.FinalityGadget = mockFinalityGadget
Expand Down
3 changes: 2 additions & 1 deletion lib/blocktree/blocktree.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"time"

"github.com/ChainSafe/gossamer/dot/types"

"github.com/ChainSafe/gossamer/lib/common"
"github.com/disiqueira/gotree"
"github.com/prometheus/client_golang/prometheus"
Expand Down Expand Up @@ -513,7 +514,7 @@ func (bt *BlockTree) StoreRuntime(hash common.Hash, in Runtime) {
func (bt *BlockTree) GetBlockRuntime(hash common.Hash) (Runtime, error) {
ins := bt.runtimes.get(hash)
if ins == nil {
return nil, ErrFailedToGetRuntime
return nil, fmt.Errorf("%w for hash %s", ErrFailedToGetRuntime, hash)
}
return ins, nil
}
3 changes: 3 additions & 0 deletions lib/grandpa/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ var (
// ErrAuthorityNotInSet is returned when a precommit within a justification is signed by a key not in the authority set
ErrAuthorityNotInSet = errors.New("authority is not in set")

// errFinalisedBlocksMismatch is returned when we find another block finalised in the same set id and round
errFinalisedBlocksMismatch = errors.New("already have finalised block with the same setID and round")

errVoteToSignatureMismatch = errors.New("votes and authority count mismatch")
errVoteBlockMismatch = errors.New("block in vote is not descendant of previously finalised block")
errVoteFromSelf = errors.New("got vote from ourselves")
Expand Down
52 changes: 30 additions & 22 deletions lib/grandpa/message_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,52 +404,60 @@ func (h *MessageHandler) verifyPreCommitJustification(msg *CatchUpResponse) erro

// VerifyBlockJustification verifies the finality justification for a block, returns scale encoded justification with
// any extra bytes removed.
func (s *Service) VerifyBlockJustification(hash common.Hash, justification []byte) ([]byte, error) {
func (s *Service) VerifyBlockJustification(hash common.Hash, justification []byte) error {
fj := Justification{}
err := scale.Unmarshal(justification, &fj)
if err != nil {
return nil, err
return err
}

if hash != fj.Commit.Hash {
return nil, fmt.Errorf("%w: justification %s and block hash %s",
return fmt.Errorf("%w: justification %s and block hash %s",
ErrJustificationMismatch, fj.Commit.Hash.Short(), hash.Short())
}

setID, err := s.grandpaState.GetSetIDByBlockNumber(uint(fj.Commit.Number))
if err != nil {
return nil, fmt.Errorf("cannot get set ID from block number: %w", err)
return fmt.Errorf("cannot get set ID from block number: %w", err)
}

has, err := s.blockState.HasFinalisedBlock(fj.Round, setID)
if err != nil {
return nil, err
return err
}

if has {
return nil, fmt.Errorf("already have finalised block with setID=%d and round=%d", setID, fj.Round)
storedFinalisedHash, err := s.blockState.GetFinalisedHash(fj.Round, setID)
if err != nil {
return fmt.Errorf("getting finalised hash: %w", err)
}
if storedFinalisedHash != hash {
return fmt.Errorf("%w, setID=%d and round=%d", errFinalisedBlocksMismatch, setID, fj.Round)
}

return nil
}

isDescendant, err := isDescendantOfHighestFinalisedBlock(s.blockState, fj.Commit.Hash)
if err != nil {
return nil, err
return err
}

if !isDescendant {
return nil, errVoteBlockMismatch
return errVoteBlockMismatch
}

auths, err := s.grandpaState.GetAuthorities(setID)
if err != nil {
return nil, fmt.Errorf("cannot get authorities for set ID: %w", err)
return fmt.Errorf("cannot get authorities for set ID: %w", err)
}

// threshold is two-thirds the number of authorities,
// uses the current set of authorities to define the threshold
threshold := (2 * len(auths) / 3)

if len(fj.Commit.Precommits) < threshold {
return nil, ErrMinVotesNotMet
return ErrMinVotesNotMet
}

authPubKeys := make([]AuthData, len(fj.Commit.Precommits))
Expand All @@ -469,20 +477,20 @@ func (s *Service) VerifyBlockJustification(hash common.Hash, justification []byt
// check if vote was for descendant of committed block
isDescendant, err := s.blockState.IsDescendantOf(hash, just.Vote.Hash)
if err != nil {
return nil, err
return err
}

if !isDescendant {
return nil, ErrPrecommitBlockMismatch
return ErrPrecommitBlockMismatch
}

publicKey, err := ed25519.NewPublicKey(just.AuthorityID[:])
if err != nil {
return nil, err
return err
}

if !isInAuthSet(publicKey, auths) {
return nil, ErrAuthorityNotInSet
return ErrAuthorityNotInSet
}

// verify signature for each precommit
Expand All @@ -493,16 +501,16 @@ func (s *Service) VerifyBlockJustification(hash common.Hash, justification []byt
SetID: setID,
})
if err != nil {
return nil, err
return err
}

ok, err := publicKey.Verify(msg, just.Signature[:])
if err != nil {
return nil, err
return err
}

if !ok {
return nil, ErrInvalidSignature
return ErrInvalidSignature
}

if _, ok := equivocatoryVoters[just.AuthorityID]; ok {
Expand All @@ -513,30 +521,30 @@ func (s *Service) VerifyBlockJustification(hash common.Hash, justification []byt
}

if count+len(equivocatoryVoters) < threshold {
return nil, ErrMinVotesNotMet
return ErrMinVotesNotMet
}

err = verifyBlockHashAgainstBlockNumber(s.blockState, fj.Commit.Hash, uint(fj.Commit.Number))
if err != nil {
return nil, err
return err
}

for _, preCommit := range fj.Commit.Precommits {
err := verifyBlockHashAgainstBlockNumber(s.blockState, preCommit.Vote.Hash, uint(preCommit.Vote.Number))
if err != nil {
return nil, err
return err
}
}

err = s.blockState.SetFinalisedHash(hash, fj.Round, setID)
if err != nil {
return nil, err
return err
}

logger.Debugf(
"set finalised block with hash %s, round %d and set id %d",
hash, fj.Round, setID)
return scale.Marshal(fj)
return nil
}

func verifyBlockHashAgainstBlockNumber(bs BlockState, hash common.Hash, number uint) error {
Expand Down
Loading