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

Remove checks for equivocatory blocks #2596

Merged
merged 26 commits into from
Jul 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d8e422c
Remove checks for equivocatory blocks
kishansagathiya Jun 10, 2022
5cb4cca
Revert "Remove checks for equivocatory blocks"
kishansagathiya Jun 16, 2022
2e1947e
throw ErrProducerEquivocated only when both equivocatory blocks are
kishansagathiya Jun 16, 2022
1862997
lint
kishansagathiya Jun 16, 2022
fcb1eaa
addressed reviews
kishansagathiya Jun 20, 2022
3cab52f
Merge branch 'development' into kishan/fix/equivocatory-blocks
kishansagathiya Jun 28, 2022
7e49eeb
some fixes
kishansagathiya Jun 28, 2022
cb1cf9c
temp
kishansagathiya Jun 29, 2022
7831759
fix tests
kishansagathiya Jun 29, 2022
344f5fc
some cleanup
kishansagathiya Jun 29, 2022
78b70c7
more test fix
kishansagathiya Jun 29, 2022
34b90ee
fix more things
kishansagathiya Jun 29, 2022
db33dcf
Update lib/babe/verify.go
kishansagathiya Jun 30, 2022
d598901
addressed review
kishansagathiya Jul 4, 2022
3d6f476
Merge branch 'development' into kishan/fix/equivocatory-blocks
kishansagathiya Jul 4, 2022
f787848
Merge branch 'kishan/fix/equivocatory-blocks' of github.com:ChainSafe…
kishansagathiya Jul 4, 2022
b27f088
fix lint
kishansagathiya Jul 4, 2022
adae61b
Merge branch 'development' into kishan/fix/equivocatory-blocks
kishansagathiya Jul 5, 2022
387074a
addressed some more reviews
kishansagathiya Jul 5, 2022
e039a26
Update lib/babe/verify.go
kishansagathiya Jul 7, 2022
e0572ce
addressed more reviews
kishansagathiya Jul 7, 2022
350872e
Merge branch 'kishan/fix/equivocatory-blocks' of github.com:ChainSafe…
kishansagathiya Jul 7, 2022
e2149be
fixing test
kishansagathiya Jul 7, 2022
8c59cef
fixed more things
kishansagathiya Jul 11, 2022
74dd04a
Merge branch 'development' into kishan/fix/equivocatory-blocks
kishansagathiya Jul 11, 2022
1a44c28
fixed some tests
kishansagathiya Jul 12, 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
7 changes: 5 additions & 2 deletions dot/types/babe.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
package types

import (
"errors"
"fmt"
)

var ErrNoFirstPreDigest = errors.New("first digest item is not pre-digest")

// RandomnessLength is the length of the epoch randomness (32 bytes)
const RandomnessLength = 32

Expand Down Expand Up @@ -107,7 +110,7 @@ func GetSlotFromHeader(header *Header) (uint64, error) {

preDigest, ok := header.Digest.Types[0].Value().(PreRuntimeDigest)
if !ok {
return 0, fmt.Errorf("first digest item is not pre-digest")
return 0, fmt.Errorf("%w: got %T", ErrNoFirstPreDigest, header.Digest.Types[0].Value())
}

digest, err := DecodeBabePreDigest(preDigest.Data)
Expand Down Expand Up @@ -140,7 +143,7 @@ func IsPrimary(header *Header) (bool, error) {

preDigest, ok := header.Digest.Types[0].Value().(PreRuntimeDigest)
if !ok {
return false, fmt.Errorf("first digest item is not pre-digest: type=%T", header.Digest.Types[0].Value())
return false, fmt.Errorf("%w: got %T", ErrNoFirstPreDigest, header.Digest.Types[0].Value())
}

digest, err := DecodeBabePreDigest(preDigest.Data)
Expand Down
1 change: 1 addition & 0 deletions lib/babe/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ var (
errServicePaused = errors.New("service paused")
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")

other Other
invalidCustom InvalidCustom
Expand Down
37 changes: 29 additions & 8 deletions lib/babe/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,12 +262,12 @@ func (b *verifier) verifyAuthorshipRight(header *types.Header) error {

preDigest, ok := preDigestItem.Value().(types.PreRuntimeDigest)
if !ok {
return fmt.Errorf("first digest item is not pre-digest")
return fmt.Errorf("%w: got %T", types.ErrNoFirstPreDigest, preDigestItem.Value())
EclesioMeloJunior marked this conversation as resolved.
Show resolved Hide resolved
}

seal, ok := sealItem.Value().(types.SealDigest)
if !ok {
return fmt.Errorf("last digest item is not seal")
return fmt.Errorf("%w: got %T", errLastDigestItemNotSeal, sealItem.Value())
}

babePreDigest, err := b.verifyPreRuntimeDigest(&preDigest)
Expand Down Expand Up @@ -329,29 +329,50 @@ func (b *verifier) verifyAuthorshipRight(header *types.Header) error {
// hashes is hashes of all blocks with same block number as header.Number
hashes := b.blockState.GetAllBlocksAtDepth(header.ParentHash)

for _, hash := range hashes {
currentHeader, err := b.blockState.GetHeader(hash)
for _, currentHash := range hashes {
currentHeader, err := b.blockState.GetHeader(currentHash)
if err != nil {
continue
return fmt.Errorf("failed get header %s", err)
}

currentBlockProducerIndex, err := getAuthorityIndex(currentHeader)
if err != nil {
continue
return fmt.Errorf("failed to get authority index %s", err)
}

if len(currentHeader.Digest.Types) == 0 {
return fmt.Errorf("current header missing digest")
kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved
}

currentPreDigestItem := currentHeader.Digest.Types[0]
currentPreDigest, ok := currentPreDigestItem.Value().(types.PreRuntimeDigest)
if !ok {
return fmt.Errorf("%w: got %T", types.ErrNoFirstPreDigest, currentPreDigestItem.Value())
}

currentBabePreDigest, err := b.verifyPreRuntimeDigest(&currentPreDigest)
if err != nil {
return fmt.Errorf("failed to verify pre-runtime digest: %w", err)
}

_, isCurrentBlockProducerPrimary := currentBabePreDigest.(types.BabePrimaryPreDigest)

var isExistingBlockProducerPrimary bool
var existingBlockProducerIndex uint32
switch d := babePreDigest.(type) {
case types.BabePrimaryPreDigest:
existingBlockProducerIndex = d.AuthorityIndex
isExistingBlockProducerPrimary = true
case types.BabeSecondaryVRFPreDigest:
existingBlockProducerIndex = d.AuthorityIndex
case types.BabeSecondaryPlainPreDigest:
existingBlockProducerIndex = d.AuthorityIndex
}

// same authority won't produce two different blocks at the same block number
if currentBlockProducerIndex == existingBlockProducerIndex && hash != header.Hash() {
// same authority won't produce two different blocks at the same block number as primary block producer
if currentBlockProducerIndex == existingBlockProducerIndex &&
!currentHash.Equal(header.Hash()) &&
isCurrentBlockProducerPrimary == isExistingBlockProducerPrimary {
return ErrProducerEquivocated
}
}
Expand Down
165 changes: 125 additions & 40 deletions lib/babe/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,9 +470,6 @@ func Test_verifier_verifyAuthorshipRight(t *testing.T) {
ctrl := gomock.NewController(t)
mockBlockState := NewMockBlockState(ctrl)
mockBlockStateErr := NewMockBlockState(ctrl)
mockBlockStateEquiv1 := NewMockBlockState(ctrl)
mockBlockStateEquiv2 := NewMockBlockState(ctrl)
mockBlockStateEquiv3 := NewMockBlockState(ctrl)

//Generate keys
kp, err := sr25519.GenerateKeypair()
Expand Down Expand Up @@ -547,14 +544,6 @@ func Test_verifier_verifyAuthorshipRight(t *testing.T) {
mockBlockStateErr.EXPECT().GetAllBlocksAtDepth(gomock.Any()).Return(h1)
mockBlockStateErr.EXPECT().GetHeader(h).Return(nil, errors.New("get header error"))

mockBlockStateEquiv1.EXPECT().GetAllBlocksAtDepth(gomock.Any()).Return(h1)
mockBlockStateEquiv1.EXPECT().GetHeader(h).Return(testHeaderPrimary, nil)

mockBlockStateEquiv2.EXPECT().GetAllBlocksAtDepth(gomock.Any()).Return(h1)
mockBlockStateEquiv2.EXPECT().GetHeader(h).Return(testSecPlainHeader, nil)
mockBlockStateEquiv3.EXPECT().GetAllBlocksAtDepth(gomock.Any()).Return(h1)
mockBlockStateEquiv3.EXPECT().GetHeader(h).Return(testSecVrfHeader, nil)

// Case 0: First element not preruntime digest
header0 := newTestHeader(t, testInvalidSeal, testInvalidSeal)

Expand Down Expand Up @@ -613,27 +602,6 @@ func Test_verifier_verifyAuthorshipRight(t *testing.T) {
//// Case 8: Get header error
babeVerifier6 := newTestVerifier(t, kp, mockBlockStateErr, scale.MaxUint128, false)

// Case 9: Equivocate case primary
babeVerifier7 := newTestVerifier(t, kp, mockBlockStateEquiv1, scale.MaxUint128, false)

// Case 10: Equivocate case secondary plain
babeSecPlainPrd2, err := testBabeSecondaryPlainPreDigest.ToPreRuntimeDigest()
assert.NoError(t, err)
header8 := newTestHeader(t, *babeSecPlainPrd2)

hash2 := encodeAndHashHeader(t, header8)
signAndAddSeal(t, kp, header8, hash2[:])
babeVerifier8 := newTestVerifier(t, kp, mockBlockStateEquiv2, scale.MaxUint128, true)

// Case 11: equivocation case secondary VRF
encVrfDigest := newEncodedBabeDigest(t, testBabeSecondaryVRFPreDigest)
assert.NoError(t, err)
header9 := newTestHeader(t, *types.NewBABEPreRuntimeDigest(encVrfDigest))

hash3 := encodeAndHashHeader(t, header9)
signAndAddSeal(t, kp, header9, hash3[:])
babeVerifier9 := newTestVerifier(t, kp, mockBlockStateEquiv3, scale.MaxUint128, true)

tests := []struct {
name string
verifier verifier
Expand All @@ -644,19 +612,19 @@ func Test_verifier_verifyAuthorshipRight(t *testing.T) {
name: "missing digest",
verifier: verifier{},
header: types.NewEmptyHeader(),
expErr: errors.New("block header is missing digest items"),
expErr: errMissingDigestItems,
},
{
name: "first digest invalid",
verifier: verifier{},
header: header0,
expErr: errors.New("first digest item is not pre-digest"),
expErr: fmt.Errorf("%w: got types.SealDigest", types.ErrNoFirstPreDigest),
},
{
name: "last digest invalid",
verifier: verifier{},
header: header1,
expErr: errors.New("last digest item is not seal"),
expErr: fmt.Errorf("%w: got types.PreRuntimeDigest", errLastDigestItemNotSeal),
},
{
name: "invalid preruntime digest data",
Expand Down Expand Up @@ -692,28 +660,145 @@ func Test_verifier_verifyAuthorshipRight(t *testing.T) {
name: "valid digest items, getAuthorityIndex error",
verifier: *babeVerifier5,
header: header7,
expErr: errors.New("failed to get authority index no digest provided"),
},
{
name: "get header err",
verifier: *babeVerifier6,
header: header7,
expErr: errors.New("failed get header get header error"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := &tt.verifier
err := b.verifyAuthorshipRight(tt.header)
if tt.expErr != nil {
assert.EqualError(t, err, tt.expErr.Error())
} else {
assert.NoError(t, err)
}

})
}
}

func Test_verifier_verifyAuthorshipRightEquivocatory(t *testing.T) {
ctrl := gomock.NewController(t)

mockBlockStateEquiv1 := NewMockBlockState(ctrl)
mockBlockStateEquiv2 := NewMockBlockState(ctrl)
mockBlockStateEquiv3 := NewMockBlockState(ctrl)

//Generate keys
kp, err := sr25519.GenerateKeypair()
assert.NoError(t, err)

output, proof, err := kp.VrfSign(makeTranscript(Randomness{}, uint64(1), 1))
assert.NoError(t, err)

testBabeSecondaryPlainPreDigest := types.BabeSecondaryPlainPreDigest{
AuthorityIndex: 1,
SlotNumber: 1,
}
testBabeSecondaryVRFPreDigest := types.BabeSecondaryVRFPreDigest{
AuthorityIndex: 1,
SlotNumber: 1,
VrfOutput: output,
VrfProof: proof,
}

//BabePrimaryPreDigest case
secDigest1 := types.BabePrimaryPreDigest{
SlotNumber: 1,
VRFOutput: output,
VRFProof: proof,
}
prd1, err := secDigest1.ToPreRuntimeDigest()
assert.NoError(t, err)

auth := types.NewAuthority(kp.Public(), uint64(1))
vi := &verifierInfo{
authorities: []types.Authority{*auth, *auth},
threshold: scale.MaxUint128,
}

verifierEquivocatoryPrimary, err := newVerifier(mockBlockStateEquiv1, 1, vi)
assert.NoError(t, err)

headerEquivocatoryPrimary := newTestHeader(t, *prd1)
hashEquivocatoryPrimary := encodeAndHashHeader(t, headerEquivocatoryPrimary)
signAndAddSeal(t, kp, headerEquivocatoryPrimary, hashEquivocatoryPrimary[:])

mockBlockStateEquiv1.EXPECT().GetAllBlocksAtDepth(headerEquivocatoryPrimary.ParentHash).Return(
[]common.Hash{hashEquivocatoryPrimary})
mockBlockStateEquiv1.EXPECT().GetHeader(hashEquivocatoryPrimary).Return(headerEquivocatoryPrimary, nil)

// Secondary Plain Test Header
testParentPrd, err := testBabeSecondaryPlainPreDigest.ToPreRuntimeDigest()
assert.NoError(t, err)
testParentHeader := newTestHeader(t, *testParentPrd)

testParentHash := encodeAndHashHeader(t, testParentHeader)
testSecondaryPrd, err := testBabeSecondaryPlainPreDigest.ToPreRuntimeDigest()
assert.NoError(t, err)
testSecPlainHeader := newTestHeader(t, *testSecondaryPrd)
testSecPlainHeader.ParentHash = testParentHash

babeSecPlainPrd2, err := testBabeSecondaryPlainPreDigest.ToPreRuntimeDigest()
assert.NoError(t, err)
headerEquivocatorySecondaryPlain := newTestHeader(t, *babeSecPlainPrd2)

hashEquivocatorySecondaryPlain := encodeAndHashHeader(t, headerEquivocatorySecondaryPlain)
signAndAddSeal(t, kp, headerEquivocatorySecondaryPlain, hashEquivocatorySecondaryPlain[:])
babeVerifier8 := newTestVerifier(t, kp, mockBlockStateEquiv2, scale.MaxUint128, true)

mockBlockStateEquiv2.EXPECT().GetAllBlocksAtDepth(headerEquivocatorySecondaryPlain.ParentHash).Return(
[]common.Hash{hashEquivocatorySecondaryPlain})
mockBlockStateEquiv2.EXPECT().GetHeader(hashEquivocatorySecondaryPlain).Return(headerEquivocatorySecondaryPlain, nil)

// Secondary Vrf Test Header
encParentVrfDigest := newEncodedBabeDigest(t, testBabeSecondaryVRFPreDigest)
testParentVrfHeader := newTestHeader(t, *types.NewBABEPreRuntimeDigest(encParentVrfDigest))

testVrfParentHash := encodeAndHashHeader(t, testParentVrfHeader)
encVrfHeader := newEncodedBabeDigest(t, testBabeSecondaryVRFPreDigest)
testSecVrfHeader := newTestHeader(t, *types.NewBABEPreRuntimeDigest(encVrfHeader))
testSecVrfHeader.ParentHash = testVrfParentHash

encVrfDigest := newEncodedBabeDigest(t, testBabeSecondaryVRFPreDigest)
assert.NoError(t, err)
headerEquivocatorySecondaryVRF := newTestHeader(t, *types.NewBABEPreRuntimeDigest(encVrfDigest))

hashEquivocatorySecondaryVRF := encodeAndHashHeader(t, headerEquivocatorySecondaryVRF)
signAndAddSeal(t, kp, headerEquivocatorySecondaryVRF, hashEquivocatorySecondaryVRF[:])
babeVerifierEquivocatorySecondaryVRF := newTestVerifier(t, kp, mockBlockStateEquiv3, scale.MaxUint128, true)
mockBlockStateEquiv3.EXPECT().GetAllBlocksAtDepth(headerEquivocatorySecondaryVRF.ParentHash).Return(
[]common.Hash{hashEquivocatorySecondaryVRF})
mockBlockStateEquiv3.EXPECT().GetHeader(hashEquivocatorySecondaryVRF).Return(headerEquivocatorySecondaryVRF, nil)

tests := []struct {
name string
verifier verifier
header *types.Header
expErr error
}{
{
name: "equivocate - primary",
verifier: *babeVerifier7,
header: header7,
verifier: *verifierEquivocatoryPrimary,
kishansagathiya marked this conversation as resolved.
Show resolved Hide resolved
header: headerEquivocatoryPrimary,
expErr: ErrProducerEquivocated,
},
{
name: "equivocate - secondary plain",
verifier: *babeVerifier8,
header: header8,
header: headerEquivocatorySecondaryPlain,
expErr: ErrProducerEquivocated,
},
{
name: "equivocate - secondary vrf",
verifier: *babeVerifier9,
header: header9,
verifier: *babeVerifierEquivocatorySecondaryVRF,
header: headerEquivocatorySecondaryVRF,
expErr: ErrProducerEquivocated,
},
}
Expand Down