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

feat(lib/babe): implement secondary slot block production #2260

Merged
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
e41869d
feat(lib/babe): implement secondary slot block production
kishansagathiya Jan 31, 2022
ea2729c
Initialize the map
kishansagathiya Feb 3, 2022
9d691a7
Merge branch 'development' into kishan/feat/secondary-slot-block-prod…
kishansagathiya Feb 3, 2022
a42b712
adjusting to new babe changes
kishansagathiya Feb 3, 2022
a3d7d4a
adjusting to new babe changes
kishansagathiya Feb 3, 2022
25bddae
fixed some errors and addressed reviews
kishansagathiya Feb 4, 2022
1c09237
Pass pre runtime digest through functions
kishansagathiya Feb 7, 2022
c25d9d6
adjusting some tests
kishansagathiya Feb 7, 2022
a9c0d07
removing unused params
kishansagathiya Feb 7, 2022
454eab2
attempting to stress test
kishansagathiya Feb 7, 2022
151482c
addressed reviews
kishansagathiya Feb 8, 2022
25d7a92
fixing some more errors
kishansagathiya Feb 9, 2022
d771a94
rename
kishansagathiya Feb 9, 2022
1bd089b
Small changes and TODO comments while debugging
kishansagathiya Feb 14, 2022
aea4766
pass lint
kishansagathiya Feb 14, 2022
8e23302
small changes
kishansagathiya Feb 15, 2022
a7e027f
Update lib/babe/epoch_handler.go
kishansagathiya Feb 15, 2022
f2a407f
Merge branch 'kishan/feat/secondary-slot-block-production' of github.…
kishansagathiya Feb 15, 2022
1550205
make sure epochData.secondary gets filled
kishansagathiya Feb 15, 2022
a06c66b
Update lib/babe/verify_integration_test.go
kishansagathiya Feb 17, 2022
b27b37a
Update lib/babe/epoch_handler.go
kishansagathiya Feb 17, 2022
8b6a6d0
Update tests/stress/stress_test.go
kishansagathiya Feb 17, 2022
a9a9d11
addressed reviews
kishansagathiya Feb 17, 2022
f0aab84
more fixes
kishansagathiya Feb 17, 2022
255fdd7
more fixes
kishansagathiya Feb 17, 2022
c7f1730
Merge branch 'development' into kishan/feat/secondary-slot-block-prod…
kishansagathiya Feb 18, 2022
34f2605
feat(lib/babe): implement secondary vrf slot block production (#2307)
kishansagathiya Feb 25, 2022
c9b863a
Update lib/babe/epoch.go
kishansagathiya Feb 28, 2022
d1afc50
addressed reviews, modified tests
kishansagathiya Feb 28, 2022
988395c
Merge branch 'kishan/feat/secondary-slot-block-production' of github.…
kishansagathiya Feb 28, 2022
e9b24a9
fixing lint
kishansagathiya Feb 28, 2022
347a698
Addressed Eclesio's comments
kishansagathiya Mar 2, 2022
11b326b
Update tests/stress/stress_test.go
kishansagathiya Mar 2, 2022
4de8ed5
Using ToPreRuntimeDigest() method now
kishansagathiya Mar 3, 2022
678b2f8
more of quentin's comments
kishansagathiya Mar 3, 2022
c182a0d
Merge branch 'kishan/feat/secondary-slot-block-production' of github.…
kishansagathiya Mar 3, 2022
1291d1b
Merge branch 'development' into kishan/feat/secondary-slot-block-prod…
kishansagathiya Mar 3, 2022
983af6e
Update lib/babe/epoch.go
kishansagathiya Mar 3, 2022
0c8a7b0
Update lib/babe/epoch.go
kishansagathiya Mar 3, 2022
bc775c6
Update lib/babe/epoch.go
kishansagathiya Mar 3, 2022
9d6d62e
removed a comment
kishansagathiya Mar 4, 2022
e71b76d
Merge branch 'development' of github.com:ChainSafe/gossamer into kish…
kishansagathiya Mar 4, 2022
1a44de1
Merge branch 'kishan/feat/secondary-slot-block-production' of github.…
kishansagathiya Mar 4, 2022
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
15 changes: 12 additions & 3 deletions dot/sync/chain_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,24 @@ 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 {
logger.Debugf("failed to check if block state has header for hash %s: %s", bd.Hash, err)
return err
kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved
}
hasBody, err := s.blockState.HasBlockBody(bd.Hash)
if err != nil {
logger.Debugf("failed to check block state has body for hash %s: %s", bd.Hash, err)
return 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
4 changes: 2 additions & 2 deletions lib/babe/babe.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ 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, proof *VrfOutputAndProof, ifPrimary bool) error {
parentHeader, err := b.blockState.BestBlockHeader()
if err != nil {
return err
Expand Down Expand Up @@ -442,7 +442,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, proof, ifPrimary)
if err != nil {
return err
}
Expand Down
41 changes: 31 additions & 10 deletions lib/babe/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ 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, proof *VrfOutputAndProof, ifPrimary bool) (*types.Block, error) {
noot marked this conversation as resolved.
Show resolved Hide resolved
builder, err := NewBlockBuilder(
b.keypair,
b.transactionState,
Expand All @@ -42,7 +42,7 @@ func (b *Service) buildBlock(parent *types.Header, slot Slot, rt runtime.Instanc
ethmetrics.Enabled = true

start := time.Now()
block, err := builder.buildBlock(parent, slot, rt)
block, err := builder.buildBlock(parent, slot, rt, ifPrimary)
if err != nil {
builderErrors := ethmetrics.GetOrRegisterCounter(buildBlockErrors, nil)
builderErrors.Inc(1)
Expand Down Expand Up @@ -88,11 +88,11 @@ func NewBlockBuilder(kp *sr25519.Keypair, ts TransactionState,
return bb, nil
}

func (b *BlockBuilder) buildBlock(parent *types.Header, slot Slot, rt runtime.Instance) (*types.Block, error) {
func (b *BlockBuilder) buildBlock(parent *types.Header, slot Slot, rt runtime.Instance, ifPrimary bool) (*types.Block, error) {
logger.Tracef("build block with parent %s and slot: %s", parent, slot)

// create pre-digest
preDigest, err := b.buildBlockPreDigest(slot)
preDigest, err := b.buildBlockPreDigest(slot, ifPrimary)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -193,21 +193,35 @@ func (b *BlockBuilder) buildBlockSeal(header *types.Header) (*types.SealDigest,

// 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) {
func (b *BlockBuilder) buildBlockPreDigest(slot Slot, ifPrimary bool) (*types.PreRuntimeDigest, error) {

// check if secondary slot

// TODO: if author_secondary_vrf enabled, use SecondaryVRF
// otherwise BlockBabeSecondaryPlainPreDigest
babeHeader := types.NewBabeDigest()
data := b.buildBlockBABEPrimaryPreDigest(slot)
if err := babeHeader.Set(*data); err != nil {
return nil, fmt.Errorf("cannot set babe header: %w", err)
if ifPrimary {
data := b.buildBlockBABEPrimaryPreDigest(slot)
err := babeHeader.Set(*data)
if err != nil {
return nil, fmt.Errorf("cannot set babe header: %w", err)
}
} else {
data := b.buildBlockBabeSecondaryPlainPreDigest(slot)
err := babeHeader.Set(*data)
if err != nil {
return nil, fmt.Errorf("cannot set babe header: %w", err)
}
}

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

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

Expand All @@ -222,6 +236,13 @@ func (b *BlockBuilder) buildBlockBABEPrimaryPreDigest(slot Slot) *types.BabePrim
)
}

func (b *BlockBuilder) buildBlockBabeSecondaryPlainPreDigest(slot Slot) *types.BabeSecondaryPlainPreDigest {
return &types.BabeSecondaryPlainPreDigest{
AuthorityIndex: b.currentAuthorityIndex,
SlotNumber: slot.number,
}
}

// 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
34 changes: 34 additions & 0 deletions lib/babe/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"math"
"math/big"

"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/crypto"
"github.com/ChainSafe/gossamer/lib/crypto/sr25519"
"github.com/ChainSafe/gossamer/pkg/scale"
Expand Down Expand Up @@ -83,6 +84,39 @@ func checkPrimaryThreshold(randomness Randomness,
return inoutUint.Compare(threshold) < 0, nil
}

func claimSecondarySlot(randomness Randomness,
slot, epoch uint64,
authorities []types.Authority,
threshold *scale.Uint128,
keypair *sr25519.Keypair,
authorityIndex uint32,
) (*VrfOutputAndProof, error) {

kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved
secondarySlotAuthor, err := getSecondarySlotAuthor(slot, len(authorities), randomness)
if err != nil {
return nil, err
kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved
}

if int(authorityIndex) == int(secondarySlotAuthor) {
kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved
transcript := makeTranscript(randomness, slot, epoch)

out, proof, err := keypair.VrfSign(transcript)
if err != nil {
return nil, err
kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved
}

logger.Debugf("claimed secondary slot, for slot number: %d", slot)

return &VrfOutputAndProof{
output: out,
proof: proof,
}, nil
}

// It is not our turn to propose
return nil, nil
kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved
}

// CalculateThreshold calculates the slot lottery threshold
// equation: threshold = 2^128 * (1 - (1-c)^(1/len(authorities))
// see https://github.com/paritytech/substrate/blob/master/client/consensus/babe/src/authorship.rs#L44
Expand Down
17 changes: 16 additions & 1 deletion lib/babe/epoch.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ func (b *Service) getEpochDataAndStartSlot(epoch uint64) (*epochData, uint64, er
authorities: data.Authorities,
authorityIndex: idx,
threshold: threshold,
secondary: cfgData.SecondarySlots,
}

startSlot, err := b.epochState.GetStartSlotForEpoch(epoch)
Expand Down Expand Up @@ -222,11 +223,25 @@ func (b *Service) incrementEpoch() (uint64, error) {
// if it is not authorised.
// output = return[0:32]; proof = return[32:96]
func (b *Service) runLottery(slot, epoch uint64, epochData *epochData) (*VrfOutputAndProof, error) {
return claimPrimarySlot(
// TODO: Check if whether to store if a primary slot was claimed? How?
noot marked this conversation as resolved.
Show resolved Hide resolved
proof, err := claimPrimarySlot(
epochData.randomness,
slot,
epoch,
epochData.threshold,
b.keypair,
)
if err == nil {
return proof, nil
}

if epochData.secondary == 0 {
return nil, err
}

if errors.Is(err, errOverPrimarySlotThreshold) {
return claimSecondarySlot(epochData.randomness, slot, epoch, epochData.authorities, epochData.threshold, b.keypair, epochData.authorityIndex)
}

return nil, err
}
35 changes: 24 additions & 11 deletions lib/babe/epoch_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/ChainSafe/gossamer/lib/crypto/sr25519"
)

type handleSlotFunc = func(epoch, slotNum uint64, authorityIndex uint32, proof *VrfOutputAndProof) error
type handleSlotFunc = func(epoch, slotNum uint64, authorityIndex uint32, proof *VrfOutputAndProof, ifPrimary bool) error

var (
errEpochPast = errors.New("cannot run epoch that has already passed")
Expand All @@ -27,7 +27,8 @@ type epochHandler struct {
epochData *epochData

// for slots where we are a producer, store the vrf output (bytes 0-32) + proof (bytes 32-96)
slotToProof map[uint64]*VrfOutputAndProof
slotToProof map[uint64]*VrfOutputAndProof
slotToIfPrimary map[uint64]bool

handleSlot handleSlotFunc
}
Expand All @@ -36,6 +37,7 @@ func newEpochHandler(epochNumber, firstSlot uint64, epochData *epochData, consta
handleSlot handleSlotFunc, keypair *sr25519.Keypair) (*epochHandler, error) {
// determine which slots we'll be authoring in by pre-calculating VRF output
slotToProof := make(map[uint64]*VrfOutputAndProof, constants.epochLength)
slotToIfPrimary := make(map[uint64]bool)
for i := firstSlot; i < firstSlot+constants.epochLength; i++ {
proof, err := claimPrimarySlot(
epochData.randomness,
Expand All @@ -44,23 +46,34 @@ func newEpochHandler(epochNumber, firstSlot uint64, epochData *epochData, consta
epochData.threshold,
keypair,
)
if errors.Is(err, errOverPrimarySlotThreshold) {
if err == nil {
slotToProof[i] = proof
slotToIfPrimary[i] = true
logger.Debugf("epoch %d: claimed slot %d", epochNumber, i)
kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved
continue
} else if err != nil {
}
if !errors.Is(err, errOverPrimarySlotThreshold) {
return nil, fmt.Errorf("error running slot lottery at slot %d: %w", i, err)
}

proof, err = claimSecondarySlot(epochData.randomness, i, epochNumber, epochData.authorities, epochData.threshold, keypair, epochData.authorityIndex)
if err != nil {
return nil, fmt.Errorf("error running slot lottery at slot %d: %w", i, err)
}

slotToProof[i] = proof
slotToIfPrimary[i] = false
logger.Debugf("epoch %d: claimed slot %d", epochNumber, i)
}

return &epochHandler{
epochNumber: epochNumber,
firstSlot: firstSlot,
constants: constants,
epochData: epochData,
slotToProof: slotToProof,
handleSlot: handleSlot,
epochNumber: epochNumber,
firstSlot: firstSlot,
constants: constants,
epochData: epochData,
slotToProof: slotToProof,
handleSlot: handleSlot,
slotToIfPrimary: slotToIfPrimary,
}, nil
}

Expand Down Expand Up @@ -124,7 +137,7 @@ func (h *epochHandler) run(ctx context.Context, errCh chan<- error) {
panic(fmt.Sprintf("no VRF proof for authoring slot! slot=%d", swt.slotNum))
}

err := h.handleSlot(h.epochNumber, swt.slotNum, h.epochData.authorityIndex, h.slotToProof[swt.slotNum])
err := h.handleSlot(h.epochNumber, swt.slotNum, h.epochData.authorityIndex, h.slotToProof[swt.slotNum], h.slotToIfPrimary[swt.slotNum])
if err != nil {
logger.Warnf("failed to handle slot %d: %s", swt.slotNum, err)
continue
Expand Down
1 change: 1 addition & 0 deletions lib/babe/secondary.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/ChainSafe/gossamer/lib/crypto/sr25519"
)

// https://github.com/paritytech/substrate/blob/b07765f9ecc4c508fb4d0fba930b476c8509f5c2/client/consensus/babe/src/authorship.rs#L98
func getSecondarySlotAuthor(slot uint64, numAuths int, randomness Randomness) (uint32, error) {
s := make([]byte, 8)
binary.LittleEndian.PutUint64(s, slot)
Expand Down
1 change: 1 addition & 0 deletions lib/babe/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type epochData struct {
authorityIndex uint32
authorities []types.Authority
threshold *scale.Uint128
secondary byte
}

func (ed *epochData) String() string {
Expand Down