Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(lib/babe): implement secondary vrf slot
Browse files Browse the repository at this point in the history
- Also merge slot claim logic
kishansagathiya committed Feb 18, 2022
1 parent 255fdd7 commit b6f481d
Showing 5 changed files with 107 additions and 59 deletions.
14 changes: 14 additions & 0 deletions dot/types/babe_digest.go
Original file line number Diff line number Diff line change
@@ -119,5 +119,19 @@ func NewBabeSecondaryVRFPreDigest(authorityIndex uint32,
}
}

// ToPreRuntimeDigest returns the BabeSecondaryVRFPreDigest as a PreRuntimeDigest
func (d *BabeSecondaryVRFPreDigest) 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
}

// Index Returns VDT index
func (d BabeSecondaryVRFPreDigest) Index() uint { return 3 }
31 changes: 31 additions & 0 deletions lib/babe/crypto.go
Original file line number Diff line number Diff line change
@@ -84,6 +84,37 @@ func checkPrimaryThreshold(randomness Randomness,
return inoutUint.Compare(threshold) < 0, nil
}

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

secondarySlotAuthor, err := getSecondarySlotAuthor(slot, len(authorities), randomness)
if err != nil {
return nil, fmt.Errorf("cannot get secondary slot author: %w", err)
}

if authorityIndex == secondarySlotAuthor {
transcript := makeTranscript(randomness, slot, epoch)

out, proof, err := keypair.VrfSign(transcript)
if err != nil {
return nil, fmt.Errorf("cannot verify transcript: %w", err)
}

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

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

return nil, errNotOurTurnToPropose
}

func claimSecondarySlotPlain(randomness Randomness, slot uint64, authorities []types.Authority, authorityIndex uint32,
) error {
secondarySlotAuthor, err := getSecondarySlotAuthor(slot, len(authorities), randomness)
76 changes: 57 additions & 19 deletions lib/babe/epoch.go
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ import (
"math/big"

"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/crypto/sr25519"
)

// initiateEpoch sets the epochData for the given epoch, runs the lottery for the slots in the epoch,
@@ -138,7 +139,7 @@ func (b *Service) getEpochDataAndStartSlot(epoch uint64) (*epochData, uint64, er
authorities: data.Authorities,
authorityIndex: idx,
threshold: threshold,
secondary: cfgData.SecondarySlots,
secondary: types.AllowedSlots(cfgData.SecondarySlots),
}

startSlot, err := b.epochState.GetStartSlotForEpoch(epoch)
@@ -165,7 +166,7 @@ func (b *Service) getLatestEpochData() (resEpochData *epochData, error error) {
return nil, fmt.Errorf("cannot get epoch state latest config data: %w", err)
}

resEpochData.secondary = configData.SecondarySlots
resEpochData.secondary = types.AllowedSlots(configData.SecondarySlots)

resEpochData.threshold, err = CalculateThreshold(configData.C1, configData.C2, len(resEpochData.authorities))
if err != nil {
@@ -187,7 +188,7 @@ func (b *Service) getLatestEpochData() (resEpochData *epochData, error error) {
func (b *Service) getFirstAuthoringSlot(epoch uint64, epochData *epochData) (uint64, error) {
startSlot := getCurrentSlot(b.constants.slotDuration)
for i := startSlot; i < startSlot+b.constants.epochLength; i++ {
_, err := b.runLottery(i, epoch, epochData)
_, err := claimSlot(epoch, i, epochData, b.keypair)
if err != nil {
if errors.Is(err, errOverPrimarySlotThreshold) {
continue
@@ -221,37 +222,74 @@ func (b *Service) incrementEpoch() (uint64, error) {
return next, nil
}

// runLottery runs the lottery for a specific slot number.
// claimSlot claims slot for a specific slot number.
// It returns an encoded VrfOutputAndProof if the validator is authorised
// to produce a block for that slot.
// It returns the wrapped error errOverPrimarySlotThreshold
// if it is not authorised.
// output = return[0:32]; proof = return[32:96]
func (b *Service) runLottery(slot, epoch uint64, epochData *epochData) (*types.PreRuntimeDigest, error) {
func claimSlot(epochNumber uint64, slotNumber uint64, epochData *epochData, keypair *sr25519.Keypair) (*types.PreRuntimeDigest, error) {
proof, err := claimPrimarySlot(
epochData.randomness,
slot,
epoch,
slotNumber,
epochNumber,
epochData.threshold,
b.keypair,
keypair,
)
if err == nil {
return types.NewBabePrimaryPreDigest(
epochData.authorityIndex, slot, proof.output, proof.proof).ToPreRuntimeDigest()

switch err {
case nil:
preRuntimeDigest, err := types.NewBabePrimaryPreDigest(
epochData.authorityIndex, slotNumber, proof.output, proof.proof).ToPreRuntimeDigest()
if err != nil {
return nil, fmt.Errorf("error converting babe primary pre-digest to pre-runtime digest: %w", err)
}
logger.Debugf("epoch %d: claimed primary slot %d", epochNumber, slotNumber)
return preRuntimeDigest, nil
default:
if !errors.Is(err, errOverPrimarySlotThreshold) {
return nil, fmt.Errorf("error running slot lottery at slot %d: %w", slotNumber, err)
}
}

if errors.Is(err, errOverPrimarySlotThreshold) {
if epochData.secondary == 0 {
return nil, errSecondarySlotProductionDisabled
switch epochData.secondary {
case types.PrimarySlots:
return nil, errSecondarySlotProductionDisabled
case types.PrimaryAndSecondaryVRFSlots:
proof, err := claimSecondarySlotVRF(
epochData.randomness, slotNumber, epochNumber, epochData.authorities, keypair, epochData.authorityIndex)
// if errors.Is(err, errNotOurTurnToPropose) {
// continue
// }
if err != nil {
return nil, fmt.Errorf("error claim secondary vrf slot at %d: %w", slotNumber, err)
}
preRuntimeDigest, err := types.NewBabeSecondaryVRFPreDigest(
epochData.authorityIndex, slotNumber, proof.output, proof.proof).ToPreRuntimeDigest()
if err != nil {
return nil, fmt.Errorf("error converting babe secondary vrf pre-digest to pre-runtime digest: %w", err)
}
logger.Debugf("epoch %d: claimed secondary vrf slot %d", epochNumber, slotNumber)
return preRuntimeDigest, nil
case types.PrimaryAndSecondaryPlainSlots:
err = claimSecondarySlotPlain(
epochData.randomness, slotNumber, epochData.authorities, epochData.authorityIndex)
// if errors.Is(err, errNotOurTurnToPropose) {
// continue
// }
if err != nil {
return nil, fmt.Errorf("error claiming secondary plain slot at %d: %w", slotNumber, err)
}

err = claimSecondarySlotPlain(epochData.randomness, slot,
epochData.authorities, epochData.authorityIndex)
preRuntimeDigest, err := types.NewBabeSecondaryPlainPreDigest(
epochData.authorityIndex, slotNumber).ToPreRuntimeDigest()
if err != nil {
return nil, fmt.Errorf("cannot claim secondary slot: %w", err)
return nil, fmt.Errorf(
"failed to get preruntime digest from babe secondary plain predigest for slot %d: %w", slotNumber, err)
}
return types.NewBabeSecondaryPlainPreDigest(epochData.authorityIndex, slot).ToPreRuntimeDigest()
logger.Debugf("epoch %d: claimed secondary plain slot %d", epochNumber, slotNumber)
return preRuntimeDigest, nil
}

return nil, err
return nil, errors.New("invalid slot claiming technique")
}
43 changes: 4 additions & 39 deletions lib/babe/epoch_handler.go
Original file line number Diff line number Diff line change
@@ -37,52 +37,17 @@ func newEpochHandler(epochNumber, firstSlot uint64, epochData *epochData, consta
// determine which slots we'll be authoring in by pre-calculating VRF output
slotToPreRuntimeDigest := make(map[uint64]*types.PreRuntimeDigest, constants.epochLength)
for i := firstSlot; i < firstSlot+constants.epochLength; i++ {
proof, err := claimPrimarySlot(
epochData.randomness,
i,
epochNumber,
epochData.threshold,
keypair,
)

switch err {
case nil:
preRuntimeDigest, err := types.NewBabePrimaryPreDigest(
epochData.authorityIndex, i, proof.output, proof.proof).ToPreRuntimeDigest()
if err != nil {
return nil, fmt.Errorf("error converting babe primary pre-digest to pre-runtime digest: %w", err)
}
preRuntimeDigest, err := claimSlot(epochNumber, i, epochData, keypair)
if err == nil {
slotToPreRuntimeDigest[i] = preRuntimeDigest
logger.Debugf("epoch %d: claimed primary slot %d", epochNumber, i)
continue
default:
if !errors.Is(err, errOverPrimarySlotThreshold) {
return nil, fmt.Errorf("error running slot lottery at slot %d: %w", i, err)
}
}

if epochData.secondary == 0 {
if errors.Is(err, errNotOurTurnToPropose) || errors.Is(err, errSecondarySlotProductionDisabled) {
continue
}

err = claimSecondarySlotPlain(
epochData.randomness, i, epochData.authorities, epochData.authorityIndex)
if errors.Is(err, errNotOurTurnToPropose) {
continue
}
if err != nil {
return nil, fmt.Errorf("error running slot lottery at slot %d: %w", i, err)
}

preRuntimeDigest, err := types.NewBabeSecondaryPlainPreDigest(
epochData.authorityIndex, i).ToPreRuntimeDigest()
if err != nil {
return nil, fmt.Errorf(
"failed to get preruntime digest from babe secondary plain predigest for slot %d: %w", i, err)
}
slotToPreRuntimeDigest[i] = preRuntimeDigest
logger.Debugf("epoch %d: claimed secondary slot %d", epochNumber, i)

return nil, err
}

return &epochHandler{
2 changes: 1 addition & 1 deletion lib/babe/types.go
Original file line number Diff line number Diff line change
@@ -60,7 +60,7 @@ type epochData struct {
authorityIndex uint32
authorities []types.Authority
threshold *scale.Uint128
secondary byte
secondary types.AllowedSlots
}

func (ed *epochData) String() string {

0 comments on commit b6f481d

Please sign in to comment.