Skip to content

Commit

Permalink
feat(lib/babe): implement secondary slot block production (#2260)
Browse files Browse the repository at this point in the history
implement secondary VRF slot block production and secondary plain slot block production
  • Loading branch information
kishansagathiya authored Mar 4, 2022
1 parent be078e5 commit fcb81a3
Show file tree
Hide file tree
Showing 17 changed files with 457 additions and 161 deletions.
13 changes: 10 additions & 3 deletions dot/sync/chain_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,22 @@ func (s *chainProcessor) processReadyBlocks() {
}

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

hasHeader, _ := s.blockState.HasHeader(bd.Hash)
hasBody, _ := s.blockState.HasBlockBody(bd.Hash)
hasHeader, err := s.blockState.HasHeader(bd.Hash)
if err != nil {
return fmt.Errorf("failed to check if block state has header for hash %s: %w", bd.Hash, err)
}
hasBody, err := s.blockState.HasBlockBody(bd.Hash)
if err != nil {
return fmt.Errorf("failed to check block state has body for hash %s: %w", bd.Hash, err)
}

if hasHeader && hasBody {
// TODO: fix this; sometimes when the node shuts down the "best block" isn't stored properly,
// so when the node restarts it has blocks higher than what it thinks is the best, causing it not to sync
Expand Down
13 changes: 13 additions & 0 deletions dot/types/babe.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@ import (
// RandomnessLength is the length of the epoch randomness (32 bytes)
const RandomnessLength = 32

// AllowedSlots tells in what ways a slot can be claimed.
type AllowedSlots byte

// https://github.com/paritytech/substrate/blob/ded44948e2d5a398abcb4e342b0513cb690961bb/primitives/consensus/babe/src/lib.rs#L219-L226
const (
// PrimarySlots only allows primary slots.
PrimarySlots AllowedSlots = iota
// PrimaryAndSecondaryPlainSlots allow primary and secondary plain slots.
PrimaryAndSecondaryPlainSlots
// PrimaryAndSecondaryVRFSlots allows primary and secondary VRF slots.
PrimaryAndSecondaryVRFSlots
)

// BabeConfiguration contains the genesis data for BABE
//nolint:lll
// see: https://github.com/paritytech/substrate/blob/426c26b8bddfcdbaf8d29f45b128e0864b57de1c/core/consensus/babe/primitives/src/lib.rs#L132
Expand Down
46 changes: 25 additions & 21 deletions dot/types/babe_digest.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package types

import (
"errors"
"fmt"

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

// ToPreRuntimeDigest returns the BabePrimaryPreDigest as a PreRuntimeDigest
func (d *BabePrimaryPreDigest) ToPreRuntimeDigest() (*PreRuntimeDigest, error) {
digest := NewBabeDigest()
err := digest.Set(*d)
if err != nil {
return nil, err
}
enc, err := scale.Marshal(digest)
if err != nil {
return nil, err
}
return NewBABEPreRuntimeDigest(enc), nil
return toPreRuntimeDigest(*d)
}

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

// ToPreRuntimeDigest returns the BabePrimaryPreDigest as a PreRuntimeDigest
// ToPreRuntimeDigest returns the BabeSecondaryPlainPreDigest as a PreRuntimeDigest
func (d *BabeSecondaryPlainPreDigest) ToPreRuntimeDigest() (*PreRuntimeDigest, error) {
digest := NewBabeDigest()
err := digest.Set(*d)
if err != nil {
return nil, err
}
enc, err := scale.Marshal(digest)
if err != nil {
return nil, err
}
return NewBABEPreRuntimeDigest(enc), nil
return toPreRuntimeDigest(*d)
}

// Index Returns VDT index
Expand All @@ -119,5 +102,26 @@ func NewBabeSecondaryVRFPreDigest(authorityIndex uint32,
}
}

// ToPreRuntimeDigest returns the BabeSecondaryVRFPreDigest as a PreRuntimeDigest
func (d *BabeSecondaryVRFPreDigest) ToPreRuntimeDigest() (*PreRuntimeDigest, error) {
return toPreRuntimeDigest(*d)
}

// Index Returns VDT index
func (d BabeSecondaryVRFPreDigest) Index() uint { return 3 }

// toPreRuntimeDigest returns the VaryingDataTypeValue as a PreRuntimeDigest
func toPreRuntimeDigest(value scale.VaryingDataTypeValue) (*PreRuntimeDigest, error) {
digest := NewBabeDigest()
err := digest.Set(value)
if err != nil {
return nil, fmt.Errorf("cannot set varying data type value to babe digest: %w", err)
}

enc, err := scale.Marshal(digest)
if err != nil {
return nil, fmt.Errorf("cannot marshal babe digest: %w", err)
}

return NewBABEPreRuntimeDigest(enc), nil
}
8 changes: 6 additions & 2 deletions lib/babe/babe.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ func (b *Service) handleEpoch(epoch uint64) (next uint64, err error) {
// stop current epoch handler
cancel()
case err := <-errCh:
// TODO: errEpochPast is sent on this channel, but it doesnot get logged here
cleanup()
logger.Errorf("error from epochHandler: %s", err)
}
Expand All @@ -400,7 +401,10 @@ func (b *Service) handleEpoch(epoch uint64) (next uint64, err error) {
return next, nil
}

func (b *Service) handleSlot(epoch, slotNum uint64, authorityIndex uint32, proof *VrfOutputAndProof) error {
func (b *Service) handleSlot(epoch, slotNum uint64,
authorityIndex uint32,
preRuntimeDigest *types.PreRuntimeDigest,
) error {
parentHeader, err := b.blockState.BestBlockHeader()
if err != nil {
return err
Expand Down Expand Up @@ -442,7 +446,7 @@ func (b *Service) handleSlot(epoch, slotNum uint64, authorityIndex uint32, proof

rt.SetContextStorage(ts)

block, err := b.buildBlock(parent, currentSlot, rt, authorityIndex, proof)
block, err := b.buildBlock(parent, currentSlot, rt, authorityIndex, preRuntimeDigest)
if err != nil {
return err
}
Expand Down
66 changes: 14 additions & 52 deletions lib/babe/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ const (

// construct a block for this slot with the given parent
func (b *Service) buildBlock(parent *types.Header, slot Slot, rt runtime.Instance,
authorityIndex uint32, proof *VrfOutputAndProof) (*types.Block, error) {
authorityIndex uint32, preRuntimeDigest *types.PreRuntimeDigest) (*types.Block, error) {
builder, err := NewBlockBuilder(
b.keypair,
b.transactionState,
b.blockState,
proof,
authorityIndex,
preRuntimeDigest,
)
if err != nil {
return nil, fmt.Errorf("failed to create block builder: %w", err)
Expand All @@ -59,30 +59,31 @@ type BlockBuilder struct {
keypair *sr25519.Keypair
transactionState TransactionState
blockState BlockState
proof *VrfOutputAndProof
currentAuthorityIndex uint32
preRuntimeDigest *types.PreRuntimeDigest
}

// NewBlockBuilder creates a new block builder.
func NewBlockBuilder(kp *sr25519.Keypair, ts TransactionState,
bs BlockState, proof *VrfOutputAndProof,
authidx uint32) (*BlockBuilder, error) {
func NewBlockBuilder(
kp *sr25519.Keypair,
ts TransactionState,
bs BlockState,
authidx uint32,
preRuntimeDigest *types.PreRuntimeDigest,
) (*BlockBuilder, error) {
if ts == nil {
return nil, ErrNilTransactionState
}
if bs == nil {
return nil, ErrNilBlockState
}
if proof == nil {
return nil, ErrNilVRFProof
}

bb := &BlockBuilder{
keypair: kp,
transactionState: ts,
blockState: bs,
proof: proof,
currentAuthorityIndex: authidx,
preRuntimeDigest: preRuntimeDigest,
}

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

// create pre-digest
preDigest, err := b.buildBlockPreDigest(slot)
if err != nil {
return nil, err
}

logger.Trace("built pre-digest")

// create new block header
number := big.NewInt(0).Add(parent.Number, big.NewInt(1))
digest := types.NewDigest()
err = digest.Add(*preDigest)
err := digest.Add(*b.preRuntimeDigest)
if err != nil {
return nil, err
}
Expand All @@ -120,7 +113,7 @@ func (b *BlockBuilder) buildBlock(parent *types.Header, slot Slot, rt runtime.In
logger.Trace("initialised block")

// add block inherents
inherents, err := b.buildBlockInherents(slot, rt)
inherents, err := buildBlockInherents(slot, rt)
if err != nil {
return nil, fmt.Errorf("cannot build inherents: %s", err)
}
Expand Down Expand Up @@ -191,37 +184,6 @@ func (b *BlockBuilder) buildBlockSeal(header *types.Header) (*types.SealDigest,
}, nil
}

// buildBlockPreDigest creates the pre-digest for the slot.
// the pre-digest consists of the ConsensusEngineID and the encoded BABE header for the slot.
func (b *BlockBuilder) buildBlockPreDigest(slot Slot) (*types.PreRuntimeDigest, error) {
babeHeader := types.NewBabeDigest()
data := b.buildBlockBABEPrimaryPreDigest(slot)
if err := babeHeader.Set(*data); err != nil {
return nil, fmt.Errorf("cannot set babe header: %w", err)
}

encBABEPrimaryPreDigest, err := scale.Marshal(babeHeader)
if err != nil {
return nil, err
}

return &types.PreRuntimeDigest{
ConsensusEngineID: types.BabeEngineID,
Data: encBABEPrimaryPreDigest,
}, nil
}

// buildBlockBABEPrimaryPreDigest creates the BABE header for the slot.
// the BABE header includes the proof of authorship right for this slot.
func (b *BlockBuilder) buildBlockBABEPrimaryPreDigest(slot Slot) *types.BabePrimaryPreDigest {
return types.NewBabePrimaryPreDigest(
b.currentAuthorityIndex,
slot.number,
b.proof.output,
b.proof.proof,
)
}

// buildBlockExtrinsics applies extrinsics to the block. it returns an array of included extrinsics.
// for each extrinsic in queue, add it to the block, until the slot ends or the block is full.
// if any extrinsic fails, it returns an empty array and an error.
Expand Down Expand Up @@ -276,7 +238,7 @@ func (b *BlockBuilder) buildBlockExtrinsics(slot Slot, rt runtime.Instance) []*t
return included
}

func (b *BlockBuilder) buildBlockInherents(slot Slot, rt runtime.Instance) ([][]byte, error) {
func buildBlockInherents(slot Slot, rt runtime.Instance) ([][]byte, error) {
// Setup inherents: add timstap0
idata := types.NewInherentsData()
timestamp := uint64(time.Now().UnixMilli())
Expand Down
35 changes: 18 additions & 17 deletions lib/babe/build_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ func TestSeal(t *testing.T) {
babeService.epochHandler, err = babeService.initiateAndGetEpochHandler(0)
require.NoError(t, err)

authoringSlots := getAuthoringSlots(babeService.epochHandler.slotToProof)
authoringSlots := getAuthoringSlots(babeService.epochHandler.slotToPreRuntimeDigest)
require.NotEmpty(t, authoringSlots)

builder, _ := NewBlockBuilder(
babeService.keypair,
babeService.transactionState,
babeService.blockState,
babeService.epochHandler.slotToProof[authoringSlots[0]],
babeService.epochHandler.epochData.authorityIndex,
babeService.epochHandler.slotToPreRuntimeDigest[authoringSlots[0]],
)

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

outAndProof, err := babeService.runLottery(slotNumber, epoch, epochData)
preRuntimeDigest, err := claimSlot(epoch, slotNumber, epochData, babeService.keypair)
require.NoError(t, err)

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

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

builder, _ := NewBlockBuilder(
babeService.keypair,
babeService.transactionState,
babeService.blockState,
&VrfOutputAndProof{},
authorityIndex,
)

duration, err := time.ParseDuration("1s")
require.NoError(t, err)

Expand All @@ -177,8 +169,13 @@ func TestApplyExtrinsic(t *testing.T) {
duration: duration,
number: 2,
}
testVRFOutputAndProof := &VrfOutputAndProof{}

preDigest2, err := builder.buildBlockPreDigest(slot2)
preDigest2, err := types.NewBabePrimaryPreDigest(
authorityIndex, slot2.number,
testVRFOutputAndProof.output,
testVRFOutputAndProof.proof,
).ToPreRuntimeDigest()
require.NoError(t, err)

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

preDigest, err := builder.buildBlockPreDigest(slot)
preDigest, err := types.NewBabePrimaryPreDigest(
authorityIndex, slot.number,
testVRFOutputAndProof.output,
testVRFOutputAndProof.proof,
).ToPreRuntimeDigest()
require.NoError(t, err)

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

_, err = builder.buildBlockInherents(slot, rt)
_, err = buildBlockInherents(slot, rt)
require.NoError(t, err)

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

_, err = builder.buildBlockInherents(slot, rt)
_, err = buildBlockInherents(slot, rt)
require.NoError(t, err)

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

const authorityIndex uint32 = 0
_, err = babeService.buildBlock(parentHeader, slot, rt, authorityIndex, &VrfOutputAndProof{})
_, err = babeService.buildBlock(parentHeader, slot, rt, authorityIndex, &types.PreRuntimeDigest{})
require.NotNil(t, err)
require.Equal(t, "cannot build extrinsics: error applying extrinsic: Apply error, type: Payment",
err.Error(), "Did not receive expected error text")
Expand Down
Loading

0 comments on commit fcb81a3

Please sign in to comment.