From b6d5a609505e9a7d0c4a2bb9252022aaa9f7c522 Mon Sep 17 00:00:00 2001
From: jules01 <iulian.pascalau@gmail.com>
Date: Mon, 20 Feb 2023 14:29:12 +0200
Subject: [PATCH] - fixed self-blacklisted event in consensus due to edge case
 when setting the current epoch

---
 process/block/shardblock.go      |  10 +++
 process/block/shardblock_test.go | 145 +++++++++++++++++++++++++++++++
 2 files changed, 155 insertions(+)

diff --git a/process/block/shardblock.go b/process/block/shardblock.go
index 16b88d5bf9d..a43a98111b2 100644
--- a/process/block/shardblock.go
+++ b/process/block/shardblock.go
@@ -807,6 +807,16 @@ func (sp *shardProcessor) CreateBlock(
 		if err != nil {
 			return nil, nil, err
 		}
+
+		epoch := sp.epochStartTrigger.MetaEpoch()
+		if initialHdr.GetEpoch() != epoch {
+			log.Debug("shardProcessor.CreateBlock: epoch from header is not the same as epoch from epoch start trigger, overwriting",
+				"epoch from header", initialHdr.GetEpoch(), "epoch from epoch start trigger", epoch)
+			err = shardHdr.SetEpoch(epoch)
+			if err != nil {
+				return nil, nil, err
+			}
+		}
 	}
 
 	sp.epochNotifier.CheckEpoch(shardHdr)
diff --git a/process/block/shardblock_test.go b/process/block/shardblock_test.go
index d20d4a9ca84..0f79df9c47a 100644
--- a/process/block/shardblock_test.go
+++ b/process/block/shardblock_test.go
@@ -15,6 +15,7 @@ import (
 
 	"github.com/multiversx/mx-chain-core-go/core"
 	atomicCore "github.com/multiversx/mx-chain-core-go/core/atomic"
+	"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"
 	outportcore "github.com/multiversx/mx-chain-core-go/data/outport"
@@ -5241,3 +5242,147 @@ func TestShardProcessor_RollBackProcessedMiniBlocksInfo(t *testing.T) {
 	assert.False(t, processedMbInfo.FullyProcessed)
 	assert.Equal(t, indexOfFirstTxProcessed-1, processedMbInfo.IndexOfLastTxProcessed)
 }
+
+func TestShardProcessor_CreateBlock(t *testing.T) {
+	t.Parallel()
+
+	arguments := CreateMockArguments(createComponentHolderMocks())
+	processHandler := arguments.CoreComponents.ProcessStatusHandler()
+	mockProcessHandler := processHandler.(*testscommon.ProcessStatusHandlerStub)
+	busyIdleCalled := make([]string, 0)
+	mockProcessHandler.SetIdleCalled = func() {
+		busyIdleCalled = append(busyIdleCalled, idleIdentifier)
+	}
+	mockProcessHandler.SetBusyCalled = func(reason string) {
+		busyIdleCalled = append(busyIdleCalled, busyIdentifier)
+	}
+
+	expectedBusyIdleSequencePerCall := []string{busyIdentifier, idleIdentifier}
+	sp, errConstructor := blproc.NewShardProcessor(arguments)
+	assert.Nil(t, errConstructor)
+
+	doesHaveTime := func() bool {
+		return true
+	}
+	t.Run("nil block should error", func(t *testing.T) {
+		hdr, body, err := sp.CreateBlock(nil, doesHaveTime)
+		assert.True(t, check.IfNil(body))
+		assert.True(t, check.IfNil(hdr))
+		assert.Equal(t, process.ErrNilBlockHeader, err)
+		assert.Zero(t, len(busyIdleCalled))
+	})
+	t.Run("wrong block type should error", func(t *testing.T) {
+		meta := &block.MetaBlock{}
+
+		hdr, body, err := sp.CreateBlock(meta, doesHaveTime)
+		assert.True(t, check.IfNil(body))
+		assert.True(t, check.IfNil(hdr))
+		assert.Equal(t, process.ErrWrongTypeAssertion, err)
+		assert.Zero(t, len(busyIdleCalled))
+	})
+	t.Run("should work with empty header v1", func(t *testing.T) {
+		header := &block.Header{
+			Nonce: 37,
+			Round: 38,
+			Epoch: 1,
+		}
+
+		expectedHeader := &block.Header{
+			Nonce:           37,
+			Round:           38,
+			Epoch:           1,
+			ReceiptsHash:    []byte("receiptHash"),
+			DeveloperFees:   big.NewInt(0),
+			AccumulatedFees: big.NewInt(0),
+		}
+
+		// reset the slice, do not call these tests in parallel
+		busyIdleCalled = make([]string, 0)
+		hdr, bodyHandler, err := sp.CreateBlock(header, doesHaveTime)
+		assert.False(t, check.IfNil(bodyHandler))
+		body, ok := bodyHandler.(*block.Body)
+		assert.True(t, ok)
+
+		assert.Zero(t, len(body.MiniBlocks))
+		assert.False(t, check.IfNil(hdr))
+		assert.Equal(t, expectedHeader, header)
+		assert.Nil(t, err)
+		assert.Equal(t, expectedBusyIdleSequencePerCall, busyIdleCalled)
+	})
+	t.Run("should work with empty header v2", func(t *testing.T) {
+		header := &block.HeaderV2{
+			Header: &block.Header{
+				Nonce: 37,
+				Round: 38,
+				Epoch: 1,
+			},
+		}
+
+		expectedHeader := &block.HeaderV2{
+			Header: &block.Header{
+				Nonce:           37,
+				Round:           38,
+				Epoch:           1,
+				ReceiptsHash:    []byte("receiptHash"),
+				DeveloperFees:   big.NewInt(0),
+				AccumulatedFees: big.NewInt(0),
+			},
+		}
+
+		// reset the slice, do not call these tests in parallel
+		busyIdleCalled = make([]string, 0)
+		hdr, bodyHandler, err := sp.CreateBlock(header, doesHaveTime)
+		assert.False(t, check.IfNil(bodyHandler))
+		body, ok := bodyHandler.(*block.Body)
+		assert.True(t, ok)
+
+		assert.Zero(t, len(body.MiniBlocks))
+		assert.False(t, check.IfNil(hdr))
+		assert.Equal(t, expectedHeader, header)
+		assert.Nil(t, err)
+		assert.Equal(t, expectedBusyIdleSequencePerCall, busyIdleCalled)
+	})
+	t.Run("should work with empty header v2 and epoch start rewriting the epoch value", func(t *testing.T) {
+		argumentsLocal := CreateMockArguments(createComponentHolderMocks())
+		argumentsLocal.EpochStartTrigger = &mock.EpochStartTriggerStub{
+			IsEpochStartCalled: func() bool {
+				return true
+			},
+			MetaEpochCalled: func() uint32 {
+				return 2
+			},
+		}
+
+		spLocal, err := blproc.NewShardProcessor(argumentsLocal)
+		assert.Nil(t, err)
+
+		header := &block.HeaderV2{
+			Header: &block.Header{
+				Nonce: 37,
+				Round: 38,
+				Epoch: 1,
+			},
+		}
+
+		expectedHeader := &block.HeaderV2{
+			Header: &block.Header{
+				Nonce:           37,
+				Round:           38,
+				Epoch:           2, // epoch should be re-written
+				ReceiptsHash:    []byte("receiptHash"),
+				DeveloperFees:   big.NewInt(0),
+				AccumulatedFees: big.NewInt(0),
+			},
+		}
+
+		hdr, bodyHandler, err := spLocal.CreateBlock(header, doesHaveTime)
+		assert.False(t, check.IfNil(bodyHandler))
+		body, ok := bodyHandler.(*block.Body)
+		assert.True(t, ok)
+
+		assert.Zero(t, len(body.MiniBlocks))
+		assert.False(t, check.IfNil(hdr))
+		assert.Equal(t, expectedHeader, header)
+		assert.Nil(t, err)
+	})
+}