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

chore(lib/babe): create BlockBuilder type #1602

Merged
merged 6 commits into from
May 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
83 changes: 68 additions & 15 deletions lib/babe/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,79 @@ package babe

import (
"bytes"
"errors"
"fmt"
"math/big"
"time"

"github.com/ChainSafe/gossamer/lib/crypto/sr25519"

"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/runtime"
"github.com/ChainSafe/gossamer/lib/scale"
"github.com/ChainSafe/gossamer/lib/transaction"
)

// BuildBlock builds a block for the slot with the given parent.
// TODO: separate block builder logic into separate module. The only reason this is exported is so other packages
// can build blocks for testing, but it would be preferred to have the builder functionality separated.
func (b *Service) BuildBlock(parent *types.Header, slot Slot) (*types.Block, error) {
return b.buildBlock(parent, slot)
}

// construct a block for this slot with the given parent
func (b *Service) buildBlock(parent *types.Header, slot Slot) (*types.Block, error) {
builder, err := NewBlockBuilder(
b.rt,
b.keypair,
b.transactionState,
b.blockState,
b.slotToProof,
b.epochData.authorityIndex,
)

if err != nil {
return nil, errors.New("There was an error creating block builder - " + err.Error())
}

block, err := builder.buildBlock(parent, slot)

return block, err

}

// nolint
type BlockBuilder struct {
rt runtime.Instance
keypair *sr25519.Keypair
transactionState TransactionState
blockState BlockState
slotToProof map[uint64]*VrfOutputAndProof
currentAuthorityIndex uint32
}

// nolint
func NewBlockBuilder(rt runtime.Instance, kp *sr25519.Keypair, ts TransactionState, bs BlockState, sp map[uint64]*VrfOutputAndProof, authidx uint32) (*BlockBuilder, error) {
if rt == nil {
return nil, errors.New("cannot create block builder; runtime instance is nil")
}
if ts == nil {
return nil, errors.New("cannot create block builder; transaction state is nil")
}
if bs == nil {
return nil, errors.New("cannot create block builder; block state is nil")
}
if sp == nil {
return nil, errors.New("cannot create block builder; slot to proff is nil")
}

bb := &BlockBuilder{
rt: rt,
keypair: kp,
transactionState: ts,
blockState: bs,
slotToProof: sp,
currentAuthorityIndex: authidx,
}

return bb, nil
}

func (b *BlockBuilder) buildBlock(parent *types.Header, slot Slot) (*types.Block, error) {
logger.Trace("build block", "parent", parent, "slot", slot)

// create pre-digest
Expand Down Expand Up @@ -112,7 +166,7 @@ func (b *Service) buildBlock(parent *types.Header, slot Slot) (*types.Block, err

// buildBlockSeal creates the seal for the block header.
// the seal consists of the ConsensusEngineID and a signature of the encoded block header.
func (b *Service) buildBlockSeal(header *types.Header) (*types.SealDigest, error) {
func (b *BlockBuilder) buildBlockSeal(header *types.Header) (*types.SealDigest, error) {
encHeader, err := header.Encode()
if err != nil {
return nil, err
Expand All @@ -136,7 +190,7 @@ func (b *Service) buildBlockSeal(header *types.Header) (*types.SealDigest, error

// 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 *Service) buildBlockPreDigest(slot Slot) (*types.PreRuntimeDigest, error) {
func (b *BlockBuilder) buildBlockPreDigest(slot Slot) (*types.PreRuntimeDigest, error) {
babeHeader, err := b.buildBlockBABEPrimaryPreDigest(slot)
if err != nil {
return nil, err
Expand All @@ -152,14 +206,14 @@ func (b *Service) buildBlockPreDigest(slot Slot) (*types.PreRuntimeDigest, error

// buildBlockBABEPrimaryPreDigest creates the BABE header for the slot.
// the BABE header includes the proof of authorship right for this slot.
func (b *Service) buildBlockBABEPrimaryPreDigest(slot Slot) (*types.BabePrimaryPreDigest, error) {
func (b *BlockBuilder) buildBlockBABEPrimaryPreDigest(slot Slot) (*types.BabePrimaryPreDigest, error) {
if b.slotToProof[slot.number] == nil {
return nil, ErrNotAuthorized
}

outAndProof := b.slotToProof[slot.number]
return types.NewBabePrimaryPreDigest(
b.epochData.authorityIndex,
b.currentAuthorityIndex,
slot.number,
outAndProof.output,
outAndProof.proof,
Expand All @@ -169,7 +223,7 @@ func (b *Service) buildBlockBABEPrimaryPreDigest(slot Slot) (*types.BabePrimaryP
// 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.
func (b *Service) buildBlockExtrinsics(slot Slot) []*transaction.ValidTransaction {
func (b *BlockBuilder) buildBlockExtrinsics(slot Slot) []*transaction.ValidTransaction {
var included []*transaction.ValidTransaction

for !hasSlotEnded(slot) {
Expand Down Expand Up @@ -211,8 +265,7 @@ func (b *Service) buildBlockExtrinsics(slot Slot) []*transaction.ValidTransactio
return included
}

// buildBlockInherents applies the inherents for a block
func (b *Service) buildBlockInherents(slot Slot) ([][]byte, error) {
func (b *BlockBuilder) buildBlockInherents(slot Slot) ([][]byte, error) {
// Setup inherents: add timstap0
idata := types.NewInherentsData()
err := idata.SetInt64Inherent(types.Timstap0, uint64(time.Now().Unix()))
Expand Down Expand Up @@ -275,7 +328,7 @@ func (b *Service) buildBlockInherents(slot Slot) ([][]byte, error) {
return exts.([][]byte), nil
}

func (b *Service) addToQueue(txs []*transaction.ValidTransaction) {
func (b *BlockBuilder) addToQueue(txs []*transaction.ValidTransaction) {
for _, t := range txs {
hash, err := b.transactionState.Push(t)
if err != nil {
Expand Down
22 changes: 20 additions & 2 deletions lib/babe/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ func TestSeal(t *testing.T) {

babeService := createTestService(t, cfg)

builder, _ := NewBlockBuilder(
babeService.rt,
babeService.keypair,
babeService.transactionState,
babeService.blockState,
babeService.slotToProof,
babeService.epochData.authorityIndex,
)

zeroHash, err := common.HexToHash("0x00")
require.NoError(t, err)

Expand All @@ -57,7 +66,7 @@ func TestSeal(t *testing.T) {
hash, err := common.Blake2bHash(encHeader)
require.NoError(t, err)

seal, err := babeService.buildBlockSeal(header)
seal, err := builder.buildBlockSeal(header)
require.NoError(t, err)

ok, err := kp.Public().Verify(hash[:], seal.Data)
Expand Down Expand Up @@ -115,13 +124,22 @@ func TestBuildBlock_ok(t *testing.T) {
babeService := createTestService(t, cfg)
babeService.epochData.threshold = maxThreshold

builder, _ := NewBlockBuilder(
babeService.rt,
babeService.keypair,
babeService.transactionState,
babeService.blockState,
babeService.slotToProof,
babeService.epochData.authorityIndex,
)

// TODO: re-add extrinsic
exts := [][]byte{}

block, slot := createTestBlock(t, babeService, emptyHeader, exts, 1, testEpochIndex)

// create pre-digest
preDigest, err := babeService.buildBlockPreDigest(slot)
preDigest, err := builder.buildBlockPreDigest(slot)
require.NoError(t, err)

expectedBlockHeader := &types.Header{
Expand Down
22 changes: 20 additions & 2 deletions lib/babe/median_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ func addBlocksToState(t *testing.T, babeService *Service, depth int, blockState
previousHash := blockState.BestBlockHash()
previousAT := startTime
duration, err := time.ParseDuration("1s")
builder, _ := NewBlockBuilder(
babeService.rt,
babeService.keypair,
babeService.transactionState,
babeService.blockState,
babeService.slotToProof,
babeService.epochData.authorityIndex,
)
require.NoError(t, err)

for i := 1; i <= depth; i++ {
Expand All @@ -88,7 +96,7 @@ func addBlocksToState(t *testing.T, babeService *Service, depth int, blockState
number: slotNumber,
}

predigest, err := babeService.buildBlockPreDigest(slot)
predigest, err := builder.buildBlockPreDigest(slot)
require.NoError(t, err)

block := &types.Block{
Expand Down Expand Up @@ -130,6 +138,16 @@ func TestEstimateCurrentSlot(t *testing.T) {
// create proof that we can authorize this block
babeService.epochData.threshold = maxThreshold
babeService.epochData.authorityIndex = 0

builder, _ := NewBlockBuilder(
babeService.rt,
babeService.keypair,
babeService.transactionState,
babeService.blockState,
babeService.slotToProof,
babeService.epochData.authorityIndex,
)

slotNumber := uint64(17)

outAndProof, err := babeService.runLottery(slotNumber, testEpochIndex)
Expand All @@ -145,7 +163,7 @@ func TestEstimateCurrentSlot(t *testing.T) {
number: slotNumber,
}

predigest, err := babeService.buildBlockPreDigest(slot)
predigest, err := builder.buildBlockPreDigest(slot)
require.NoError(t, err)

block := &types.Block{
Expand Down
12 changes: 11 additions & 1 deletion lib/babe/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,16 @@ func TestVerifyPimarySlotWinner(t *testing.T) {
// create proof that we can authorize this block
babeService.epochData.threshold = maxThreshold
babeService.epochData.authorityIndex = 0

builder, _ := NewBlockBuilder(
babeService.rt,
babeService.keypair,
babeService.transactionState,
babeService.blockState,
babeService.slotToProof,
babeService.epochData.authorityIndex,
)

var slotNumber uint64 = 1

addAuthorshipProof(t, babeService, slotNumber, testEpochIndex)
Expand All @@ -315,7 +325,7 @@ func TestVerifyPimarySlotWinner(t *testing.T) {
}

// create babe header
babeHeader, err := babeService.buildBlockBABEPrimaryPreDigest(slot)
babeHeader, err := builder.buildBlockBABEPrimaryPreDigest(slot)
require.NoError(t, err)

Authorities := make([]*types.Authority, 1)
Expand Down