Skip to content

Commit 028d25e

Browse files
authored
feat(lib/grandpa): fully verify justifications using GrandpaState (#1544)
1 parent ebe6dec commit 028d25e

File tree

17 files changed

+394
-166
lines changed

17 files changed

+394
-166
lines changed

dot/core/digest.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,18 +97,20 @@ func NewDigestHandler(blockState BlockState, epochState EpochState, grandpaState
9797
}
9898

9999
// Start starts the DigestHandler
100-
func (h *DigestHandler) Start() {
100+
func (h *DigestHandler) Start() error {
101101
go h.handleBlockImport(h.ctx)
102102
go h.handleBlockFinalisation(h.ctx)
103+
return nil
103104
}
104105

105106
// Stop stops the DigestHandler
106-
func (h *DigestHandler) Stop() {
107+
func (h *DigestHandler) Stop() error {
107108
h.cancel()
108109
h.blockState.UnregisterImportedChannel(h.importedID)
109110
h.blockState.UnregisterFinalizedChannel(h.finalisedID)
110111
close(h.imported)
111112
close(h.finalised)
113+
return nil
112114
}
113115

114116
// NextGrandpaAuthorityChange returns the block number of the next upcoming grandpa authorities change.
@@ -145,6 +147,8 @@ func (h *DigestHandler) HandleConsensusDigest(d *types.ConsensusDigest, header *
145147
return h.handleScheduledChange(d, header)
146148
case types.GrandpaForcedChangeType:
147149
return h.handleForcedChange(d, header)
150+
case types.GrandpaOnDisabledType:
151+
return nil // do nothing, as this is not implemented in substrate
148152
case types.GrandpaPauseType:
149153
return h.handlePause(d)
150154
case types.GrandpaResumeType:
@@ -208,37 +212,49 @@ func (h *DigestHandler) handleBlockFinalisation(ctx context.Context) {
208212

209213
func (h *DigestHandler) handleGrandpaChangesOnImport(num *big.Int) error {
210214
resume := h.grandpaResume
211-
if resume != nil && num.Cmp(resume.atBlock) == 0 {
215+
if resume != nil && num.Cmp(resume.atBlock) > -1 {
212216
h.grandpaResume = nil
213217
}
214218

215219
fc := h.grandpaForcedChange
216-
if fc != nil && num.Cmp(fc.atBlock) == 0 {
220+
if fc != nil && num.Cmp(fc.atBlock) > -1 {
217221
err := h.grandpaState.IncrementSetID()
218222
if err != nil {
219223
return err
220224
}
221225

222226
h.grandpaForcedChange = nil
227+
curr, err := h.grandpaState.GetCurrentSetID()
228+
if err != nil {
229+
return err
230+
}
231+
232+
logger.Debug("incremented grandpa set ID", "set ID", curr)
223233
}
224234

225235
return nil
226236
}
227237

228238
func (h *DigestHandler) handleGrandpaChangesOnFinalization(num *big.Int) error {
229239
pause := h.grandpaPause
230-
if pause != nil && num.Cmp(pause.atBlock) == 0 {
240+
if pause != nil && num.Cmp(pause.atBlock) > -1 {
231241
h.grandpaPause = nil
232242
}
233243

234244
sc := h.grandpaScheduledChange
235-
if sc != nil && num.Cmp(sc.atBlock) == 0 {
245+
if sc != nil && num.Cmp(sc.atBlock) > -1 {
236246
err := h.grandpaState.IncrementSetID()
237247
if err != nil {
238248
return err
239249
}
240250

241251
h.grandpaScheduledChange = nil
252+
curr, err := h.grandpaState.GetCurrentSetID()
253+
if err != nil {
254+
return err
255+
}
256+
257+
logger.Debug("incremented grandpa set ID", "set ID", curr)
242258
}
243259

244260
// if blocks get finalised before forced change takes place, disregard it
@@ -281,7 +297,11 @@ func (h *DigestHandler) handleScheduledChange(d *types.ConsensusDigest, header *
281297
return err
282298
}
283299

284-
return h.grandpaState.SetNextChange(types.NewGrandpaVotersFromAuthorities(auths), big.NewInt(0).Add(header.Number, big.NewInt(int64(sc.Delay))))
300+
logger.Debug("setting GrandpaScheduledChange", "at block", big.NewInt(0).Add(header.Number, big.NewInt(int64(sc.Delay))))
301+
return h.grandpaState.SetNextChange(
302+
types.NewGrandpaVotersFromAuthorities(auths),
303+
big.NewInt(0).Add(header.Number, big.NewInt(int64(sc.Delay))),
304+
)
285305
}
286306

287307
func (h *DigestHandler) handleForcedChange(d *types.ConsensusDigest, header *types.Header) error {
@@ -318,6 +338,7 @@ func (h *DigestHandler) handleForcedChange(d *types.ConsensusDigest, header *typ
318338
return err
319339
}
320340

341+
logger.Debug("setting GrandpaForcedChange", "at block", big.NewInt(0).Add(header.Number, big.NewInt(int64(fc.Delay))))
321342
return h.grandpaState.SetNextChange(
322343
types.NewGrandpaVotersFromAuthorities(auths),
323344
big.NewInt(0).Add(header.Number, big.NewInt(int64(fc.Delay))),

dot/core/interface.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,5 @@ type GrandpaState interface {
9999
IncrementSetID() error
100100
SetNextPause(number *big.Int) error
101101
SetNextResume(number *big.Int) error
102+
GetCurrentSetID() (uint64, error)
102103
}

dot/network/sync.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,7 @@ func (q *syncQueue) handleBlockAnnounceHandshake(blockNum uint32, from peer.ID)
793793

794794
func (q *syncQueue) handleBlockAnnounce(msg *BlockAnnounceMessage, from peer.ID) {
795795
q.updatePeerScore(from, 1)
796-
logger.Info("received BlockAnnounce", "number", msg.Number, "from", from)
796+
logger.Debug("received BlockAnnounce", "number", msg.Number, "from", from)
797797

798798
header, err := types.NewHeader(msg.ParentHash, msg.StateRoot, msg.ExtrinsicsRoot, msg.Number, msg.Digest)
799799
if err != nil {

dot/node.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore, stopFunc func()) (*Node,
241241
if err != nil {
242242
return nil, err
243243
}
244+
nodeSrvcs = append(nodeSrvcs, dh)
244245

245246
// create GRANDPA service
246247
fg, err := createGRANDPAService(cfg, rt, stateSrvc, dh, ks.Gran, networkSrvc)

dot/state/block_notify.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ func (bs *BlockState) notifyFinalized(hash common.Hash) {
125125
return
126126
}
127127

128-
logger.Trace("notifying finalised block chans...", "chans", bs.finalised)
128+
logger.Debug("notifying finalised block chans...", "chans", bs.finalised)
129129

130130
for _, ch := range bs.finalised {
131131
go func(ch chan<- *types.Header) {

dot/state/grandpa.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ func NewGrandpaStateFromGenesis(db chaindb.Database, genesisAuthorities []*types
5959
return nil, err
6060
}
6161

62+
err = s.setSetIDChangeAtBlock(genesisSetID, big.NewInt(0))
63+
if err != nil {
64+
return nil, err
65+
}
66+
6267
return s, nil
6368
}
6469

@@ -180,6 +185,49 @@ func (s *GrandpaState) GetSetIDChange(setID uint64) (*big.Int, error) {
180185
return big.NewInt(0).SetBytes(num), nil
181186
}
182187

188+
// GetSetIDByBlockNumber returns the set ID for a given block number
189+
func (s *GrandpaState) GetSetIDByBlockNumber(num *big.Int) (uint64, error) {
190+
curr, err := s.GetCurrentSetID()
191+
if err != nil {
192+
return 0, err
193+
}
194+
195+
for {
196+
changeUpper, err := s.GetSetIDChange(curr + 1)
197+
if err == chaindb.ErrKeyNotFound {
198+
if curr == 0 {
199+
return 0, nil
200+
}
201+
curr = curr - 1
202+
continue
203+
}
204+
if err != nil {
205+
return 0, err
206+
}
207+
208+
changeLower, err := s.GetSetIDChange(curr)
209+
if err != nil {
210+
return 0, err
211+
}
212+
213+
// if the given block number is greater or equal to the block number of the set ID change,
214+
// return the current set ID
215+
if num.Cmp(changeUpper) < 1 && num.Cmp(changeLower) == 1 {
216+
return curr, nil
217+
}
218+
219+
if num.Cmp(changeUpper) == 1 {
220+
return curr + 1, nil
221+
}
222+
223+
curr = curr - 1
224+
225+
if int(curr) < 0 {
226+
return 0, nil
227+
}
228+
}
229+
}
230+
183231
// SetNextPause sets the next grandpa pause at the given block number
184232
func (s *GrandpaState) SetNextPause(number *big.Int) error {
185233
return s.db.Put(pauseKey, number.Bytes())

dot/state/grandpa_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ func TestNewGrandpaStateFromGenesis(t *testing.T) {
4646
auths, err := gs.GetAuthorities(currSetID)
4747
require.NoError(t, err)
4848
require.Equal(t, testAuths, auths)
49+
50+
num, err := gs.GetSetIDChange(0)
51+
require.NoError(t, err)
52+
require.Equal(t, big.NewInt(0), num)
4953
}
5054

5155
func TestGrandpaState_SetNextChange(t *testing.T) {
@@ -81,3 +85,40 @@ func TestGrandpaState_IncrementSetID(t *testing.T) {
8185
require.NoError(t, err)
8286
require.Equal(t, genesisSetID+1, setID)
8387
}
88+
89+
func TestGrandpaState_GetSetIDByBlockNumber(t *testing.T) {
90+
db := NewInMemoryDB(t)
91+
gs, err := NewGrandpaStateFromGenesis(db, testAuths)
92+
require.NoError(t, err)
93+
94+
testAuths2 := []*types.GrandpaVoter{
95+
{Key: kr.Bob().Public().(*ed25519.PublicKey), ID: 0},
96+
}
97+
98+
err = gs.SetNextChange(testAuths2, big.NewInt(100))
99+
require.NoError(t, err)
100+
101+
setID, err := gs.GetSetIDByBlockNumber(big.NewInt(50))
102+
require.NoError(t, err)
103+
require.Equal(t, genesisSetID, setID)
104+
105+
setID, err = gs.GetSetIDByBlockNumber(big.NewInt(100))
106+
require.NoError(t, err)
107+
require.Equal(t, genesisSetID, setID)
108+
109+
setID, err = gs.GetSetIDByBlockNumber(big.NewInt(101))
110+
require.NoError(t, err)
111+
require.Equal(t, genesisSetID+1, setID)
112+
113+
err = gs.IncrementSetID()
114+
require.NoError(t, err)
115+
116+
setID, err = gs.GetSetIDByBlockNumber(big.NewInt(100))
117+
require.NoError(t, err)
118+
require.Equal(t, genesisSetID, setID)
119+
120+
setID, err = gs.GetSetIDByBlockNumber(big.NewInt(101))
121+
require.NoError(t, err)
122+
require.Equal(t, genesisSetID+1, setID)
123+
124+
}

dot/state/service.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,30 @@ func (s *Service) Rewind(toBlock int64) error {
218218
return err
219219
}
220220

221+
// update the current grandpa set ID
222+
prevSetID, err := s.Grandpa.GetCurrentSetID()
223+
if err != nil {
224+
return err
225+
}
226+
227+
newSetID, err := s.Grandpa.GetSetIDByBlockNumber(header.Number)
228+
if err != nil {
229+
return err
230+
}
231+
232+
err = s.Grandpa.setCurrentSetID(newSetID)
233+
if err != nil {
234+
return err
235+
}
236+
237+
// remove previously set grandpa changes, need to go up to prevSetID+1 in case of a scheduled change
238+
for i := newSetID + 1; i <= prevSetID+1; i++ {
239+
err = s.Grandpa.db.Del(setIDChangeKey(i))
240+
if err != nil {
241+
return err
242+
}
243+
}
244+
221245
return s.Base.StoreBestBlockHash(newHead)
222246
}
223247

dot/state/service_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/ChainSafe/gossamer/lib/trie"
2929
"github.com/ChainSafe/gossamer/lib/utils"
3030

31+
"github.com/ChainSafe/chaindb"
3132
log "github.com/ChainSafe/log15"
3233
"github.com/stretchr/testify/require"
3334
)
@@ -229,13 +230,38 @@ func TestService_Rewind(t *testing.T) {
229230
err = serv.Start()
230231
require.NoError(t, err)
231232

233+
err = serv.Grandpa.setCurrentSetID(3)
234+
require.NoError(t, err)
235+
236+
err = serv.Grandpa.setSetIDChangeAtBlock(1, big.NewInt(5))
237+
require.NoError(t, err)
238+
239+
err = serv.Grandpa.setSetIDChangeAtBlock(2, big.NewInt(8))
240+
require.NoError(t, err)
241+
242+
err = serv.Grandpa.setSetIDChangeAtBlock(3, big.NewInt(10))
243+
require.NoError(t, err)
244+
232245
AddBlocksToState(t, serv.Block, 12)
233246
err = serv.Rewind(6)
234247
require.NoError(t, err)
235248

236249
num, err := serv.Block.BestBlockNumber()
237250
require.NoError(t, err)
238251
require.Equal(t, big.NewInt(6), num)
252+
253+
setID, err := serv.Grandpa.GetCurrentSetID()
254+
require.NoError(t, err)
255+
require.Equal(t, uint64(1), setID)
256+
257+
_, err = serv.Grandpa.GetSetIDChange(1)
258+
require.NoError(t, err)
259+
260+
_, err = serv.Grandpa.GetSetIDChange(2)
261+
require.Equal(t, chaindb.ErrKeyNotFound, err)
262+
263+
_, err = serv.Grandpa.GetSetIDChange(3)
264+
require.Equal(t, chaindb.ErrKeyNotFound, err)
239265
}
240266

241267
func TestService_Import(t *testing.T) {

dot/sync/interface.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ type BlockProducer interface {
6969

7070
// DigestHandler is the interface for the consensus digest handler
7171
type DigestHandler interface {
72-
Start()
73-
Stop()
7472
HandleConsensusDigest(*types.ConsensusDigest, *types.Header) error
7573
}
7674

0 commit comments

Comments
 (0)