Skip to content

Commit

Permalink
Merge pull request #6686 from multiversx/equivalent-proofs-feat-stabi…
Browse files Browse the repository at this point in the history
…lization

stabilization-equivalent-proofs
  • Loading branch information
AdoAdoAdo authored Jan 20, 2025
2 parents 7e35a87 + 3bd8598 commit fd92b5a
Show file tree
Hide file tree
Showing 102 changed files with 1,836 additions and 449 deletions.
4 changes: 2 additions & 2 deletions cmd/node/config/enableEpochs.toml
Original file line number Diff line number Diff line change
Expand Up @@ -319,10 +319,10 @@
CryptoOpcodesV2EnableEpoch = 4

# EquivalentMessagesEnableEpoch represents the epoch when the equivalent messages are enabled
EquivalentMessagesEnableEpoch = 4
EquivalentMessagesEnableEpoch = 8 # the chain simulator tests for staking v4 fail if this is set earlier, as they test the transition in epochs 4-7

# FixedOrderInConsensusEnableEpoch represents the epoch when the fixed order in consensus is enabled
FixedOrderInConsensusEnableEpoch = 4
FixedOrderInConsensusEnableEpoch = 8 # the chain simulator tests for staking v4 fail if this is set earlier, as they test the transition in epochs 4-7

# BLSMultiSignerEnableEpoch represents the activation epoch for different types of BLS multi-signers
BLSMultiSignerEnableEpoch = [
Expand Down
57 changes: 57 additions & 0 deletions common/common.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package common

import (
"fmt"

"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/storage"
"github.com/multiversx/mx-chain-vm-v1_2-go/ipc/marshaling"
)

// IsEpochChangeBlockForFlagActivation returns true if the provided header is the first one after the specified flag's activation
Expand All @@ -19,3 +24,55 @@ func IsFlagEnabledAfterEpochsStartBlock(header data.HeaderHandler, enableEpochsH
isEpochStartBlock := IsEpochChangeBlockForFlagActivation(header, enableEpochsHandler, flag)
return isFlagEnabled && !isEpochStartBlock
}

// ShouldBlockHavePrevProof returns true if the block should have a proof
func ShouldBlockHavePrevProof(header data.HeaderHandler, enableEpochsHandler EnableEpochsHandler, flag core.EnableEpochFlag) bool {
return IsFlagEnabledAfterEpochsStartBlock(header, enableEpochsHandler, flag) && header.GetNonce() > 1
}

// VerifyProofAgainstHeader verifies the fields on the proof match the ones on the header
func VerifyProofAgainstHeader(proof data.HeaderProofHandler, header data.HeaderHandler) error {
if check.IfNilReflect(proof) {
return ErrInvalidHeaderProof
}

if proof.GetHeaderNonce() != header.GetNonce() {
return fmt.Errorf("%w, nonce mismatch", ErrInvalidHeaderProof)
}
if proof.GetHeaderShardId() != header.GetShardID() {
return fmt.Errorf("%w, shard id mismatch", ErrInvalidHeaderProof)
}
if proof.GetHeaderEpoch() != header.GetEpoch() {
return fmt.Errorf("%w, epoch mismatch", ErrInvalidHeaderProof)
}
if proof.GetHeaderRound() != header.GetRound() {
return fmt.Errorf("%w, round mismatch", ErrInvalidHeaderProof)
}

return nil
}

// GetHeader tries to get the header from pool first and if not found, searches for it through storer
func GetHeader(
headerHash []byte,
headersPool HeadersPool,
headersStorer storage.Storer,
marshaller marshaling.Marshalizer,
) (data.HeaderHandler, error) {
header, err := headersPool.GetHeaderByHash(headerHash)
if err == nil {
return header, nil
}

headerBytes, err := headersStorer.SearchFirst(headerHash)
if err != nil {
return nil, err
}

err = marshaller.Unmarshal(header, headerBytes)
if err != nil {
return nil, err
}

return header, nil
}
3 changes: 3 additions & 0 deletions common/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ var ErrNilWasmChangeLocker = errors.New("nil wasm change locker")

// ErrNilStateSyncNotifierSubscriber signals that a nil state sync notifier subscriber has been provided
var ErrNilStateSyncNotifierSubscriber = errors.New("nil state sync notifier subscriber")

// ErrInvalidHeaderProof signals that an invalid equivalent proof has been provided
var ErrInvalidHeaderProof = errors.New("invalid equivalent proof")
5 changes: 5 additions & 0 deletions common/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,8 @@ type ChainParametersSubscriptionHandler interface {
ChainParametersChanged(chainParameters config.ChainParametersByEpochConfig)
IsInterfaceNil() bool
}

// HeadersPool defines what a headers pool structure can perform
type HeadersPool interface {
GetHeaderByHash(hash []byte) (data.HeaderHandler, error)
}
33 changes: 9 additions & 24 deletions consensus/spos/bls/proxy/subroundsHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package proxy
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"
logger "github.com/multiversx/mx-chain-logger-go"

"github.com/multiversx/mx-chain-go/common"
Expand Down Expand Up @@ -57,6 +56,14 @@ type SubroundsHandler struct {
currentConsensusType consensusStateMachineType
}

// EpochConfirmed is called when the epoch is confirmed (this is registered as callback)
func (s *SubroundsHandler) EpochConfirmed(epoch uint32, _ uint64) {
err := s.initSubroundsForEpoch(epoch)
if err != nil {
log.Error("SubroundsHandler.EpochConfirmed: cannot initialize subrounds", "error", err)
}
}

const (
consensusNone consensusStateMachineType = iota
consensusV1
Expand Down Expand Up @@ -85,7 +92,7 @@ func NewSubroundsHandler(args *SubroundsHandlerArgs) (*SubroundsHandler, error)
currentConsensusType: consensusNone,
}

subroundHandler.consensusCoreHandler.EpochStartRegistrationHandler().RegisterHandler(subroundHandler)
subroundHandler.consensusCoreHandler.EpochNotifier().RegisterNotifyHandler(subroundHandler)

return subroundHandler, nil
}
Expand Down Expand Up @@ -189,28 +196,6 @@ func (s *SubroundsHandler) initSubroundsForEpoch(epoch uint32) error {
return nil
}

// EpochStartAction is called when the epoch starts
func (s *SubroundsHandler) EpochStartAction(hdr data.HeaderHandler) {
if check.IfNil(hdr) {
log.Error("SubroundsHandler.EpochStartAction: nil header")
return
}

err := s.initSubroundsForEpoch(hdr.GetEpoch())
if err != nil {
log.Error("SubroundsHandler.EpochStartAction: cannot initialize subrounds", "error", err)
}
}

// EpochStartPrepare prepares the subrounds handler for the epoch start
func (s *SubroundsHandler) EpochStartPrepare(_ data.HeaderHandler, _ data.BodyHandler) {
}

// NotifyOrder returns the order of the subrounds handler
func (s *SubroundsHandler) NotifyOrder() uint32 {
return common.ConsensusHandlerOrder
}

// IsInterfaceNil returns true if there is no value under the interface
func (s *SubroundsHandler) IsInterfaceNil() bool {
return s == nil
Expand Down
51 changes: 28 additions & 23 deletions consensus/spos/bls/proxy/subroundsHandler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
crypto "github.com/multiversx/mx-chain-crypto-go"
"github.com/stretchr/testify/require"

chainCommon "github.com/multiversx/mx-chain-go/common"
mock2 "github.com/multiversx/mx-chain-go/consensus/mock"
"github.com/multiversx/mx-chain-go/testscommon"
"github.com/multiversx/mx-chain-go/testscommon/bootstrapperStubs"
Expand All @@ -17,6 +16,7 @@ import (
"github.com/multiversx/mx-chain-go/testscommon/cryptoMocks"
"github.com/multiversx/mx-chain-go/testscommon/dataRetriever"
"github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock"
epochNotifierMock "github.com/multiversx/mx-chain-go/testscommon/epochNotifier"
mock "github.com/multiversx/mx-chain-go/testscommon/epochstartmock"
outportStub "github.com/multiversx/mx-chain-go/testscommon/outport"
"github.com/multiversx/mx-chain-go/testscommon/shardingMocks"
Expand All @@ -29,6 +29,7 @@ func getDefaultArgumentsSubroundHandler() (*SubroundsHandlerArgs, *consensus.Con
epochsEnable := &enableEpochsHandlerMock.EnableEpochsHandlerStub{}
epochStartNotifier := &mock.EpochStartNotifierStub{}
consensusState := &consensus.ConsensusStateMock{}
epochNotifier := &epochNotifierMock.EpochNotifierStub{}
worker := &consensus.SposWorkerMock{
RemoveAllReceivedMessagesCallsCalled: func() {},
GetConsensusStateChangedChannelsCalled: func() chan bool {
Expand Down Expand Up @@ -78,6 +79,7 @@ func getDefaultArgumentsSubroundHandler() (*SubroundsHandlerArgs, *consensus.Con
consensusCore.SetSigningHandler(&consensus.SigningHandlerStub{})
consensusCore.SetEnableEpochsHandler(epochsEnable)
consensusCore.SetEquivalentProofsPool(&dataRetriever.ProofsPoolMock{})
consensusCore.SetEpochNotifier(epochNotifier)
handlerArgs.ConsensusCoreHandler = consensusCore

return handlerArgs, consensusCore
Expand Down Expand Up @@ -221,12 +223,14 @@ func TestSubroundsHandler_initSubroundsForEpoch(t *testing.T) {
sh, err := NewSubroundsHandler(handlerArgs)
require.Nil(t, err)
require.NotNil(t, sh)
// first call on register to EpochNotifier
require.Equal(t, int32(1), startCalled.Load())
sh.currentConsensusType = consensusNone

err = sh.initSubroundsForEpoch(0)
require.Nil(t, err)
require.Equal(t, consensusV1, sh.currentConsensusType)
require.Equal(t, int32(1), startCalled.Load())
require.Equal(t, int32(2), startCalled.Load())
})
t.Run("equivalent messages not enabled, with previous consensus type consensusV1", func(t *testing.T) {
t.Parallel()
Expand All @@ -251,12 +255,15 @@ func TestSubroundsHandler_initSubroundsForEpoch(t *testing.T) {
sh, err := NewSubroundsHandler(handlerArgs)
require.Nil(t, err)
require.NotNil(t, sh)
// first call on register to EpochNotifier
require.Equal(t, int32(1), startCalled.Load())
sh.currentConsensusType = consensusV1

err = sh.initSubroundsForEpoch(0)
require.Nil(t, err)
require.Equal(t, consensusV1, sh.currentConsensusType)
require.Equal(t, int32(0), startCalled.Load())
require.Equal(t, int32(1), startCalled.Load())

})
t.Run("equivalent messages enabled, with previous consensus type consensusNone", func(t *testing.T) {
t.Parallel()
Expand All @@ -280,12 +287,14 @@ func TestSubroundsHandler_initSubroundsForEpoch(t *testing.T) {
sh, err := NewSubroundsHandler(handlerArgs)
require.Nil(t, err)
require.NotNil(t, sh)
// first call on register to EpochNotifier
require.Equal(t, int32(1), startCalled.Load())
sh.currentConsensusType = consensusNone

err = sh.initSubroundsForEpoch(0)
require.Nil(t, err)
require.Equal(t, consensusV2, sh.currentConsensusType)
require.Equal(t, int32(1), startCalled.Load())
require.Equal(t, int32(2), startCalled.Load())
})
t.Run("equivalent messages enabled, with previous consensus type consensusV1", func(t *testing.T) {
t.Parallel()
Expand All @@ -309,12 +318,14 @@ func TestSubroundsHandler_initSubroundsForEpoch(t *testing.T) {
sh, err := NewSubroundsHandler(handlerArgs)
require.Nil(t, err)
require.NotNil(t, sh)
// first call on register to EpochNotifier
require.Equal(t, int32(1), startCalled.Load())
sh.currentConsensusType = consensusV1

err = sh.initSubroundsForEpoch(0)
require.Nil(t, err)
require.Equal(t, consensusV2, sh.currentConsensusType)
require.Equal(t, int32(1), startCalled.Load())
require.Equal(t, int32(2), startCalled.Load())
})
t.Run("equivalent messages enabled, with previous consensus type consensusV2", func(t *testing.T) {
t.Parallel()
Expand All @@ -339,12 +350,14 @@ func TestSubroundsHandler_initSubroundsForEpoch(t *testing.T) {
sh, err := NewSubroundsHandler(handlerArgs)
require.Nil(t, err)
require.NotNil(t, sh)
// first call on register to EpochNotifier
require.Equal(t, int32(1), startCalled.Load())
sh.currentConsensusType = consensusV2

err = sh.initSubroundsForEpoch(0)
require.Nil(t, err)
require.Equal(t, consensusV2, sh.currentConsensusType)
require.Equal(t, int32(0), startCalled.Load())
require.Equal(t, int32(1), startCalled.Load())
})
}

Expand Down Expand Up @@ -375,27 +388,17 @@ func TestSubroundsHandler_Start(t *testing.T) {
sh, err := NewSubroundsHandler(handlerArgs)
require.Nil(t, err)
require.NotNil(t, sh)
// first call on init of EpochNotifier
require.Equal(t, int32(1), startCalled.Load())
sh.currentConsensusType = consensusNone

err = sh.Start(0)
require.Nil(t, err)
require.Equal(t, consensusV1, sh.currentConsensusType)
require.Equal(t, int32(1), startCalled.Load())
require.Equal(t, int32(2), startCalled.Load())
})
}

func TestSubroundsHandler_NotifyOrder(t *testing.T) {
t.Parallel()

handlerArgs, _ := getDefaultArgumentsSubroundHandler()
sh, err := NewSubroundsHandler(handlerArgs)
require.Nil(t, err)
require.NotNil(t, sh)

order := sh.NotifyOrder()
require.Equal(t, uint32(chainCommon.ConsensusHandlerOrder), order)
}

func TestSubroundsHandler_IsInterfaceNil(t *testing.T) {
t.Parallel()

Expand All @@ -417,7 +420,7 @@ func TestSubroundsHandler_IsInterfaceNil(t *testing.T) {
})
}

func TestSubroundsHandler_EpochStartAction(t *testing.T) {
func TestSubroundsHandler_EpochConfirmed(t *testing.T) {
t.Parallel()

t.Run("nil handler does not panic", func(t *testing.T) {
Expand All @@ -431,7 +434,7 @@ func TestSubroundsHandler_EpochStartAction(t *testing.T) {
handlerArgs, _ := getDefaultArgumentsSubroundHandler()
sh, err := NewSubroundsHandler(handlerArgs)
require.Nil(t, err)
sh.EpochStartAction(&testscommon.HeaderHandlerStub{})
sh.EpochConfirmed(0, 0)
})

// tested through initSubroundsForEpoch
Expand All @@ -458,11 +461,13 @@ func TestSubroundsHandler_EpochStartAction(t *testing.T) {
sh, err := NewSubroundsHandler(handlerArgs)
require.Nil(t, err)
require.NotNil(t, sh)
// first call on register to EpochNotifier
require.Equal(t, int32(1), startCalled.Load())

sh.currentConsensusType = consensusNone
sh.EpochStartAction(&testscommon.HeaderHandlerStub{})
sh.EpochConfirmed(0, 0)
require.Nil(t, err)
require.Equal(t, consensusV1, sh.currentConsensusType)
require.Equal(t, int32(1), startCalled.Load())
require.Equal(t, int32(2), startCalled.Load())
})
}
2 changes: 2 additions & 0 deletions consensus/spos/bls/v1/blsSubroundsFactory.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ func (fct *factory) GenerateSubrounds() error {
fct.initConsensusThreshold()
fct.consensusCore.Chronology().RemoveAllSubrounds()
fct.worker.RemoveAllReceivedMessagesCalls()
fct.worker.RemoveAllReceivedHeaderHandlers()

err := fct.generateStartRoundSubround()
if err != nil {
Expand Down Expand Up @@ -206,6 +207,7 @@ func (fct *factory) generateBlockSubround() error {
fct.worker.AddReceivedMessageCall(bls.MtBlockBodyAndHeader, subroundBlockInstance.receivedBlockBodyAndHeader)
fct.worker.AddReceivedMessageCall(bls.MtBlockBody, subroundBlockInstance.receivedBlockBody)
fct.worker.AddReceivedMessageCall(bls.MtBlockHeader, subroundBlockInstance.receivedBlockHeader)
fct.worker.AddReceivedHeaderHandler(subroundBlockInstance.receivedFullHeader)
fct.consensusCore.Chronology().AddSubround(subroundBlockInstance)

return nil
Expand Down
3 changes: 3 additions & 0 deletions consensus/spos/bls/v1/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ import "errors"

// ErrNilSentSignatureTracker defines the error for setting a nil SentSignatureTracker
var ErrNilSentSignatureTracker = errors.New("nil sent signature tracker")

// ErrEquivalentMessagesFlagEnabledWithConsensusV1 defines the error for running with the equivalent messages flag enabled under v1 consensus
var ErrEquivalentMessagesFlagEnabledWithConsensusV1 = errors.New("equivalent messages flag enabled with consensus v1")
Loading

0 comments on commit fd92b5a

Please sign in to comment.