Skip to content

Commit

Permalink
Author a sibling block in case best block's slot is same as current s…
Browse files Browse the repository at this point in the history
…lot (#2726)
  • Loading branch information
kishansagathiya authored and timwu20 committed Sep 13, 2022
1 parent de2d65d commit 6cbce6d
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 2 deletions.
1 change: 1 addition & 0 deletions dot/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ func (nb nodeBuilder) createBABEService(cfg *Config, st *state.Service, ks keyst
cs *core.Service, telemetryMailer telemetry.Client) (babe.ServiceIFace, error) {
return nb.createBABEServiceWithBuilder(cfg, st, ks, cs, telemetryMailer, babe.Builder{})
}

func (nodeBuilder) createBABEServiceWithBuilder(cfg *Config, st *state.Service, ks keystore.Keystore,
cs *core.Service, telemetryMailer telemetry.Client, newBabeService ServiceBuilder) (babe.
ServiceIFace, error) {
Expand Down
4 changes: 3 additions & 1 deletion dot/types/authority.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import (

// Authority struct to hold authority data
type Authority struct {
Key crypto.PublicKey
Key crypto.PublicKey
// Weight exists for potential improvements in the protocol and could
// have a use-case in the future. In polkadot all authorities have the weight = 1.
Weight uint64
}

Expand Down
24 changes: 24 additions & 0 deletions lib/babe/babe.go
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,30 @@ func (b *Service) handleSlot(epoch, slotNum uint64,
return errNilParentHeader
}

atGenesisBlock := b.blockState.GenesisHash().Equal(parentHeader.Hash())
if !atGenesisBlock {
bestBlockSlotNum, err := b.blockState.GetSlotForBlock(parentHeader.Hash())
if err != nil {
return fmt.Errorf("could not get slot for block %s: %w", parentHeader.Hash(), err)
}

if bestBlockSlotNum > slotNum {
return errLaggingSlot
}

if bestBlockSlotNum == slotNum {
// pick parent of best block instead to handle slot
newParentHeader, err := b.blockState.GetHeader(parentHeader.ParentHash)
if err != nil {
return fmt.Errorf("could not get header for hash %s: %w", parentHeader.ParentHash, err)
}
if newParentHeader == nil {
return errNilParentHeader
}
parentHeader = newParentHeader
}
}

// there is a chance that the best block header may change in the course of building the block,
// so let's copy it first.
parent, err := parentHeader.DeepCopy()
Expand Down
171 changes: 171 additions & 0 deletions lib/babe/babe_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/internal/log"
"github.com/ChainSafe/gossamer/lib/babe/mocks"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/crypto/sr25519"
"github.com/ChainSafe/gossamer/lib/genesis"
"github.com/ChainSafe/gossamer/lib/runtime"
Expand Down Expand Up @@ -293,3 +294,173 @@ func TestService_PauseAndResume(t *testing.T) {
err = bs.Stop()
require.NoError(t, err)
}

func TestService_HandleSlotWithLaggingSlot(t *testing.T) {
cfg := &ServiceConfig{
Authority: true,
Lead: true,
}
babeService := createTestService(t, cfg)

err := babeService.Start()
require.NoError(t, err)
defer func() {
_ = babeService.Stop()
}()

// add a block
parentHash := babeService.blockState.GenesisHash()
rt, err := babeService.blockState.GetRuntime(nil)
require.NoError(t, err)

epochData, err := babeService.initiateEpoch(testEpochIndex)
require.NoError(t, err)

ext := runtime.NewTestExtrinsic(t, rt, parentHash, parentHash, 0, "System.remark", []byte{0xab, 0xcd})
block := createTestBlock(t, babeService, emptyHeader, [][]byte{common.MustHexToBytes(ext)},
1, testEpochIndex, epochData)

babeService.blockState.AddBlock(block)
time.Sleep(babeService.constants.slotDuration * 1)

header, err := babeService.blockState.BestBlockHeader()
require.NoError(t, err)

bestBlockSlotNum, err := babeService.blockState.GetSlotForBlock(header.Hash())
require.NoError(t, err)

slotnum := uint64(1)
slot := Slot{
start: time.Now(),
duration: 1 * time.Second,
number: slotnum,
}
testVRFOutputAndProof := &VrfOutputAndProof{}
preRuntimeDigest, err := types.NewBabePrimaryPreDigest(
0, slot.number,
testVRFOutputAndProof.output,
testVRFOutputAndProof.proof,
).ToPreRuntimeDigest()

require.NoError(t, err)

err = babeService.handleSlot(
babeService.epochHandler.epochNumber,
bestBlockSlotNum-1,
babeService.epochHandler.epochData.authorityIndex,
preRuntimeDigest)

require.ErrorIs(t, err, errLaggingSlot)
}

func TestService_HandleSlotWithSameSlot(t *testing.T) {
alice := keyring.Alice().(*sr25519.Keypair)
bob := keyring.Bob().(*sr25519.Keypair)

// Create babe service for alice
cfgAlice := &ServiceConfig{
Authority: true,
Lead: true,
Keypair: alice,
}
cfgAlice.AuthData = []types.Authority{
{
Key: alice.Public().(*sr25519.PublicKey),
Weight: 1,
},
{
Key: bob.Public().(*sr25519.PublicKey),
Weight: 1,
},
}

// Create babe service for bob
cfgBob := &ServiceConfig{
Authority: true,
Lead: true,
Keypair: bob,
}
cfgBob.AuthData = []types.Authority{
{
Key: alice.Public().(*sr25519.PublicKey),
Weight: 1,
},
{
Key: bob.Public().(*sr25519.PublicKey),
Weight: 1,
},
}

babeServiceBob := createTestService(t, cfgBob)

err := babeServiceBob.Start()
require.NoError(t, err)
defer func() {
_ = babeServiceBob.Stop()
}()

time.Sleep(babeServiceBob.constants.slotDuration * 5)

// create a block using bob
parentHash := babeServiceBob.blockState.GenesisHash()
rt, err := babeServiceBob.blockState.GetRuntime(nil)
require.NoError(t, err)

epochData, err := babeServiceBob.initiateEpoch(testEpochIndex)
require.NoError(t, err)

ext := runtime.NewTestExtrinsic(t, rt, parentHash, parentHash, 0, "System.remark", []byte{0xab, 0xcd})
block := createTestBlock(t, babeServiceBob, emptyHeader, [][]byte{common.MustHexToBytes(ext)},
1, testEpochIndex, epochData)

err = babeServiceBob.Stop()
require.NoError(t, err)

babeServiceAlice := createTestService(t, cfgAlice)

err = babeServiceAlice.Start()
require.NoError(t, err)
defer func() {
_ = babeServiceAlice.Stop()
}()
time.Sleep(babeServiceAlice.constants.slotDuration * 1)

// Add block created by Bob to Alice
babeServiceAlice.blockState.AddBlock(block)

time.Sleep(babeServiceAlice.constants.slotDuration * 1)

bestBlockHeader, err := babeServiceAlice.blockState.BestBlockHeader()
require.NoError(t, err)

require.Equal(t, block.Header.Hash().String(), bestBlockHeader.Hash().String())

// If the slot we are claiming is same as slot in best header, test that we don't
// through any error and can claim slot.
bestBlockSlotNum, err := babeServiceAlice.blockState.GetSlotForBlock(bestBlockHeader.Hash())
require.NoError(t, err)

err = babeServiceAlice.Stop()
require.NoError(t, err)

slot := Slot{
start: time.Now(),
duration: 1 * time.Second,
number: bestBlockSlotNum,
}
testVRFOutputAndProof := &VrfOutputAndProof{}
preRuntimeDigest, err := types.NewBabePrimaryPreDigest(
0, slot.number,
testVRFOutputAndProof.output,
testVRFOutputAndProof.proof,
).ToPreRuntimeDigest()
require.NoError(t, err)

err = babeServiceAlice.handleSlot(
babeServiceAlice.epochHandler.epochNumber,
bestBlockSlotNum,
babeServiceAlice.epochHandler.epochData.authorityIndex,
preRuntimeDigest)
require.NoError(t, err)

}
1 change: 1 addition & 0 deletions lib/babe/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ var (
errInvalidSlotTechnique = errors.New("invalid slot claiming technique")
errNoBABEAuthorityKeyProvided = errors.New("cannot create BABE service as authority; no keypair provided")
errLastDigestItemNotSeal = errors.New("last digest item is not seal")
errLaggingSlot = errors.New("cannot claim slot, current slot is smaller than slot of best block")

other Other
invalidCustom InvalidCustom
Expand Down
17 changes: 16 additions & 1 deletion lib/genesis/test_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/trie"
"github.com/ChainSafe/gossamer/lib/utils"
"github.com/ChainSafe/gossamer/pkg/scale"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -100,8 +101,22 @@ func newGenesisTrieAndHeader(t *testing.T, gen *Genesis) (*trie.Trie, *types.Hea
genTrie, err := NewTrieFromGenesis(gen)
require.NoError(t, err)

babeDigest := types.NewBabeDigest()
err = babeDigest.Set(types.BabePrimaryPreDigest{AuthorityIndex: 0})
require.NoError(t, err)

bdEnc, err := scale.Marshal(babeDigest)
require.NoError(t, err)

digest := types.NewDigest()
err = digest.Add(types.PreRuntimeDigest{
ConsensusEngineID: types.BabeEngineID,
Data: bdEnc,
})
require.NoError(t, err)

genesisHeader, err := types.NewHeader(common.NewHash([]byte{0}),
genTrie.MustHash(), trie.EmptyHash, 0, types.NewDigest())
genTrie.MustHash(), trie.EmptyHash, 0, digest)
require.NoError(t, err)

return genTrie, genesisHeader
Expand Down

0 comments on commit 6cbce6d

Please sign in to comment.