Skip to content

Commit fcb81a3

Browse files
feat(lib/babe): implement secondary slot block production (#2260)
implement secondary VRF slot block production and secondary plain slot block production
1 parent be078e5 commit fcb81a3

17 files changed

+457
-161
lines changed

dot/sync/chain_processor.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,22 @@ func (s *chainProcessor) processReadyBlocks() {
103103
}
104104

105105
// processBlockData processes the BlockData from a BlockResponse and
106-
// eturns the index of the last BlockData it handled on success,
106+
// returns the index of the last BlockData it handled on success,
107107
// or the index of the block data that errored on failure.
108108
func (s *chainProcessor) processBlockData(bd *types.BlockData) error {
109109
if bd == nil {
110110
return ErrNilBlockData
111111
}
112112

113-
hasHeader, _ := s.blockState.HasHeader(bd.Hash)
114-
hasBody, _ := s.blockState.HasBlockBody(bd.Hash)
113+
hasHeader, err := s.blockState.HasHeader(bd.Hash)
114+
if err != nil {
115+
return fmt.Errorf("failed to check if block state has header for hash %s: %w", bd.Hash, err)
116+
}
117+
hasBody, err := s.blockState.HasBlockBody(bd.Hash)
118+
if err != nil {
119+
return fmt.Errorf("failed to check block state has body for hash %s: %w", bd.Hash, err)
120+
}
121+
115122
if hasHeader && hasBody {
116123
// TODO: fix this; sometimes when the node shuts down the "best block" isn't stored properly,
117124
// so when the node restarts it has blocks higher than what it thinks is the best, causing it not to sync

dot/types/babe.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,19 @@ import (
1010
// RandomnessLength is the length of the epoch randomness (32 bytes)
1111
const RandomnessLength = 32
1212

13+
// AllowedSlots tells in what ways a slot can be claimed.
14+
type AllowedSlots byte
15+
16+
// https://github.com/paritytech/substrate/blob/ded44948e2d5a398abcb4e342b0513cb690961bb/primitives/consensus/babe/src/lib.rs#L219-L226
17+
const (
18+
// PrimarySlots only allows primary slots.
19+
PrimarySlots AllowedSlots = iota
20+
// PrimaryAndSecondaryPlainSlots allow primary and secondary plain slots.
21+
PrimaryAndSecondaryPlainSlots
22+
// PrimaryAndSecondaryVRFSlots allows primary and secondary VRF slots.
23+
PrimaryAndSecondaryVRFSlots
24+
)
25+
1326
// BabeConfiguration contains the genesis data for BABE
1427
//nolint:lll
1528
// see: https://github.com/paritytech/substrate/blob/426c26b8bddfcdbaf8d29f45b128e0864b57de1c/core/consensus/babe/primitives/src/lib.rs#L132

dot/types/babe_digest.go

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package types
55

66
import (
77
"errors"
8+
"fmt"
89

910
"github.com/ChainSafe/gossamer/lib/crypto/sr25519"
1011
"github.com/ChainSafe/gossamer/pkg/scale"
@@ -53,16 +54,7 @@ func NewBabePrimaryPreDigest(authorityIndex uint32,
5354

5455
// ToPreRuntimeDigest returns the BabePrimaryPreDigest as a PreRuntimeDigest
5556
func (d *BabePrimaryPreDigest) ToPreRuntimeDigest() (*PreRuntimeDigest, error) {
56-
digest := NewBabeDigest()
57-
err := digest.Set(*d)
58-
if err != nil {
59-
return nil, err
60-
}
61-
enc, err := scale.Marshal(digest)
62-
if err != nil {
63-
return nil, err
64-
}
65-
return NewBABEPreRuntimeDigest(enc), nil
57+
return toPreRuntimeDigest(*d)
6658
}
6759

6860
// Index Returns VDT index
@@ -82,18 +74,9 @@ func NewBabeSecondaryPlainPreDigest(authorityIndex uint32, slotNumber uint64) *B
8274
}
8375
}
8476

85-
// ToPreRuntimeDigest returns the BabePrimaryPreDigest as a PreRuntimeDigest
77+
// ToPreRuntimeDigest returns the BabeSecondaryPlainPreDigest as a PreRuntimeDigest
8678
func (d *BabeSecondaryPlainPreDigest) ToPreRuntimeDigest() (*PreRuntimeDigest, error) {
87-
digest := NewBabeDigest()
88-
err := digest.Set(*d)
89-
if err != nil {
90-
return nil, err
91-
}
92-
enc, err := scale.Marshal(digest)
93-
if err != nil {
94-
return nil, err
95-
}
96-
return NewBABEPreRuntimeDigest(enc), nil
79+
return toPreRuntimeDigest(*d)
9780
}
9881

9982
// Index Returns VDT index
@@ -119,5 +102,26 @@ func NewBabeSecondaryVRFPreDigest(authorityIndex uint32,
119102
}
120103
}
121104

105+
// ToPreRuntimeDigest returns the BabeSecondaryVRFPreDigest as a PreRuntimeDigest
106+
func (d *BabeSecondaryVRFPreDigest) ToPreRuntimeDigest() (*PreRuntimeDigest, error) {
107+
return toPreRuntimeDigest(*d)
108+
}
109+
122110
// Index Returns VDT index
123111
func (d BabeSecondaryVRFPreDigest) Index() uint { return 3 }
112+
113+
// toPreRuntimeDigest returns the VaryingDataTypeValue as a PreRuntimeDigest
114+
func toPreRuntimeDigest(value scale.VaryingDataTypeValue) (*PreRuntimeDigest, error) {
115+
digest := NewBabeDigest()
116+
err := digest.Set(value)
117+
if err != nil {
118+
return nil, fmt.Errorf("cannot set varying data type value to babe digest: %w", err)
119+
}
120+
121+
enc, err := scale.Marshal(digest)
122+
if err != nil {
123+
return nil, fmt.Errorf("cannot marshal babe digest: %w", err)
124+
}
125+
126+
return NewBABEPreRuntimeDigest(enc), nil
127+
}

lib/babe/babe.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ func (b *Service) handleEpoch(epoch uint64) (next uint64, err error) {
386386
// stop current epoch handler
387387
cancel()
388388
case err := <-errCh:
389+
// TODO: errEpochPast is sent on this channel, but it doesnot get logged here
389390
cleanup()
390391
logger.Errorf("error from epochHandler: %s", err)
391392
}
@@ -400,7 +401,10 @@ func (b *Service) handleEpoch(epoch uint64) (next uint64, err error) {
400401
return next, nil
401402
}
402403

403-
func (b *Service) handleSlot(epoch, slotNum uint64, authorityIndex uint32, proof *VrfOutputAndProof) error {
404+
func (b *Service) handleSlot(epoch, slotNum uint64,
405+
authorityIndex uint32,
406+
preRuntimeDigest *types.PreRuntimeDigest,
407+
) error {
404408
parentHeader, err := b.blockState.BestBlockHeader()
405409
if err != nil {
406410
return err
@@ -442,7 +446,7 @@ func (b *Service) handleSlot(epoch, slotNum uint64, authorityIndex uint32, proof
442446

443447
rt.SetContextStorage(ts)
444448

445-
block, err := b.buildBlock(parent, currentSlot, rt, authorityIndex, proof)
449+
block, err := b.buildBlock(parent, currentSlot, rt, authorityIndex, preRuntimeDigest)
446450
if err != nil {
447451
return err
448452
}

lib/babe/build.go

Lines changed: 14 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ const (
2626

2727
// construct a block for this slot with the given parent
2828
func (b *Service) buildBlock(parent *types.Header, slot Slot, rt runtime.Instance,
29-
authorityIndex uint32, proof *VrfOutputAndProof) (*types.Block, error) {
29+
authorityIndex uint32, preRuntimeDigest *types.PreRuntimeDigest) (*types.Block, error) {
3030
builder, err := NewBlockBuilder(
3131
b.keypair,
3232
b.transactionState,
3333
b.blockState,
34-
proof,
3534
authorityIndex,
35+
preRuntimeDigest,
3636
)
3737
if err != nil {
3838
return nil, fmt.Errorf("failed to create block builder: %w", err)
@@ -59,30 +59,31 @@ type BlockBuilder struct {
5959
keypair *sr25519.Keypair
6060
transactionState TransactionState
6161
blockState BlockState
62-
proof *VrfOutputAndProof
6362
currentAuthorityIndex uint32
63+
preRuntimeDigest *types.PreRuntimeDigest
6464
}
6565

6666
// NewBlockBuilder creates a new block builder.
67-
func NewBlockBuilder(kp *sr25519.Keypair, ts TransactionState,
68-
bs BlockState, proof *VrfOutputAndProof,
69-
authidx uint32) (*BlockBuilder, error) {
67+
func NewBlockBuilder(
68+
kp *sr25519.Keypair,
69+
ts TransactionState,
70+
bs BlockState,
71+
authidx uint32,
72+
preRuntimeDigest *types.PreRuntimeDigest,
73+
) (*BlockBuilder, error) {
7074
if ts == nil {
7175
return nil, ErrNilTransactionState
7276
}
7377
if bs == nil {
7478
return nil, ErrNilBlockState
7579
}
76-
if proof == nil {
77-
return nil, ErrNilVRFProof
78-
}
7980

8081
bb := &BlockBuilder{
8182
keypair: kp,
8283
transactionState: ts,
8384
blockState: bs,
84-
proof: proof,
8585
currentAuthorityIndex: authidx,
86+
preRuntimeDigest: preRuntimeDigest,
8687
}
8788

8889
return bb, nil
@@ -91,18 +92,10 @@ func NewBlockBuilder(kp *sr25519.Keypair, ts TransactionState,
9192
func (b *BlockBuilder) buildBlock(parent *types.Header, slot Slot, rt runtime.Instance) (*types.Block, error) {
9293
logger.Tracef("build block with parent %s and slot: %s", parent, slot)
9394

94-
// create pre-digest
95-
preDigest, err := b.buildBlockPreDigest(slot)
96-
if err != nil {
97-
return nil, err
98-
}
99-
100-
logger.Trace("built pre-digest")
101-
10295
// create new block header
10396
number := big.NewInt(0).Add(parent.Number, big.NewInt(1))
10497
digest := types.NewDigest()
105-
err = digest.Add(*preDigest)
98+
err := digest.Add(*b.preRuntimeDigest)
10699
if err != nil {
107100
return nil, err
108101
}
@@ -120,7 +113,7 @@ func (b *BlockBuilder) buildBlock(parent *types.Header, slot Slot, rt runtime.In
120113
logger.Trace("initialised block")
121114

122115
// add block inherents
123-
inherents, err := b.buildBlockInherents(slot, rt)
116+
inherents, err := buildBlockInherents(slot, rt)
124117
if err != nil {
125118
return nil, fmt.Errorf("cannot build inherents: %s", err)
126119
}
@@ -191,37 +184,6 @@ func (b *BlockBuilder) buildBlockSeal(header *types.Header) (*types.SealDigest,
191184
}, nil
192185
}
193186

194-
// buildBlockPreDigest creates the pre-digest for the slot.
195-
// the pre-digest consists of the ConsensusEngineID and the encoded BABE header for the slot.
196-
func (b *BlockBuilder) buildBlockPreDigest(slot Slot) (*types.PreRuntimeDigest, error) {
197-
babeHeader := types.NewBabeDigest()
198-
data := b.buildBlockBABEPrimaryPreDigest(slot)
199-
if err := babeHeader.Set(*data); err != nil {
200-
return nil, fmt.Errorf("cannot set babe header: %w", err)
201-
}
202-
203-
encBABEPrimaryPreDigest, err := scale.Marshal(babeHeader)
204-
if err != nil {
205-
return nil, err
206-
}
207-
208-
return &types.PreRuntimeDigest{
209-
ConsensusEngineID: types.BabeEngineID,
210-
Data: encBABEPrimaryPreDigest,
211-
}, nil
212-
}
213-
214-
// buildBlockBABEPrimaryPreDigest creates the BABE header for the slot.
215-
// the BABE header includes the proof of authorship right for this slot.
216-
func (b *BlockBuilder) buildBlockBABEPrimaryPreDigest(slot Slot) *types.BabePrimaryPreDigest {
217-
return types.NewBabePrimaryPreDigest(
218-
b.currentAuthorityIndex,
219-
slot.number,
220-
b.proof.output,
221-
b.proof.proof,
222-
)
223-
}
224-
225187
// buildBlockExtrinsics applies extrinsics to the block. it returns an array of included extrinsics.
226188
// for each extrinsic in queue, add it to the block, until the slot ends or the block is full.
227189
// if any extrinsic fails, it returns an empty array and an error.
@@ -276,7 +238,7 @@ func (b *BlockBuilder) buildBlockExtrinsics(slot Slot, rt runtime.Instance) []*t
276238
return included
277239
}
278240

279-
func (b *BlockBuilder) buildBlockInherents(slot Slot, rt runtime.Instance) ([][]byte, error) {
241+
func buildBlockInherents(slot Slot, rt runtime.Instance) ([][]byte, error) {
280242
// Setup inherents: add timstap0
281243
idata := types.NewInherentsData()
282244
timestamp := uint64(time.Now().UnixMilli())

lib/babe/build_integration_test.go

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@ func TestSeal(t *testing.T) {
4141
babeService.epochHandler, err = babeService.initiateAndGetEpochHandler(0)
4242
require.NoError(t, err)
4343

44-
authoringSlots := getAuthoringSlots(babeService.epochHandler.slotToProof)
44+
authoringSlots := getAuthoringSlots(babeService.epochHandler.slotToPreRuntimeDigest)
4545
require.NotEmpty(t, authoringSlots)
4646

4747
builder, _ := NewBlockBuilder(
4848
babeService.keypair,
4949
babeService.transactionState,
5050
babeService.blockState,
51-
babeService.epochHandler.slotToProof[authoringSlots[0]],
5251
babeService.epochHandler.epochData.authorityIndex,
52+
babeService.epochHandler.slotToPreRuntimeDigest[authoringSlots[0]],
5353
)
5454

5555
zeroHash, err := common.HexToHash("0x00")
@@ -92,10 +92,10 @@ func createTestBlock(t *testing.T, babeService *Service, parent *types.Header,
9292
rt, err := babeService.blockState.GetRuntime(nil)
9393
require.NoError(t, err)
9494

95-
outAndProof, err := babeService.runLottery(slotNumber, epoch, epochData)
95+
preRuntimeDigest, err := claimSlot(epoch, slotNumber, epochData, babeService.keypair)
9696
require.NoError(t, err)
9797

98-
block, err := babeService.buildBlock(parent, slot, rt, epochData.authorityIndex, outAndProof)
98+
block, err := babeService.buildBlock(parent, slot, rt, epochData.authorityIndex, preRuntimeDigest)
9999
require.NoError(t, err)
100100

101101
babeService.blockState.StoreRuntime(block.Header.Hash(), rt)
@@ -154,14 +154,6 @@ func TestApplyExtrinsic(t *testing.T) {
154154
babeService := createTestService(t, cfg)
155155
const authorityIndex = 0
156156

157-
builder, _ := NewBlockBuilder(
158-
babeService.keypair,
159-
babeService.transactionState,
160-
babeService.blockState,
161-
&VrfOutputAndProof{},
162-
authorityIndex,
163-
)
164-
165157
duration, err := time.ParseDuration("1s")
166158
require.NoError(t, err)
167159

@@ -177,8 +169,13 @@ func TestApplyExtrinsic(t *testing.T) {
177169
duration: duration,
178170
number: 2,
179171
}
172+
testVRFOutputAndProof := &VrfOutputAndProof{}
180173

181-
preDigest2, err := builder.buildBlockPreDigest(slot2)
174+
preDigest2, err := types.NewBabePrimaryPreDigest(
175+
authorityIndex, slot2.number,
176+
testVRFOutputAndProof.output,
177+
testVRFOutputAndProof.proof,
178+
).ToPreRuntimeDigest()
182179
require.NoError(t, err)
183180

184181
parentHash := babeService.blockState.GenesisHash()
@@ -190,7 +187,11 @@ func TestApplyExtrinsic(t *testing.T) {
190187
require.NoError(t, err)
191188
rt.SetContextStorage(ts)
192189

193-
preDigest, err := builder.buildBlockPreDigest(slot)
190+
preDigest, err := types.NewBabePrimaryPreDigest(
191+
authorityIndex, slot.number,
192+
testVRFOutputAndProof.output,
193+
testVRFOutputAndProof.proof,
194+
).ToPreRuntimeDigest()
194195
require.NoError(t, err)
195196

196197
digest := types.NewDigest()
@@ -204,7 +205,7 @@ func TestApplyExtrinsic(t *testing.T) {
204205
err = rt.InitializeBlock(header)
205206
require.NoError(t, err)
206207

207-
_, err = builder.buildBlockInherents(slot, rt)
208+
_, err = buildBlockInherents(slot, rt)
208209
require.NoError(t, err)
209210

210211
header1, err := rt.FinalizeBlock()
@@ -223,7 +224,7 @@ func TestApplyExtrinsic(t *testing.T) {
223224
err = rt.InitializeBlock(header2)
224225
require.NoError(t, err)
225226

226-
_, err = builder.buildBlockInherents(slot, rt)
227+
_, err = buildBlockInherents(slot, rt)
227228
require.NoError(t, err)
228229

229230
res, err := rt.ApplyExtrinsic(extBytes)
@@ -381,7 +382,7 @@ func TestBuildBlock_failing(t *testing.T) {
381382
require.NoError(t, err)
382383

383384
const authorityIndex uint32 = 0
384-
_, err = babeService.buildBlock(parentHeader, slot, rt, authorityIndex, &VrfOutputAndProof{})
385+
_, err = babeService.buildBlock(parentHeader, slot, rt, authorityIndex, &types.PreRuntimeDigest{})
385386
require.NotNil(t, err)
386387
require.Equal(t, "cannot build extrinsics: error applying extrinsic: Apply error, type: Payment",
387388
err.Error(), "Did not receive expected error text")

0 commit comments

Comments
 (0)