diff --git a/cmd/node/config/config.toml b/cmd/node/config/config.toml index 85bd2557636..2f884b596e0 100644 --- a/cmd/node/config/config.toml +++ b/cmd/node/config/config.toml @@ -42,7 +42,8 @@ # ChainParametersByEpoch defines chain operation configurable values that can be modified based on epochs ChainParametersByEpoch = [ - { EnableEpoch = 0, RoundDuration = 6000, ShardConsensusGroupSize = 7, ShardMinNumNodes = 10, MetachainConsensusGroupSize = 10, MetachainMinNumNodes = 10, Hysteresis = 0.2, Adaptivity = false } + { EnableEpoch = 0, RoundDuration = 6000, ShardConsensusGroupSize = 7, ShardMinNumNodes = 10, MetachainConsensusGroupSize = 10, MetachainMinNumNodes = 10, Hysteresis = 0.2, Adaptivity = false }, + { EnableEpoch = 8, RoundDuration = 6000, ShardConsensusGroupSize = 10, ShardMinNumNodes = 10, MetachainConsensusGroupSize = 10, MetachainMinNumNodes = 10, Hysteresis = 0.2, Adaptivity = false } ] [HardwareRequirements] diff --git a/common/common.go b/common/common.go index b2c035ca240..efb42541832 100644 --- a/common/common.go +++ b/common/common.go @@ -41,8 +41,8 @@ func IsEpochChangeBlockForFlagActivation(header data.HeaderHandler, enableEpochs return isStartOfEpochBlock && isBlockInActivationEpoch } -// 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 { +// 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()) isEpochStartBlock := IsEpochChangeBlockForFlagActivation(header, enableEpochsHandler, flag) return isFlagEnabled && !isEpochStartBlock @@ -50,7 +50,7 @@ func IsFlagEnabledAfterEpochsStartBlock(header data.HeaderHandler, enableEpochsH // 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 + return isFlagEnabledAfterEpochsStartBlock(header, enableEpochsHandler, flag) && header.GetNonce() > 1 } // VerifyProofAgainstHeader verifies the fields on the proof match the ones on the header @@ -71,6 +71,9 @@ func VerifyProofAgainstHeader(proof data.HeaderProofHandler, header data.HeaderH if proof.GetHeaderRound() != header.GetRound() { return fmt.Errorf("%w, round mismatch", ErrInvalidHeaderProof) } + if proof.GetIsStartOfEpoch() != header.IsStartOfEpochBlock() { + return fmt.Errorf("%w, is start of epoch mismatch", ErrInvalidHeaderProof) + } return nil } diff --git a/consensus/chronology/chronology.go b/consensus/chronology/chronology.go index 0c195c2e31a..5f79612a176 100644 --- a/consensus/chronology/chronology.go +++ b/consensus/chronology/chronology.go @@ -104,6 +104,7 @@ func (chr *chronology) RemoveAllSubrounds() { chr.subrounds = make(map[int]int) chr.subroundHandlers = make([]consensus.SubroundHandler, 0) + chr.subroundId = srBeforeStartRound chr.mutSubrounds.Unlock() } diff --git a/consensus/spos/bls/v2/export_test.go b/consensus/spos/bls/v2/export_test.go index 8dbfc1f151e..38ecf9c9d59 100644 --- a/consensus/spos/bls/v2/export_test.go +++ b/consensus/spos/bls/v2/export_test.go @@ -263,7 +263,7 @@ func (sr *subroundEndRound) DoEndRoundJobByNode() bool { // CreateAndBroadcastProof calls the unexported createAndBroadcastHeaderFinalInfo function func (sr *subroundEndRound) CreateAndBroadcastProof(signature []byte, bitmap []byte) { - _, _ = sr.createAndBroadcastProof(signature, bitmap) + _ = sr.createAndBroadcastProof(signature, bitmap) } // ReceivedProof calls the unexported receivedProof function diff --git a/consensus/spos/bls/v2/subroundBlock.go b/consensus/spos/bls/v2/subroundBlock.go index 3de20efe41a..36f62391257 100644 --- a/consensus/spos/bls/v2/subroundBlock.go +++ b/consensus/spos/bls/v2/subroundBlock.go @@ -364,7 +364,7 @@ func (sr *subroundBlock) saveProofForPreviousHeaderIfNeeded(header data.HeaderHa proof := header.GetPreviousProof() err := common.VerifyProofAgainstHeader(proof, prevHeader) if err != nil { - log.Debug("saveProofForPreviousHeaderIfNeeded: invalid proof, %w", err) + log.Debug("saveProofForPreviousHeaderIfNeeded: invalid proof", "error", err.Error()) return } diff --git a/consensus/spos/bls/v2/subroundEndRound.go b/consensus/spos/bls/v2/subroundEndRound.go index 414862bbeb1..cf23daa3ae4 100644 --- a/consensus/spos/bls/v2/subroundEndRound.go +++ b/consensus/spos/bls/v2/subroundEndRound.go @@ -11,7 +11,6 @@ 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-core-go/data/block" "github.com/multiversx/mx-chain-core-go/display" @@ -247,10 +246,36 @@ func (sr *subroundEndRound) doEndRoundJobByNode() bool { if !sr.waitForSignalSync() { return false } + sr.sendProof() } - proof, ok := sr.sendProof() - if !ok { + return sr.finalizeConfirmedBlock() +} + +func (sr *subroundEndRound) waitForProof() bool { + shardID := sr.ShardCoordinator().SelfId() + headerHash := sr.GetData() + if sr.EquivalentProofsPool().HasProof(shardID, headerHash) { + return true + } + + ctx, cancel := context.WithTimeout(context.Background(), sr.RoundHandler().TimeDuration()) + defer cancel() + + for { + select { + case <-time.After(time.Millisecond): + if sr.EquivalentProofsPool().HasProof(shardID, headerHash) { + return true + } + case <-ctx.Done(): + return false + } + } +} + +func (sr *subroundEndRound) finalizeConfirmedBlock() bool { + if !sr.waitForProof() { return false } @@ -259,14 +284,6 @@ func (sr *subroundEndRound) doEndRoundJobByNode() bool { return false } - // if proof not nil, it was created and broadcasted so it has to be added to the pool - if proof != nil { - ok := sr.EquivalentProofsPool().AddProof(proof) - if !ok { - log.Trace("doEndRoundJobByNode.AddProof", "added", ok) - } - } - sr.SetStatus(sr.Current(), spos.SsFinished) sr.worker.DisplayStatistics() @@ -281,29 +298,29 @@ func (sr *subroundEndRound) doEndRoundJobByNode() bool { return true } -func (sr *subroundEndRound) sendProof() (data.HeaderProofHandler, bool) { +func (sr *subroundEndRound) sendProof() { if !sr.shouldSendProof() { - return nil, true + return } bitmap := sr.GenerateBitmap(bls.SrSignature) err := sr.checkSignaturesValidity(bitmap) if err != nil { log.Debug("sendProof.checkSignaturesValidity", "error", err.Error()) - return nil, false + return } // Aggregate signatures, handle invalid signers and send final info if needed bitmap, sig, err := sr.aggregateSigsAndHandleInvalidSigners(bitmap) if err != nil { log.Debug("sendProof.aggregateSigsAndHandleInvalidSigners", "error", err.Error()) - return nil, false + return } ok := sr.ScheduledProcessor().IsProcessedOKWithTimeout() // placeholder for subroundEndRound.doEndRoundJobByLeader script if !ok { - return nil, false + return } roundHandler := sr.RoundHandler() @@ -311,12 +328,14 @@ func (sr *subroundEndRound) sendProof() (data.HeaderProofHandler, bool) { log.Debug("sendProof: time is out -> cancel broadcasting final info and header", "round time stamp", roundHandler.TimeStamp(), "current time", time.Now()) - return nil, false + return } // broadcast header proof - proof, err := sr.createAndBroadcastProof(sig, bitmap) - return proof, err == nil + err = sr.createAndBroadcastProof(sig, bitmap) + if err != nil { + log.Warn("sendProof.createAndBroadcastProof", "error", err.Error()) + } } func (sr *subroundEndRound) shouldSendProof() bool { @@ -524,7 +543,7 @@ func (sr *subroundEndRound) computeAggSigOnValidNodes() ([]byte, []byte, error) return bitmap, sig, nil } -func (sr *subroundEndRound) createAndBroadcastProof(signature []byte, bitmap []byte) (*block.HeaderProof, error) { +func (sr *subroundEndRound) createAndBroadcastProof(signature []byte, bitmap []byte) error { headerProof := &block.HeaderProof{ PubKeysBitmap: bitmap, AggregatedSignature: signature, @@ -533,18 +552,19 @@ func (sr *subroundEndRound) createAndBroadcastProof(signature []byte, bitmap []b HeaderNonce: sr.GetHeader().GetNonce(), HeaderShardId: sr.GetHeader().GetShardID(), HeaderRound: sr.GetHeader().GetRound(), + IsStartOfEpoch: sr.GetHeader().IsStartOfEpochBlock(), } err := sr.BroadcastMessenger().BroadcastEquivalentProof(headerProof, []byte(sr.SelfPubKey())) if err != nil { - return nil, err + return err } log.Debug("step 3: block header proof has been sent", "PubKeysBitmap", bitmap, "AggregateSignature", signature) - return headerProof, nil + return nil } func (sr *subroundEndRound) createAndBroadcastInvalidSigners(invalidSigners []byte) { diff --git a/consensus/spos/bls/v2/subroundEndRound_test.go b/consensus/spos/bls/v2/subroundEndRound_test.go index 394df2b8d20..966d7fcbea3 100644 --- a/consensus/spos/bls/v2/subroundEndRound_test.go +++ b/consensus/spos/bls/v2/subroundEndRound_test.go @@ -572,6 +572,11 @@ func TestSubroundEndRound_DoEndRoundJobAllOK(t *testing.T) { t.Parallel() container := consensusMocks.InitConsensusCore() + container.SetEquivalentProofsPool(&dataRetriever.ProofsPoolMock{ + HasProofCalled: func(shardID uint32, headerHash []byte) bool { + return true + }, + }) sr := initSubroundEndRoundWithContainer(container, &statusHandler.AppStatusHandlerStub{}) sr.SetSelfPubKey("A") @@ -1176,6 +1181,16 @@ func TestSubroundEndRound_DoEndRoundJobByNode(t *testing.T) { t.Parallel() container := consensusMocks.InitConsensusCore() + numCalls := 0 + container.SetEquivalentProofsPool(&dataRetriever.ProofsPoolMock{ + HasProofCalled: func(shardID uint32, headerHash []byte) bool { + if numCalls <= 1 { + numCalls++ + return false + } + return true + }, + }) sr := initSubroundEndRoundWithContainer(container, &statusHandler.AppStatusHandlerStub{}) verifySigShareNumCalls := 0 @@ -1227,14 +1242,17 @@ func TestSubroundEndRound_DoEndRoundJobByNode(t *testing.T) { t.Run("should work with equivalent messages flag active", func(t *testing.T) { t.Parallel() - providedPrevSig := []byte("prev sig") - providedPrevBitmap := []byte{1, 1, 1, 1, 1, 1, 1, 1, 1} container := consensusMocks.InitConsensusCore() container.SetBlockchain(&testscommon.ChainHandlerStub{ GetGenesisHeaderCalled: func() data.HeaderHandler { return &block.HeaderV2{} }, }) + container.SetEquivalentProofsPool(&dataRetriever.ProofsPoolMock{ + HasProofCalled: func(shardID uint32, headerHash []byte) bool { + return true + }, + }) enableEpochsHandler := &enableEpochsHandlerMock.EnableEpochsHandlerStub{ IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { return flag == common.EquivalentMessagesFlag @@ -1242,16 +1260,6 @@ func TestSubroundEndRound_DoEndRoundJobByNode(t *testing.T) { } container.SetEnableEpochsHandler(enableEpochsHandler) - wasSetCurrentHeaderProofCalled := false - container.SetEquivalentProofsPool(&dataRetriever.ProofsPoolMock{ - AddProofCalled: func(headerProof data.HeaderProofHandler) bool { - wasSetCurrentHeaderProofCalled = true - require.NotEqual(t, providedPrevSig, headerProof.GetAggregatedSignature()) - require.NotEqual(t, providedPrevBitmap, headerProof.GetPubKeysBitmap()) - return true - }, - }) - ch := make(chan bool, 1) consensusState := initializers.InitConsensusState() sr, _ := spos.NewSubround( @@ -1295,7 +1303,6 @@ func TestSubroundEndRound_DoEndRoundJobByNode(t *testing.T) { r := srEndRound.DoEndRoundJobByNode() require.True(t, r) - require.True(t, wasSetCurrentHeaderProofCalled) }) } diff --git a/dataRetriever/dataPool/proofsCache/proofsPool.go b/dataRetriever/dataPool/proofsCache/proofsPool.go index e894dc220b0..6a9b4d18406 100644 --- a/dataRetriever/dataPool/proofsCache/proofsPool.go +++ b/dataRetriever/dataPool/proofsCache/proofsPool.go @@ -53,20 +53,22 @@ func (pp *proofsPool) AddProof( } pp.mutCache.Lock() - defer pp.mutCache.Unlock() - proofsPerShard, ok := pp.cache[shardID] if !ok { proofsPerShard = newProofsCache() pp.cache[shardID] = proofsPerShard } + pp.mutCache.Unlock() - log.Trace("added proof to pool", + log.Debug("added proof to pool", "header hash", headerProof.GetHeaderHash(), "epoch", headerProof.GetHeaderEpoch(), "nonce", headerProof.GetHeaderNonce(), "shardID", headerProof.GetHeaderShardId(), "pubKeys bitmap", headerProof.GetPubKeysBitmap(), + "round", headerProof.GetHeaderRound(), + "nonce", headerProof.GetHeaderNonce(), + "isStartOfEpoch", headerProof.GetIsStartOfEpoch(), ) proofsPerShard.addProof(headerProof) @@ -98,9 +100,8 @@ func (pp *proofsPool) CleanupProofsBehindNonce(shardID uint32, nonce uint64) err nonce -= pp.cleanupNonceDelta pp.mutCache.RLock() - defer pp.mutCache.RUnlock() - proofsPerShard, ok := pp.cache[shardID] + pp.mutCache.RUnlock() if !ok { return fmt.Errorf("%w: proofs cache per shard not found, shard ID: %d", ErrMissingProof, shardID) } @@ -123,16 +124,14 @@ func (pp *proofsPool) GetProof( if headerHash == nil { return nil, fmt.Errorf("nil header hash") } - - pp.mutCache.RLock() - defer pp.mutCache.RUnlock() - log.Trace("trying to get proof", "headerHash", headerHash, "shardID", shardID, ) + pp.mutCache.RLock() proofsPerShard, ok := pp.cache[shardID] + pp.mutCache.RUnlock() if !ok { return nil, fmt.Errorf("%w: proofs cache per shard not found, shard ID: %d", ErrMissingProof, shardID) } diff --git a/epochStart/bootstrap/epochStartMetaBlockProcessor.go b/epochStart/bootstrap/epochStartMetaBlockProcessor.go index 8ee40232287..93b993e81cc 100644 --- a/epochStart/bootstrap/epochStartMetaBlockProcessor.go +++ b/epochStart/bootstrap/epochStartMetaBlockProcessor.go @@ -264,7 +264,7 @@ func (e *epochStartMetaBlockProcessor) waitForConfMetaBlock(ctx context.Context, return epochStart.ErrNilMetaBlock } - err := e.requestConfirmationMetaBlock(metaBlock.GetNonce()) + err := e.requestConfirmationMetaBlock(metaBlock.GetNonce() + 1) if err != nil { return err } @@ -278,7 +278,7 @@ func (e *epochStartMetaBlockProcessor) waitForConfMetaBlock(ctx context.Context, case <-ctx.Done(): return epochStart.ErrTimeoutWaitingForMetaBlock case <-chanRequests: - err = e.requestConfirmationMetaBlock(metaBlock.GetNonce()) + err = e.requestConfirmationMetaBlock(metaBlock.GetNonce() + 1) if err != nil { return err } diff --git a/epochStart/shardchain/trigger.go b/epochStart/shardchain/trigger.go index f2166f7d5b9..27d231df9d2 100644 --- a/epochStart/shardchain/trigger.go +++ b/epochStart/shardchain/trigger.go @@ -569,6 +569,7 @@ func (t *trigger) receivedProof(headerProof data.HeaderProofHandler) { if headerProof.GetHeaderShardId() != core.MetachainShardId { return } + t.mutTrigger.Lock() defer t.mutTrigger.Unlock() diff --git a/factory/bootstrap/bootstrapComponents.go b/factory/bootstrap/bootstrapComponents.go index af6dc1aafa3..2154289a4c7 100644 --- a/factory/bootstrap/bootstrapComponents.go +++ b/factory/bootstrap/bootstrapComponents.go @@ -3,9 +3,11 @@ package bootstrap import ( "fmt" "path/filepath" + "time" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" + interceptorFactory "github.com/multiversx/mx-chain-go/process/interceptors/factory" logger "github.com/multiversx/mx-chain-logger-go" nodeFactory "github.com/multiversx/mx-chain-go/cmd/node/factory" @@ -200,6 +202,12 @@ func (bcf *bootstrapComponentsFactory) Create() (*bootstrapComponents, error) { return nil, err } + // create a new instance of interceptedDataVerifier which will be used for bootstrap only + interceptedDataVerifierFactory := interceptorFactory.NewInterceptedDataVerifierFactory(interceptorFactory.InterceptedDataVerifierFactoryArgs{ + CacheSpan: time.Duration(bcf.config.InterceptedDataVerifier.CacheSpanInSec) * time.Second, + CacheExpiry: time.Duration(bcf.config.InterceptedDataVerifier.CacheExpiryInSec) * time.Second, + }) + epochStartBootstrapArgs := bootstrap.ArgsEpochStartBootstrap{ CoreComponentsHolder: bcf.coreComponents, CryptoComponentsHolder: bcf.cryptoComponents, @@ -227,6 +235,7 @@ func (bcf *bootstrapComponentsFactory) Create() (*bootstrapComponents, error) { StateStatsHandler: bcf.statusCoreComponents.StateStatsHandler(), NodesCoordinatorRegistryFactory: nodesCoordinatorRegistryFactory, EnableEpochsHandler: bcf.coreComponents.EnableEpochsHandler(), + InterceptedDataVerifierFactory: interceptedDataVerifierFactory, } var epochStartBootstrapper factory.EpochStartBootstrapper diff --git a/go.mod b/go.mod index 50b426cbbd0..a0736593c96 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/klauspost/cpuid/v2 v2.2.5 github.com/mitchellh/mapstructure v1.5.0 github.com/multiversx/mx-chain-communication-go v1.1.1 - github.com/multiversx/mx-chain-core-go v1.2.25-0.20250128130418-5550d48d5e66 + github.com/multiversx/mx-chain-core-go v1.2.25-0.20250128131118-6d145bb2a7b4 github.com/multiversx/mx-chain-crypto-go v1.2.12 github.com/multiversx/mx-chain-es-indexer-go v1.7.14 github.com/multiversx/mx-chain-logger-go v1.0.15 diff --git a/go.sum b/go.sum index 9094014f9ed..5382a93a886 100644 --- a/go.sum +++ b/go.sum @@ -387,8 +387,8 @@ github.com/multiversx/concurrent-map v0.1.4 h1:hdnbM8VE4b0KYJaGY5yJS2aNIW9TFFsUY github.com/multiversx/concurrent-map v0.1.4/go.mod h1:8cWFRJDOrWHOTNSqgYCUvwT7c7eFQ4U2vKMOp4A/9+o= github.com/multiversx/mx-chain-communication-go v1.1.1 h1:y4DoQeQOJTaSUsRzczQFazf8JYQmInddypApqA3AkwM= github.com/multiversx/mx-chain-communication-go v1.1.1/go.mod h1:WK6bP4pGEHGDDna/AYRIMtl6G9OA0NByI1Lw8PmOnRM= -github.com/multiversx/mx-chain-core-go v1.2.25-0.20250128130418-5550d48d5e66 h1:V44pZBVEu3rqTM8CFZljCyYu36yOV4S+4AkRi19FxWE= -github.com/multiversx/mx-chain-core-go v1.2.25-0.20250128130418-5550d48d5e66/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE= +github.com/multiversx/mx-chain-core-go v1.2.25-0.20250128131118-6d145bb2a7b4 h1:GInmVgiTE3vRGL8V8XK7+RQJ1renroYNEuejxkxZcBA= +github.com/multiversx/mx-chain-core-go v1.2.25-0.20250128131118-6d145bb2a7b4/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE= github.com/multiversx/mx-chain-crypto-go v1.2.12 h1:zWip7rpUS4CGthJxfKn5MZfMfYPjVjIiCID6uX5BSOk= github.com/multiversx/mx-chain-crypto-go v1.2.12/go.mod h1:HzcPpCm1zanNct/6h2rIh+MFrlXbjA5C8+uMyXj3LI4= github.com/multiversx/mx-chain-es-indexer-go v1.7.14 h1:V4fuubEUYskWCLQIkbuoB0WHoKyldLQRq/fllIzW1CU= diff --git a/integrationTests/testProcessorNode.go b/integrationTests/testProcessorNode.go index 136f701bcab..95b5ab76256 100644 --- a/integrationTests/testProcessorNode.go +++ b/integrationTests/testProcessorNode.go @@ -2838,6 +2838,7 @@ func (tpn *TestProcessorNode) setBlockSignatures(blockHeader data.HeaderHandler) HeaderEpoch: currHdr.GetEpoch(), HeaderNonce: currHdr.GetNonce(), HeaderShardId: currHdr.GetShardID(), + IsStartOfEpoch: blockHeader.IsStartOfEpochBlock(), } blockHeader.SetPreviousProof(previousProof) diff --git a/process/block/baseProcess.go b/process/block/baseProcess.go index 0f81fda12ba..5b56ae4e7b6 100644 --- a/process/block/baseProcess.go +++ b/process/block/baseProcess.go @@ -372,12 +372,22 @@ func displayHeader(headerHandler data.HeaderHandler) []*display.LineData { proof := headerHandler.GetPreviousProof() - var prevAggregatedSig, prevBitmap []byte + var prevAggregatedSig, prevBitmap, prevHash []byte + var proofShard, proofEpoch uint32 + var proofRound, proofNonce uint64 + var isStartOfEpoch, hasProofInfo bool if !check.IfNilReflect(proof) { + hasProofInfo = true prevAggregatedSig, prevBitmap = proof.GetAggregatedSignature(), proof.GetPubKeysBitmap() + prevHash = proof.GetHeaderHash() + proofShard = proof.GetHeaderShardId() + proofEpoch = proof.GetHeaderEpoch() + proofRound = proof.GetHeaderRound() + proofNonce = proof.GetHeaderNonce() + isStartOfEpoch = proof.GetIsStartOfEpoch() } - return []*display.LineData{ + logLines := []*display.LineData{ display.NewLineData(false, []string{ "", "ChainID", @@ -438,19 +448,50 @@ func displayHeader(headerHandler data.HeaderHandler) []*display.LineData { "", "Receipts hash", logger.DisplayByteSlice(headerHandler.GetReceiptsHash())}), - display.NewLineData(false, []string{ + display.NewLineData(true, []string{ "", "Epoch start meta hash", logger.DisplayByteSlice(epochStartMetaHash)}), - display.NewLineData(false, []string{ - "Previous proof", - "Aggregated signature", - logger.DisplayByteSlice(prevAggregatedSig)}), - display.NewLineData(true, []string{ - "", - "Pub keys bitmap", - logger.DisplayByteSlice(prevBitmap)}), } + + if hasProofInfo { + logLines = append(logLines, + display.NewLineData(false, []string{ + "Previous proof", + "Header hash", + logger.DisplayByteSlice(prevHash)}), + display.NewLineData(false, []string{ + "", + "Aggregated signature", + logger.DisplayByteSlice(prevAggregatedSig)}), + display.NewLineData(false, []string{ + "", + "Pub keys bitmap", + logger.DisplayByteSlice(prevBitmap)}), + display.NewLineData(false, []string{ + "", + "Epoch", + fmt.Sprintf("%d", proofEpoch)}), + display.NewLineData(false, []string{ + "", + "Round", + fmt.Sprintf("%d", proofRound)}), + display.NewLineData(false, []string{ + "", + "Shard", + fmt.Sprintf("%d", proofShard)}), + display.NewLineData(false, []string{ + "", + "Nonce", + fmt.Sprintf("%d", proofNonce)}), + display.NewLineData(true, []string{ + "", + "IsStartOfEpoch", + fmt.Sprintf("%t", isStartOfEpoch)}), + ) + } + + return logLines } // checkProcessorParameters will check the input parameters values @@ -624,51 +665,67 @@ func (bp *baseProcessor) verifyFees(header data.HeaderHandler) error { return nil } -// TODO: remove bool parameter and give instead the set to sort -func (bp *baseProcessor) sortHeadersForCurrentBlockByNonce(usedInBlock bool) (map[uint32][]data.HeaderHandler, error) { +func (bp *baseProcessor) computeHeadersForCurrentBlock(usedInBlock bool) (map[uint32][]data.HeaderHandler, error) { hdrsForCurrentBlock := make(map[uint32][]data.HeaderHandler) - bp.hdrsForCurrBlock.mutHdrsForBlock.RLock() for hdrHash, headerInfo := range bp.hdrsForCurrBlock.hdrHashAndInfo { if headerInfo.usedInBlock != usedInBlock { continue } if bp.hasMissingProof(headerInfo, hdrHash) { - bp.hdrsForCurrBlock.mutHdrsForBlock.RUnlock() return nil, fmt.Errorf("%w for header with hash %s", process.ErrMissingHeaderProof, hex.EncodeToString([]byte(hdrHash))) } hdrsForCurrentBlock[headerInfo.hdr.GetShardID()] = append(hdrsForCurrentBlock[headerInfo.hdr.GetShardID()], headerInfo.hdr) } - bp.hdrsForCurrBlock.mutHdrsForBlock.RUnlock() - - // sort headers for each shard - for _, hdrsForShard := range hdrsForCurrentBlock { - process.SortHeadersByNonce(hdrsForShard) - } return hdrsForCurrentBlock, nil } -func (bp *baseProcessor) sortHeaderHashesForCurrentBlockByNonce(usedInBlock bool) (map[uint32][][]byte, error) { +func (bp *baseProcessor) computeHeadersForCurrentBlockInfo(usedInBlock bool) (map[uint32][]*nonceAndHashInfo, error) { hdrsForCurrentBlockInfo := make(map[uint32][]*nonceAndHashInfo) - bp.hdrsForCurrBlock.mutHdrsForBlock.RLock() for metaBlockHash, headerInfo := range bp.hdrsForCurrBlock.hdrHashAndInfo { if headerInfo.usedInBlock != usedInBlock { continue } if bp.hasMissingProof(headerInfo, metaBlockHash) { - bp.hdrsForCurrBlock.mutHdrsForBlock.RUnlock() return nil, fmt.Errorf("%w for header with hash %s", process.ErrMissingHeaderProof, hex.EncodeToString([]byte(metaBlockHash))) } hdrsForCurrentBlockInfo[headerInfo.hdr.GetShardID()] = append(hdrsForCurrentBlockInfo[headerInfo.hdr.GetShardID()], &nonceAndHashInfo{nonce: headerInfo.hdr.GetNonce(), hash: []byte(metaBlockHash)}) } + + return hdrsForCurrentBlockInfo, nil +} + +// TODO: remove bool parameter and give instead the set to sort +func (bp *baseProcessor) sortHeadersForCurrentBlockByNonce(usedInBlock bool) (map[uint32][]data.HeaderHandler, error) { + bp.hdrsForCurrBlock.mutHdrsForBlock.RLock() + hdrsForCurrentBlock, err := bp.computeHeadersForCurrentBlock(usedInBlock) + bp.hdrsForCurrBlock.mutHdrsForBlock.RUnlock() + if err != nil { + return nil, err + } + + // sort headers for each shard + for _, hdrsForShard := range hdrsForCurrentBlock { + process.SortHeadersByNonce(hdrsForShard) + } + + return hdrsForCurrentBlock, nil +} + +func (bp *baseProcessor) sortHeaderHashesForCurrentBlockByNonce(usedInBlock bool) (map[uint32][][]byte, error) { + bp.hdrsForCurrBlock.mutHdrsForBlock.RLock() + hdrsForCurrentBlockInfo, err := bp.computeHeadersForCurrentBlockInfo(usedInBlock) bp.hdrsForCurrBlock.mutHdrsForBlock.RUnlock() + if err != nil { + return nil, err + } for _, hdrsForShard := range hdrsForCurrentBlockInfo { if len(hdrsForShard) > 1 { @@ -690,8 +747,11 @@ func (bp *baseProcessor) sortHeaderHashesForCurrentBlockByNonce(usedInBlock bool func (bp *baseProcessor) hasMissingProof(headerInfo *hdrInfo, hdrHash string) bool { isFlagEnabledForHeader := bp.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, headerInfo.hdr.GetEpoch()) - hasProof := bp.proofsPool.HasProof(headerInfo.hdr.GetShardID(), []byte(hdrHash)) - return isFlagEnabledForHeader && !hasProof + if !isFlagEnabledForHeader { + return false + } + + return !bp.proofsPool.HasProof(headerInfo.hdr.GetShardID(), []byte(hdrHash)) } func (bp *baseProcessor) createMiniBlockHeaderHandlers( @@ -2214,5 +2274,26 @@ func (bp *baseProcessor) addPrevProofIfNeeded(header data.HeaderHandler) error { } header.SetPreviousProof(prevBlockProof) + + log.Debug("added proof on header", + "header hash", prevBlockProof.GetHeaderHash(), + "epoch", prevBlockProof.GetHeaderEpoch(), + "nonce", prevBlockProof.GetHeaderNonce(), + "shardID", prevBlockProof.GetHeaderShardId(), + "pubKeys bitmap", prevBlockProof.GetPubKeysBitmap(), + "round", prevBlockProof.GetHeaderRound(), + "nonce", prevBlockProof.GetHeaderNonce(), + "isStartOfEpoch", prevBlockProof.GetIsStartOfEpoch(), + ) + return nil } + +func (bp *baseProcessor) getHeaderHash(header data.HeaderHandler) ([]byte, error) { + marshalledHeader, errMarshal := bp.marshalizer.Marshal(header) + if errMarshal != nil { + return nil, errMarshal + } + + return bp.hasher.Compute(string(marshalledHeader)), nil +} diff --git a/process/block/export_test.go b/process/block/export_test.go index 56365b44a22..a1edd702f9b 100644 --- a/process/block/export_test.go +++ b/process/block/export_test.go @@ -31,14 +31,17 @@ import ( storageStubs "github.com/multiversx/mx-chain-go/testscommon/storage" ) +// ComputeHeaderHash - func (bp *baseProcessor) ComputeHeaderHash(hdr data.HeaderHandler) ([]byte, error) { return core.CalculateHash(bp.marshalizer, bp.hasher, hdr) } +// VerifyStateRoot - func (bp *baseProcessor) VerifyStateRoot(rootHash []byte) bool { return bp.verifyStateRoot(rootHash) } +// CheckBlockValidity - func (bp *baseProcessor) CheckBlockValidity( headerHandler data.HeaderHandler, bodyHandler data.BodyHandler, @@ -46,6 +49,7 @@ func (bp *baseProcessor) CheckBlockValidity( return bp.checkBlockValidity(headerHandler, bodyHandler) } +// RemoveHeadersBehindNonceFromPools - func (bp *baseProcessor) RemoveHeadersBehindNonceFromPools( shouldRemoveBlockBody bool, shardId uint32, @@ -54,34 +58,42 @@ func (bp *baseProcessor) RemoveHeadersBehindNonceFromPools( bp.removeHeadersBehindNonceFromPools(shouldRemoveBlockBody, shardId, nonce) } +// GetPruningHandler - func (bp *baseProcessor) GetPruningHandler(finalHeaderNonce uint64) state.PruningHandler { return bp.getPruningHandler(finalHeaderNonce) } +// SetLastRestartNonce - func (bp *baseProcessor) SetLastRestartNonce(lastRestartNonce uint64) { bp.lastRestartNonce = lastRestartNonce } +// CommitTrieEpochRootHashIfNeeded - func (bp *baseProcessor) CommitTrieEpochRootHashIfNeeded(metaBlock *block.MetaBlock, rootHash []byte) error { return bp.commitTrieEpochRootHashIfNeeded(metaBlock, rootHash) } +// ReceivedMetaBlock - func (sp *shardProcessor) ReceivedMetaBlock(header data.HeaderHandler, metaBlockHash []byte) { sp.receivedMetaBlock(header, metaBlockHash) } +// CreateMiniBlocks - func (sp *shardProcessor) CreateMiniBlocks(haveTime func() bool) (*block.Body, map[string]*processedMb.ProcessedMiniBlockInfo, error) { return sp.createMiniBlocks(haveTime, []byte("random")) } +// GetOrderedProcessedMetaBlocksFromHeader - func (sp *shardProcessor) GetOrderedProcessedMetaBlocksFromHeader(header data.HeaderHandler) ([]data.HeaderHandler, error) { return sp.getOrderedProcessedMetaBlocksFromHeader(header) } +// UpdateCrossShardInfo - func (sp *shardProcessor) UpdateCrossShardInfo(processedMetaHdrs []data.HeaderHandler) error { return sp.updateCrossShardInfo(processedMetaHdrs) } +// UpdateStateStorage - func (sp *shardProcessor) UpdateStateStorage(finalHeaders []data.HeaderHandler, currentHeader data.HeaderHandler) { currShardHeader, ok := currentHeader.(data.ShardHeaderHandler) if !ok { @@ -90,6 +102,7 @@ func (sp *shardProcessor) UpdateStateStorage(finalHeaders []data.HeaderHandler, sp.updateState(finalHeaders, currShardHeader) } +// NewShardProcessorEmptyWith3shards - func NewShardProcessorEmptyWith3shards( tdp dataRetriever.PoolsHolder, genesisBlocks map[uint32]data.HeaderHandler, @@ -176,18 +189,22 @@ func NewShardProcessorEmptyWith3shards( return shardProc, err } +// RequestBlockHeaders - func (mp *metaProcessor) RequestBlockHeaders(header *block.MetaBlock) (uint32, uint32) { return mp.requestShardHeaders(header) } +// ReceivedShardHeader - func (mp *metaProcessor) ReceivedShardHeader(header data.HeaderHandler, shardHeaderHash []byte) { mp.receivedShardHeader(header, shardHeaderHash) } +// GetDataPool - func (mp *metaProcessor) GetDataPool() dataRetriever.PoolsHolder { return mp.dataPool } +// AddHdrHashToRequestedList - func (mp *metaProcessor) AddHdrHashToRequestedList(hdr data.HeaderHandler, hdrHash []byte) { mp.hdrsForCurrBlock.mutHdrsForBlock.Lock() defer mp.hdrsForCurrBlock.mutHdrsForBlock.Unlock() @@ -204,6 +221,7 @@ func (mp *metaProcessor) AddHdrHashToRequestedList(hdr data.HeaderHandler, hdrHa mp.hdrsForCurrBlock.missingHdrs++ } +// IsHdrMissing - func (mp *metaProcessor) IsHdrMissing(hdrHash []byte) bool { mp.hdrsForCurrBlock.mutHdrsForBlock.RLock() defer mp.hdrsForCurrBlock.mutHdrsForBlock.RUnlock() @@ -216,10 +234,12 @@ func (mp *metaProcessor) IsHdrMissing(hdrHash []byte) bool { return check.IfNil(hdrInfoValue.hdr) } +// CreateShardInfo - func (mp *metaProcessor) CreateShardInfo() ([]data.ShardDataHandler, error) { return mp.createShardInfo() } +// RequestMissingFinalityAttestingShardHeaders - func (mp *metaProcessor) RequestMissingFinalityAttestingShardHeaders() uint32 { mp.hdrsForCurrBlock.mutHdrsForBlock.Lock() defer mp.hdrsForCurrBlock.mutHdrsForBlock.Unlock() @@ -227,10 +247,12 @@ func (mp *metaProcessor) RequestMissingFinalityAttestingShardHeaders() uint32 { return mp.requestMissingFinalityAttestingShardHeaders() } +// SaveMetricCrossCheckBlockHeight - func (mp *metaProcessor) SaveMetricCrossCheckBlockHeight() { mp.saveMetricCrossCheckBlockHeight() } +// NotarizedHdrs - func (bp *baseProcessor) NotarizedHdrs() map[uint32][]data.HeaderHandler { lastCrossNotarizedHeaders := make(map[uint32][]data.HeaderHandler) for shardID := uint32(0); shardID < bp.shardCoordinator.NumberOfShards(); shardID++ { @@ -248,6 +270,7 @@ func (bp *baseProcessor) NotarizedHdrs() map[uint32][]data.HeaderHandler { return lastCrossNotarizedHeaders } +// LastNotarizedHdrForShard - func (bp *baseProcessor) LastNotarizedHdrForShard(shardID uint32) data.HeaderHandler { lastCrossNotarizedHeaderForShard, _, _ := bp.blockTracker.GetLastCrossNotarizedHeader(shardID) if check.IfNil(lastCrossNotarizedHeaderForShard) { @@ -257,76 +280,94 @@ func (bp *baseProcessor) LastNotarizedHdrForShard(shardID uint32) data.HeaderHan return lastCrossNotarizedHeaderForShard } +// SetMarshalizer - func (bp *baseProcessor) SetMarshalizer(marshal marshal.Marshalizer) { bp.marshalizer = marshal } +// SetHasher - func (bp *baseProcessor) SetHasher(hasher hashing.Hasher) { bp.hasher = hasher } +// SetHeaderValidator - func (bp *baseProcessor) SetHeaderValidator(validator process.HeaderConstructionValidator) { bp.headerValidator = validator } +// RequestHeadersIfMissing - func (bp *baseProcessor) RequestHeadersIfMissing(sortedHdrs []data.HeaderHandler, shardId uint32) error { return bp.requestHeadersIfMissing(sortedHdrs, shardId) } +// SetShardBlockFinality - func (mp *metaProcessor) SetShardBlockFinality(val uint32) { mp.hdrsForCurrBlock.mutHdrsForBlock.Lock() mp.shardBlockFinality = val mp.hdrsForCurrBlock.mutHdrsForBlock.Unlock() } +// SaveLastNotarizedHeader - func (mp *metaProcessor) SaveLastNotarizedHeader(header *block.MetaBlock) error { return mp.saveLastNotarizedHeader(header) } +// CheckShardHeadersValidity - func (mp *metaProcessor) CheckShardHeadersValidity(header *block.MetaBlock) (map[uint32]data.HeaderHandler, error) { return mp.checkShardHeadersValidity(header) } +// CheckShardHeadersFinality - func (mp *metaProcessor) CheckShardHeadersFinality(highestNonceHdrs map[uint32]data.HeaderHandler) error { return mp.checkShardHeadersFinality(highestNonceHdrs) } +// CheckHeaderBodyCorrelation - func (mp *metaProcessor) CheckHeaderBodyCorrelation(hdr data.HeaderHandler, body *block.Body) error { return mp.checkHeaderBodyCorrelation(hdr.GetMiniBlockHeaderHandlers(), body) } +// IsHdrConstructionValid - func (bp *baseProcessor) IsHdrConstructionValid(currHdr, prevHdr data.HeaderHandler) error { return bp.headerValidator.IsHeaderConstructionValid(currHdr, prevHdr) } +// ChRcvAllHdrs - func (mp *metaProcessor) ChRcvAllHdrs() chan bool { return mp.chRcvAllHdrs } +// UpdateShardsHeadersNonce - func (mp *metaProcessor) UpdateShardsHeadersNonce(key uint32, value uint64) { mp.updateShardHeadersNonce(key, value) } +// GetShardsHeadersNonce - func (mp *metaProcessor) GetShardsHeadersNonce() *sync.Map { return mp.shardsHeadersNonce } +// SaveLastNotarizedHeader - func (sp *shardProcessor) SaveLastNotarizedHeader(shardId uint32, processedHdrs []data.HeaderHandler) error { return sp.saveLastNotarizedHeader(shardId, processedHdrs) } +// CheckHeaderBodyCorrelation - func (sp *shardProcessor) CheckHeaderBodyCorrelation(hdr data.HeaderHandler, body *block.Body) error { return sp.checkHeaderBodyCorrelation(hdr.GetMiniBlockHeaderHandlers(), body) } +// CheckAndRequestIfMetaHeadersMissing - func (sp *shardProcessor) CheckAndRequestIfMetaHeadersMissing() { sp.checkAndRequestIfMetaHeadersMissing() } +// GetHashAndHdrStruct - func (sp *shardProcessor) GetHashAndHdrStruct(header data.HeaderHandler, hash []byte) *hashAndHdr { return &hashAndHdr{header, hash} } +// RequestMissingFinalityAttestingHeaders - func (sp *shardProcessor) RequestMissingFinalityAttestingHeaders() uint32 { sp.hdrsForCurrBlock.mutHdrsForBlock.Lock() defer sp.hdrsForCurrBlock.mutHdrsForBlock.Unlock() @@ -337,10 +378,12 @@ func (sp *shardProcessor) RequestMissingFinalityAttestingHeaders() uint32 { ) } +// CheckMetaHeadersValidityAndFinality - func (sp *shardProcessor) CheckMetaHeadersValidityAndFinality() error { return sp.checkMetaHeadersValidityAndFinality() } +// CreateAndProcessMiniBlocksDstMe - func (sp *shardProcessor) CreateAndProcessMiniBlocksDstMe( haveTime func() bool, ) (block.MiniBlockSlice, uint32, uint32, error) { @@ -348,6 +391,7 @@ func (sp *shardProcessor) CreateAndProcessMiniBlocksDstMe( return createAndProcessInfo.miniBlocks, createAndProcessInfo.numHdrsAdded, createAndProcessInfo.numTxsAdded, err } +// DisplayLogInfo - func (sp *shardProcessor) DisplayLogInfo( header data.HeaderHandler, body *block.Body, @@ -360,10 +404,12 @@ func (sp *shardProcessor) DisplayLogInfo( sp.txCounter.displayLogInfo(header, body, headerHash, numShards, selfId, dataPool, blockTracker) } +// GetHighestHdrForOwnShardFromMetachain - func (sp *shardProcessor) GetHighestHdrForOwnShardFromMetachain(processedHdrs []data.HeaderHandler) ([]data.HeaderHandler, [][]byte, error) { return sp.getHighestHdrForOwnShardFromMetachain(processedHdrs) } +// RestoreMetaBlockIntoPool - func (sp *shardProcessor) RestoreMetaBlockIntoPool( miniBlockHashes map[string]uint32, metaBlockHashes [][]byte, @@ -372,60 +418,94 @@ func (sp *shardProcessor) RestoreMetaBlockIntoPool( return sp.restoreMetaBlockIntoPool(headerHandler, miniBlockHashes, metaBlockHashes) } +// GetAllMiniBlockDstMeFromMeta - func (sp *shardProcessor) GetAllMiniBlockDstMeFromMeta( header data.ShardHeaderHandler, ) (map[string][]byte, error) { return sp.getAllMiniBlockDstMeFromMeta(header) } +// SetHdrForCurrentBlock - func (bp *baseProcessor) SetHdrForCurrentBlock(headerHash []byte, headerHandler data.HeaderHandler, usedInBlock bool) { bp.hdrsForCurrBlock.mutHdrsForBlock.Lock() bp.hdrsForCurrBlock.hdrHashAndInfo[string(headerHash)] = &hdrInfo{hdr: headerHandler, usedInBlock: usedInBlock} bp.hdrsForCurrBlock.mutHdrsForBlock.Unlock() } +// SetHighestHdrNonceForCurrentBlock - func (bp *baseProcessor) SetHighestHdrNonceForCurrentBlock(shardId uint32, value uint64) { bp.hdrsForCurrBlock.mutHdrsForBlock.Lock() bp.hdrsForCurrBlock.highestHdrNonce[shardId] = value bp.hdrsForCurrBlock.mutHdrsForBlock.Unlock() } +// LastNotarizedHeaderInfo - +type LastNotarizedHeaderInfo struct { + Header data.HeaderHandler + Hash []byte + NotarizedBasedOnProof bool + HasProof bool +} + +// SetLastNotarizedHeaderForShard - +func (bp *baseProcessor) SetLastNotarizedHeaderForShard(shardId uint32, info *LastNotarizedHeaderInfo) { + bp.hdrsForCurrBlock.mutHdrsForBlock.Lock() + lastNotarizedShardInfo := &lastNotarizedHeaderInfo{ + header: info.Header, + hash: info.Hash, + notarizedBasedOnProof: info.NotarizedBasedOnProof, + hasProof: info.HasProof, + } + bp.hdrsForCurrBlock.lastNotarizedShardHeaders[shardId] = lastNotarizedShardInfo + bp.hdrsForCurrBlock.mutHdrsForBlock.Unlock() +} + +// CreateBlockStarted - func (bp *baseProcessor) CreateBlockStarted() error { return bp.createBlockStarted() } +// AddProcessedCrossMiniBlocksFromHeader - func (sp *shardProcessor) AddProcessedCrossMiniBlocksFromHeader(header data.HeaderHandler) error { return sp.addProcessedCrossMiniBlocksFromHeader(header) } +// VerifyCrossShardMiniBlockDstMe - func (mp *metaProcessor) VerifyCrossShardMiniBlockDstMe(header *block.MetaBlock) error { return mp.verifyCrossShardMiniBlockDstMe(header) } +// ApplyBodyToHeader - func (mp *metaProcessor) ApplyBodyToHeader(metaHdr data.MetaHeaderHandler, body *block.Body) (data.BodyHandler, error) { return mp.applyBodyToHeader(metaHdr, body) } +// ApplyBodyToHeader - func (sp *shardProcessor) ApplyBodyToHeader(shardHdr data.ShardHeaderHandler, body *block.Body, processedMiniBlocksDestMeInfo map[string]*processedMb.ProcessedMiniBlockInfo) (*block.Body, error) { return sp.applyBodyToHeader(shardHdr, body, processedMiniBlocksDestMeInfo) } +// CreateBlockBody - func (mp *metaProcessor) CreateBlockBody(metaBlock data.HeaderHandler, haveTime func() bool) (data.BodyHandler, error) { return mp.createBlockBody(metaBlock, haveTime) } +// CreateBlockBody - func (sp *shardProcessor) CreateBlockBody(shardHdr data.HeaderHandler, haveTime func() bool) (data.BodyHandler, map[string]*processedMb.ProcessedMiniBlockInfo, error) { return sp.createBlockBody(shardHdr, haveTime) } +// CheckEpochCorrectnessCrossChain - func (sp *shardProcessor) CheckEpochCorrectnessCrossChain() error { return sp.checkEpochCorrectnessCrossChain() } +// CheckEpochCorrectness - func (sp *shardProcessor) CheckEpochCorrectness(header *block.Header) error { return sp.checkEpochCorrectness(header) } +// GetBootstrapHeadersInfo - func (sp *shardProcessor) GetBootstrapHeadersInfo( selfNotarizedHeaders []data.HeaderHandler, selfNotarizedHeadersHashes [][]byte, @@ -433,18 +513,22 @@ func (sp *shardProcessor) GetBootstrapHeadersInfo( return sp.getBootstrapHeadersInfo(selfNotarizedHeaders, selfNotarizedHeadersHashes) } +// RequestMetaHeadersIfNeeded - func (sp *shardProcessor) RequestMetaHeadersIfNeeded(hdrsAdded uint32, lastMetaHdr data.HeaderHandler) { sp.requestMetaHeadersIfNeeded(hdrsAdded, lastMetaHdr) } +// RequestShardHeadersIfNeeded - func (mp *metaProcessor) RequestShardHeadersIfNeeded(hdrsAddedForShard map[uint32]uint32, lastShardHdr map[uint32]data.HeaderHandler) { mp.requestShardHeadersIfNeeded(hdrsAddedForShard, lastShardHdr) } +// AddHeaderIntoTrackerPool - func (bp *baseProcessor) AddHeaderIntoTrackerPool(nonce uint64, shardID uint32) { bp.addHeaderIntoTrackerPool(nonce, shardID) } +// UpdateState - func (bp *baseProcessor) UpdateState( finalHeader data.HeaderHandler, rootHash []byte, @@ -454,14 +538,17 @@ func (bp *baseProcessor) UpdateState( bp.updateStateStorage(finalHeader, rootHash, prevRootHash, accounts) } +// GasAndFeesDelta - func GasAndFeesDelta(initialGasAndFees, finalGasAndFees scheduled.GasAndFees) scheduled.GasAndFees { return gasAndFeesDelta(initialGasAndFees, finalGasAndFees) } +// RequestEpochStartInfo - func (sp *shardProcessor) RequestEpochStartInfo(header data.ShardHeaderHandler, haveTime func() time.Duration) error { return sp.requestEpochStartInfo(header, haveTime) } +// ProcessEpochStartMetaBlock - func (mp *metaProcessor) ProcessEpochStartMetaBlock( header *block.MetaBlock, body *block.Body, @@ -469,30 +556,37 @@ func (mp *metaProcessor) ProcessEpochStartMetaBlock( return mp.processEpochStartMetaBlock(header, body) } +// UpdateEpochStartHeader - func (mp *metaProcessor) UpdateEpochStartHeader(metaHdr *block.MetaBlock) error { return mp.updateEpochStartHeader(metaHdr) } +// CreateEpochStartBody - func (mp *metaProcessor) CreateEpochStartBody(metaBlock *block.MetaBlock) (data.BodyHandler, error) { return mp.createEpochStartBody(metaBlock) } +// GetIndexOfFirstMiniBlockToBeExecuted - func (bp *baseProcessor) GetIndexOfFirstMiniBlockToBeExecuted(header data.HeaderHandler) int { return bp.getIndexOfFirstMiniBlockToBeExecuted(header) } +// GetFinalMiniBlocks - func (bp *baseProcessor) GetFinalMiniBlocks(header data.HeaderHandler, body *block.Body) (*block.Body, error) { return bp.getFinalMiniBlocks(header, body) } +// GetScheduledMiniBlocksFromMe - func GetScheduledMiniBlocksFromMe(headerHandler data.HeaderHandler, bodyHandler data.BodyHandler) (block.MiniBlockSlice, error) { return getScheduledMiniBlocksFromMe(headerHandler, bodyHandler) } +// CheckScheduledMiniBlocksValidity - func (bp *baseProcessor) CheckScheduledMiniBlocksValidity(headerHandler data.HeaderHandler) error { return bp.checkScheduledMiniBlocksValidity(headerHandler) } +// SetMiniBlockHeaderReservedField - func (bp *baseProcessor) SetMiniBlockHeaderReservedField( miniBlock *block.MiniBlock, miniBlockHeaderHandler data.MiniBlockHeaderHandler, @@ -501,18 +595,22 @@ func (bp *baseProcessor) SetMiniBlockHeaderReservedField( return bp.setMiniBlockHeaderReservedField(miniBlock, miniBlockHeaderHandler, processedMiniBlocksDestMeInfo) } +// GetFinalMiniBlockHeaders - func (mp *metaProcessor) GetFinalMiniBlockHeaders(miniBlockHeaderHandlers []data.MiniBlockHeaderHandler) []data.MiniBlockHeaderHandler { return mp.getFinalMiniBlockHeaders(miniBlockHeaderHandlers) } +// CheckProcessorNilParameters - func CheckProcessorNilParameters(arguments ArgBaseProcessor) error { return checkProcessorParameters(arguments) } +// SetIndexOfFirstTxProcessed - func (bp *baseProcessor) SetIndexOfFirstTxProcessed(miniBlockHeaderHandler data.MiniBlockHeaderHandler) error { return bp.setIndexOfFirstTxProcessed(miniBlockHeaderHandler) } +// SetIndexOfLastTxProcessed - func (bp *baseProcessor) SetIndexOfLastTxProcessed( miniBlockHeaderHandler data.MiniBlockHeaderHandler, processedMiniBlocksDestMeInfo map[string]*processedMb.ProcessedMiniBlockInfo, @@ -520,10 +618,12 @@ func (bp *baseProcessor) SetIndexOfLastTxProcessed( return bp.setIndexOfLastTxProcessed(miniBlockHeaderHandler, processedMiniBlocksDestMeInfo) } +// GetProcessedMiniBlocksTracker - func (bp *baseProcessor) GetProcessedMiniBlocksTracker() process.ProcessedMiniBlocksTracker { return bp.processedMiniBlocksTracker } +// SetProcessingTypeAndConstructionStateForScheduledMb - func (bp *baseProcessor) SetProcessingTypeAndConstructionStateForScheduledMb( miniBlockHeaderHandler data.MiniBlockHeaderHandler, processedMiniBlocksDestMeInfo map[string]*processedMb.ProcessedMiniBlockInfo, @@ -531,6 +631,7 @@ func (bp *baseProcessor) SetProcessingTypeAndConstructionStateForScheduledMb( return bp.setProcessingTypeAndConstructionStateForScheduledMb(miniBlockHeaderHandler, processedMiniBlocksDestMeInfo) } +// SetProcessingTypeAndConstructionStateForNormalMb - func (bp *baseProcessor) SetProcessingTypeAndConstructionStateForNormalMb( miniBlockHeaderHandler data.MiniBlockHeaderHandler, processedMiniBlocksDestMeInfo map[string]*processedMb.ProcessedMiniBlockInfo, @@ -538,26 +639,32 @@ func (bp *baseProcessor) SetProcessingTypeAndConstructionStateForNormalMb( return bp.setProcessingTypeAndConstructionStateForNormalMb(miniBlockHeaderHandler, processedMiniBlocksDestMeInfo) } +// RollBackProcessedMiniBlockInfo - func (sp *shardProcessor) RollBackProcessedMiniBlockInfo(miniBlockHeader data.MiniBlockHeaderHandler, miniBlockHash []byte) { sp.rollBackProcessedMiniBlockInfo(miniBlockHeader, miniBlockHash) } +// SetProcessedMiniBlocksInfo - func (sp *shardProcessor) SetProcessedMiniBlocksInfo(miniBlockHashes [][]byte, metaBlockHash string, metaBlock *block.MetaBlock) { sp.setProcessedMiniBlocksInfo(miniBlockHashes, metaBlockHash, metaBlock) } +// GetIndexOfLastTxProcessedInMiniBlock - func (sp *shardProcessor) GetIndexOfLastTxProcessedInMiniBlock(miniBlockHash []byte, metaBlock *block.MetaBlock) int32 { return getIndexOfLastTxProcessedInMiniBlock(miniBlockHash, metaBlock) } +// RollBackProcessedMiniBlocksInfo - func (sp *shardProcessor) RollBackProcessedMiniBlocksInfo(headerHandler data.HeaderHandler, mapMiniBlockHashes map[string]uint32) { sp.rollBackProcessedMiniBlocksInfo(headerHandler, mapMiniBlockHashes) } +// CheckConstructionStateAndIndexesCorrectness - func (bp *baseProcessor) CheckConstructionStateAndIndexesCorrectness(mbh data.MiniBlockHeaderHandler) error { return checkConstructionStateAndIndexesCorrectness(mbh) } +// GetAllMarshalledTxs - func (mp *metaProcessor) GetAllMarshalledTxs(body *block.Body) map[string][][]byte { return mp.getAllMarshalledTxs(body) } diff --git a/process/block/hdrForBlock.go b/process/block/hdrForBlock.go index fd7384aedc7..74463b42943 100644 --- a/process/block/hdrForBlock.go +++ b/process/block/hdrForBlock.go @@ -6,18 +6,27 @@ import ( "github.com/multiversx/mx-chain-core-go/data" ) +type lastNotarizedHeaderInfo struct { + header data.HeaderHandler + hash []byte + notarizedBasedOnProof bool + hasProof bool +} + type hdrForBlock struct { missingHdrs uint32 missingFinalityAttestingHdrs uint32 highestHdrNonce map[uint32]uint64 mutHdrsForBlock sync.RWMutex hdrHashAndInfo map[string]*hdrInfo + lastNotarizedShardHeaders map[uint32]*lastNotarizedHeaderInfo } func newHdrForBlock() *hdrForBlock { return &hdrForBlock{ - hdrHashAndInfo: make(map[string]*hdrInfo), - highestHdrNonce: make(map[uint32]uint64), + hdrHashAndInfo: make(map[string]*hdrInfo), + highestHdrNonce: make(map[uint32]uint64), + lastNotarizedShardHeaders: make(map[uint32]*lastNotarizedHeaderInfo), } } @@ -25,6 +34,7 @@ func (hfb *hdrForBlock) initMaps() { hfb.mutHdrsForBlock.Lock() hfb.hdrHashAndInfo = make(map[string]*hdrInfo) hfb.highestHdrNonce = make(map[uint32]uint64) + hfb.lastNotarizedShardHeaders = make(map[uint32]*lastNotarizedHeaderInfo) hfb.mutHdrsForBlock.Unlock() } diff --git a/process/block/interceptedBlocks/interceptedEquivalentProof.go b/process/block/interceptedBlocks/interceptedEquivalentProof.go index 49bc7cad791..5f39e0e03f1 100644 --- a/process/block/interceptedBlocks/interceptedEquivalentProof.go +++ b/process/block/interceptedBlocks/interceptedEquivalentProof.go @@ -110,6 +110,7 @@ func createEquivalentProof(marshaller marshal.Marshalizer, buff []byte) (*block. "header round", headerProof.HeaderRound, "bitmap", logger.DisplayByteSlice(headerProof.PubKeysBitmap), "signature", logger.DisplayByteSlice(headerProof.AggregatedSignature), + "isEpochStart", headerProof.IsStartOfEpoch, ) return headerProof, nil @@ -130,6 +131,7 @@ func extractIsForCurrentShard(shardCoordinator sharding.Coordinator, equivalentP // CheckValidity checks if the received proof is valid func (iep *interceptedEquivalentProof) CheckValidity() error { + log.Debug("Checking intercepted equivalent proof validity", "proof header hash", iep.proof.HeaderHash) err := iep.integrity() if err != nil { return err @@ -183,13 +185,15 @@ func (iep *interceptedEquivalentProof) Identifiers() [][]byte { // String returns the proof's most important fields as string func (iep *interceptedEquivalentProof) String() string { - return fmt.Sprintf("bitmap=%s, signature=%s, hash=%s, epoch=%d, shard=%d, nonce=%d", + return fmt.Sprintf("bitmap=%s, signature=%s, hash=%s, epoch=%d, shard=%d, nonce=%d, round=%d, isEpochStart=%t", logger.DisplayByteSlice(iep.proof.PubKeysBitmap), logger.DisplayByteSlice(iep.proof.AggregatedSignature), logger.DisplayByteSlice(iep.proof.HeaderHash), iep.proof.HeaderEpoch, iep.proof.HeaderShardId, iep.proof.HeaderNonce, + iep.proof.HeaderRound, + iep.proof.IsStartOfEpoch, ) } diff --git a/process/block/interceptedBlocks/interceptedEquivalentProof_test.go b/process/block/interceptedBlocks/interceptedEquivalentProof_test.go index 86879344097..9c35bfc1102 100644 --- a/process/block/interceptedBlocks/interceptedEquivalentProof_test.go +++ b/process/block/interceptedBlocks/interceptedEquivalentProof_test.go @@ -282,6 +282,8 @@ func TestInterceptedEquivalentProof_Getters(t *testing.T) { HeaderEpoch: 123, HeaderNonce: 345, HeaderShardId: 0, + HeaderRound: 123456, + IsStartOfEpoch: false, } args := createMockArgInterceptedEquivalentProof() args.DataBuff, _ = args.Marshaller.Marshal(proof) @@ -293,7 +295,7 @@ func TestInterceptedEquivalentProof_Getters(t *testing.T) { require.Equal(t, hash, iep.Hash()) require.Equal(t, [][]byte{proof.HeaderHash}, iep.Identifiers()) require.Equal(t, interceptedEquivalentProofType, iep.Type()) - expectedStr := fmt.Sprintf("bitmap=%s, signature=%s, hash=%s, epoch=123, shard=0, nonce=345", + expectedStr := fmt.Sprintf("bitmap=%s, signature=%s, hash=%s, epoch=123, shard=0, nonce=345, round=123456, isEpochStart=false", logger.DisplayByteSlice(proof.PubKeysBitmap), logger.DisplayByteSlice(proof.AggregatedSignature), logger.DisplayByteSlice(proof.HeaderHash)) diff --git a/process/block/metablock.go b/process/block/metablock.go index 5026766749d..868713789f2 100644 --- a/process/block/metablock.go +++ b/process/block/metablock.go @@ -441,6 +441,10 @@ func (mp *metaProcessor) checkProofsForShardData(header *block.MetaBlock) error return fmt.Errorf("%w for header hash %s", process.ErrMissingHeaderProof, hex.EncodeToString(shardData.HeaderHash)) } + if !common.ShouldBlockHavePrevProof(shardHeader.hdr, mp.enableEpochsHandler, common.EquivalentMessagesFlag) { + continue + } + shardHeadersStorer, err := mp.store.GetStorer(dataRetriever.BlockHeaderUnit) if err != nil { return err @@ -1898,10 +1902,18 @@ func (mp *metaProcessor) checkShardHeadersValidity(metaHdr *block.MetaBlock) (ma if shardData.DeveloperFees.Cmp(shardHdr.GetDeveloperFees()) != 0 { return nil, process.ErrDeveloperFeesDoNotMatch } + if mp.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, shardHdr.GetEpoch()) { + if shardData.Epoch != shardHdr.GetEpoch() { + return nil, process.ErrEpochMismatch + } + } - err = verifyProof(shardData.GetPreviousProof()) - if err != nil { - return nil, err + if common.ShouldBlockHavePrevProof(shardHdr, mp.enableEpochsHandler, common.EquivalentMessagesFlag) { + // TODO: proper check of the fields + err = verifyProof(shardData.GetPreviousProof()) + if err != nil { + return nil, err + } } mapMiniBlockHeadersInMetaBlock := make(map[string]struct{}) @@ -1978,22 +1990,18 @@ func (mp *metaProcessor) checkShardHeadersFinality( continue } + isNotarizedBasedOnProofs, errCheckProof := mp.checkShardHeaderFinalityBasedOnProofs(lastVerifiedHdr, shardId) + if isNotarizedBasedOnProofs { + if errCheckProof != nil { + return errCheckProof + } + + continue + } + // verify if there are "K" block after current to make this one final nextBlocksVerified := uint32(0) - isNotarizedBasedOnProofs := false for _, shardHdr := range finalityAttestingShardHdrs[shardId] { - var errCheckProof error - isNotarizedBasedOnProofs, errCheckProof = mp.checkShardHeaderFinalityBasedOnProofs(shardHdr, shardId) - // if the header is notarized based on proofs and there is no error, break the loop - // if there is any error, forward it, header is not final - if isNotarizedBasedOnProofs { - if errCheckProof != nil { - return err - } - - break - } - if nextBlocksVerified >= mp.shardBlockFinality { break } @@ -2007,6 +2015,15 @@ func (mp *metaProcessor) checkShardHeadersFinality( continue } + isNotarizedBasedOnProofs, errCheckProof = mp.checkShardHeaderFinalityBasedOnProofs(shardHdr, shardId) + if isNotarizedBasedOnProofs { + if errCheckProof != nil { + return errCheckProof + } + + break + } + lastVerifiedHdr = shardHdr nextBlocksVerified += 1 } @@ -2069,10 +2086,10 @@ func (mp *metaProcessor) receivedShardHeader(headerHandler data.HeaderHandler, s if shardHeader.GetNonce() > mp.hdrsForCurrBlock.highestHdrNonce[shardHeader.GetShardID()] { mp.hdrsForCurrBlock.highestHdrNonce[shardHeader.GetShardID()] = shardHeader.GetNonce() } + mp.updateLastNotarizedBlockForShard(shardHeader, shardHeaderHash) } - shouldConsiderProofsForNotarization := mp.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, shardHeader.GetEpoch()) - if mp.hdrsForCurrBlock.missingHdrs == 0 && !shouldConsiderProofsForNotarization { + if mp.hdrsForCurrBlock.missingHdrs == 0 { mp.hdrsForCurrBlock.missingFinalityAttestingHdrs = mp.requestMissingFinalityAttestingShardHeaders() if mp.hdrsForCurrBlock.missingFinalityAttestingHdrs == 0 { log.Debug("received all missing finality attesting shard headers") @@ -2096,17 +2113,20 @@ func (mp *metaProcessor) receivedShardHeader(headerHandler data.HeaderHandler, s // requestMissingFinalityAttestingShardHeaders requests the headers needed to accept the current selected headers for // processing the current block. It requests the shardBlockFinality headers greater than the highest shard header, -// for each shard, related to the block which should be processed +// for the given shard, related to the block which should be processed // this method should be called only under the mutex protection: hdrsForCurrBlock.mutHdrsForBlock func (mp *metaProcessor) requestMissingFinalityAttestingShardHeaders() uint32 { missingFinalityAttestingShardHeaders := uint32(0) for shardId := uint32(0); shardId < mp.shardCoordinator.NumberOfShards(); shardId++ { - missingFinalityAttestingHeaders := mp.requestMissingFinalityAttestingHeaders( - shardId, - mp.shardBlockFinality, - ) - + lastNotarizedShardHeader := mp.hdrsForCurrBlock.lastNotarizedShardHeaders[shardId] + missingFinalityAttestingHeaders := uint32(0) + if lastNotarizedShardHeader != nil && !lastNotarizedShardHeader.notarizedBasedOnProof { + missingFinalityAttestingHeaders = mp.requestMissingFinalityAttestingHeaders( + shardId, + mp.shardBlockFinality, + ) + } missingFinalityAttestingShardHeaders += missingFinalityAttestingHeaders } @@ -2123,11 +2143,31 @@ func (mp *metaProcessor) requestShardHeaders(metaBlock *block.MetaBlock) (uint32 return mp.computeExistingAndRequestMissingShardHeaders(metaBlock) } +func (mp *metaProcessor) updateLastNotarizedBlockForShard(hdr data.ShardHeaderHandler, headerHash []byte) { + lastNotarizedForShard := mp.hdrsForCurrBlock.lastNotarizedShardHeaders[hdr.GetShardID()] + if lastNotarizedForShard == nil { + lastNotarizedForShard = &lastNotarizedHeaderInfo{header: hdr} + mp.hdrsForCurrBlock.lastNotarizedShardHeaders[hdr.GetShardID()] = lastNotarizedForShard + } + + if hdr.GetNonce() >= lastNotarizedForShard.header.GetNonce() { + notarizedBasedOnProofs := mp.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, hdr.GetEpoch()) + hasProof := false + if notarizedBasedOnProofs { + hasProof = mp.proofsPool.HasProof(hdr.GetShardID(), headerHash) + } + + lastNotarizedForShard.header = hdr + lastNotarizedForShard.hash = headerHash + lastNotarizedForShard.notarizedBasedOnProof = notarizedBasedOnProofs + lastNotarizedForShard.hasProof = hasProof + } +} + func (mp *metaProcessor) computeExistingAndRequestMissingShardHeaders(metaBlock *block.MetaBlock) (uint32, uint32) { mp.hdrsForCurrBlock.mutHdrsForBlock.Lock() defer mp.hdrsForCurrBlock.mutHdrsForBlock.Unlock() - notarizedShardHdrsBasedOnProofs := 0 for _, shardData := range metaBlock.ShardInfo { if shardData.Nonce == mp.genesisNonce { lastCrossNotarizedHeaderForShard, hash, err := mp.blockTracker.GetLastCrossNotarizedHeader(shardData.ShardID) @@ -2136,7 +2176,7 @@ func (mp *metaProcessor) computeExistingAndRequestMissingShardHeaders(metaBlock continue } if !bytes.Equal(hash, shardData.HeaderHash) { - log.Warn("genesis hash missmatch", + log.Warn("genesis hash mismatch", "last notarized nonce", lastCrossNotarizedHeaderForShard.GetNonce(), "last notarized hash", hash, "genesis nonce", mp.genesisNonce, @@ -2169,13 +2209,10 @@ func (mp *metaProcessor) computeExistingAndRequestMissingShardHeaders(metaBlock mp.hdrsForCurrBlock.highestHdrNonce[shardData.ShardID] = hdr.GetNonce() } - if mp.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, hdr.GetEpoch()) { - notarizedShardHdrsBasedOnProofs++ - } + mp.updateLastNotarizedBlockForShard(hdr, shardData.HeaderHash) } - shouldRequestMissingFinalityAttestingShardHeaders := notarizedShardHdrsBasedOnProofs != len(metaBlock.ShardInfo) - if mp.hdrsForCurrBlock.missingHdrs == 0 && shouldRequestMissingFinalityAttestingShardHeaders { + if mp.hdrsForCurrBlock.missingHdrs == 0 { mp.hdrsForCurrBlock.missingFinalityAttestingHdrs = mp.requestMissingFinalityAttestingShardHeaders() } @@ -2223,6 +2260,8 @@ func (mp *metaProcessor) createShardInfo() ([]data.ShardDataHandler, error) { if err != nil { return nil, err } + + shardData.Epoch = shardHdr.GetEpoch() } shardData.NumPendingMiniBlocks = uint32(len(mp.pendingMiniBlocksHandler.GetPendingMiniBlocks(shardData.ShardID))) header, _, err := mp.blockTracker.GetLastSelfNotarizedHeader(shardHdr.GetShardID()) diff --git a/process/block/metablock_test.go b/process/block/metablock_test.go index d26f5074eae..ec196bbe0b1 100644 --- a/process/block/metablock_test.go +++ b/process/block/metablock_test.go @@ -820,10 +820,28 @@ func TestMetaProcessor_RequestFinalMissingHeaderShouldPass(t *testing.T) { mp, _ := blproc.NewMetaProcessor(arguments) mp.AddHdrHashToRequestedList(&block.Header{}, []byte("header_hash")) mp.SetHighestHdrNonceForCurrentBlock(0, 1) + mp.SetLastNotarizedHeaderForShard(0, &blproc.LastNotarizedHeaderInfo{ + Header: &block.Header{Nonce: 0, ShardID: 0}, + Hash: []byte("header hash"), + NotarizedBasedOnProof: false, + HasProof: false, + }) mp.SetHighestHdrNonceForCurrentBlock(1, 2) + mp.SetLastNotarizedHeaderForShard(1, &blproc.LastNotarizedHeaderInfo{ + Header: &block.Header{Nonce: 2, ShardID: 1}, + Hash: []byte("header hash"), + NotarizedBasedOnProof: false, + HasProof: false, + }) mp.SetHighestHdrNonceForCurrentBlock(2, 3) + mp.SetLastNotarizedHeaderForShard(2, &blproc.LastNotarizedHeaderInfo{ + Header: &block.Header{Nonce: 3, ShardID: 2}, + Hash: []byte("header hash"), + NotarizedBasedOnProof: false, + HasProof: false, + }) res := mp.RequestMissingFinalityAttestingShardHeaders() - assert.Equal(t, res, uint32(3)) + assert.Equal(t, uint32(3), res) } // ------- CommitBlock diff --git a/process/block/shardblock.go b/process/block/shardblock.go index 8541f24f060..830a4f46d3c 100644 --- a/process/block/shardblock.go +++ b/process/block/shardblock.go @@ -578,15 +578,14 @@ func (sp *shardProcessor) checkMetaHdrFinality(header data.HeaderHandler) error return process.ErrNilBlockHeader } - if common.IsFlagEnabledAfterEpochsStartBlock(header, sp.enableEpochsHandler, common.EquivalentMessagesFlag) { - marshalledHeader, err := sp.marshalizer.Marshal(header) - if err != nil { - return err + if sp.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, header.GetEpoch()) { + hash, errHash := sp.getHeaderHash(header) + if errHash != nil { + return errHash } - headerHash := sp.hasher.Compute(string(marshalledHeader)) - if !sp.proofsPool.HasProof(header.GetShardID(), headerHash) { - return fmt.Errorf("%w, missing proof for header %s", process.ErrHeaderNotFinal, hex.EncodeToString(headerHash)) + if !sp.proofsPool.HasProof(header.GetShardID(), hash) { + return fmt.Errorf("%w, missing proof for header %s", process.ErrHeaderNotFinal, hex.EncodeToString(hash)) } return nil @@ -614,6 +613,19 @@ func (sp *shardProcessor) checkMetaHdrFinality(header data.HeaderHandler) error continue } + if sp.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, metaHdr.GetEpoch()) { + hash, errHash := sp.getHeaderHash(metaHdr) + if errHash != nil { + return errHash + } + + if sp.proofsPool.HasProof(core.MetachainShardId, hash) { + return nil + } + + return process.ErrHeaderNotFinal + } + lastVerifiedHdr = metaHdr nextBlocksVerified += 1 } diff --git a/process/errors.go b/process/errors.go index ac928235556..cba8b0f3de4 100644 --- a/process/errors.go +++ b/process/errors.go @@ -1283,3 +1283,6 @@ var ErrInvalidHeaderProof = errors.New("invalid equivalent proof") // ErrUnexpectedHeaderProof signals that a header proof has been provided unexpectedly var ErrUnexpectedHeaderProof = errors.New("unexpected header proof") + +// ErrEpochMismatch signals that the epoch do not match +var ErrEpochMismatch = errors.New("epoch mismatch") diff --git a/process/headerCheck/headerSignatureVerify.go b/process/headerCheck/headerSignatureVerify.go index a1f838981ad..cce3ef6665b 100644 --- a/process/headerCheck/headerSignatureVerify.go +++ b/process/headerCheck/headerSignatureVerify.go @@ -126,6 +126,43 @@ func isIndexInBitmap(index uint16, bitmap []byte) error { return nil } +func (hsv *HeaderSigVerifier) getConsensusSignersForEquivalentProofs(proof data.HeaderProofHandler) ([][]byte, error) { + if check.IfNilReflect(proof) { + return nil, process.ErrNilHeaderProof + } + if !hsv.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, proof.GetHeaderEpoch()) { + return nil, process.ErrUnexpectedHeaderProof + } + + // TODO: remove if start of epochForConsensus block needs to be validated by the new epochForConsensus nodes + epochForConsensus := proof.GetHeaderEpoch() + if proof.GetIsStartOfEpoch() && epochForConsensus > 0 { + epochForConsensus = epochForConsensus - 1 + } + + consensusPubKeys, err := hsv.nodesCoordinator.GetAllEligibleValidatorsPublicKeysForShard( + epochForConsensus, + proof.GetHeaderShardId(), + ) + + if err != nil { + return nil, err + } + + err = hsv.verifyConsensusSize( + consensusPubKeys, + proof.GetPubKeysBitmap(), + proof.GetHeaderShardId(), + proof.GetIsStartOfEpoch(), + proof.GetHeaderRound(), + proof.GetHeaderHash()) + if err != nil { + return nil, err + } + + return getPubKeySigners(consensusPubKeys, proof.GetPubKeysBitmap()), nil +} + func (hsv *HeaderSigVerifier) getConsensusSigners( randSeed []byte, shardID uint32, @@ -277,34 +314,16 @@ func (hsv *HeaderSigVerifier) VerifyHeaderProof(proofHandler data.HeaderProofHan if check.IfNilReflect(proofHandler) { return process.ErrNilHeaderProof } - if !hsv.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, proofHandler.GetHeaderEpoch()) { return fmt.Errorf("%w for flag %s", process.ErrFlagNotActive, common.EquivalentMessagesFlag) } - // for the start of epoch block the consensus is taken from the previous epoch - header, err := hsv.headersPool.GetHeaderByHash(proofHandler.GetHeaderHash()) - if err != nil { - return err - } - multiSigVerifier, err := hsv.multiSigContainer.GetMultiSigner(proofHandler.GetHeaderEpoch()) if err != nil { return err } - // TODO: add a new method to get consensus signers that does not require the header and only works with the proof - // round, prevHash and prevRandSeed could be removed when we remove fallback validation and we don't need backwards compatibility - // (e.g new binary from epoch x forward) - consensusPubKeys, err := hsv.getConsensusSigners( - header.GetPrevRandSeed(), - proofHandler.GetHeaderShardId(), - proofHandler.GetHeaderEpoch(), - header.IsStartOfEpochBlock(), - header.GetRound(), - header.GetPrevHash(), - proofHandler.GetPubKeysBitmap(), - ) + consensusPubKeys, err := hsv.getConsensusSignersForEquivalentProofs(proofHandler) if err != nil { return err } diff --git a/process/headerCheck/headerSignatureVerify_test.go b/process/headerCheck/headerSignatureVerify_test.go index adb372ba15c..ffa59035043 100644 --- a/process/headerCheck/headerSignatureVerify_test.go +++ b/process/headerCheck/headerSignatureVerify_test.go @@ -29,7 +29,7 @@ var expectedErr = errors.New("expected error") func createHeaderSigVerifierArgs() *ArgsHeaderSigVerifier { v1, _ := nodesCoordinator.NewValidator([]byte("pubKey1"), 1, defaultChancesSelection) - v2, _ := nodesCoordinator.NewValidator([]byte("pubKey1"), 1, defaultChancesSelection) + v2, _ := nodesCoordinator.NewValidator([]byte("pubKey2"), 1, defaultChancesSelection) return &ArgsHeaderSigVerifier{ Marshalizer: &mock.MarshalizerMock{}, Hasher: &hashingMocks.HasherMock{}, @@ -37,6 +37,9 @@ func createHeaderSigVerifierArgs() *ArgsHeaderSigVerifier { ComputeValidatorsGroupCalled: func(randomness []byte, round uint64, shardId uint32, epoch uint32) (leader nodesCoordinator.Validator, validators []nodesCoordinator.Validator, err error) { return v1, []nodesCoordinator.Validator{v1, v2}, nil }, + GetAllEligibleValidatorsPublicKeysForShardCalled: func(epoch uint32, shardID uint32) ([]string, error) { + return []string{"pubKey1", "pubKey2"}, nil + }, }, MultiSigContainer: cryptoMocks.NewMultiSignerContainerMock(cryptoMocks.NewMultiSigner()), SingleSigVerifier: &mock.SignerMock{}, diff --git a/process/sync/baseForkDetector.go b/process/sync/baseForkDetector.go index cfe0a675569..3fe907d954b 100644 --- a/process/sync/baseForkDetector.go +++ b/process/sync/baseForkDetector.go @@ -731,6 +731,18 @@ func (bfd *baseForkDetector) processReceivedProof(proof data.HeaderProofHandler) _ = bfd.append(hInfo) + probableHighestNonce := bfd.computeProbableHighestNonce() + bfd.setProbableHighestNonce(probableHighestNonce) + + log.Debug("forkDetector.processReceivedProof", + "round", hInfo.round, + "nonce", hInfo.nonce, + "hash", hInfo.hash, + "state", hInfo.state, + "probable highest nonce", bfd.probableHighestNonce(), + "last checkpoint nonce", bfd.lastCheckpoint().nonce, + "final checkpoint nonce", bfd.finalCheckpoint().nonce, + "has proof", hInfo.hasProof) } func (bfd *baseForkDetector) processReceivedBlock( @@ -748,11 +760,13 @@ func (bfd *baseForkDetector) processReceivedBlock( bfd.setHighestNonceReceived(header.GetNonce()) if state == process.BHProposed || !hasProof { + log.Trace("forkDetector.processReceivedBlock: block is proposed or has no proof", "state", state, "has proof", hasProof) return } isHeaderReceivedTooLate := bfd.isHeaderReceivedTooLate(header, state, process.BlockFinality) if isHeaderReceivedTooLate { + log.Trace("forkDetector.processReceivedBlock: block is received too late", "initial state", state) state = process.BHReceivedTooLate } @@ -766,6 +780,7 @@ func (bfd *baseForkDetector) processReceivedBlock( } if !bfd.append(hInfo) { + log.Trace("forkDetector.processReceivedBlock: header not appended", "nonce", hInfo.nonce, "hash", hInfo.hash) return } diff --git a/process/sync/metaForkDetector.go b/process/sync/metaForkDetector.go index f6a285fb3bc..cb45f2fdadc 100644 --- a/process/sync/metaForkDetector.go +++ b/process/sync/metaForkDetector.go @@ -108,7 +108,11 @@ func (mfd *metaForkDetector) doJobOnBHProcessed( _ [][]byte, ) { mfd.setFinalCheckpoint(mfd.lastCheckpoint()) - mfd.addCheckpoint(&checkpointInfo{nonce: header.GetNonce(), round: header.GetRound(), hash: headerHash}) + newCheckpoint := &checkpointInfo{nonce: header.GetNonce(), round: header.GetRound(), hash: headerHash} + mfd.addCheckpoint(newCheckpoint) + if mfd.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, header.GetEpoch()) { + mfd.setFinalCheckpoint(newCheckpoint) + } mfd.removePastOrInvalidRecords() } diff --git a/process/sync/shardForkDetector.go b/process/sync/shardForkDetector.go index d688c548a2e..499e232782e 100644 --- a/process/sync/shardForkDetector.go +++ b/process/sync/shardForkDetector.go @@ -112,7 +112,13 @@ func (sfd *shardForkDetector) doJobOnBHProcessed( ) { _ = sfd.appendSelfNotarizedHeaders(selfNotarizedHeaders, selfNotarizedHeadersHashes, core.MetachainShardId) sfd.computeFinalCheckpoint() - sfd.addCheckpoint(&checkpointInfo{nonce: header.GetNonce(), round: header.GetRound(), hash: headerHash}) + newCheckpoint := &checkpointInfo{nonce: header.GetNonce(), round: header.GetRound(), hash: headerHash} + sfd.addCheckpoint(newCheckpoint) + // first shard block with proof does not have increased consensus + // so instant finality will only be set after the first block with increased consensus + if common.ShouldBlockHavePrevProof(header, sfd.enableEpochsHandler, common.EquivalentMessagesFlag) { + sfd.setFinalCheckpoint(newCheckpoint) + } sfd.removePastOrInvalidRecords() } diff --git a/process/track/baseBlockTrack.go b/process/track/baseBlockTrack.go index 22eb1c86cc1..bd3a288ca3c 100644 --- a/process/track/baseBlockTrack.go +++ b/process/track/baseBlockTrack.go @@ -12,10 +12,12 @@ import ( "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" + "github.com/multiversx/mx-chain-logger-go" + + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/sharding" - "github.com/multiversx/mx-chain-logger-go" ) var _ process.ValidityAttester = (*baseBlockTrack)(nil) @@ -35,6 +37,7 @@ type baseBlockTrack struct { roundHandler process.RoundHandler shardCoordinator sharding.Coordinator headersPool dataRetriever.HeadersPool + proofsPool dataRetriever.ProofsPool store dataRetriever.StorageService blockProcessor blockProcessorHandler @@ -47,6 +50,7 @@ type baseBlockTrack struct { blockBalancer blockBalancerHandler whitelistHandler process.WhiteListHandler feeHandler process.FeeHandler + enableEpochsHandler core.EnableEpochsHandler mutHeaders sync.RWMutex headers map[uint32]map[uint64][]*HeaderInfo @@ -103,6 +107,7 @@ func createBaseBlockTrack(arguments ArgBaseTracker) (*baseBlockTrack, error) { roundHandler: arguments.RoundHandler, shardCoordinator: arguments.ShardCoordinator, headersPool: arguments.PoolsHolder.Headers(), + proofsPool: arguments.PoolsHolder.Proofs(), store: arguments.Store, crossNotarizer: crossNotarizer, selfNotarizer: selfNotarizer, @@ -114,12 +119,41 @@ func createBaseBlockTrack(arguments ArgBaseTracker) (*baseBlockTrack, error) { maxNumHeadersToKeepPerShard: maxNumHeadersToKeepPerShard, whitelistHandler: arguments.WhitelistHandler, feeHandler: arguments.FeeHandler, + enableEpochsHandler: arguments.EnableEpochsHandler, } return bbt, nil } +func (bbt *baseBlockTrack) receivedProof(proof data.HeaderProofHandler) { + if check.IfNilReflect(proof) { + return + } + + headerHash := proof.GetHeaderHash() + header, err := bbt.headersPool.GetHeaderByHash(headerHash) + if err != nil { + log.Debug("baseBlockTrack.receivedProof with missing header", "headerHash", headerHash) + return + } + log.Debug("received proof from network in block tracker", + "shard", proof.GetHeaderShardId(), + "epoch", proof.GetHeaderEpoch(), + "round", proof.GetHeaderRound(), + "nonce", proof.GetHeaderNonce(), + "hash", proof.GetHeaderHash(), + ) + + bbt.receivedHeader(header, headerHash) +} + func (bbt *baseBlockTrack) receivedHeader(headerHandler data.HeaderHandler, headerHash []byte) { + if bbt.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, headerHandler.GetEpoch()) { + if !bbt.proofsPool.HasProof(headerHandler.GetShardID(), headerHash) { + return + } + } + if headerHandler.GetShardID() == core.MetachainShardId { bbt.receivedMetaBlock(headerHandler, headerHash) return @@ -784,12 +818,18 @@ func checkTrackerNilParameters(arguments ArgBaseTracker) error { if check.IfNil(arguments.PoolsHolder.Headers()) { return process.ErrNilHeadersDataPool } + if check.IfNil(arguments.PoolsHolder.Proofs()) { + return process.ErrNilProofsPool + } if check.IfNil(arguments.FeeHandler) { return process.ErrNilEconomicsData } if check.IfNil(arguments.WhitelistHandler) { return process.ErrNilWhiteListHandler } + if check.IfNil(arguments.EnableEpochsHandler) { + return process.ErrNilEnableEpochsHandler + } return nil } diff --git a/process/track/baseBlockTrack_test.go b/process/track/baseBlockTrack_test.go index aeb6e0d0e59..e33963709c3 100644 --- a/process/track/baseBlockTrack_test.go +++ b/process/track/baseBlockTrack_test.go @@ -125,19 +125,23 @@ func CreateShardTrackerMockArguments() track.ArgShardTracker { arguments := track.ArgShardTracker{ ArgBaseTracker: track.ArgBaseTracker{ - Hasher: &hashingMocks.HasherMock{}, - HeaderValidator: headerValidator, - Marshalizer: &mock.MarshalizerMock{}, - RequestHandler: &testscommon.RequestHandlerStub{}, - RoundHandler: &mock.RoundHandlerMock{}, - ShardCoordinator: shardCoordinatorMock, - Store: initStore(), - StartHeaders: genesisBlocks, - PoolsHolder: dataRetrieverMock.NewPoolsHolderMock(), - WhitelistHandler: whitelistHandler, - FeeHandler: feeHandler, - EnableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, - ProofsPool: &dataRetrieverMock.ProofsPoolMock{}, + Hasher: &hashingMocks.HasherMock{}, + HeaderValidator: headerValidator, + Marshalizer: &mock.MarshalizerMock{}, + RequestHandler: &testscommon.RequestHandlerStub{}, + RoundHandler: &mock.RoundHandlerMock{}, + ShardCoordinator: shardCoordinatorMock, + Store: initStore(), + StartHeaders: genesisBlocks, + PoolsHolder: dataRetrieverMock.NewPoolsHolderMock(), + WhitelistHandler: whitelistHandler, + FeeHandler: feeHandler, + EnableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return false + }, + }, + ProofsPool: &dataRetrieverMock.ProofsPoolMock{}, }, } @@ -166,19 +170,23 @@ func CreateMetaTrackerMockArguments() track.ArgMetaTracker { arguments := track.ArgMetaTracker{ ArgBaseTracker: track.ArgBaseTracker{ - Hasher: &hashingMocks.HasherMock{}, - HeaderValidator: headerValidator, - Marshalizer: &mock.MarshalizerMock{}, - RequestHandler: &testscommon.RequestHandlerStub{}, - RoundHandler: &mock.RoundHandlerMock{}, - ShardCoordinator: shardCoordinatorMock, - Store: initStore(), - StartHeaders: genesisBlocks, - PoolsHolder: dataRetrieverMock.NewPoolsHolderMock(), - WhitelistHandler: whitelistHandler, - FeeHandler: feeHandler, - EnableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, - ProofsPool: &dataRetrieverMock.ProofsPoolMock{}, + Hasher: &hashingMocks.HasherMock{}, + HeaderValidator: headerValidator, + Marshalizer: &mock.MarshalizerMock{}, + RequestHandler: &testscommon.RequestHandlerStub{}, + RoundHandler: &mock.RoundHandlerMock{}, + ShardCoordinator: shardCoordinatorMock, + Store: initStore(), + StartHeaders: genesisBlocks, + PoolsHolder: dataRetrieverMock.NewPoolsHolderMock(), + WhitelistHandler: whitelistHandler, + FeeHandler: feeHandler, + EnableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return false + }, + }, + ProofsPool: &dataRetrieverMock.ProofsPoolMock{}, }, } @@ -408,6 +416,9 @@ func TestShardGetSelfHeaders_ShouldWork(t *testing.T) { }, } }, + ProofsCalled: func() dataRetriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{} + }, } sbt, _ := track.NewShardBlockTrack(shardArguments) @@ -445,6 +456,9 @@ func TestMetaGetSelfHeaders_ShouldWork(t *testing.T) { }, } }, + ProofsCalled: func() dataRetriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{} + }, } mbt, _ := track.NewMetaBlockTrack(metaArguments) @@ -1689,6 +1703,9 @@ func TestAddHeaderFromPool_ShouldWork(t *testing.T) { }, } }, + ProofsCalled: func() dataRetriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{} + }, } sbt, _ := track.NewShardBlockTrack(shardArguments) diff --git a/process/track/blockProcessor.go b/process/track/blockProcessor.go index 72fd9993283..5ce30a09889 100644 --- a/process/track/blockProcessor.go +++ b/process/track/blockProcessor.go @@ -314,9 +314,13 @@ func (bp *blockProcessor) checkHeaderFinality( return process.ErrNilBlockHeader } - if common.IsFlagEnabledAfterEpochsStartBlock(header, bp.enableEpochsHandler, common.EquivalentMessagesFlag) { + if bp.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, header.GetEpoch()) { // the index in argument is for the next block after header - if bp.proofsPool.HasProof(header.GetShardID(), sortedHeadersHashes[index-1]) { + hashIndex := index + if index > 0 { + hashIndex = index - 1 + } + if bp.proofsPool.HasProof(header.GetShardID(), sortedHeadersHashes[hashIndex]) { return nil } @@ -336,6 +340,16 @@ func (bp *blockProcessor) checkHeaderFinality( continue } + // if the currentHeader(the one that should confirm the finality of the prev) + // is the epoch start block of equivalent messages, we must check for its proof as well + if bp.enableEpochsHandler.IsFlagEnabledInEpoch(common.EquivalentMessagesFlag, currHeader.GetEpoch()) { + if bp.proofsPool.HasProof(currHeader.GetShardID(), sortedHeadersHashes[i]) { + return nil + } + + return process.ErrHeaderNotFinal + } + prevHeader = currHeader numFinalityAttestingHeaders += 1 } diff --git a/process/track/metaBlockTrack.go b/process/track/metaBlockTrack.go index 392c85eaeaf..ba1013d492f 100644 --- a/process/track/metaBlockTrack.go +++ b/process/track/metaBlockTrack.go @@ -6,6 +6,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" + "github.com/multiversx/mx-chain-go/process" ) @@ -60,6 +61,7 @@ func NewMetaBlockTrack(arguments ArgMetaTracker) (*metaBlockTrack, error) { mbt.blockProcessor = blockProcessorObject mbt.headers = make(map[uint32]map[uint64][]*HeaderInfo) mbt.headersPool.RegisterHandler(mbt.receivedHeader) + mbt.proofsPool.RegisterHandler(mbt.receivedProof) mbt.headersPool.Clear() return &mbt, nil diff --git a/process/track/shardBlockTrack.go b/process/track/shardBlockTrack.go index 72b918713d2..1241ec9a731 100644 --- a/process/track/shardBlockTrack.go +++ b/process/track/shardBlockTrack.go @@ -6,6 +6,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" + "github.com/multiversx/mx-chain-go/process" ) @@ -60,6 +61,7 @@ func NewShardBlockTrack(arguments ArgShardTracker) (*shardBlockTrack, error) { sbt.blockProcessor = blockProcessorObject sbt.headers = make(map[uint32]map[uint64][]*HeaderInfo) sbt.headersPool.RegisterHandler(sbt.receivedHeader) + sbt.proofsPool.RegisterHandler(sbt.receivedProof) sbt.headersPool.Clear() return &sbt, nil