Skip to content

Commit fee48c1

Browse files
task(lib/grandpa): Verify block hashes and number (#2468)
Verify block hashes against block number in justification
1 parent 41db27b commit fee48c1

File tree

6 files changed

+122
-20
lines changed

6 files changed

+122
-20
lines changed

dot/state/block.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,6 @@ func (bs *BlockState) GetHeader(hash common.Hash) (header *types.Header, err err
205205
return header, nil
206206
}
207207

208-
result := types.NewEmptyHeader()
209-
210208
if bs.db == nil {
211209
return nil, fmt.Errorf("database is nil")
212210
}
@@ -220,6 +218,7 @@ func (bs *BlockState) GetHeader(hash common.Hash) (header *types.Header, err err
220218
return nil, err
221219
}
222220

221+
result := types.NewEmptyHeader()
223222
err = scale.Unmarshal(data, result)
224223
if err != nil {
225224
return nil, err

dot/state/block_finalisation.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,10 @@ func (bs *BlockState) SetFinalisedHash(hash common.Hash, round, setID uint64) er
118118
bs.Lock()
119119
defer bs.Unlock()
120120

121-
has, _ := bs.HasHeader(hash)
121+
has, err := bs.HasHeader(hash)
122+
if err != nil {
123+
return fmt.Errorf("could not check header for hash %s: %w", hash, err)
124+
}
122125
if !has {
123126
return fmt.Errorf("cannot finalise unknown block %s", hash)
124127
}

dot/state/grandpa.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ func (s *GrandpaState) GetLatestRound() (uint64, error) {
138138
return round, nil
139139
}
140140

141-
// SetNextChange sets the next authority change
141+
// SetNextChange sets the next authority change at the given block number.
142+
// NOTE: This block number will be the last block in the current set and not part of the next set.
142143
func (s *GrandpaState) SetNextChange(authorities []types.GrandpaVoter, number uint) error {
143144
currSetID, err := s.GetCurrentSetID()
144145
if err != nil {
@@ -180,7 +181,7 @@ func (s *GrandpaState) setSetIDChangeAtBlock(setID uint64, number uint) error {
180181
return s.db.Put(setIDChangeKey(setID), common.UintToBytes(number))
181182
}
182183

183-
// GetSetIDChange returs the block number where the set ID was updated
184+
// GetSetIDChange returns the block number where the set ID was updated
184185
func (s *GrandpaState) GetSetIDChange(setID uint64) (blockNumber uint, err error) {
185186
num, err := s.db.Get(setIDChangeKey(setID))
186187
if err != nil {
@@ -191,7 +192,7 @@ func (s *GrandpaState) GetSetIDChange(setID uint64) (blockNumber uint, err error
191192
}
192193

193194
// GetSetIDByBlockNumber returns the set ID for a given block number
194-
func (s *GrandpaState) GetSetIDByBlockNumber(num uint) (uint64, error) {
195+
func (s *GrandpaState) GetSetIDByBlockNumber(blockNumber uint) (uint64, error) {
195196
curr, err := s.GetCurrentSetID()
196197
if err != nil {
197198
return 0, err
@@ -215,13 +216,16 @@ func (s *GrandpaState) GetSetIDByBlockNumber(num uint) (uint64, error) {
215216
return 0, err
216217
}
217218

218-
// if the given block number is greater or equal to the block number of the set ID change,
219-
// return the current set ID
220-
if num <= changeUpper && num > changeLower {
219+
// Set id changes at the last block in the set. So, block (changeLower) at which current
220+
// set id was set, does not belong to current set. Thus, all block numbers in given set
221+
// would be more than changeLower.
222+
// Next set id change happens at the last block of current set. Thus, a block number from
223+
// given set could be lower or equal to changeUpper.
224+
if blockNumber <= changeUpper && blockNumber > changeLower {
221225
return curr, nil
222226
}
223227

224-
if num > changeUpper {
228+
if blockNumber > changeUpper {
225229
return curr + 1, nil
226230
}
227231

lib/grandpa/errors.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ var (
6363
// ErrNoJustification is returned when no justification can be found for a block, ie. it has not been finalised
6464
ErrNoJustification = errors.New("no justification found for block")
6565

66+
ErrBlockHashMismatch = errors.New("block hash does not correspond to given block number")
67+
6668
// ErrMinVotesNotMet is returned when the number of votes is less than the required minimum in a Justification
6769
ErrMinVotesNotMet = errors.New("minimum number of votes not met in a Justification")
6870

lib/grandpa/message_handler.go

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"fmt"
1010
"reflect"
1111

12+
"github.com/ChainSafe/chaindb"
1213
"github.com/ChainSafe/gossamer/dot/network"
1314
"github.com/ChainSafe/gossamer/dot/telemetry"
1415
"github.com/ChainSafe/gossamer/dot/types"
@@ -104,6 +105,16 @@ func (h *MessageHandler) handleNeighbourMessage(msg *NeighbourMessage) error {
104105
func (h *MessageHandler) handleCommitMessage(msg *CommitMessage) error {
105106
logger.Debugf("received commit message, msg: %+v", msg)
106107

108+
err := verifyBlockHashAgainstBlockNumber(h.blockState, msg.Vote.Hash, uint(msg.Vote.Number))
109+
if err != nil {
110+
if errors.Is(err, chaindb.ErrKeyNotFound) {
111+
h.grandpa.tracker.addCommit(msg)
112+
logger.Infof("we might not have synced to the given block %s yet: %s", msg.Vote.Hash, err)
113+
return nil
114+
}
115+
return err
116+
}
117+
107118
containsPrecommitsSignedBy := make([]string, len(msg.AuthData))
108119
for i, authData := range msg.AuthData {
109120
containsPrecommitsSignedBy[i] = authData.AuthorityID.String()
@@ -184,6 +195,16 @@ func (h *MessageHandler) handleCatchUpResponse(msg *CatchUpResponse) error {
184195
"received catch up response with hash %s for round %d and set id %d",
185196
msg.Hash, msg.Round, msg.SetID)
186197

198+
err := verifyBlockHashAgainstBlockNumber(h.blockState, msg.Hash, uint(msg.Number))
199+
if err != nil {
200+
if errors.Is(err, chaindb.ErrKeyNotFound) {
201+
h.grandpa.tracker.addCatchUpResponse(msg)
202+
logger.Infof("we might not have synced to the given block %s yet: %s", msg.Hash, err)
203+
return nil
204+
}
205+
return err
206+
}
207+
187208
// TODO: re-add catch-up logic (#1531)
188209
if true {
189210
return nil
@@ -300,12 +321,13 @@ func (h *MessageHandler) verifyCommitMessageJustification(fm *CommitMessage) err
300321

301322
err := h.verifyJustification(just, fm.Round, h.grandpa.state.setID, precommit)
302323
if err != nil {
324+
logger.Errorf("could not verify justification: %s", err)
303325
continue
304326
}
305327

306328
isDescendant, err := h.blockState.IsDescendantOf(fm.Vote.Hash, just.Vote.Hash)
307329
if err != nil {
308-
logger.Warnf("verifyCommitMessageJustification: %s", err)
330+
logger.Warnf("could not check for descendant: %s", err)
309331
continue
310332
}
311333

@@ -330,6 +352,17 @@ func (h *MessageHandler) verifyPreVoteJustification(msg *CatchUpResponse) (commo
330352
voters := make(map[ed25519.PublicKeyBytes]map[common.Hash]int, len(msg.PreVoteJustification))
331353
eqVotesByHash := make(map[common.Hash]map[ed25519.PublicKeyBytes]struct{})
332354

355+
for _, pvj := range msg.PreVoteJustification {
356+
err := verifyBlockHashAgainstBlockNumber(h.blockState, pvj.Vote.Hash, uint(pvj.Vote.Number))
357+
if err != nil {
358+
if errors.Is(err, chaindb.ErrKeyNotFound) {
359+
h.grandpa.tracker.addCatchUpResponse(msg)
360+
logger.Infof("we might not have synced to the given block %s yet: %s", pvj.Vote.Hash, err)
361+
continue
362+
}
363+
return common.Hash{}, err
364+
}
365+
}
333366
// identify equivocatory votes by hash
334367
for _, justification := range msg.PreVoteJustification {
335368
hashsToCount, ok := voters[justification.AuthorityID]
@@ -386,6 +419,18 @@ func (h *MessageHandler) verifyPreVoteJustification(msg *CatchUpResponse) (commo
386419
}
387420

388421
func (h *MessageHandler) verifyPreCommitJustification(msg *CatchUpResponse) error {
422+
for _, pcj := range msg.PreCommitJustification {
423+
err := verifyBlockHashAgainstBlockNumber(h.blockState, pcj.Vote.Hash, uint(pcj.Vote.Number))
424+
if err != nil {
425+
if errors.Is(err, chaindb.ErrKeyNotFound) {
426+
h.grandpa.tracker.addCatchUpResponse(msg)
427+
logger.Infof("we might not have synced to the given block %s yet: %s", pcj.Vote.Hash, err)
428+
continue
429+
}
430+
return err
431+
}
432+
}
433+
389434
auths := make([]AuthData, len(msg.PreCommitJustification))
390435
for i, pcj := range msg.PreCommitJustification {
391436
auths[i] = AuthData{AuthorityID: pcj.AuthorityID}
@@ -562,6 +607,18 @@ func (s *Service) VerifyBlockJustification(hash common.Hash, justification []byt
562607
return ErrMinVotesNotMet
563608
}
564609

610+
err = verifyBlockHashAgainstBlockNumber(s.blockState, fj.Commit.Hash, uint(fj.Commit.Number))
611+
if err != nil {
612+
return err
613+
}
614+
615+
for _, preCommit := range fj.Commit.Precommits {
616+
err := verifyBlockHashAgainstBlockNumber(s.blockState, preCommit.Vote.Hash, uint(preCommit.Vote.Number))
617+
if err != nil {
618+
return err
619+
}
620+
}
621+
565622
err = s.blockState.SetFinalisedHash(hash, fj.Round, setID)
566623
if err != nil {
567624
return err
@@ -573,6 +630,19 @@ func (s *Service) VerifyBlockJustification(hash common.Hash, justification []byt
573630
return nil
574631
}
575632

633+
func verifyBlockHashAgainstBlockNumber(bs BlockState, hash common.Hash, number uint) error {
634+
header, err := bs.GetHeader(hash)
635+
if err != nil {
636+
return fmt.Errorf("could not get header from block hash: %w", err)
637+
}
638+
639+
if header.Number != number {
640+
return fmt.Errorf("%w: expected number %d from header but got number %d",
641+
ErrBlockHashMismatch, header.Number, number)
642+
}
643+
return nil
644+
}
645+
576646
func isInAuthSet(auth *ed25519.PublicKey, set []types.GrandpaVoter) bool {
577647
for _, a := range set {
578648
if bytes.Equal(a.Key.Encode(), auth.Encode()) {

lib/grandpa/message_handler_test.go

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ func TestMessageHandler_VerifyJustification_InvalidSig(t *testing.T) {
250250
func TestMessageHandler_CommitMessage_NoCatchUpRequest_ValidSig(t *testing.T) {
251251
gs, st := newTestService(t)
252252

253-
round := uint64(77)
253+
round := uint64(1)
254254
gs.state.round = round
255255
just := buildTestJustification(t, int(gs.state.threshold()), round, gs.state.setID, kr, precommit)
256256
err := st.Grandpa.SetPrecommits(round, gs.state.setID, just)
@@ -505,6 +505,18 @@ func TestMessageHandler_VerifyPreVoteJustification(t *testing.T) {
505505
telemetryMock.EXPECT().SendMessage(gomock.Any()).AnyTimes()
506506

507507
gs, st := newTestService(t)
508+
509+
body, err := types.NewBodyFromBytes([]byte{0})
510+
require.NoError(t, err)
511+
512+
block := &types.Block{
513+
Header: *testHeader,
514+
Body: *body,
515+
}
516+
517+
err = st.Block.AddBlock(block)
518+
require.NoError(t, err)
519+
508520
h := NewMessageHandler(gs, st.Block, telemetryMock)
509521

510522
just := buildTestJustification(t, int(gs.state.threshold()), 1, gs.state.setID, kr, prevote)
@@ -525,6 +537,18 @@ func TestMessageHandler_VerifyPreCommitJustification(t *testing.T) {
525537
telemetryMock.EXPECT().SendMessage(gomock.Any()).AnyTimes()
526538

527539
gs, st := newTestService(t)
540+
541+
body, err := types.NewBodyFromBytes([]byte{0})
542+
require.NoError(t, err)
543+
544+
block := &types.Block{
545+
Header: *testHeader,
546+
Body: *body,
547+
}
548+
549+
err = st.Block.AddBlock(block)
550+
require.NoError(t, err)
551+
528552
h := NewMessageHandler(gs, st.Block, telemetryMock)
529553

530554
round := uint64(1)
@@ -537,7 +561,7 @@ func TestMessageHandler_VerifyPreCommitJustification(t *testing.T) {
537561
Number: uint32(round),
538562
}
539563

540-
err := h.verifyPreCommitJustification(msg)
564+
err = h.verifyPreCommitJustification(msg)
541565
require.NoError(t, err)
542566
}
543567

@@ -553,7 +577,7 @@ func TestMessageHandler_HandleCatchUpResponse(t *testing.T) {
553577

554578
h := NewMessageHandler(gs, st.Block, telemetryMock)
555579

556-
round := uint64(77)
580+
round := uint64(1)
557581
gs.state.round = round + 1
558582

559583
pvJust := buildTestJustification(t, int(gs.state.threshold()), round, gs.state.setID, kr, prevote)
@@ -605,7 +629,7 @@ func TestMessageHandler_VerifyBlockJustification_WithEquivocatoryVotes(t *testin
605629
}
606630

607631
gs, st := newTestService(t)
608-
err := st.Grandpa.SetNextChange(auths, 1)
632+
err := st.Grandpa.SetNextChange(auths, 0)
609633
require.NoError(t, err)
610634

611635
body, err := types.NewBodyFromBytes([]byte{0})
@@ -623,8 +647,8 @@ func TestMessageHandler_VerifyBlockJustification_WithEquivocatoryVotes(t *testin
623647
require.NoError(t, err)
624648
require.Equal(t, uint64(1), setID)
625649

626-
round := uint64(2)
627-
number := uint32(2)
650+
round := uint64(1)
651+
number := uint32(1)
628652
precommits := buildTestJustification(t, 20, round, setID, kr, precommit)
629653
just := newJustification(round, testHash, number, precommits)
630654
data, err := scale.Marshal(*just)
@@ -647,7 +671,7 @@ func TestMessageHandler_VerifyBlockJustification(t *testing.T) {
647671
}
648672

649673
gs, st := newTestService(t)
650-
err := st.Grandpa.SetNextChange(auths, 1)
674+
err := st.Grandpa.SetNextChange(auths, 0)
651675
require.NoError(t, err)
652676

653677
body, err := types.NewBodyFromBytes([]byte{0})
@@ -667,8 +691,8 @@ func TestMessageHandler_VerifyBlockJustification(t *testing.T) {
667691

668692
genhash := st.Block.GenesisHash()
669693

670-
round := uint64(2)
671-
number := uint32(2)
694+
round := uint64(1)
695+
number := uint32(1)
672696
precommits := buildTestJustification(t, 2, round, setID, kr, precommit)
673697
just := newJustification(round, testHash, number, precommits)
674698
data, err := scale.Marshal(*just)

0 commit comments

Comments
 (0)