Skip to content

Commit

Permalink
Merge pull request #6769 from multiversx/consensus-verifier-fix
Browse files Browse the repository at this point in the history
add header verification unit tests
  • Loading branch information
AdoAdoAdo authored Feb 6, 2025
2 parents a06e1f7 + 996d495 commit 1694574
Show file tree
Hide file tree
Showing 5 changed files with 260 additions and 13 deletions.
9 changes: 9 additions & 0 deletions common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/multiversx/mx-chain-core-go/core"
"github.com/multiversx/mx-chain-core-go/core/check"
"github.com/multiversx/mx-chain-core-go/data"
"github.com/multiversx/mx-chain-go/consensus"
)

// IsValidRelayedTxV3 returns true if the provided transaction is a valid transaction of type relayed v3
Expand Down Expand Up @@ -39,6 +40,14 @@ func IsEpochChangeBlockForFlagActivation(header data.HeaderHandler, enableEpochs
return isStartOfEpochBlock && isBlockInActivationEpoch
}

// IsEpochStartProofForFlagActivation returns true if the provided proof is the proof of the epoch start block on the activation epoch of equivalent messages
func IsEpochStartProofForFlagActivation(proof consensus.ProofHandler, enableEpochsHandler EnableEpochsHandler) bool {
isStartOfEpochProof := proof.GetIsStartOfEpoch()
isProofInActivationEpoch := proof.GetHeaderEpoch() == enableEpochsHandler.GetActivationEpoch(EquivalentMessagesFlag)

return isStartOfEpochProof && isProofInActivationEpoch
}

// isFlagEnabledAfterEpochsStartBlock returns true if the flag is enabled for the header, but it is not the epoch start block
func isFlagEnabledAfterEpochsStartBlock(header data.HeaderHandler, enableEpochsHandler EnableEpochsHandler, flag core.EnableEpochFlag) bool {
isFlagEnabled := enableEpochsHandler.IsFlagEnabledInEpoch(flag, header.GetEpoch())
Expand Down
1 change: 1 addition & 0 deletions consensus/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,4 +216,5 @@ type ProofHandler interface {
GetHeaderEpoch() uint32
GetHeaderNonce() uint64
GetHeaderShardId() uint32
GetIsStartOfEpoch() bool
}
6 changes: 6 additions & 0 deletions process/block/interceptedBlocks/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,12 @@ func checkMetaShardInfo(
return err
}

isSelfMeta := coordinator.SelfId() == core.MetachainShardId
isHeaderFromSelf := sd.GetShardID() == coordinator.SelfId()
if !(isSelfMeta || isHeaderFromSelf) {
continue
}

wgProofsVerification.Add(1)
checkProofAsync(sd.GetPreviousProof(), headerSigVerifier, &wgProofsVerification, errChan)
}
Expand Down
29 changes: 16 additions & 13 deletions process/headerCheck/headerSignatureVerify.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ func (hsv *HeaderSigVerifier) getConsensusSignersForEquivalentProofs(proof data.
epochForConsensus,
proof.GetHeaderShardId(),
)

if err != nil {
return nil, err
}
Expand Down Expand Up @@ -315,7 +314,7 @@ func (hsv *HeaderSigVerifier) VerifyHeaderWithProof(header data.HeaderHandler) e
}

prevProof := header.GetPreviousProof()
if prevProof.GetIsStartOfEpoch() {
if common.IsEpochStartProofForFlagActivation(prevProof, hsv.enableEpochsHandler) {
return hsv.verifyHeaderProofAtTransition(prevProof)
}

Expand All @@ -332,33 +331,33 @@ func (hsv *HeaderSigVerifier) getHeaderForProof(proof data.HeaderProofHandler) (
return process.GetHeader(proof.GetHeaderHash(), hsv.headersPool, headersStorer, hsv.marshalizer, proof.GetHeaderShardId())
}

func (hsv *HeaderSigVerifier) verifyHeaderProofAtTransition(prevProof data.HeaderProofHandler) error {
if check.IfNilReflect(prevProof) {
func (hsv *HeaderSigVerifier) verifyHeaderProofAtTransition(proof data.HeaderProofHandler) error {
if check.IfNilReflect(proof) {
return process.ErrNilHeaderProof
}
header, err := hsv.getHeaderForProof(prevProof)
header, err := hsv.getHeaderForProof(proof)
if err != nil {
return err
}

consensusPubKeys, err := hsv.getConsensusSigners(
header.GetPrevRandSeed(),
prevProof.GetHeaderShardId(),
prevProof.GetHeaderEpoch(),
prevProof.GetIsStartOfEpoch(),
prevProof.GetHeaderRound(),
prevProof.GetHeaderHash(),
prevProof.GetPubKeysBitmap())
proof.GetHeaderShardId(),
proof.GetHeaderEpoch(),
proof.GetIsStartOfEpoch(),
proof.GetHeaderRound(),
proof.GetHeaderHash(),
proof.GetPubKeysBitmap())
if err != nil {
return err
}

multiSigVerifier, err := hsv.multiSigContainer.GetMultiSigner(prevProof.GetHeaderEpoch())
multiSigVerifier, err := hsv.multiSigContainer.GetMultiSigner(proof.GetHeaderEpoch())
if err != nil {
return err
}

return multiSigVerifier.VerifyAggregatedSig(consensusPubKeys, prevProof.GetHeaderHash(), prevProof.GetAggregatedSignature())
return multiSigVerifier.VerifyAggregatedSig(consensusPubKeys, proof.GetHeaderHash(), proof.GetAggregatedSignature())
}

// VerifyHeaderProof checks if the proof is correct for the header
Expand All @@ -370,6 +369,10 @@ func (hsv *HeaderSigVerifier) VerifyHeaderProof(proofHandler data.HeaderProofHan
return fmt.Errorf("%w for flag %s", process.ErrFlagNotActive, common.EquivalentMessagesFlag)
}

if common.IsEpochStartProofForFlagActivation(proofHandler, hsv.enableEpochsHandler) {
return hsv.verifyHeaderProofAtTransition(proofHandler)
}

multiSigVerifier, err := hsv.multiSigContainer.GetMultiSigner(proofHandler.GetHeaderEpoch())
if err != nil {
return err
Expand Down
228 changes: 228 additions & 0 deletions process/headerCheck/headerSignatureVerify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package headerCheck
import (
"bytes"
"errors"
"fmt"
"strconv"
"strings"
"testing"

Expand All @@ -21,6 +23,7 @@ import (
"github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock"
"github.com/multiversx/mx-chain-go/testscommon/genericMocks"
"github.com/multiversx/mx-chain-go/testscommon/hashingMocks"
"github.com/multiversx/mx-chain-go/testscommon/pool"
"github.com/multiversx/mx-chain-go/testscommon/shardingMocks"
)

Expand Down Expand Up @@ -712,6 +715,231 @@ func TestHeaderSigVerifier_VerifySignatureOkWhenFallbackThresholdCouldBeApplied(
require.True(t, wasCalled)
}

func TestHeaderSigVerifier_VerifySignatureWithEquivalentProofsActivated(t *testing.T) {
wasCalled := false
args := createHeaderSigVerifierArgs()
numValidatorsConsensusBeforeActivation := 7
numValidatorsConsensusAfterActivation := 10
eligibleListSize := numValidatorsConsensusAfterActivation
eligibleValidatorsKeys := make([]string, eligibleListSize)
eligibleValidators := make([]nodesCoordinator.Validator, eligibleListSize)
activationEpoch := uint32(1)

for i := 0; i < eligibleListSize; i++ {
eligibleValidatorsKeys[i] = "pubKey" + strconv.Itoa(i)
eligibleValidators[i], _ = nodesCoordinator.NewValidator([]byte(eligibleValidatorsKeys[i]), 1, defaultChancesSelection)
}

nc := &shardingMocks.NodesCoordinatorMock{
ComputeValidatorsGroupCalled: func(randomness []byte, round uint64, shardId uint32, epoch uint32) (leader nodesCoordinator.Validator, validators []nodesCoordinator.Validator, err error) {
if epoch < activationEpoch {
return eligibleValidators[0], eligibleValidators[:numValidatorsConsensusBeforeActivation], nil
}
return eligibleValidators[0], eligibleValidators, nil
},
GetAllEligibleValidatorsPublicKeysForShardCalled: func(epoch uint32, shardID uint32) ([]string, error) {
return eligibleValidatorsKeys, nil
},
}

t.Run("check transition block which has no previous proof", func(t *testing.T) {
enableEpochs := &enableEpochsHandlerMock.EnableEpochsHandlerStub{}
args.EnableEpochsHandler = enableEpochs
enableEpochs.IsFlagEnabledInEpochCalled = func(flag core.EnableEpochFlag, epoch uint32) bool {
return epoch >= activationEpoch
}
enableEpochs.GetActivationEpochCalled = func(flag core.EnableEpochFlag) uint32 {
return activationEpoch
}

args.NodesCoordinator = nc
args.MultiSigContainer = cryptoMocks.NewMultiSignerContainerMock(&cryptoMocks.MultisignerMock{
VerifyAggregatedSigCalled: func(pubKeysSigners [][]byte, message []byte, aggSig []byte) error {
wasCalled = true
return nil
}})
hdrSigVerifier, _ := NewHeaderSigVerifier(args)
header := &dataBlock.HeaderV2{
Header: &dataBlock.Header{
ShardID: 0,
PrevRandSeed: []byte("prevRandSeed"),
PubKeysBitmap: nil,
Signature: nil,
Epoch: 1,
EpochStartMetaHash: []byte("epoch start meta hash"), // to make this the epoch start block in the shard

},
PreviousHeaderProof: nil,
}

err := hdrSigVerifier.VerifySignature(header)
require.Nil(t, err)
require.False(t, wasCalled)

// check current block proof
err = hdrSigVerifier.VerifyHeaderProof(&dataBlock.HeaderProof{
PubKeysBitmap: []byte{0xff}, // bitmap should still have the old format
AggregatedSignature: []byte("aggregated signature"),
HeaderHash: []byte("hash"),
HeaderEpoch: 1,
IsStartOfEpoch: true,
})
require.Nil(t, err)
})
t.Run("check shard block following the transition block, which has lower consensus size but with a proof", func(t *testing.T) {
enableEpochs := &enableEpochsHandlerMock.EnableEpochsHandlerStub{}
args.EnableEpochsHandler = enableEpochs
args.StorageService = &genericMocks.ChainStorerMock{}

prevHeader := &dataBlock.HeaderV2{
Header: &dataBlock.Header{
Nonce: 99,
Round: 99,
ShardID: 0,
PrevHash: []byte("prevPrevHash"),
PrevRandSeed: []byte("prevRandSeed"),
PubKeysBitmap: nil,
Signature: nil,
Epoch: 0,
EpochStartMetaHash: []byte("epoch start meta hash"), // to make this the epoch start block in the shard
},
PreviousHeaderProof: nil,
}
prevHeaderHash := []byte("prevHeaderHash")
headersPool := &pool.HeadersPoolStub{
GetHeaderByHashCalled: func(hash []byte) (data.HeaderHandler, error) {
if bytes.Equal(hash, []byte("prevHeaderHash")) {
return prevHeader, nil
}
return nil, fmt.Errorf("header not found")
},
}
args.HeadersPool = headersPool
args.NodesCoordinator = nc
args.MultiSigContainer = cryptoMocks.NewMultiSignerContainerMock(&cryptoMocks.MultisignerMock{
VerifyAggregatedSigCalled: func(pubKeysSigners [][]byte, message []byte, aggSig []byte) error {
wasCalled = true
return nil
}})
enableEpochs.IsFlagEnabledInEpochCalled = func(flag core.EnableEpochFlag, epoch uint32) bool {
return epoch >= activationEpoch
}
enableEpochs.GetActivationEpochCalled = func(flag core.EnableEpochFlag) uint32 {
return activationEpoch
}

hdrSigVerifier, _ := NewHeaderSigVerifier(args)
header := &dataBlock.HeaderV2{
Header: &dataBlock.Header{
Nonce: 100,
Round: 100,
ShardID: 0,
PrevHash: prevHeaderHash,
PrevRandSeed: []byte("prevRandSeed"),
PubKeysBitmap: nil,
Signature: nil,
Epoch: 1,
},
PreviousHeaderProof: &dataBlock.HeaderProof{
PubKeysBitmap: []byte{0x3F},
AggregatedSignature: []byte("aggregated signature"),
HeaderHash: prevHeaderHash,
HeaderEpoch: 1,
HeaderNonce: 99,
HeaderShardId: 0,
HeaderRound: 99,
IsStartOfEpoch: true,
},
}

err := hdrSigVerifier.VerifySignature(header)
require.Nil(t, err)
require.True(t, wasCalled)
})
t.Run("check regular shard block with full size consensus for previous proof", func(t *testing.T) {
enableEpochs := &enableEpochsHandlerMock.EnableEpochsHandlerStub{}
args.EnableEpochsHandler = enableEpochs
args.StorageService = &genericMocks.ChainStorerMock{}

prevHeader := &dataBlock.HeaderV2{
Header: &dataBlock.Header{
Nonce: 100,
Round: 100,
ShardID: 0,
PrevHash: []byte("prevPrevHash"),
PrevRandSeed: []byte("prevRandSeed"),
PubKeysBitmap: nil,
Signature: nil,
Epoch: 1,
},
PreviousHeaderProof: &dataBlock.HeaderProof{},
}
prevHeaderHash := []byte("prevHeaderHash")
headersPool := &pool.HeadersPoolStub{
GetHeaderByHashCalled: func(hash []byte) (data.HeaderHandler, error) {
if bytes.Equal(hash, []byte("prevHeaderHash")) {
return prevHeader, nil
}
return nil, fmt.Errorf("header not found")
},
}
args.HeadersPool = headersPool
args.NodesCoordinator = nc
args.MultiSigContainer = cryptoMocks.NewMultiSignerContainerMock(&cryptoMocks.MultisignerMock{
VerifyAggregatedSigCalled: func(pubKeysSigners [][]byte, message []byte, aggSig []byte) error {
wasCalled = true
return nil
}})
enableEpochs.IsFlagEnabledInEpochCalled = func(flag core.EnableEpochFlag, epoch uint32) bool {
return epoch >= activationEpoch
}
enableEpochs.GetActivationEpochCalled = func(flag core.EnableEpochFlag) uint32 {
return activationEpoch
}

hdrSigVerifier, _ := NewHeaderSigVerifier(args)
header := &dataBlock.HeaderV2{
Header: &dataBlock.Header{
Nonce: 101,
Round: 101,
ShardID: 0,
PrevHash: prevHeaderHash,
PrevRandSeed: []byte("prevRandSeed"),
PubKeysBitmap: nil,
Signature: nil,
Epoch: 1,
},
PreviousHeaderProof: &dataBlock.HeaderProof{
PubKeysBitmap: []byte{0xff, 0x03},
AggregatedSignature: []byte("aggregated signature"),
HeaderHash: prevHeaderHash,
HeaderEpoch: 1,
HeaderNonce: 100,
HeaderShardId: 0,
HeaderRound: 100,
IsStartOfEpoch: false,
},
}

err := hdrSigVerifier.VerifySignature(header)
require.Nil(t, err)
require.True(t, wasCalled)

// check current block proof
err = hdrSigVerifier.VerifyHeaderProof(&dataBlock.HeaderProof{
PubKeysBitmap: []byte{0xff, 0x3f}, // for current block, bitmap should have the new format
AggregatedSignature: []byte("aggregated signature"),
HeaderHash: []byte("hash"),
HeaderEpoch: 1,
HeaderNonce: 100,
HeaderShardId: 0,
HeaderRound: 100,
IsStartOfEpoch: false,
})
require.Nil(t, err)
})
}

func getFilledHeader() data.HeaderHandler {
return &dataBlock.Header{
PrevHash: []byte("prev hash"),
Expand Down

0 comments on commit 1694574

Please sign in to comment.