diff --git a/common/common.go b/common/common.go index ba48a524eb5..12067a514f5 100644 --- a/common/common.go +++ b/common/common.go @@ -416,7 +416,6 @@ func ExtractBaseExecutionResultHandler(lastExecResultsHandler data.LastExecution // GetMiniBlocksHeaderHandlersFromExecResult returns miniblock handlers based on execution result func GetMiniBlocksHeaderHandlersFromExecResult( baseExecResult data.BaseExecutionResultHandler, - headerShard uint32, ) ([]data.MiniBlockHeaderHandler, error) { if check.IfNil(baseExecResult) { return nil, ErrNilBaseExecutionResult diff --git a/common/common_test.go b/common/common_test.go index e0c244536c4..50ffd4ca67c 100644 --- a/common/common_test.go +++ b/common/common_test.go @@ -11,13 +11,14 @@ import ( "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/data/smartContractResult" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/stretchr/testify/require" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/config" commonErrors "github.com/multiversx/mx-chain-go/errors" "github.com/multiversx/mx-chain-go/testscommon" "github.com/multiversx/mx-chain-go/testscommon/chainParameters" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" - "github.com/stretchr/testify/require" ) var testFlag = core.EnableEpochFlag("test flag") @@ -584,7 +585,7 @@ func TestGetMiniBlockHeaderHandlersFromExecResults(t *testing.T) { t.Run("should fail if nil base execution result", func(t *testing.T) { t.Parallel() - retExecResult, err := common.GetMiniBlocksHeaderHandlersFromExecResult(nil, 1) + retExecResult, err := common.GetMiniBlocksHeaderHandlersFromExecResult(nil) require.Equal(t, common.ErrNilBaseExecutionResult, err) require.Nil(t, retExecResult) }) @@ -594,7 +595,7 @@ func TestGetMiniBlockHeaderHandlersFromExecResults(t *testing.T) { execResult := &block.BaseExecutionResult{} - retExecResult, err := common.GetMiniBlocksHeaderHandlersFromExecResult(execResult, 1) + retExecResult, err := common.GetMiniBlocksHeaderHandlersFromExecResult(execResult) require.Equal(t, common.ErrWrongTypeAssertion, err) require.Nil(t, retExecResult) }) @@ -623,7 +624,7 @@ func TestGetMiniBlockHeaderHandlersFromExecResults(t *testing.T) { &mbh2, } - retExecResult, err := common.GetMiniBlocksHeaderHandlersFromExecResult(execResult, 1) + retExecResult, err := common.GetMiniBlocksHeaderHandlersFromExecResult(execResult) require.Nil(t, err) require.Equal(t, expMiniBlockHandlers, retExecResult) }) @@ -652,7 +653,7 @@ func TestGetMiniBlockHeaderHandlersFromExecResults(t *testing.T) { &mbh2, } - retExecResult, err := common.GetMiniBlocksHeaderHandlersFromExecResult(execResult, core.MetachainShardId) + retExecResult, err := common.GetMiniBlocksHeaderHandlersFromExecResult(execResult) require.Nil(t, err) require.Equal(t, expMiniBlockHandlers, retExecResult) }) diff --git a/dblookupext/common.go b/dblookupext/common.go index 641ae506bcb..396d4cd896c 100644 --- a/dblookupext/common.go +++ b/dblookupext/common.go @@ -7,6 +7,7 @@ import ( "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/marshal" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/storage" @@ -61,7 +62,7 @@ func getIntraMbs(cache storage.Cacher, marshaller marshal.Marshalizer, headerHas } func getBody(cache storage.Cacher, marshaller marshal.Marshalizer, baseExecResult data.BaseExecutionResultHandler, shardID uint32) (*block.Body, error) { - miniBlockHeaderHandlers, err := common.GetMiniBlocksHeaderHandlersFromExecResult(baseExecResult, shardID) + miniBlockHeaderHandlers, err := common.GetMiniBlocksHeaderHandlersFromExecResult(baseExecResult) if err != nil { return nil, err } diff --git a/dblookupext/esdtSupply/esdtSuppliesProcessor.go b/dblookupext/esdtSupply/esdtSuppliesProcessor.go index bd23a50774f..0a09f1668a8 100644 --- a/dblookupext/esdtSupply/esdtSuppliesProcessor.go +++ b/dblookupext/esdtSupply/esdtSuppliesProcessor.go @@ -10,9 +10,10 @@ import ( "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/marshal" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/storage" - logger "github.com/multiversx/mx-chain-logger-go" ) var log = logger.GetOrCreate("dblookupext/esdtSupply") @@ -89,7 +90,7 @@ func (sp *suppliesProcessor) RevertChanges(header data.HeaderHandler, body data. func (sp *suppliesProcessor) revertChangesBasedOnExecutionResults(header data.HeaderHandler) error { for _, executionResult := range header.GetExecutionResultsHandlers() { - mbHeaders, err := common.GetMiniBlocksHeaderHandlersFromExecResult(executionResult, header.GetShardID()) + mbHeaders, err := common.GetMiniBlocksHeaderHandlersFromExecResult(executionResult) if err != nil { return err } diff --git a/epochStart/bootstrap/disabled/disabledEpochStartTrigger.go b/epochStart/bootstrap/disabled/disabledEpochStartTrigger.go index 4a5c64680d9..b5fbc67fa6e 100644 --- a/epochStart/bootstrap/disabled/disabledEpochStartTrigger.go +++ b/epochStart/bootstrap/disabled/disabledEpochStartTrigger.go @@ -22,13 +22,8 @@ func (e *epochStartTrigger) ShouldProposeEpochChange(_ uint64, _ uint64) bool { return false } -// UpdateRound - -func (e *epochStartTrigger) UpdateRound(_ uint64) { - -} - // SetEpochChange - -func (e *epochStartTrigger) SetEpochChange() {} +func (e *epochStartTrigger) SetEpochChange(_ uint64) {} // ReceivedHeader - func (e *epochStartTrigger) ReceivedHeader(_ data.HeaderHandler) { diff --git a/epochStart/bootstrap/process.go b/epochStart/bootstrap/process.go index 6f1c1061fa6..1b2e47dfb78 100644 --- a/epochStart/bootstrap/process.go +++ b/epochStart/bootstrap/process.go @@ -1357,7 +1357,7 @@ func getStartOfEpochRootHashFromExecutionResults( for i := len(header.GetExecutionResultsHandlers()) - 1; i >= 0; i-- { executionResult := header.GetExecutionResultsHandlers()[i] - miniBlockHeaderHandlers, err := common.GetMiniBlocksHeaderHandlersFromExecResult(executionResult, header.GetShardID()) + miniBlockHeaderHandlers, err := common.GetMiniBlocksHeaderHandlersFromExecResult(executionResult) if err != nil { return nil, err } diff --git a/epochStart/bootstrap/shardStorageHandler.go b/epochStart/bootstrap/shardStorageHandler.go index ce322653e7d..20ca491403b 100644 --- a/epochStart/bootstrap/shardStorageHandler.go +++ b/epochStart/bootstrap/shardStorageHandler.go @@ -10,13 +10,14 @@ import ( "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" + logger "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/epochStart" "github.com/multiversx/mx-chain-go/epochStart/bootstrap/disabled" "github.com/multiversx/mx-chain-go/process/block/bootstrapStorage" "github.com/multiversx/mx-chain-go/storage/factory" - logger "github.com/multiversx/mx-chain-logger-go" ) type shardStorageHandler struct { @@ -942,7 +943,7 @@ func getMetaHeaderMiniBlockHandlersFromExecutionResults( execResultsMiniBlockHeaderHandlers := make([]data.MiniBlockHeaderHandler, 0) for _, baseExecutionResult := range baseExecutionResults { - miniBlockHeaderHandlers, err := common.GetMiniBlocksHeaderHandlersFromExecResult(baseExecutionResult, metaBlock.GetShardID()) + miniBlockHeaderHandlers, err := common.GetMiniBlocksHeaderHandlersFromExecResult(baseExecutionResult) if err != nil { log.Warn("failed to get mini blocks header handlers from execution result", "err", err) return nil diff --git a/epochStart/interface.go b/epochStart/interface.go index d86c52e5518..9f75157381e 100644 --- a/epochStart/interface.go +++ b/epochStart/interface.go @@ -21,8 +21,7 @@ type TriggerHandler interface { Epoch() uint32 MetaEpoch() uint32 Update(round uint64, nonce uint64) - UpdateRound(round uint64) - SetEpochChange() + SetEpochChange(round uint64) ShouldProposeEpochChange(round uint64, nonce uint64) bool EpochStartRound() uint64 EpochStartMetaHdrHash() []byte diff --git a/epochStart/metachain/trigger.go b/epochStart/metachain/trigger.go index ce16e66c383..a1e8b06a596 100644 --- a/epochStart/metachain/trigger.go +++ b/epochStart/metachain/trigger.go @@ -15,13 +15,14 @@ import ( "github.com/multiversx/mx-chain-core-go/display" "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/epochStart" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/storage" - logger "github.com/multiversx/mx-chain-logger-go" ) var log = logger.GetOrCreate("epochStart/metachain") @@ -54,7 +55,6 @@ type trigger struct { isEpochStart bool epoch uint32 epochStartMeta data.HeaderHandler - currentRound uint64 epochFinalityAttestingRound uint64 currEpochStartRound uint64 prevEpochStartRound uint64 @@ -212,14 +212,6 @@ func (t *trigger) getMinRoundsBetweenEpochs(epoch uint32) uint64 { return uint64(chainParametersForEpoch.MinRoundsBetweenEpochs) } -// UpdateRound will set the current round -func (t *trigger) UpdateRound(round uint64) { - t.mutTrigger.Lock() - defer t.mutTrigger.Unlock() - - t.currentRound = round -} - // ShouldProposeEpochChange will return true if an epoch change event should be trigger func (t *trigger) ShouldProposeEpochChange(currentRound uint64, currentNonce uint64) bool { t.mutTrigger.RLock() @@ -241,33 +233,30 @@ func (t *trigger) shouldTriggerEpochStart(currentRound uint64, currentNonce uint func (t *trigger) Update(round uint64, nonce uint64) { t.mutTrigger.Lock() defer t.mutTrigger.Unlock() - - t.currentRound = round - if t.isEpochStart { return } if t.shouldTriggerEpochStart(round, nonce) { - t.setEpochChange() + t.setEpochChange(round) } } // SetEpochChange will increment the epoch field and all fields related with epoch change -func (t *trigger) SetEpochChange() { +func (t *trigger) SetEpochChange(round uint64) { t.mutTrigger.Lock() defer t.mutTrigger.Unlock() - t.setEpochChange() + t.setEpochChange(round) } -func (t *trigger) setEpochChange() { +func (t *trigger) setEpochChange(round uint64) { t.epoch += 1 t.isEpochStart = true t.prevEpochStartRound = t.currEpochStartRound - t.currEpochStartRound = t.currentRound + t.currEpochStartRound = round - msg := fmt.Sprintf("EPOCH %d BEGINS IN ROUND (%d)", t.epoch, t.currentRound) + msg := fmt.Sprintf("EPOCH %d BEGINS IN ROUND (%d)", t.epoch, t.currEpochStartRound) log.Debug(display.Headline(msg, "", "#")) log.Debug("trigger.Update", "isEpochStart", t.isEpochStart) logger.SetCorrelationEpoch(t.epoch) @@ -300,7 +289,6 @@ func (t *trigger) SetProcessed(header data.HeaderHandler, body data.BodyHandler) t.currEpochStartRound = metaBlock.Round t.epoch = metaBlock.Epoch t.isEpochStart = false - t.currentRound = metaBlock.Round t.epochStartMeta = metaBlock t.epochStartMetaHash = metaHash @@ -367,10 +355,6 @@ func (t *trigger) RevertStateToBlock(header data.HeaderHandler) error { return err } - t.mutTrigger.Lock() - t.currentRound = header.GetRound() - t.mutTrigger.Unlock() - return nil } @@ -500,7 +484,6 @@ func (t *trigger) SetEpochStartMetaHdrHash(metaHdrHash []byte) { func (t *trigger) SetCurrentEpochStartRound(round uint64) { t.mutTrigger.Lock() t.currEpochStartRound = round - t.currentRound = round t.saveCurrentState(round) t.mutTrigger.Unlock() } diff --git a/epochStart/metachain/triggerRegistry.go b/epochStart/metachain/triggerRegistry.go index 82451e9aae9..badac850bc6 100644 --- a/epochStart/metachain/triggerRegistry.go +++ b/epochStart/metachain/triggerRegistry.go @@ -5,6 +5,7 @@ import ( "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/marshal" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/epochStart" ) @@ -26,7 +27,6 @@ func (t *trigger) LoadState(key []byte) error { t.mutTrigger.Lock() t.triggerStateKey = key - t.currentRound = state.CurrentRound t.epochFinalityAttestingRound = state.EpochFinalityAttestingRound t.currEpochStartRound = state.CurrEpochStartRound t.prevEpochStartRound = state.PrevEpochStartRound @@ -64,7 +64,6 @@ func (t *trigger) saveState(key []byte) error { return epochStart.ErrWrongTypeAssertion } registry := &block.MetaTriggerRegistry{} - registry.CurrentRound = t.currentRound registry.EpochFinalityAttestingRound = t.epochFinalityAttestingRound registry.CurrEpochStartRound = t.currEpochStartRound registry.PrevEpochStartRound = t.prevEpochStartRound diff --git a/epochStart/metachain/triggerRegistry_test.go b/epochStart/metachain/triggerRegistry_test.go index 33a4afe5ed5..e2689eef849 100644 --- a/epochStart/metachain/triggerRegistry_test.go +++ b/epochStart/metachain/triggerRegistry_test.go @@ -6,12 +6,13 @@ import ( "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" + "github.com/stretchr/testify/require" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/storage" "github.com/multiversx/mx-chain-go/testscommon/genericMocks" storageStubs "github.com/multiversx/mx-chain-go/testscommon/storage" - "github.com/stretchr/testify/require" ) func cloneTrigger(t *trigger) *trigger { @@ -19,7 +20,6 @@ func cloneTrigger(t *trigger) *trigger { rt.isEpochStart = t.isEpochStart rt.epoch = t.epoch - rt.currentRound = t.currentRound rt.epochFinalityAttestingRound = t.epochFinalityAttestingRound rt.currEpochStartRound = t.currEpochStartRound rt.prevEpochStartRound = t.prevEpochStartRound @@ -59,7 +59,6 @@ func TestTrigger_LoadStateAfterSave(t *testing.T) { key := []byte("key") epochStartTrigger1.triggerStateKey = key epochStartTrigger1.epoch = 6 - epochStartTrigger1.currentRound = 1000 epochStartTrigger1.epochFinalityAttestingRound = 998 epochStartTrigger1.currEpochStartRound = 800 epochStartTrigger1.prevEpochStartRound = 650 @@ -92,7 +91,6 @@ func TestTrigger_LoadStateBackwardsCompatibility(t *testing.T) { key := []byte("key") epochStartTrigger1.triggerStateKey = key epochStartTrigger1.epoch = 6 - epochStartTrigger1.currentRound = 1000 epochStartTrigger1.epochFinalityAttestingRound = 998 epochStartTrigger1.currEpochStartRound = 800 epochStartTrigger1.prevEpochStartRound = 650 @@ -124,7 +122,6 @@ func createLegacyTriggerRegistryFromTrigger(t *trigger) *legacyTriggerRegistry { metaBlock, _ := t.epochStartMeta.(*block.MetaBlock) return &legacyTriggerRegistry{ Epoch: t.epoch, - CurrentRound: t.currentRound, EpochFinalityAttestingRound: t.epochFinalityAttestingRound, CurrEpochStartRound: t.currEpochStartRound, PrevEpochStartRound: t.prevEpochStartRound, diff --git a/epochStart/metachain/trigger_test.go b/epochStart/metachain/trigger_test.go index 84c2a195d91..8bf3f146fa5 100644 --- a/epochStart/metachain/trigger_test.go +++ b/epochStart/metachain/trigger_test.go @@ -11,6 +11,9 @@ import ( "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/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/epochStart" @@ -23,8 +26,6 @@ import ( statusHandlerMock "github.com/multiversx/mx-chain-go/testscommon/statusHandler" storageStubs "github.com/multiversx/mx-chain-go/testscommon/storage" vic "github.com/multiversx/mx-chain-go/testscommon/validatorInfoCacher" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func createMockEpochStartTriggerArguments() *ArgsNewMetaEpochStartTrigger { @@ -168,18 +169,13 @@ func TestNewEpochStartTrigger_UpdateRoundAndSetEpochChange(t *testing.T) { require.Nil(t, err) epoch := uint32(0) - round := uint64(0) + round := uint64(2) nonce := uint64(100) - epochStartTrigger.UpdateRound(round) - round++ - epochStartTrigger.UpdateRound(round) - round++ - epochStartTrigger.UpdateRound(round) shouldProposeEpochChange := epochStartTrigger.ShouldProposeEpochChange(round, nonce) require.True(t, shouldProposeEpochChange) - epochStartTrigger.SetEpochChange() + epochStartTrigger.SetEpochChange(round) currentEpoch := epochStartTrigger.Epoch() require.Equal(t, epoch+1, currentEpoch) require.True(t, epochStartTrigger.IsEpochStart()) @@ -228,7 +224,6 @@ func TestTrigger_ForceEpochStartCloseToNormalEpochStartShouldNotForce(t *testing arguments := createMockEpochStartTriggerArguments() epochStartTrigger, _ := NewEpochStartTrigger(arguments) - epochStartTrigger.currentRound = 20 epochStartTrigger.ForceEpochStart(201) assert.Equal(t, uint64(math.MaxUint64), epochStartTrigger.nextEpochStartRound) @@ -247,7 +242,6 @@ func TestTrigger_ForceEpochStartUnderMinimumBetweenEpochs(t *testing.T) { }, } epochStartTrigger, _ := NewEpochStartTrigger(arguments) - epochStartTrigger.currentRound = 1 epochStartTrigger.ForceEpochStart(10) assert.Equal(t, uint64(20), epochStartTrigger.nextEpochStartRound) @@ -271,8 +265,6 @@ func TestTrigger_ForceEpochStartShouldOk(t *testing.T) { epochStartTrigger, err := NewEpochStartTrigger(arguments) require.Nil(t, err) - epochStartTrigger.currentRound = 50 - expectedRound := uint64(60) epochStartTrigger.ForceEpochStart(60) diff --git a/epochStart/shardchain/trigger.go b/epochStart/shardchain/trigger.go index 2f28b423488..1db78124c66 100644 --- a/epochStart/shardchain/trigger.go +++ b/epochStart/shardchain/trigger.go @@ -19,12 +19,13 @@ import ( "github.com/multiversx/mx-chain-core-go/display" "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" + logger "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/epochStart" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/storage" - logger "github.com/multiversx/mx-chain-logger-go" ) var log = logger.GetOrCreate("epochStart/shardchain") @@ -1231,12 +1232,8 @@ func (t *trigger) GetSavedStateKey() []byte { func (t *trigger) Update(_ uint64, _ uint64) { } -// UpdateRound will do nothing -func (t *trigger) UpdateRound(_ uint64) { -} - // SetEpochChange will do nothing -func (t *trigger) SetEpochChange() { +func (t *trigger) SetEpochChange(_ uint64) { } // ShouldProposeEpochChange will always return false diff --git a/factory/mock/epochStartTriggerStub.go b/factory/mock/epochStartTriggerStub.go index 4687fc68f98..17d5409030f 100644 --- a/factory/mock/epochStartTriggerStub.go +++ b/factory/mock/epochStartTriggerStub.go @@ -4,6 +4,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/epochStart" ) @@ -20,21 +21,13 @@ type EpochStartTriggerStub struct { ProcessedCalled func(header data.HeaderHandler) EpochStartRoundCalled func() uint64 ShouldProposeEpochChangeCalled func(round uint64, nonce uint64) bool - UpdateRoundCalled func(round uint64) - SetEpochChangeCalled func() -} - -// UpdateRound - -func (e *EpochStartTriggerStub) UpdateRound(round uint64) { - if e.UpdateRoundCalled != nil { - e.UpdateRoundCalled(round) - } + SetEpochChangeCalled func(round uint64) } // SetEpochChange - -func (e *EpochStartTriggerStub) SetEpochChange() { +func (e *EpochStartTriggerStub) SetEpochChange(round uint64) { if e.SetEpochChangeCalled != nil { - e.SetEpochChangeCalled() + e.SetEpochChangeCalled(round) } } diff --git a/factory/processing/blockProcessorCreator.go b/factory/processing/blockProcessorCreator.go index 68bf8bea6c5..073bd28f000 100644 --- a/factory/processing/blockProcessorCreator.go +++ b/factory/processing/blockProcessorCreator.go @@ -1205,6 +1205,17 @@ func (pcf *processComponentsFactory) newMetaBlockProcessor( return nil, err } + shardInfoCreator, err := block.NewShardInfoCreateData( + pcf.coreData.EnableEpochsHandler(), + pcf.data.Datapool().Headers(), + pcf.data.Datapool().Proofs(), + pendingMiniBlocksHandler, + blockTracker, + ) + if err != nil { + return nil, err + } + arguments := block.ArgMetaProcessor{ ArgBaseProcessor: argumentsBaseProcessor, SCToProtocol: smartContractToProtocol, @@ -1215,6 +1226,7 @@ func (pcf *processComponentsFactory) newMetaBlockProcessor( EpochValidatorInfoCreator: validatorInfoCreator, ValidatorStatisticsProcessor: validatorStatisticsProcessor, EpochSystemSCProcessor: epochStartSystemSCProcessor, + ShardInfoCreator: shardInfoCreator, } metaProcessor, err := block.NewMetaProcessor(arguments) diff --git a/go.mod b/go.mod index eb7cdb7af20..16827fe47ec 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/libp2p/go-libp2p-pubsub v0.13.0 github.com/mitchellh/mapstructure v1.5.0 github.com/multiversx/mx-chain-communication-go v1.3.0 - github.com/multiversx/mx-chain-core-go v1.4.2-0.20251028132358-ff06ba9e2822 + github.com/multiversx/mx-chain-core-go v1.4.2-0.20251104092828-1dd9514c928a github.com/multiversx/mx-chain-crypto-go v1.3.0 github.com/multiversx/mx-chain-es-indexer-go v1.9.3-0.20251021150757-bd6aa66a0a90 github.com/multiversx/mx-chain-logger-go v1.1.0 diff --git a/go.sum b/go.sum index b3fd028fdde..89a13e151a3 100644 --- a/go.sum +++ b/go.sum @@ -401,8 +401,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.3.0 h1:ziNM1dRuiR/7al2L/jGEA/a/hjurtJ/HEqgazHNt9P8= github.com/multiversx/mx-chain-communication-go v1.3.0/go.mod h1:gDVWn6zUW6aCN1YOm/FbbT5MUmhgn/L1Rmpl8EoH3Yg= -github.com/multiversx/mx-chain-core-go v1.4.2-0.20251028132358-ff06ba9e2822 h1:BsbYhJaA70o9wt4boKslLau+f98IsOxZAMex8LcrbQw= -github.com/multiversx/mx-chain-core-go v1.4.2-0.20251028132358-ff06ba9e2822/go.mod h1:IO+vspNan+gT0WOHnJ95uvWygiziHZvfXpff6KnxV7g= +github.com/multiversx/mx-chain-core-go v1.4.2-0.20251104092828-1dd9514c928a h1:sOp+4UkIJNbLFlpefo3G1cFKjYnVbMIpVbQsTSZzooo= +github.com/multiversx/mx-chain-core-go v1.4.2-0.20251104092828-1dd9514c928a/go.mod h1:IO+vspNan+gT0WOHnJ95uvWygiziHZvfXpff6KnxV7g= github.com/multiversx/mx-chain-crypto-go v1.3.0 h1:0eK2bkDOMi8VbSPrB1/vGJSYT81IBtfL4zw+C4sWe/k= github.com/multiversx/mx-chain-crypto-go v1.3.0/go.mod h1:nPIkxxzyTP8IquWKds+22Q2OJ9W7LtusC7cAosz7ojM= github.com/multiversx/mx-chain-es-indexer-go v1.9.3-0.20251021150757-bd6aa66a0a90 h1:Hf6AqpCSHccBo5ZV5Bfaxz3UdPYhtYCynU+3CF4jAa8= diff --git a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go index abece9cbac1..ebd8190ca75 100644 --- a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go +++ b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go @@ -111,13 +111,13 @@ func testRelayedV3MoveBalance( cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3EnableEpoch = providedActivationEpoch cfg.EpochConfig.EnableEpochs.RelayedTransactionsV3FixESDTTransferEnableEpoch = providedActivationEpoch - cfg.EpochConfig.EnableEpochs.SupernovaEnableEpoch = 0 + cfg.EpochConfig.EnableEpochs.SupernovaEnableEpoch = 10 cfg.RoundConfig.RoundActivations = map[string]config.ActivationRoundByName{ "DisableAsyncCallV1": { Round: "0", }, "SupernovaEnableRound": { - Round: "0", + Round: "10000", }, } } diff --git a/integrationTests/mock/endOfEpochTriggerStub.go b/integrationTests/mock/endOfEpochTriggerStub.go index 956825f9c2e..13f78aedf5e 100644 --- a/integrationTests/mock/endOfEpochTriggerStub.go +++ b/integrationTests/mock/endOfEpochTriggerStub.go @@ -4,6 +4,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/epochStart" ) @@ -20,21 +21,13 @@ type EpochStartTriggerStub struct { ProcessedCalled func(header data.HeaderHandler) EpochStartRoundCalled func() uint64 ShouldProposeEpochChangeCalled func(round uint64, nonce uint64) bool - UpdateRoundCalled func(round uint64) - SetEpochChangeCalled func() -} - -// UpdateRound - -func (e *EpochStartTriggerStub) UpdateRound(round uint64) { - if e.UpdateRoundCalled != nil { - e.UpdateRoundCalled(round) - } + SetEpochChangeCalled func(round uint64) } // SetEpochChange - -func (e *EpochStartTriggerStub) SetEpochChange() { +func (e *EpochStartTriggerStub) SetEpochChange(round uint64) { if e.SetEpochChangeCalled != nil { - e.SetEpochChangeCalled() + e.SetEpochChangeCalled(round) } } diff --git a/integrationTests/testFullNode.go b/integrationTests/testFullNode.go index b51698eb0ed..066b568cde8 100644 --- a/integrationTests/testFullNode.go +++ b/integrationTests/testFullNode.go @@ -1175,6 +1175,15 @@ func (tpn *TestFullNode) initBlockProcessor( epochStartSystemSCProcessor, _ := metachain.NewSystemSCProcessor(argsEpochSystemSC) tpn.EpochStartSystemSCProcessor = epochStartSystemSCProcessor + shardInfoCreator, errShardInfoCreate := block.NewShardInfoCreateData( + tpn.EnableEpochsHandler, + tpn.DataPool.Headers(), + tpn.DataPool.Proofs(), + &mock.PendingMiniBlocksHandlerStub{}, + argumentsBase.BlockTracker, + ) + log.LogIfError(errShardInfoCreate) + arguments := block.ArgMetaProcessor{ ArgBaseProcessor: argumentsBase, SCToProtocol: scToProtocolInstance, @@ -1185,6 +1194,7 @@ func (tpn *TestFullNode) initBlockProcessor( EpochValidatorInfoCreator: epochStartValidatorInfo, ValidatorStatisticsProcessor: tpn.ValidatorStatisticsProcessor, EpochSystemSCProcessor: epochStartSystemSCProcessor, + ShardInfoCreator: shardInfoCreator, } tpn.BlockProcessor, err = block.NewMetaProcessor(arguments) @@ -1365,6 +1375,16 @@ func (tpn *TestFullNode) initBlockProcessorWithSync( if tpn.ShardCoordinator.SelfId() == core.MetachainShardId { argumentsBase.ForkDetector = tpn.ForkDetector argumentsBase.TxCoordinator = &mock.TransactionCoordinatorMock{} + + shardInfoCreator, errShardInfoCreate := block.NewShardInfoCreateData( + coreComponents.EnableEpochsHandler(), + dataComponents.DataPool.Headers(), + dataComponents.DataPool.Proofs(), + &mock.PendingMiniBlocksHandlerStub{}, + argumentsBase.BlockTracker, + ) + log.LogIfError(errShardInfoCreate) + arguments := block.ArgMetaProcessor{ ArgBaseProcessor: argumentsBase, SCToProtocol: &mock.SCToProtocolStub{}, @@ -1379,6 +1399,7 @@ func (tpn *TestFullNode) initBlockProcessorWithSync( }, }, EpochSystemSCProcessor: &testscommon.EpochStartSystemSCStub{}, + ShardInfoCreator: shardInfoCreator, } tpn.BlockProcessor, err = block.NewMetaProcessor(arguments) diff --git a/integrationTests/testProcessorNode.go b/integrationTests/testProcessorNode.go index d5ee502e838..4cc6cd1c3b1 100644 --- a/integrationTests/testProcessorNode.go +++ b/integrationTests/testProcessorNode.go @@ -31,11 +31,12 @@ import ( ed25519SingleSig "github.com/multiversx/mx-chain-crypto-go/signing/ed25519/singlesig" "github.com/multiversx/mx-chain-crypto-go/signing/mcl" mclsig "github.com/multiversx/mx-chain-crypto-go/signing/mcl/singlesig" - "github.com/multiversx/mx-chain-go/process/asyncExecution/queue" vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/multiversx/mx-chain-vm-common-go/parsers" wasmConfig "github.com/multiversx/mx-chain-vm-go/config" + "github.com/multiversx/mx-chain-go/process/asyncExecution/queue" + "github.com/multiversx/mx-chain-go/process/asyncExecution/executionTrack" "github.com/multiversx/mx-chain-go/process/estimator" "github.com/multiversx/mx-chain-go/process/missingData" @@ -2736,6 +2737,15 @@ func (tpn *TestProcessorNode) initBlockProcessor() { epochStartSystemSCProcessor, _ := metachain.NewSystemSCProcessor(argsEpochSystemSC) tpn.EpochStartSystemSCProcessor = epochStartSystemSCProcessor + shardInfoCreator, errShardInfoCreator := block.NewShardInfoCreateData( + tpn.EnableEpochsHandler, + tpn.DataPool.Headers(), + tpn.DataPool.Proofs(), + &mock.PendingMiniBlocksHandlerStub{}, + argumentsBase.BlockTracker, + ) + log.LogIfError(errShardInfoCreator) + arguments := block.ArgMetaProcessor{ ArgBaseProcessor: argumentsBase, SCToProtocol: scToProtocolInstance, @@ -2746,6 +2756,7 @@ func (tpn *TestProcessorNode) initBlockProcessor() { EpochValidatorInfoCreator: epochStartValidatorInfo, ValidatorStatisticsProcessor: tpn.ValidatorStatisticsProcessor, EpochSystemSCProcessor: epochStartSystemSCProcessor, + ShardInfoCreator: shardInfoCreator, } tpn.BlockProcessor, err = block.NewMetaProcessor(arguments) diff --git a/integrationTests/testSyncNode.go b/integrationTests/testSyncNode.go index 1bce972e72c..cac6d4c8d56 100644 --- a/integrationTests/testSyncNode.go +++ b/integrationTests/testSyncNode.go @@ -5,6 +5,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data" + "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/process/asyncExecution/executionTrack" "github.com/multiversx/mx-chain-go/process/asyncExecution/queue" @@ -224,6 +225,15 @@ func (tpn *TestProcessorNode) initBlockProcessorWithSync() { ) argumentsBase.ForkDetector = tpn.ForkDetector argumentsBase.TxCoordinator = &mock.TransactionCoordinatorMock{} + shardInfoCreator, errShardInfoCreator := block.NewShardInfoCreateData( + tpn.EnableEpochsHandler, + tpn.DataPool.Headers(), + tpn.DataPool.Proofs(), + &mock.PendingMiniBlocksHandlerStub{}, + argumentsBase.BlockTracker, + ) + log.LogIfError(errShardInfoCreator) + arguments := block.ArgMetaProcessor{ ArgBaseProcessor: argumentsBase, SCToProtocol: &mock.SCToProtocolStub{}, @@ -238,6 +248,7 @@ func (tpn *TestProcessorNode) initBlockProcessorWithSync() { }, }, EpochSystemSCProcessor: &testscommon.EpochStartSystemSCStub{}, + ShardInfoCreator: shardInfoCreator, } tpn.BlockProcessor, err = block.NewMetaProcessor(arguments) diff --git a/integrationTests/vm/staking/metaBlockProcessorCreator.go b/integrationTests/vm/staking/metaBlockProcessorCreator.go index ac30b4d6eb4..04d84628fe7 100644 --- a/integrationTests/vm/staking/metaBlockProcessorCreator.go +++ b/integrationTests/vm/staking/metaBlockProcessorCreator.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/config" "github.com/multiversx/mx-chain-go/process/asyncExecution/executionTrack" "github.com/multiversx/mx-chain-go/process/estimator" @@ -129,6 +130,14 @@ func createMetaBlockProcessor( } missingDataResolver, _ := missingData.NewMissingDataResolver(missingDataArgs) + shardInfoCreator, _ := blproc.NewShardInfoCreateData( + coreComponents.EnableEpochsHandler(), + dataComponents.Datapool().Headers(), + dataComponents.Datapool().Proofs(), + &mock.PendingMiniBlocksHandlerStub{}, + blockTracker, + ) + args := blproc.ArgMetaProcessor{ ArgBaseProcessor: blproc.ArgBaseProcessor{ CoreComponents: coreComponents, @@ -185,6 +194,7 @@ func createMetaBlockProcessor( EpochValidatorInfoCreator: valInfoCreator, ValidatorStatisticsProcessor: validatorsInfoCreator, EpochSystemSCProcessor: systemSCProcessor, + ShardInfoCreator: shardInfoCreator, } metaProc, _ := blproc.NewMetaProcessor(args) diff --git a/integrationTests/vm/wasm/wasmvm/wasmVM_test.go b/integrationTests/vm/wasm/wasmvm/wasmVM_test.go index c0416a4a4e7..b8b826a20b0 100644 --- a/integrationTests/vm/wasm/wasmvm/wasmVM_test.go +++ b/integrationTests/vm/wasm/wasmvm/wasmVM_test.go @@ -973,7 +973,6 @@ func TestCommunityContract_CrossShard_TxProcessor(t *testing.T) { parentCode := codePath + "/parent_old.wasm" logger.ToggleLoggerName(true) - // logger.SetLogLevel("*:TRACE") // Deploy Funder SC in shard 0 funderOwnerAccount, _ := testContextFunderSC.Accounts.LoadAccount(funderOwner) diff --git a/node/mock/endOfEpochTriggerStub.go b/node/mock/endOfEpochTriggerStub.go index 4a86a81352b..b695a90a45e 100644 --- a/node/mock/endOfEpochTriggerStub.go +++ b/node/mock/endOfEpochTriggerStub.go @@ -19,21 +19,13 @@ type EpochStartTriggerStub struct { ProcessedCalled func(header data.HeaderHandler) EpochStartRoundCalled func() uint64 ShouldProposeEpochChangeCalled func(round uint64, nonce uint64) bool - UpdateRoundCalled func(round uint64) - SetEpochChangeCalled func() -} - -// UpdateRound - -func (e *EpochStartTriggerStub) UpdateRound(round uint64) { - if e.UpdateRoundCalled != nil { - e.UpdateRoundCalled(round) - } + SetEpochChangeCalled func(round uint64) } // SetEpochChange - -func (e *EpochStartTriggerStub) SetEpochChange() { +func (e *EpochStartTriggerStub) SetEpochChange(round uint64) { if e.SetEpochChangeCalled != nil { - e.SetEpochChangeCalled() + e.SetEpochChangeCalled(round) } } diff --git a/outport/process/transactionsfee/transactionsFeeProcessor_test.go b/outport/process/transactionsfee/transactionsFeeProcessor_test.go index d443b580154..0142ddf1ab0 100644 --- a/outport/process/transactionsfee/transactionsFeeProcessor_test.go +++ b/outport/process/transactionsfee/transactionsFeeProcessor_test.go @@ -10,6 +10,8 @@ import ( outportcore "github.com/multiversx/mx-chain-core-go/data/outport" "github.com/multiversx/mx-chain-core-go/data/smartContractResult" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/stretchr/testify/require" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/outport/mock" "github.com/multiversx/mx-chain-go/process" @@ -19,8 +21,6 @@ import ( "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" "github.com/multiversx/mx-chain-go/testscommon/genericMocks" "github.com/multiversx/mx-chain-go/testscommon/marshallerMock" - logger "github.com/multiversx/mx-chain-logger-go" - "github.com/stretchr/testify/require" ) var pubKeyConverter, _ = pubkeyConverter.NewBech32PubkeyConverter(32, "erd") @@ -465,8 +465,6 @@ func silentDecodeAddress(address string) []byte { func TestPutFeeAndGasUsedScrWithRefundNoTx(t *testing.T) { t.Parallel() - _ = logger.SetLogLevel("*:TRACE") - txHash := []byte("relayedTx") scrWithRefund := []byte("scrWithRefund") @@ -495,6 +493,8 @@ func TestPutFeeAndGasUsedScrWithRefundNoTx(t *testing.T) { wasCalled := false txsFeeProc, err := NewTransactionsFeeProcessor(arg) + require.Nil(t, err) + txsFeeProc.log = &testscommon.LoggerStub{ TraceCalled: func(message string, args ...interface{}) { wasCalled = true diff --git a/process/block/argProcessor.go b/process/block/argProcessor.go index 1e520bc5eb8..86ccdbca83c 100644 --- a/process/block/argProcessor.go +++ b/process/block/argProcessor.go @@ -127,4 +127,5 @@ type ArgMetaProcessor struct { EpochValidatorInfoCreator process.EpochStartValidatorInfoCreator EpochSystemSCProcessor process.EpochStartSystemSCProcessor ValidatorStatisticsProcessor process.ValidatorStatisticsProcessor + ShardInfoCreator process.ShardInfoCreator } diff --git a/process/block/baseProcess.go b/process/block/baseProcess.go index 47f12c5bb7b..b508ff65af1 100644 --- a/process/block/baseProcess.go +++ b/process/block/baseProcess.go @@ -20,6 +20,8 @@ import ( "github.com/multiversx/mx-chain-core-go/display" "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" + logger "github.com/multiversx/mx-chain-logger-go" + nodeFactory "github.com/multiversx/mx-chain-go/cmd/node/factory" "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/common/errChan" @@ -43,11 +45,17 @@ import ( "github.com/multiversx/mx-chain-go/state/parsers" "github.com/multiversx/mx-chain-go/storage" "github.com/multiversx/mx-chain-go/storage/storageunit" - logger "github.com/multiversx/mx-chain-logger-go" ) var log = logger.GetOrCreate("process/block") +// CrossShardIncomingMbsCreationResult represents the result of creating cross-shard mini blocks +type CrossShardIncomingMbsCreationResult struct { + HeaderFinished bool + PendingMiniBlocks []block.MiniblockAndHash + AddedMiniBlocks []block.MiniblockAndHash +} + type hashAndHdr struct { hdr data.HeaderHandler hash []byte @@ -1155,7 +1163,6 @@ func (bp *baseProcessor) cleanupPools(headerHandler data.HeaderHandler) { // cleanup all log events bp.dataPool.PostProcessTransactions().Remove(common.PrepareLogEventsKey(executionResultHeaderHash)) } - } func (bp *baseProcessor) cleanupPoolsForCrossShard( @@ -1307,7 +1314,7 @@ func (bp *baseProcessor) getFinalMiniBlocksFromExecutionResults( executedMiniBlocksCache := bp.dataPool.ExecutedMiniBlocks() for _, baseExecutionResult := range baseExecutionResults { - miniBlockHeaderHandlers, err := common.GetMiniBlocksHeaderHandlersFromExecResult(baseExecutionResult, header.GetShardID()) + miniBlockHeaderHandlers, err := common.GetMiniBlocksHeaderHandlersFromExecResult(baseExecutionResult) if err != nil { return nil, err } @@ -2531,7 +2538,7 @@ func (bp *baseProcessor) saveMiniBlocksFromExecutionResults(header data.HeaderHa } for _, baseExecutionResult := range baseExecutionResults { - miniBlockHeaderHandlers, err := common.GetMiniBlocksHeaderHandlersFromExecResult(baseExecutionResult, header.GetShardID()) + miniBlockHeaderHandlers, err := common.GetMiniBlocksHeaderHandlersFromExecResult(baseExecutionResult) if err != nil { return err } @@ -2714,3 +2721,104 @@ func getStorageUnitFromBlockType(blockType block.Type) (dataRetriever.UnitType, } return 0, process.ErrInvalidBlockType } + +func (bp *baseProcessor) checkInclusionEstimationForExecutionResults(header data.HeaderHandler) error { + prevBlockLastExecutionResult, err := process.GetPrevBlockLastExecutionResult(bp.blockChain) + if err != nil { + return err + } + + lastResultData, err := process.CreateDataForInclusionEstimation(prevBlockLastExecutionResult) + if err != nil { + return err + } + executionResults := header.GetExecutionResultsHandlers() + allowed := bp.executionResultsInclusionEstimator.Decide(lastResultData, executionResults, header.GetRound()) + if allowed != len(executionResults) { + log.Warn("number of execution results included in the header is not correct", + "expected", allowed, + "actual", len(executionResults), + ) + return process.ErrInvalidNumberOfExecutionResultsInHeader + } + + return nil +} + +func (bp *baseProcessor) addExecutionResultsOnHeader(header data.HeaderHandler) error { + pendingExecutionResults, err := bp.executionResultsTracker.GetPendingExecutionResults() + if err != nil { + return err + } + + lastExecutionResultHandler, err := process.GetPrevBlockLastExecutionResult(bp.blockChain) + if err != nil { + return err + } + + lastNotarizedExecutionResultInfo, err := process.CreateDataForInclusionEstimation(lastExecutionResultHandler) + if err != nil { + return err + } + + var lastExecutionResultForCurrentBlock data.LastExecutionResultHandler + numToInclude := bp.executionResultsInclusionEstimator.Decide(lastNotarizedExecutionResultInfo, pendingExecutionResults, header.GetRound()) + + executionResultsToInclude := pendingExecutionResults[:numToInclude] + lastExecutionResultForCurrentBlock = lastExecutionResultHandler + if len(executionResultsToInclude) > 0 { + lastExecutionResult := executionResultsToInclude[len(executionResultsToInclude)-1] + lastExecutionResultForCurrentBlock, err = process.CreateLastExecutionResultInfoFromExecutionResult(header.GetRound(), lastExecutionResult, bp.shardCoordinator.SelfId()) + if err != nil { + return err + } + } + + err = header.SetLastExecutionResultHandler(lastExecutionResultForCurrentBlock) + if err != nil { + return err + } + + return header.SetExecutionResultsHandlers(executionResultsToInclude) +} + +func (bp *baseProcessor) createMbsCrossShardDstMe( + currentBlockHash []byte, + currentBlock data.HeaderHandler, + miniBlockProcessingInfo map[string]*processedMb.ProcessedMiniBlockInfo, +) (*CrossShardIncomingMbsCreationResult, error) { + currMiniBlocksAdded, pendingMiniBlocks, currNumTxsAdded, hdrFinished, errCreate := bp.txCoordinator.CreateMbsCrossShardDstMe( + currentBlock, + miniBlockProcessingInfo, + ) + if errCreate != nil { + return nil, errCreate + } + + if !hdrFinished { + log.Debug("block cannot be fully processed", + "round", currentBlock.GetRound(), + "nonce", currentBlock.GetNonce(), + "hash", currentBlockHash, + "num mbs added", len(currMiniBlocksAdded), + "num txs added", currNumTxsAdded) + } + + return &CrossShardIncomingMbsCreationResult{ + HeaderFinished: hdrFinished, + PendingMiniBlocks: pendingMiniBlocks, + AddedMiniBlocks: currMiniBlocksAdded, + }, nil +} + +func (bp *baseProcessor) revertGasForCrossShardDstMeMiniBlocks(added, pending []block.MiniblockAndHash) { + miniBlockHashesToRevert := make([][]byte, 0, len(added)) + for _, mbAndHash := range added { + miniBlockHashesToRevert = append(miniBlockHashesToRevert, mbAndHash.Hash) + } + for _, mbAndHash := range pending { + miniBlockHashesToRevert = append(miniBlockHashesToRevert, mbAndHash.Hash) + } + + bp.gasComputation.RevertIncomingMiniBlocks(miniBlockHashesToRevert) +} diff --git a/process/block/baseProcess_test.go b/process/block/baseProcess_test.go index 1d461a9eecf..42984fd079d 100644 --- a/process/block/baseProcess_test.go +++ b/process/block/baseProcess_test.go @@ -66,7 +66,6 @@ import ( "github.com/multiversx/mx-chain-go/testscommon/mainFactoryMocks" "github.com/multiversx/mx-chain-go/testscommon/marshallerMock" "github.com/multiversx/mx-chain-go/testscommon/outport" - "github.com/multiversx/mx-chain-go/testscommon/preprocMocks" "github.com/multiversx/mx-chain-go/testscommon/shardingMocks" stateMock "github.com/multiversx/mx-chain-go/testscommon/state" statusHandlerMock "github.com/multiversx/mx-chain-go/testscommon/statusHandler" @@ -134,7 +133,6 @@ func createArgBaseProcessor( var execResultsVerifier blproc.ExecutionResultsVerifier var missingDataResolver blproc.MissingDataResolver if check.IfNil(dataComponents) || check.IfNil(dataComponents.Datapool()) || check.IfNil(coreComponents) || check.IfNil(bootstrapComponents) { - blockDataRequester = &preprocMocks.BlockDataRequesterStub{} inclusionEstimator = &processMocks.InclusionEstimatorMock{} mbSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{} execResultsVerifier = &processMocks.ExecutionResultsVerifierMock{} @@ -3759,14 +3757,14 @@ func TestBaseProcessor_GetFinalMiniBlocksFromExecutionResult(t *testing.T) { bp, _ := blproc.NewShardProcessor(arguments) executionResults := []*block.ExecutionResult{ - &block.ExecutionResult{ + { MiniBlockHeaders: []block.MiniBlockHeader{ - block.MiniBlockHeader{ + { Hash: []byte("mbHash1"), ReceiverShardID: 1, SenderShardID: 0, }, - block.MiniBlockHeader{ + { Hash: []byte("mbHash2"), ReceiverShardID: 1, SenderShardID: 0, @@ -3804,9 +3802,9 @@ func TestBaseProcessor_GetFinalMiniBlocksFromExecutionResult(t *testing.T) { bp, _ := blproc.NewShardProcessor(arguments) executionResults := []*block.ExecutionResult{ - &block.ExecutionResult{ + { MiniBlockHeaders: []block.MiniBlockHeader{ - block.MiniBlockHeader{ + { Hash: []byte("mbHash1"), ReceiverShardID: 1, SenderShardID: 0, @@ -3856,9 +3854,9 @@ func TestBaseProcessor_GetFinalMiniBlocksFromExecutionResult(t *testing.T) { bp, _ := blproc.NewShardProcessor(arguments) executionResults := []*block.ExecutionResult{ - &block.ExecutionResult{ + { MiniBlockHeaders: []block.MiniBlockHeader{ - block.MiniBlockHeader{ + { Hash: []byte("mbHash1"), ReceiverShardID: 1, SenderShardID: 0, diff --git a/process/block/export_test.go b/process/block/export_test.go index 1d3f17d8e92..c18a0aae6c8 100644 --- a/process/block/export_test.go +++ b/process/block/export_test.go @@ -280,8 +280,8 @@ func (mp *metaProcessor) IsHdrMissing(hdrHash []byte) bool { } // CreateShardInfo - -func (mp *metaProcessor) CreateShardInfo() ([]data.ShardDataHandler, error) { - return mp.createShardInfo() +func (mp *metaProcessor) CreateShardInfo(metaHeader data.MetaHeaderHandler) ([]data.ShardDataHandler, error) { + return mp.createShardInfo(metaHeader) } // SaveMetricCrossCheckBlockHeight - @@ -844,3 +844,46 @@ func ConstructPartialShardBlockProcessorForTest(subcomponents map[string]interfa } return sp, err } + +// SetEpochStartData - +func (mp *metaProcessor) SetEpochStartData(epochStartData *block.EpochStart) { + mp.epochStartData = epochStartData +} + +// GetTxCountExecutionResults - +func GetTxCountExecutionResults(metaHeader data.MetaHeaderHandler) (uint32, error) { + return getTxCountExecutionResults(metaHeader) +} + +// HasStartOfEpochExecutionResults - +func (mp *metaProcessor) HasStartOfEpochExecutionResults(metaHeader data.MetaHeaderHandler) (bool, error) { + return mp.hasStartOfEpochExecutionResults(metaHeader) +} + +// HasRewardOrPeerMiniBlocksFromMeta - +func HasRewardOrPeerMiniBlocksFromMeta(miniBlockHeaders []data.MiniBlockHeaderHandler) bool { + return hasRewardOrPeerMiniBlocksFromMeta(miniBlockHeaders) +} + +// CreateProposalMiniBlocks - +func (mp *metaProcessor) CreateProposalMiniBlocks(haveTime func() bool) error { + return mp.createProposalMiniBlocks(haveTime) +} + +// SelectIncomingMiniBlocksForProposal - +func (mp *metaProcessor) SelectIncomingMiniBlocksForProposal( + haveTime func() bool, +) error { + return mp.selectIncomingMiniBlocksForProposal(haveTime) +} + +// SelectIncomingMiniBlocks - +func (mp *metaProcessor) SelectIncomingMiniBlocks( + lastShardHdr map[uint32]ShardHeaderInfo, + orderedHdrs []data.HeaderHandler, + orderedHdrsHashes [][]byte, + maxNumHeadersFromSameShard uint32, + haveTime func() bool, +) error { + return mp.selectIncomingMiniBlocks(lastShardHdr, orderedHdrs, orderedHdrsHashes, maxNumHeadersFromSameShard, haveTime) +} diff --git a/process/block/gasConsumption.go b/process/block/gasConsumption.go index 90aa2ecbceb..a05c208f3dd 100644 --- a/process/block/gasConsumption.go +++ b/process/block/gasConsumption.go @@ -7,8 +7,10 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data" - "github.com/multiversx/mx-chain-go/process" + "golang.org/x/exp/slices" + + "github.com/multiversx/mx-chain-go/process" ) // gasType defines the type of gas consumption diff --git a/process/block/interface.go b/process/block/interface.go index 00284c07d0e..a53c4f56700 100644 --- a/process/block/interface.go +++ b/process/block/interface.go @@ -60,11 +60,10 @@ type MiniBlocksSelectionSession interface { GetMiniBlockHeaderHandlers() []data.MiniBlockHeaderHandler GetMiniBlocks() block.MiniBlockSlice GetMiniBlockHashes() [][]byte - AddReferencedMetaBlock(metaBlock data.HeaderHandler, metaBlockHash []byte) - GetReferencedMetaBlockHashes() [][]byte - GetReferencedMetaBlocks() []data.HeaderHandler - GetLastMetaBlock() data.HeaderHandler - GetGasProvided() uint64 + AddReferencedHeader(header data.HeaderHandler, headerHash []byte) + GetReferencedHeaderHashes() [][]byte + GetReferencedHeaders() []data.HeaderHandler + GetLastHeader() data.HeaderHandler GetNumTxsAdded() uint32 AddMiniBlocksAndHashes(miniBlocksAndHashes []block.MiniblockAndHash) error CreateAndAddMiniBlockFromTransactions(txHashes [][]byte) error diff --git a/process/block/mbsSelectionSession.go b/process/block/mbsSelectionSession.go index 3962471f47e..2af925d89cc 100644 --- a/process/block/mbsSelectionSession.go +++ b/process/block/mbsSelectionSession.go @@ -20,15 +20,16 @@ type miniBlocksSelectionConfig struct { } type miniBlocksSelectionResult struct { - miniBlockHeaderHandlers []data.MiniBlockHeaderHandler - miniBlocks block.MiniBlockSlice - miniBlockHashes [][]byte - referencedMetaBlockHashes [][]byte - referencedMetaBlocks []data.HeaderHandler - lastMetaBlock data.HeaderHandler - gasProvided uint64 - numTxsAdded uint32 - mut sync.RWMutex + miniBlockHeaderHandlers []data.MiniBlockHeaderHandler + miniBlocks block.MiniBlockSlice + miniBlockHashes [][]byte + miniBlockHashesUnique map[string]struct{} + referenceHeaderHashesUnique map[string]struct{} + referencedHeaderHashes [][]byte + referencedHeader []data.HeaderHandler + lastHeader data.HeaderHandler + numTxsAdded uint32 + mut sync.RWMutex } type miniBlocksSelectionSession struct { @@ -54,14 +55,15 @@ func NewMiniBlocksSelectionSession(shardID uint32, marshaller marshal.Marshalize hasher: hasher, }, miniBlocksSelectionResult: &miniBlocksSelectionResult{ - miniBlockHeaderHandlers: make([]data.MiniBlockHeaderHandler, 0, defaultCapacity), - miniBlocks: make(block.MiniBlockSlice, 0, defaultCapacity), - miniBlockHashes: make([][]byte, 0, defaultCapacity), - referencedMetaBlockHashes: make([][]byte, 0, defaultCapacity), - referencedMetaBlocks: make([]data.HeaderHandler, 0, defaultCapacity), - lastMetaBlock: nil, - gasProvided: 0, - numTxsAdded: 0, + miniBlockHeaderHandlers: make([]data.MiniBlockHeaderHandler, 0, defaultCapacity), + miniBlocks: make(block.MiniBlockSlice, 0, defaultCapacity), + miniBlockHashes: make([][]byte, 0, defaultCapacity), + miniBlockHashesUnique: make(map[string]struct{}), + referenceHeaderHashesUnique: make(map[string]struct{}), + referencedHeaderHashes: make([][]byte, 0, defaultCapacity), + referencedHeader: make([]data.HeaderHandler, 0, defaultCapacity), + lastHeader: nil, + numTxsAdded: 0, }, }, nil } @@ -74,10 +76,11 @@ func (s *miniBlocksSelectionSession) ResetSelectionSession() { s.miniBlockHeaderHandlers = make([]data.MiniBlockHeaderHandler, 0, defaultCapacity) s.miniBlocks = make(block.MiniBlockSlice, 0, defaultCapacity) s.miniBlockHashes = make([][]byte, 0, defaultCapacity) - s.referencedMetaBlockHashes = make([][]byte, 0, defaultCapacity) - s.referencedMetaBlocks = make([]data.HeaderHandler, 0, defaultCapacity) - s.lastMetaBlock = nil - s.gasProvided = 0 + s.miniBlockHashesUnique = make(map[string]struct{}) + s.referenceHeaderHashesUnique = make(map[string]struct{}) + s.referencedHeaderHashes = make([][]byte, 0, defaultCapacity) + s.referencedHeader = make([]data.HeaderHandler, 0, defaultCapacity) + s.lastHeader = nil s.numTxsAdded = 0 } @@ -126,64 +129,60 @@ func (s *miniBlocksSelectionSession) GetMiniBlockHashes() [][]byte { return miniBlockHashes } -// AddReferencedMetaBlock adds a meta block and its hash to the session -func (s *miniBlocksSelectionSession) AddReferencedMetaBlock(metaBlock data.HeaderHandler, metaBlockHash []byte) { +// AddReferencedHeader adds a header and its hash to the session +func (s *miniBlocksSelectionSession) AddReferencedHeader(header data.HeaderHandler, headerHash []byte) { + if check.IfNil(header) || len(headerHash) == 0 { + return + } + s.mut.Lock() defer s.mut.Unlock() - if check.IfNil(metaBlock) || len(metaBlockHash) == 0 { - return + _, ok := s.referenceHeaderHashesUnique[string(headerHash)] + if !ok { + s.referenceHeaderHashesUnique[string(headerHash)] = struct{}{} + s.referencedHeader = append(s.referencedHeader, header) + s.referencedHeaderHashes = append(s.referencedHeaderHashes, headerHash) + s.lastHeader = header } - - s.referencedMetaBlocks = append(s.referencedMetaBlocks, metaBlock) - s.referencedMetaBlockHashes = append(s.referencedMetaBlockHashes, metaBlockHash) - s.lastMetaBlock = metaBlock } -// GetReferencedMetaBlockHashes returns the hashes of the referenced meta blocks -func (s *miniBlocksSelectionSession) GetReferencedMetaBlockHashes() [][]byte { +// GetReferencedHeaderHashes returns the hashes of the referenced headers +func (s *miniBlocksSelectionSession) GetReferencedHeaderHashes() [][]byte { s.mut.RLock() defer s.mut.RUnlock() - if len(s.referencedMetaBlockHashes) == 0 { + if len(s.referencedHeaderHashes) == 0 { return nil } - referencedMetaBlockHashes := make([][]byte, len(s.referencedMetaBlockHashes)) - copy(referencedMetaBlockHashes, s.referencedMetaBlockHashes) + referencedHeaderHashes := make([][]byte, len(s.referencedHeaderHashes)) + copy(referencedHeaderHashes, s.referencedHeaderHashes) - return referencedMetaBlockHashes + return referencedHeaderHashes } -// GetReferencedMetaBlocks returns the referenced meta blocks -func (s *miniBlocksSelectionSession) GetReferencedMetaBlocks() []data.HeaderHandler { +// GetReferencedHeaders returns the referenced headers +func (s *miniBlocksSelectionSession) GetReferencedHeaders() []data.HeaderHandler { s.mut.RLock() defer s.mut.RUnlock() - if len(s.referencedMetaBlocks) == 0 { + if len(s.referencedHeader) == 0 { return nil } - referencedMetaBlocks := make([]data.HeaderHandler, len(s.referencedMetaBlocks)) - copy(referencedMetaBlocks, s.referencedMetaBlocks) - - return referencedMetaBlocks -} - -// GetLastMetaBlock returns the last meta block -func (s *miniBlocksSelectionSession) GetLastMetaBlock() data.HeaderHandler { - s.mut.RLock() - defer s.mut.RUnlock() + referencedHeaders := make([]data.HeaderHandler, len(s.referencedHeader)) + copy(referencedHeaders, s.referencedHeader) - return s.lastMetaBlock + return referencedHeaders } -// GetGasProvided returns the gas provided for the mini blocks -func (s *miniBlocksSelectionSession) GetGasProvided() uint64 { +// GetLastHeader returns the last header +func (s *miniBlocksSelectionSession) GetLastHeader() data.HeaderHandler { s.mut.RLock() defer s.mut.RUnlock() - return s.gasProvided + return s.lastHeader } // GetNumTxsAdded returns the number of transactions added to the mini blocks @@ -202,7 +201,17 @@ func (s *miniBlocksSelectionSession) AddMiniBlocksAndHashes(miniBlocksAndHashes miniBlockHashes := make([][]byte, 0, len(miniBlocksAndHashes)) numTxsAdded := 0 + s.mut.Lock() + defer s.mut.Unlock() + for _, miniBlockInfo := range miniBlocksAndHashes { + _, ok := s.miniBlockHashesUnique[string(miniBlockInfo.Hash)] + if ok { + continue + } + + s.miniBlockHashesUnique[string(miniBlockInfo.Hash)] = struct{}{} + txCount := len(miniBlockInfo.Miniblock.GetTxHashes()) mbHeader := &block.MiniBlockHeader{ @@ -223,8 +232,6 @@ func (s *miniBlocksSelectionSession) AddMiniBlocksAndHashes(miniBlocksAndHashes numTxsAdded += txCount } - s.mut.Lock() - defer s.mut.Unlock() s.miniBlocks = append(s.miniBlocks, miniBlocks...) s.miniBlockHeaderHandlers = append(s.miniBlockHeaderHandlers, miniBlockHeaderHandlers...) s.miniBlockHashes = append(s.miniBlockHashes, miniBlockHashes...) diff --git a/process/block/mbsSelectionSession_test.go b/process/block/mbsSelectionSession_test.go index 10f29d5aed8..8192434e7f2 100644 --- a/process/block/mbsSelectionSession_test.go +++ b/process/block/mbsSelectionSession_test.go @@ -47,15 +47,26 @@ func TestMiniBlockSelectionSession_ResetSelectionSession(t *testing.T) { t.Parallel() session := createDummyFilledSession() + + require.NotEmpty(t, session.GetMiniBlocks()) + require.NotEmpty(t, session.GetMiniBlockHeaderHandlers()) + require.NotEmpty(t, session.GetMiniBlockHashes()) + require.NotEmpty(t, session.GetReferencedHeaderHashes()) + require.NotEmpty(t, session.GetReferencedHeaders()) + require.NotEmpty(t, session.referenceHeaderHashesUnique) + require.NotEmpty(t, session.miniBlockHashesUnique) + require.NotNil(t, session.GetLastHeader()) + session.ResetSelectionSession() require.Empty(t, session.GetMiniBlocks()) require.Empty(t, session.GetMiniBlockHeaderHandlers()) require.Empty(t, session.GetMiniBlockHashes()) - require.Empty(t, session.GetReferencedMetaBlockHashes()) - require.Empty(t, session.GetReferencedMetaBlocks()) - require.Nil(t, session.GetLastMetaBlock()) - require.Equal(t, uint64(0), session.GetGasProvided()) + require.Empty(t, session.GetReferencedHeaderHashes()) + require.Empty(t, session.GetReferencedHeaders()) + require.Empty(t, session.referenceHeaderHashesUnique) + require.Empty(t, session.miniBlockHashesUnique) + require.Nil(t, session.GetLastHeader()) require.Equal(t, uint32(0), session.GetNumTxsAdded()) } @@ -67,10 +78,9 @@ func TestMiniBlockSelectionSession_Getters(t *testing.T) { require.Len(t, session.GetMiniBlockHeaderHandlers(), 1) require.Len(t, session.GetMiniBlocks(), 1) require.Len(t, session.GetMiniBlockHashes(), 1) - require.Len(t, session.GetReferencedMetaBlockHashes(), 1) - require.Len(t, session.GetReferencedMetaBlocks(), 1) - require.NotNil(t, session.GetLastMetaBlock()) - require.Equal(t, uint64(100), session.GetGasProvided()) + require.Len(t, session.GetReferencedHeaderHashes(), 1) + require.Len(t, session.GetReferencedHeaders(), 1) + require.NotNil(t, session.GetLastHeader()) require.Equal(t, uint32(2), session.GetNumTxsAdded()) } @@ -89,6 +99,27 @@ func TestMiniBlockSelectionSession_AddMiniBlocksAndHashes(t *testing.T) { require.Empty(t, session.GetMiniBlocks()) require.Empty(t, session.GetMiniBlockHashes()) }) + + t.Run("should not add same mini block twice", func(t *testing.T) { + t.Parallel() + + session, _ := NewMiniBlocksSelectionSession(1, marshaller, hasher) + err := session.AddMiniBlocksAndHashes([]block.MiniblockAndHash{ + {Miniblock: &block.MiniBlock{}, Hash: []byte("hash1")}, + {Miniblock: &block.MiniBlock{}, Hash: []byte("hash2")}, + {Miniblock: &block.MiniBlock{}, Hash: []byte("hash3")}, + }) + + require.NoError(t, err) + require.Equal(t, 3, len(session.GetMiniBlocks())) + require.Equal(t, 3, len(session.GetMiniBlockHashes())) + + err = session.AddMiniBlocksAndHashes([]block.MiniblockAndHash{{Miniblock: &block.MiniBlock{}, Hash: []byte("hash1")}}) + + require.NoError(t, err) + require.Equal(t, 3, len(session.GetMiniBlocks())) + }) + t.Run("should add mini blocks and hashes successfully", func(t *testing.T) { t.Parallel() @@ -210,31 +241,49 @@ func TestMiniBlocksSelectionSession_AddReferencedMetaBlock(t *testing.T) { session, _ := NewMiniBlocksSelectionSession(1, marshaller, hasher) metaBlock := &block.MetaBlock{Epoch: 1, Round: 1} metaBlockHash := []byte("metaHash") - session.AddReferencedMetaBlock(metaBlock, metaBlockHash) + session.AddReferencedHeader(metaBlock, metaBlockHash) - require.Len(t, session.GetReferencedMetaBlocks(), 1) - require.Len(t, session.GetReferencedMetaBlockHashes(), 1) - require.Equal(t, metaBlock, session.GetReferencedMetaBlocks()[0]) - require.Equal(t, metaBlockHash, session.GetReferencedMetaBlockHashes()[0]) + require.Len(t, session.GetReferencedHeaders(), 1) + require.Len(t, session.GetReferencedHeaderHashes(), 1) + require.Equal(t, metaBlock, session.GetReferencedHeaders()[0]) + require.Equal(t, metaBlockHash, session.GetReferencedHeaderHashes()[0]) }) t.Run("should not add nil meta block", func(t *testing.T) { t.Parallel() session, _ := NewMiniBlocksSelectionSession(1, marshaller, hasher) - session.AddReferencedMetaBlock(nil, []byte("metaHash")) + session.AddReferencedHeader(nil, []byte("metaHash")) - require.Empty(t, session.GetReferencedMetaBlocks()) - require.Empty(t, session.GetReferencedMetaBlockHashes()) + require.Empty(t, session.GetReferencedHeaders()) + require.Empty(t, session.GetReferencedHeaderHashes()) }) t.Run("should not add empty meta block hash", func(t *testing.T) { t.Parallel() session, _ := NewMiniBlocksSelectionSession(1, marshaller, hasher) metaBlock := &block.MetaBlock{Epoch: 1, Round: 1} - session.AddReferencedMetaBlock(metaBlock, nil) + session.AddReferencedHeader(metaBlock, nil) + + require.Empty(t, session.GetReferencedHeaders()) + require.Empty(t, session.GetReferencedHeaderHashes()) + }) + t.Run("should not add same reference header twice", func(t *testing.T) { + session, _ := NewMiniBlocksSelectionSession(1, marshaller, hasher) + metaBlock := &block.MetaBlock{Epoch: 1, Round: 1} + metaBlockHash := []byte("metaHash") + session.AddReferencedHeader(metaBlock, metaBlockHash) + + require.Len(t, session.GetReferencedHeaders(), 1) + require.Len(t, session.GetReferencedHeaderHashes(), 1) + require.Equal(t, metaBlock, session.GetReferencedHeaders()[0]) + require.Equal(t, metaBlockHash, session.GetReferencedHeaderHashes()[0]) + + session.AddReferencedHeader(metaBlock, metaBlockHash) - require.Empty(t, session.GetReferencedMetaBlocks()) - require.Empty(t, session.GetReferencedMetaBlockHashes()) + require.Len(t, session.GetReferencedHeaders(), 1) + require.Len(t, session.GetReferencedHeaderHashes(), 1) + require.Equal(t, metaBlock, session.GetReferencedHeaders()[0]) + require.Equal(t, metaBlockHash, session.GetReferencedHeaderHashes()[0]) }) } @@ -276,11 +325,12 @@ func createDummyFilledSession() *miniBlocksSelectionSession { // Add dummy mini blocks and hashes session.miniBlocks = append(session.miniBlocks, miniBlock) session.miniBlockHashes = append(session.miniBlockHashes, miniBlockHash) + session.miniBlockHashesUnique[string(miniBlockHash)] = struct{}{} + session.referenceHeaderHashesUnique[string(metaBlockHash)] = struct{}{} session.miniBlockHeaderHandlers = append(session.miniBlockHeaderHandlers, miniBlockHeader) - session.referencedMetaBlockHashes = append(session.referencedMetaBlockHashes, metaBlockHash) - session.referencedMetaBlocks = append(session.referencedMetaBlocks, metaBlock) - session.lastMetaBlock = metaBlock - session.gasProvided = 100 + session.referencedHeaderHashes = append(session.referencedHeaderHashes, metaBlockHash) + session.referencedHeader = append(session.referencedHeader, metaBlock) + session.lastHeader = metaBlock session.numTxsAdded = 2 return session diff --git a/process/block/metablock.go b/process/block/metablock.go index 1528b410df4..e24236b8bdf 100644 --- a/process/block/metablock.go +++ b/process/block/metablock.go @@ -32,6 +32,14 @@ const firstHeaderNonce = uint64(1) var _ process.BlockProcessor = (*metaProcessor)(nil) // metaProcessor implements metaProcessor interface, and actually it tries to execute block + +// ShardHeaderInfo holds info about a shard header +type ShardHeaderInfo struct { + Header data.HeaderHandler + Hash []byte + UsedInBlock bool +} + type metaProcessor struct { *baseProcessor scToProtocol process.SmartContractToProtocolHandler @@ -45,6 +53,10 @@ type metaProcessor struct { shardsHeadersNonce *sync.Map shardBlockFinality uint32 headersCounter *headersCounter + // TODO: fill this up on execution of epoch start + epochStartData *block.EpochStart + // TODO: initialize this + shardInfoCreateData process.ShardInfoCreator } // NewMetaProcessor creates a new metaProcessor object @@ -80,7 +92,9 @@ func NewMetaProcessor(arguments ArgMetaProcessor) (*metaProcessor, error) { if check.IfNil(arguments.ReceiptsRepository) { return nil, process.ErrNilReceiptsRepository } - + if check.IfNil(arguments.ShardInfoCreator) { + return nil, process.ErrNilShardInfoCreator + } mp := metaProcessor{ baseProcessor: base, headersCounter: NewHeaderCounter(), @@ -92,6 +106,7 @@ func NewMetaProcessor(arguments ArgMetaProcessor) (*metaProcessor, error) { validatorStatisticsProcessor: arguments.ValidatorStatisticsProcessor, validatorInfoCreator: arguments.EpochValidatorInfoCreator, epochSystemSCProcessor: arguments.EpochSystemSCProcessor, + shardInfoCreateData: arguments.ShardInfoCreator, } argsTransactionCounter := ArgsTransactionCounter{ @@ -319,15 +334,6 @@ func (mp *metaProcessor) ProcessBlock( return nil } -// TODO: move this to the ProcessBlockProposal for meta chain -func (mp *metaProcessor) updateEpochStartTrigger(round, nonce uint64) { - if common.IsAsyncExecutionEnabled(mp.enableEpochsHandler, mp.enableRoundsHandler) { - mp.epochStartTrigger.UpdateRound(round) - } else { - mp.epochStartTrigger.Update(round, nonce) - } -} - func (mp *metaProcessor) processEpochStartMetaBlock( header *block.MetaBlock, body *block.Body, @@ -997,7 +1003,7 @@ func (mp *metaProcessor) createAndProcessCrossMiniBlocksDstMe( "num shard headers", len(orderedHdrs), ) - lastShardHdr, err := mp.getLastCrossNotarizedShardHdrs() + lastShardHdr, err := mp.getLastCrossNotarizedShardHdrsAndAddToCurrentBlock() if err != nil { return nil, 0, 0, err } @@ -1718,8 +1724,23 @@ func (mp *metaProcessor) saveLastNotarizedHeader(header *block.MetaBlock) error return nil } -func (mp *metaProcessor) getLastCrossNotarizedShardHdrs() (map[uint32]data.HeaderHandler, error) { - lastCrossNotarizedHeader := make(map[uint32]data.HeaderHandler, mp.shardCoordinator.NumberOfShards()) +func (mp *metaProcessor) getLastCrossNotarizedShardHdrsAndAddToCurrentBlock() (map[uint32]data.HeaderHandler, error) { + lastCrossNotarizedHeader, err := mp.getLastCrossNotarizedShardHeaders() + if err != nil { + return nil, err + } + mp.addHeaders(lastCrossNotarizedHeader) + + headers := make(map[uint32]data.HeaderHandler, len(lastCrossNotarizedHeader)) + for k, v := range lastCrossNotarizedHeader { + headers[k] = v.Header + } + + return headers, nil +} + +func (mp *metaProcessor) getLastCrossNotarizedShardHeaders() (map[uint32]ShardHeaderInfo, error) { + lastCrossNotarizedHeader := make(map[uint32]ShardHeaderInfo, mp.shardCoordinator.NumberOfShards()) for shardID := uint32(0); shardID < mp.shardCoordinator.NumberOfShards(); shardID++ { lastCrossNotarizedHeaderForShard, hash, err := mp.blockTracker.GetLastCrossNotarizedHeader(shardID) if err != nil { @@ -1727,15 +1748,27 @@ func (mp *metaProcessor) getLastCrossNotarizedShardHdrs() (map[uint32]data.Heade } log.Debug("lastCrossNotarizedHeader for shard", "shardID", shardID, "hash", hash) - lastCrossNotarizedHeader[shardID] = lastCrossNotarizedHeaderForShard usedInBlock := mp.isGenesisShardBlockAndFirstMeta(lastCrossNotarizedHeaderForShard.GetNonce()) - - mp.addHeader(hash, lastCrossNotarizedHeaderForShard, usedInBlock) + lastCrossNotarizedHeader[shardID] = ShardHeaderInfo{ + Header: lastCrossNotarizedHeaderForShard, + Hash: hash, + UsedInBlock: usedInBlock, + } } return lastCrossNotarizedHeader, nil } +func (mp *metaProcessor) addHeaders(shardHeadersInfo map[uint32]ShardHeaderInfo) { + for shardID := uint32(0); shardID < mp.shardCoordinator.NumberOfShards(); shardID++ { + shardHdrInfo, ok := shardHeadersInfo[shardID] + if !ok { + continue + } + mp.addHeader(shardHdrInfo.Hash, shardHdrInfo.Header, shardHdrInfo.UsedInBlock) + } +} + func (mp *metaProcessor) addHeader(hash []byte, header data.HeaderHandler, usedInBlock bool) { if usedInBlock { mp.hdrsForCurrBlock.AddHeaderUsedInBlock(string(hash), header) @@ -1748,7 +1781,7 @@ func (mp *metaProcessor) addHeader(hash []byte, header data.HeaderHandler, usedI // check if shard headers were signed and constructed correctly and returns headers which has to be // checked for finality func (mp *metaProcessor) checkShardHeadersValidity(metaHdr *block.MetaBlock) (map[uint32]data.HeaderHandler, error) { - lastCrossNotarizedHeader, err := mp.getLastCrossNotarizedShardHdrs() + lastCrossNotarizedHeader, err := mp.getLastCrossNotarizedShardHdrsAndAddToCurrentBlock() if err != nil { return nil, err } @@ -1928,13 +1961,15 @@ func (mp *metaProcessor) checkShardHeaderFinalityBasedOnProofs(shardHdr data.Hea return true, nil } -func (mp *metaProcessor) createShardInfo() ([]data.ShardDataHandler, error) { +func (mp *metaProcessor) createShardInfo(metaHdr data.MetaHeaderHandler) ([]data.ShardDataHandler, error) { var shardInfo []data.ShardDataHandler - if mp.epochStartTrigger.IsEpochStart() { + if metaHdr.IsStartOfEpochBlock() { return shardInfo, nil } hdrHashAndInfo := mp.hdrsForCurrBlock.GetHeadersInfoMap() + headers := make([]data.ShardHeaderHandler, 0, len(hdrHashAndInfo)) + headerHashes := make([][]byte, 0, len(hdrHashAndInfo)) for hdrHash, headerInfo := range hdrHashAndInfo { if !headerInfo.UsedInBlock() { continue @@ -1952,52 +1987,16 @@ func (mp *metaProcessor) createShardInfo() ([]data.ShardDataHandler, error) { return nil, process.ErrWrongTypeAssertion } - shardData := block.ShardData{} - shardData.TxCount = shardHdr.GetTxCount() - shardData.ShardID = shardHdr.GetShardID() - shardData.HeaderHash = []byte(hdrHash) - shardData.Round = shardHdr.GetRound() - shardData.PrevHash = shardHdr.GetPrevHash() - shardData.Nonce = shardHdr.GetNonce() - shardData.PrevRandSeed = shardHdr.GetPrevRandSeed() - shardData.PubKeysBitmap = shardHdr.GetPubKeysBitmap() - if mp.enableEpochsHandler.IsFlagEnabledInEpoch(common.AndromedaFlag, shardHdr.GetEpoch()) { - shardData.Epoch = shardHdr.GetEpoch() - } - shardData.NumPendingMiniBlocks = uint32(len(mp.pendingMiniBlocksHandler.GetPendingMiniBlocks(shardData.ShardID))) - header, _, err := mp.blockTracker.GetLastSelfNotarizedHeader(shardHdr.GetShardID()) - if err != nil { - return nil, err - } - shardData.LastIncludedMetaNonce = header.GetNonce() - shardData.AccumulatedFees = shardHdr.GetAccumulatedFees() - shardData.DeveloperFees = shardHdr.GetDeveloperFees() - - for i := 0; i < len(shardHdr.GetMiniBlockHeaderHandlers()); i++ { - if mp.enableEpochsHandler.IsFlagEnabled(common.ScheduledMiniBlocksFlag) { - miniBlockHeader := shardHdr.GetMiniBlockHeaderHandlers()[i] - if !miniBlockHeader.IsFinal() { - log.Debug("metaProcessor.createShardInfo: do not create shard data with mini block which is not final", "mb hash", miniBlockHeader.GetHash()) - continue - } - } - - shardMiniBlockHeader := block.MiniBlockHeader{} - shardMiniBlockHeader.SenderShardID = shardHdr.GetMiniBlockHeaderHandlers()[i].GetSenderShardID() - shardMiniBlockHeader.ReceiverShardID = shardHdr.GetMiniBlockHeaderHandlers()[i].GetReceiverShardID() - shardMiniBlockHeader.Hash = shardHdr.GetMiniBlockHeaderHandlers()[i].GetHash() - shardMiniBlockHeader.TxCount = shardHdr.GetMiniBlockHeaderHandlers()[i].GetTxCount() - shardMiniBlockHeader.Type = block.Type(shardHdr.GetMiniBlockHeaderHandlers()[i].GetTypeInt32()) - - shardData.ShardMiniBlockHeaders = append(shardData.ShardMiniBlockHeaders, shardMiniBlockHeader) - } + headers = append(headers, shardHdr) + headerHashes = append(headerHashes, []byte(hdrHash)) + } - shardInfo = append(shardInfo, &shardData) + shardInfo, err := mp.shardInfoCreateData.CreateShardInfoFromLegacyMeta(metaHdr, headers, headerHashes) + if err != nil { + return nil, err } - log.Debug("created shard data", - "size", len(shardInfo), - ) + log.Debug("created shard data", "size", len(shardInfo)) return shardInfo, nil } @@ -2083,7 +2082,7 @@ func (mp *metaProcessor) applyBodyToHeader(metaHdr data.MetaHeaderHandler, bodyH }() sw.Start("createShardInfo") - shardInfo, err := mp.createShardInfo() + shardInfo, err := mp.createShardInfo(metaHdr) sw.Stop("createShardInfo") if err != nil { return nil, err @@ -2228,9 +2227,7 @@ func (mp *metaProcessor) verifyValidatorStatisticsRootHash(header *block.MetaBlo // CreateNewHeader creates a new header func (mp *metaProcessor) CreateNewHeader(round uint64, nonce uint64) (data.HeaderHandler, error) { - mp.updateEpochStartTrigger(round, nonce) - - epochChangeProposed := mp.epochStartTrigger.ShouldProposeEpochChange(round, nonce) + mp.epochStartTrigger.Update(round, nonce) epoch := mp.epochStartTrigger.Epoch() header := mp.versionedHeaderFactory.Create(epoch, round) @@ -2240,7 +2237,7 @@ func (mp *metaProcessor) CreateNewHeader(round uint64, nonce uint64) (data.Heade } if metaHeader.IsHeaderV3() { - metaHeader.SetEpochChangeProposed(epochChangeProposed) + return nil, process.ErrInvalidHeader } err := metaHeader.SetRound(round) @@ -2301,9 +2298,6 @@ func (mp *metaProcessor) setHeaderVersionData(metaHeader data.MetaHeaderHandler) if check.IfNil(metaHeader) { return process.ErrNilHeaderHandler } - if metaHeader.IsHeaderV3() { - return nil - } rootHash, err := mp.accountsDB[state.UserAccountsState].RootHash() if err != nil { diff --git a/process/block/metablockProposal.go b/process/block/metablockProposal.go index e2787e0f132..07fd3b2f2bb 100644 --- a/process/block/metablockProposal.go +++ b/process/block/metablockProposal.go @@ -3,9 +3,187 @@ package block import ( "time" + "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" + logger "github.com/multiversx/mx-chain-logger-go" + + "github.com/multiversx/mx-chain-go/common" + "github.com/multiversx/mx-chain-go/process" ) +// CreateNewHeaderProposal creates a new header +func (mp *metaProcessor) CreateNewHeaderProposal(round uint64, nonce uint64) (data.HeaderHandler, error) { + // TODO: the trigger would need to be changed upon commit of a block with the epoch start results + epoch := mp.epochStartTrigger.Epoch() + + header := mp.versionedHeaderFactory.Create(epoch, round) + metaHeader, ok := header.(data.MetaHeaderHandler) + if !ok { + return nil, process.ErrWrongTypeAssertion + } + + if !metaHeader.IsHeaderV3() { + return nil, process.ErrInvalidHeader + } + + epochChangeProposed := mp.epochStartTrigger.ShouldProposeEpochChange(round, nonce) + metaHeader.SetEpochChangeProposed(epochChangeProposed) + err := metaHeader.SetRound(round) + if err != nil { + return nil, err + } + + err = metaHeader.SetNonce(nonce) + if err != nil { + return nil, err + } + + err = mp.addExecutionResultsOnHeader(metaHeader) + if err != nil { + return nil, err + } + + hasEpochStartResults, err := mp.hasStartOfEpochExecutionResults(metaHeader) + if err != nil { + return nil, err + } + if !hasEpochStartResults { + return metaHeader, nil + } + + err = metaHeader.SetEpoch(epoch + 1) + if err != nil { + return nil, err + } + if mp.epochStartData == nil { + return nil, process.ErrNilEpochStartData + } + + // TODO: clean up the epoch start data upon commit + err = metaHeader.SetEpochStartHandler(mp.epochStartData) + if err != nil { + return nil, err + } + + return metaHeader, nil +} + +// CreateBlockProposal creates a block proposal without executing any of the transactions +func (mp *metaProcessor) CreateBlockProposal( + initialHdr data.HeaderHandler, + haveTime func() bool, +) (data.HeaderHandler, data.BodyHandler, error) { + if check.IfNil(initialHdr) { + return nil, nil, process.ErrNilBlockHeader + } + if !initialHdr.IsHeaderV3() { + return nil, nil, process.ErrInvalidHeader + } + + metaHdr, ok := initialHdr.(*block.MetaBlockV3) + if !ok { + return nil, nil, process.ErrWrongTypeAssertion + } + + metaHdr.SoftwareVersion = []byte(mp.headerIntegrityVerifier.GetVersion(metaHdr.Epoch, metaHdr.Round)) + + if metaHdr.IsStartOfEpochBlock() || metaHdr.GetEpochChangeProposed() { + // no new transactions in start of epoch block + // to simplify bootstrapping + return metaHdr, &block.Body{}, nil + } + + mp.gasComputation.Reset() + mp.miniBlocksSelectionSession.ResetSelectionSession() + err := mp.createBlockBodyProposal(metaHdr, haveTime) + if err != nil { + return nil, nil, err + } + + mbsToMe := mp.miniBlocksSelectionSession.GetMiniBlocks() + miniBlocksHeadersToMe := mp.miniBlocksSelectionSession.GetMiniBlockHeaderHandlers() + numTxs := mp.miniBlocksSelectionSession.GetNumTxsAdded() + referencedShardHeaderHashes := mp.miniBlocksSelectionSession.GetReferencedHeaderHashes() + referencedShardHeaders := mp.miniBlocksSelectionSession.GetReferencedHeaders() + body := &block.Body{ + MiniBlocks: mbsToMe, + } + + if len(mbsToMe) > 0 { + log.Debug("created miniblocks with txs with destination in self shard", + "num miniblocks", len(mbsToMe), + "num txs proposed", numTxs, + "num shard headers", len(referencedShardHeaderHashes), + ) + } + + defer func() { + go mp.checkAndRequestIfShardHeadersMissing() + }() + + // TODO: referenced shard headers should be also notarized, not only the headers corresponding to the execution results + // this will be needed for metachain to keep track of the already processed headers. + shardDataProposalHandlers, shardDataHandlers, err := mp.shardInfoCreateData.CreateShardInfoV3(metaHdr, referencedShardHeaders, referencedShardHeaderHashes) + if err != nil { + return nil, nil, err + } + + err = metaHdr.SetShardInfoHandlers(shardDataHandlers) + if err != nil { + return nil, nil, err + } + + err = metaHdr.SetShardInfoProposalHandlers(shardDataProposalHandlers) + if err != nil { + return nil, nil, err + } + + err = metaHdr.SetMiniBlockHeaderHandlers(miniBlocksHeadersToMe) + if err != nil { + return nil, nil, err + } + + txsInExecutionResults, err := getTxCountExecutionResults(metaHdr) + if err != nil { + return nil, nil, err + } + + totalProcessedTxs := getTxCount(shardDataHandlers) + txsInExecutionResults + // TODO: consider if tx count per metablock header is still needed + // as we still have it in the execution results + err = metaHdr.SetTxCount(totalProcessedTxs) + if err != nil { + return nil, nil, err + } + + marshalizedBody, err := mp.marshalizer.Marshal(body) + if err != nil { + return nil, nil, err + } + mp.blockSizeThrottler.Add(metaHdr.GetRound(), uint32(len(marshalizedBody))) + + return metaHdr, body, nil +} + +func getTxCountExecutionResults(metaHeader data.MetaHeaderHandler) (uint32, error) { + if check.IfNil(metaHeader) { + return 0, nil + } + + totalTxs := uint64(0) + execResults := metaHeader.GetExecutionResultsHandlers() + for _, execResult := range execResults { + execResultsMeta, ok := execResult.(data.MetaExecutionResultHandler) + if !ok { + return 0, process.ErrWrongTypeAssertion + } + totalTxs += execResultsMeta.GetExecutedTxCount() + } + return uint32(totalTxs), nil +} + // VerifyBlockProposal will be implemented in a further PR func (mp *metaProcessor) VerifyBlockProposal( _ data.HeaderHandler, @@ -14,3 +192,216 @@ func (mp *metaProcessor) VerifyBlockProposal( ) error { return nil } + +// ProcessBlockProposal processes the proposed block. It returns nil if all ok or the specific error +func (mp *metaProcessor) ProcessBlockProposal( + headerHandler data.HeaderHandler, + bodyHandler data.BodyHandler, +) (data.BaseExecutionResultHandler, error) { + return nil, nil +} + +func (mp *metaProcessor) hasStartOfEpochExecutionResults(metaHeader data.MetaHeaderHandler) (bool, error) { + if check.IfNil(metaHeader) { + return false, process.ErrNilHeaderHandler + } + execResults := metaHeader.GetExecutionResultsHandlers() + for _, execResult := range execResults { + mbHeaders, err := common.GetMiniBlocksHeaderHandlersFromExecResult(execResult) + if err != nil { + return false, err + } + if hasRewardOrPeerMiniBlocksFromMeta(mbHeaders) { + return true, nil + } + } + return false, nil +} + +func hasRewardOrPeerMiniBlocksFromMeta(miniBlockHeaders []data.MiniBlockHeaderHandler) bool { + for _, mbHeader := range miniBlockHeaders { + if mbHeader.GetSenderShardID() != common.MetachainShardId { + continue + } + if mbHeader.GetTypeInt32() == int32(block.RewardsBlock) || + mbHeader.GetTypeInt32() == int32(block.PeerBlock) { + return true + } + } + return false +} + +func (mp *metaProcessor) createBlockBodyProposal( + metaHdr data.MetaHeaderHandler, + haveTime func() bool, +) error { + mp.blockSizeThrottler.ComputeCurrentMaxSize() + + log.Debug("started creating block body", + "epoch", metaHdr.GetEpoch(), + "round", metaHdr.GetRound(), + "nonce", metaHdr.GetNonce(), + ) + + return mp.createProposalMiniBlocks(haveTime) +} + +func (mp *metaProcessor) createProposalMiniBlocks(haveTime func() bool) error { + if !haveTime() { + log.Debug("metaProcessor.createProposalMiniBlocks", "error", process.ErrTimeIsOut) + return nil + } + + startTime := time.Now() + err := mp.selectIncomingMiniBlocksForProposal(haveTime) + if err != nil { + return err + } + elapsedTime := time.Since(startTime) + log.Debug("elapsed time to create mbs to me", "time", elapsedTime) + + return nil +} + +func (mp *metaProcessor) selectIncomingMiniBlocksForProposal( + haveTime func() bool, +) error { + sw := core.NewStopWatch() + sw.Start("ComputeLongestShardsChainsFromLastNotarized") + orderedHdrs, orderedHdrsHashes, _, err := mp.blockTracker.ComputeLongestShardsChainsFromLastNotarized() + sw.Stop("ComputeLongestShardsChainsFromLastNotarized") + log.Debug("measurements ComputeLongestShardsChainsFromLastNotarized", sw.GetMeasurements()...) + if err != nil { + return err + } + + log.Debug("shard headers ordered", + "num shard headers", len(orderedHdrs), + ) + + lastShardHdr, err := mp.getLastCrossNotarizedShardHeaders() + if err != nil { + return err + } + + maxShardHeadersFromSameShard := core.MaxUint32( + process.MinShardHeadersFromSameShardInOneMetaBlock, + process.MaxShardHeadersAllowedInOneMetaBlock/mp.shardCoordinator.NumberOfShards(), + ) + err = mp.selectIncomingMiniBlocks(lastShardHdr, orderedHdrs, orderedHdrsHashes, maxShardHeadersFromSameShard, haveTime) + if err != nil { + return err + } + + return nil +} + +func (mp *metaProcessor) selectIncomingMiniBlocks( + lastShardHdr map[uint32]ShardHeaderInfo, + orderedHdrs []data.HeaderHandler, + orderedHdrsHashes [][]byte, + maxShardHeadersFromSameShard uint32, + haveTime func() bool, +) error { + hdrsAdded := uint32(0) + maxShardHeadersAllowedInOneMetaBlock := maxShardHeadersFromSameShard * mp.shardCoordinator.NumberOfShards() + hdrsAddedForShard := make(map[uint32]uint32) + var err error + + if len(orderedHdrs) != len(orderedHdrsHashes) { + return process.ErrInconsistentShardHeadersAndHashes + } + + for i := 0; i < len(orderedHdrs); i++ { + if !haveTime() { + log.Debug("time is up after putting cross txs with destination to metachain", + "num txs", mp.miniBlocksSelectionSession.GetNumTxsAdded(), + ) + break + } + + if hdrsAdded >= maxShardHeadersAllowedInOneMetaBlock { + log.Debug("maximum shard headers allowed to be included in one meta block has been reached", + "shard headers added", hdrsAdded, + ) + break + } + + currHdr := orderedHdrs[i] + currHdrHash := orderedHdrsHashes[i] + lastShardHeaderInfo, ok := lastShardHdr[currHdr.GetShardID()] + if !ok { + return process.ErrMissingHeader + } + if currHdr.GetNonce() != lastShardHeaderInfo.Header.GetNonce()+1 { + log.Trace("skip searching", + "shard", currHdr.GetShardID(), + "last shard hdr nonce", lastShardHeaderInfo.Header.GetNonce(), + "curr shard hdr nonce", currHdr.GetNonce()) + continue + } + + if hdrsAddedForShard[currHdr.GetShardID()] >= maxShardHeadersFromSameShard { + log.Trace("maximum shard headers from same shard allowed to be included in one meta block has been reached", + "shard", currHdr.GetShardID(), + "shard headers added", hdrsAddedForShard[currHdr.GetShardID()], + ) + continue + } + + hasProofForHdr := mp.proofsPool.HasProof(currHdr.GetShardID(), currHdrHash) + if !hasProofForHdr { + log.Trace("no proof for shard header", + "shard", currHdr.GetShardID(), + "hash", logger.DisplayByteSlice(currHdrHash), + ) + continue + } + + if len(currHdr.GetMiniBlockHeadersWithDst(mp.shardCoordinator.SelfId())) == 0 { + mp.miniBlocksSelectionSession.AddReferencedHeader(currHdr, currHdrHash) + lastShardHdr[currHdr.GetShardID()] = ShardHeaderInfo{ + Header: currHdr, + Hash: currHdrHash, + UsedInBlock: true, + } + hdrsAddedForShard[currHdr.GetShardID()]++ + hdrsAdded++ + continue + } + + createIncomingMbsResult, errCreated := mp.createMbsCrossShardDstMe(currHdrHash, currHdr, nil) + if errCreated != nil { + return errCreated + } + if !createIncomingMbsResult.HeaderFinished { + mp.revertGasForCrossShardDstMeMiniBlocks(createIncomingMbsResult.AddedMiniBlocks, createIncomingMbsResult.PendingMiniBlocks) + log.Debug("shard header cannot be fully added", + "round", currHdr.GetRound(), + "nonce", currHdr.GetNonce(), + "hash", currHdrHash) + break + } + + if len(createIncomingMbsResult.AddedMiniBlocks) > 0 { + err = mp.miniBlocksSelectionSession.AddMiniBlocksAndHashes(createIncomingMbsResult.AddedMiniBlocks) + if err != nil { + return err + } + } + + mp.miniBlocksSelectionSession.AddReferencedHeader(currHdr, currHdrHash) + lastShardHdr[currHdr.GetShardID()] = ShardHeaderInfo{ + Header: currHdr, + Hash: currHdrHash, + UsedInBlock: true, + } + hdrsAddedForShard[currHdr.GetShardID()]++ + hdrsAdded++ + } + + // TODO: wold need to request in advance new shard headers and proofs if last added headers are old (on an old epoch) + // maybe move this logic in processing a block, so it is common for all nodes in shard. + + return nil +} diff --git a/process/block/metablockProposal_test.go b/process/block/metablockProposal_test.go index f3c38aa00fd..799e9224ec3 100644 --- a/process/block/metablockProposal_test.go +++ b/process/block/metablockProposal_test.go @@ -3,12 +3,729 @@ package block_test import ( "testing" + "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/stretchr/testify/require" + "github.com/multiversx/mx-chain-go/common" + retriever "github.com/multiversx/mx-chain-go/dataRetriever" + "github.com/multiversx/mx-chain-go/process" blproc "github.com/multiversx/mx-chain-go/process/block" + "github.com/multiversx/mx-chain-go/process/block/processedMb" + "github.com/multiversx/mx-chain-go/process/mock" + "github.com/multiversx/mx-chain-go/testscommon" + dataRetrieverMock "github.com/multiversx/mx-chain-go/testscommon/dataRetriever" + "github.com/multiversx/mx-chain-go/testscommon/executionTrack" + "github.com/multiversx/mx-chain-go/testscommon/mbSelection" + "github.com/multiversx/mx-chain-go/testscommon/processMocks" ) +func TestMetaProcessor_CreateNewHeaderProposal(t *testing.T) { + t.Parallel() + + defaultBootstrapComponents := &mock.BootstrapComponentsMock{ + Coordinator: mock.NewOneShardCoordinatorMock(), + HdrIntegrityVerifier: &mock.HeaderIntegrityVerifierStub{}, + VersionedHdrFactory: &testscommon.VersionedHeaderFactoryStub{ + CreateCalled: func(epoch uint32, _ uint64) data.HeaderHandler { + return &block.MetaBlock{} + }, + }, + } + + validMetaHeaderV3 := testscommon.HeaderHandlerStub{ + IsHeaderV3Called: func() bool { + return true + }, + } + + prevValidMetaBlockV3 := testscommon.HeaderHandlerStub{ + IsHeaderV3Called: func() bool { + return true + }, + GetLastExecutionResultHandlerCalled: func() data.LastExecutionResultHandler { + return &block.MetaExecutionResultInfo{ + ExecutionResult: &block.BaseMetaExecutionResult{}, + } + }, + } + validMetaExecutionResultsWithEpochChange := []data.BaseExecutionResultHandler{ + &block.MetaExecutionResult{ + ExecutionResult: &block.BaseMetaExecutionResult{}, + MiniBlockHeaders: []block.MiniBlockHeader{ + { + Hash: []byte("mb hash"), + SenderShardID: core.MetachainShardId, + Type: block.RewardsBlock, // this miniBlock marks the epoch start + }, + }, + }, + } + validMetaExecutionResultsWithoutEpochChange := []data.BaseExecutionResultHandler{ + &block.MetaExecutionResult{ + ExecutionResult: &block.BaseMetaExecutionResult{}, + MiniBlockHeaders: []block.MiniBlockHeader{ + { + Hash: []byte("mb hash"), + ReceiverShardID: core.MetachainShardId, + SenderShardID: 0, + Type: block.TxBlock, + }, + }, + }, + } + + t.Run("versioned header factory creates an invalid meta header, should error", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.EpochStartTrigger = &testscommon.EpochStartTriggerStub{ + EpochCalled: func() uint32 { + return 1 + }, + } + bc := *defaultBootstrapComponents + bc.VersionedHdrFactory = &testscommon.VersionedHeaderFactoryStub{ + CreateCalled: func(epoch uint32, _ uint64) data.HeaderHandler { + return &block.Header{} + }, + } + + arguments.BootstrapComponents = &bc + + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + header, err := mp.CreateNewHeaderProposal(1, 1) + require.Nil(t, header) + require.Equal(t, process.ErrWrongTypeAssertion, err) + }) + t.Run("versioned header factory creates a metablock but with version < v3, should error", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.EpochStartTrigger = &testscommon.EpochStartTriggerStub{ + EpochCalled: func() uint32 { + return 1 + }, + } + bc := *defaultBootstrapComponents + bc.VersionedHdrFactory = &testscommon.VersionedHeaderFactoryStub{ + CreateCalled: func(epoch uint32, _ uint64) data.HeaderHandler { + return &block.MetaBlock{} + }, + } + + arguments.BootstrapComponents = &bc + + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + header, err := mp.CreateNewHeaderProposal(1, 1) + require.Nil(t, header) + require.Equal(t, process.ErrInvalidHeader, err) + }) + t.Run("correct meta header version, set round error", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + + bc := *defaultBootstrapComponents + bc.VersionedHdrFactory = &testscommon.VersionedHeaderFactoryStub{ + CreateCalled: func(epoch uint32, _ uint64) data.HeaderHandler { + return &testscommon.HeaderHandlerStub{ + IsHeaderV3Called: func() bool { + return true + }, + SetRoundCalled: func(_ uint64) error { + return expectedErr + }, + } + }, + } + + arguments.BootstrapComponents = &bc + + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + header, err := mp.CreateNewHeaderProposal(1, 1) + require.Nil(t, header) + require.Equal(t, expectedErr, err) + }) + t.Run("correct meta header version, set nonce error", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + + bc := *defaultBootstrapComponents + versionedHeader := validMetaHeaderV3 + versionedHeader.SetNonceCalled = func(_ uint64) error { + return expectedErr + } + bc.VersionedHdrFactory = &testscommon.VersionedHeaderFactoryStub{ + CreateCalled: func(epoch uint32, _ uint64) data.HeaderHandler { + return &versionedHeader + }, + } + + arguments.BootstrapComponents = &bc + + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + header, err := mp.CreateNewHeaderProposal(1, 1) + require.Nil(t, header) + require.Equal(t, expectedErr, err) + }) + t.Run("correct meta header version, add execution result error", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.ExecutionResultsTracker = &executionTrack.ExecutionResultsTrackerStub{ + GetPendingExecutionResultsCalled: func() ([]data.BaseExecutionResultHandler, error) { + return nil, expectedErr + }, + } + bc := *defaultBootstrapComponents + bc.VersionedHdrFactory = &testscommon.VersionedHeaderFactoryStub{ + CreateCalled: func(epoch uint32, _ uint64) data.HeaderHandler { + return &validMetaHeaderV3 + }, + } + + arguments.BootstrapComponents = &bc + + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + header, err := mp.CreateNewHeaderProposal(1, 1) + require.Nil(t, header) + require.Equal(t, expectedErr, err) + }) + t.Run("error checking epoch start data in execution results, should error", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.ExecutionResultsTracker = &executionTrack.ExecutionResultsTrackerStub{ + GetPendingExecutionResultsCalled: func() ([]data.BaseExecutionResultHandler, error) { + return nil, nil + }, + } + + metaBlockWithInvalidExecutionResult := validMetaHeaderV3 + metaBlockWithInvalidExecutionResult.GetExecutionResultsHandlersCalled = func() []data.BaseExecutionResultHandler { + return []data.BaseExecutionResultHandler{ + &block.BaseExecutionResult{}, // invalid for meta block + } + } + + bc := *defaultBootstrapComponents + bc.VersionedHdrFactory = &testscommon.VersionedHeaderFactoryStub{ + CreateCalled: func(epoch uint32, _ uint64) data.HeaderHandler { + return &metaBlockWithInvalidExecutionResult + }, + } + + arguments.BootstrapComponents = &bc + dataComponentsModified := *dataComponents + dataComponentsModified.BlockChain = &testscommon.ChainHandlerStub{ + GetCurrentBlockHeaderCalled: func() data.HeaderHandler { + return &prevValidMetaBlockV3 + }, + GetCurrentBlockHeaderHashCalled: func() []byte { + return []byte("prev header hash") + }, + } + arguments.DataComponents = &dataComponentsModified + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + header, err := mp.CreateNewHeaderProposal(1, 1) + require.Nil(t, header) + require.Equal(t, process.ErrWrongTypeAssertion, err) + }) + t.Run("with epoch start data in execution results, but missing epoch start data in meta block processor", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.ExecutionResultsTracker = &executionTrack.ExecutionResultsTrackerStub{ + GetPendingExecutionResultsCalled: func() ([]data.BaseExecutionResultHandler, error) { + return nil, nil + }, + } + + metaBlockWithValidExecutionResult := validMetaHeaderV3 + metaBlockWithValidExecutionResult.GetExecutionResultsHandlersCalled = func() []data.BaseExecutionResultHandler { + return []data.BaseExecutionResultHandler{ + &block.MetaExecutionResult{ + ExecutionResult: &block.BaseMetaExecutionResult{}, + MiniBlockHeaders: []block.MiniBlockHeader{ + { + Hash: []byte("mb hash"), + SenderShardID: core.MetachainShardId, + Type: block.RewardsBlock, // this miniBlock marks the epoch start + }, + }, + }, + } + } + + bc := *defaultBootstrapComponents + bc.VersionedHdrFactory = &testscommon.VersionedHeaderFactoryStub{ + CreateCalled: func(epoch uint32, _ uint64) data.HeaderHandler { + return &metaBlockWithValidExecutionResult + }, + } + + arguments.BootstrapComponents = &bc + dataComponentsModified := *dataComponents + dataComponentsModified.BlockChain = &testscommon.ChainHandlerStub{ + GetCurrentBlockHeaderCalled: func() data.HeaderHandler { + return &prevValidMetaBlockV3 + }, + GetCurrentBlockHeaderHashCalled: func() []byte { + return []byte("prev header hash") + }, + } + arguments.DataComponents = &dataComponentsModified + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + header, err := mp.CreateNewHeaderProposal(1, 1) + require.Equal(t, process.ErrNilEpochStartData, err) + require.Nil(t, header) + }) + t.Run("with epoch start data in execution results and in meta block processor, error on set epoch", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.ExecutionResultsTracker = &executionTrack.ExecutionResultsTrackerStub{ + GetPendingExecutionResultsCalled: func() ([]data.BaseExecutionResultHandler, error) { + return nil, nil + }, + } + + metaBlockWithValidExecutionResult := validMetaHeaderV3 + metaBlockWithValidExecutionResult.GetExecutionResultsHandlersCalled = func() []data.BaseExecutionResultHandler { + return validMetaExecutionResultsWithEpochChange + } + metaBlockWithValidExecutionResult.SetEpochCalled = func(epoch uint32) error { + require.Equal(t, uint32(1), epoch) + return expectedErr + } + + bc := *defaultBootstrapComponents + bc.VersionedHdrFactory = &testscommon.VersionedHeaderFactoryStub{ + CreateCalled: func(epoch uint32, _ uint64) data.HeaderHandler { + return &metaBlockWithValidExecutionResult + }, + } + + arguments.BootstrapComponents = &bc + dataComponentsModified := *dataComponents + dataComponentsModified.BlockChain = &testscommon.ChainHandlerStub{ + GetCurrentBlockHeaderCalled: func() data.HeaderHandler { + return &prevValidMetaBlockV3 + }, + GetCurrentBlockHeaderHashCalled: func() []byte { + return []byte("prev header hash") + }, + } + arguments.DataComponents = &dataComponentsModified + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + mp.SetEpochStartData(&block.EpochStart{ + LastFinalizedHeaders: make([]block.EpochStartShardData, 3), + Economics: block.Economics{}, + }) + header, err := mp.CreateNewHeaderProposal(1, 1) + require.Equal(t, expectedErr, err) + require.Nil(t, header) + }) + t.Run("with epoch start data in execution results and in meta block processor, error on set epoch start data", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.ExecutionResultsTracker = &executionTrack.ExecutionResultsTrackerStub{ + GetPendingExecutionResultsCalled: func() ([]data.BaseExecutionResultHandler, error) { + return nil, nil + }, + } + + metaBlockWithValidExecutionResult := validMetaHeaderV3 + metaBlockWithValidExecutionResult.GetExecutionResultsHandlersCalled = func() []data.BaseExecutionResultHandler { + return validMetaExecutionResultsWithEpochChange + } + metaBlockWithValidExecutionResult.SetEpochStartHandlerCalled = func(_ data.EpochStartHandler) error { + return expectedErr + } + + bc := *defaultBootstrapComponents + bc.VersionedHdrFactory = &testscommon.VersionedHeaderFactoryStub{ + CreateCalled: func(epoch uint32, _ uint64) data.HeaderHandler { + return &metaBlockWithValidExecutionResult + }, + } + + arguments.BootstrapComponents = &bc + dataComponentsModified := *dataComponents + dataComponentsModified.BlockChain = &testscommon.ChainHandlerStub{ + GetCurrentBlockHeaderCalled: func() data.HeaderHandler { + return &prevValidMetaBlockV3 + }, + GetCurrentBlockHeaderHashCalled: func() []byte { + return []byte("prev header hash") + }, + } + arguments.DataComponents = &dataComponentsModified + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + mp.SetEpochStartData(&block.EpochStart{ + LastFinalizedHeaders: make([]block.EpochStartShardData, 3), + Economics: block.Economics{}, + }) + header, err := mp.CreateNewHeaderProposal(1, 1) + require.Equal(t, expectedErr, err) + require.Nil(t, header) + }) + t.Run("without epoch start data in execution results, should pass and not change epoch", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.ExecutionResultsTracker = &executionTrack.ExecutionResultsTrackerStub{ + GetPendingExecutionResultsCalled: func() ([]data.BaseExecutionResultHandler, error) { + return nil, nil + }, + } + + metaBlockWithValidExecutionResult := validMetaHeaderV3 + metaBlockWithValidExecutionResult.GetExecutionResultsHandlersCalled = func() []data.BaseExecutionResultHandler { + return validMetaExecutionResultsWithoutEpochChange + } + metaBlockWithValidExecutionResult.SetEpochCalled = func(epoch uint32) error { + require.Fail(t, "should not have been called") + return nil + } + + bc := *defaultBootstrapComponents + bc.VersionedHdrFactory = &testscommon.VersionedHeaderFactoryStub{ + CreateCalled: func(epoch uint32, _ uint64) data.HeaderHandler { + return &metaBlockWithValidExecutionResult + }, + } + + arguments.BootstrapComponents = &bc + dataComponentsModified := *dataComponents + dataComponentsModified.BlockChain = &testscommon.ChainHandlerStub{ + GetCurrentBlockHeaderCalled: func() data.HeaderHandler { + return &prevValidMetaBlockV3 + }, + GetCurrentBlockHeaderHashCalled: func() []byte { + return []byte("prev header hash") + }, + } + arguments.DataComponents = &dataComponentsModified + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + header, err := mp.CreateNewHeaderProposal(1, 1) + require.Nil(t, err) + require.NotNil(t, header) + }) + t.Run("with epoch start data in execution results and in meta block processor, should pass and change epoch", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.ExecutionResultsTracker = &executionTrack.ExecutionResultsTrackerStub{ + GetPendingExecutionResultsCalled: func() ([]data.BaseExecutionResultHandler, error) { + return nil, nil + }, + } + + metaBlockWithValidExecutionResult := validMetaHeaderV3 + metaBlockWithValidExecutionResult.GetExecutionResultsHandlersCalled = func() []data.BaseExecutionResultHandler { + return validMetaExecutionResultsWithEpochChange + } + metaBlockWithValidExecutionResult.SetEpochCalled = func(epoch uint32) error { + require.Equal(t, uint32(1), epoch) + return nil + } + + bc := *defaultBootstrapComponents + bc.VersionedHdrFactory = &testscommon.VersionedHeaderFactoryStub{ + CreateCalled: func(epoch uint32, _ uint64) data.HeaderHandler { + return &metaBlockWithValidExecutionResult + }, + } + + arguments.BootstrapComponents = &bc + dataComponentsModified := *dataComponents + dataComponentsModified.BlockChain = &testscommon.ChainHandlerStub{ + GetCurrentBlockHeaderCalled: func() data.HeaderHandler { + return &prevValidMetaBlockV3 + }, + GetCurrentBlockHeaderHashCalled: func() []byte { + return []byte("prev header hash") + }, + } + arguments.DataComponents = &dataComponentsModified + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + mp.SetEpochStartData(&block.EpochStart{ + LastFinalizedHeaders: make([]block.EpochStartShardData, 3), + Economics: block.Economics{}, + }) + header, err := mp.CreateNewHeaderProposal(1, 1) + require.Nil(t, err) + require.NotNil(t, header) + }) +} + +func TestMetaProcessor_CreateBlockProposal(t *testing.T) { + t.Parallel() + + t.Run("nil header", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + checkCreateBlockProposalResult(t, mp, nil, haveTimeTrue, process.ErrNilBlockHeader) + }) + t.Run("not header v3", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + notV3Header := &block.MetaBlock{} + checkCreateBlockProposalResult(t, mp, notV3Header, haveTimeTrue, process.ErrInvalidHeader) + }) + t.Run("shard header v3", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + shardHeaderV3 := &block.HeaderV3{} + checkCreateBlockProposalResult(t, mp, shardHeaderV3, haveTimeTrue, process.ErrWrongTypeAssertion) + }) + t.Run("createBlockBodyProposal error (ComputeLongestShardsChainsFromLastNotarized error)", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.BlockTracker = &mock.BlockTrackerMock{ + ComputeLongestShardsChainsFromLastNotarizedCalled: func() ([]data.HeaderHandler, [][]byte, map[uint32][]data.HeaderHandler, error) { + return nil, nil, nil, expectedErr + }, + } + + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + validMetaHeaderV3 := &block.MetaBlockV3{} + checkCreateBlockProposalResult(t, mp, validMetaHeaderV3, haveTimeTrue, expectedErr) + }) + t.Run("createShardInfoV3 error", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.ShardInfoCreator = &processMocks.ShardInfoCreatorMock{ + CreateShardInfoV3Called: func(metaHeader data.MetaHeaderHandler, shardHeaders []data.HeaderHandler, shardHeaderHashes [][]byte) ([]data.ShardDataProposalHandler, []data.ShardDataHandler, error) { + return nil, nil, expectedErr + }, + } + + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + validMetaHeaderV3 := &block.MetaBlockV3{} + checkCreateBlockProposalResult(t, mp, validMetaHeaderV3, haveTimeTrue, expectedErr) + }) + t.Run("set shard info error", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ + GetMiniBlocksCalled: func() block.MiniBlockSlice { + return make([]*block.MiniBlock, 5) // coverage + }, + } + var invalidShardData data.ShardDataHandler + arguments.ShardInfoCreator = &processMocks.ShardInfoCreatorMock{ + CreateShardInfoV3Called: func(metaHeader data.MetaHeaderHandler, shardHeaders []data.HeaderHandler, shardHeaderHashes [][]byte) ([]data.ShardDataProposalHandler, []data.ShardDataHandler, error) { + return nil, []data.ShardDataHandler{invalidShardData}, nil + }, + } + + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + validMetaHeaderV3 := &block.MetaBlockV3{} + checkCreateBlockProposalResult(t, mp, validMetaHeaderV3, haveTimeTrue, data.ErrInvalidTypeAssertion) + }) + t.Run("set shard info proposal error", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + var invalidShardDataProposal data.ShardDataProposalHandler + arguments.ShardInfoCreator = &processMocks.ShardInfoCreatorMock{ + CreateShardInfoV3Called: func(metaHeader data.MetaHeaderHandler, shardHeaders []data.HeaderHandler, shardHeaderHashes [][]byte) ([]data.ShardDataProposalHandler, []data.ShardDataHandler, error) { + return []data.ShardDataProposalHandler{invalidShardDataProposal}, []data.ShardDataHandler{}, nil + }, + } + + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + validMetaHeaderV3 := &block.MetaBlockV3{} + checkCreateBlockProposalResult(t, mp, validMetaHeaderV3, haveTimeTrue, data.ErrInvalidTypeAssertion) + }) + t.Run("set mini block header handlers error", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + var invalidMiniBlockHeader data.MiniBlockHeaderHandler + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ + GetMiniBlockHeaderHandlersCalled: func() []data.MiniBlockHeaderHandler { + return []data.MiniBlockHeaderHandler{invalidMiniBlockHeader} + }, + } + + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + validMetaHeaderV3 := &block.MetaBlockV3{} + checkCreateBlockProposalResult(t, mp, validMetaHeaderV3, haveTimeTrue, data.ErrInvalidTypeAssertion) + }) + t.Run("marshall error", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ + GetMiniBlockHeaderHandlersCalled: func() []data.MiniBlockHeaderHandler { return nil }, + } + cc := coreComponents + cc.IntMarsh = &testscommon.MarshallerStub{ + MarshalCalled: func(obj interface{}) ([]byte, error) { + return nil, expectedErr + }, + } + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + validMetaHeaderV3 := &block.MetaBlockV3{} + checkCreateBlockProposalResult(t, mp, validMetaHeaderV3, haveTimeTrue, expectedErr) + }) + t.Run("successful creation, non start of epoch block", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ + GetMiniBlockHeaderHandlersCalled: func() []data.MiniBlockHeaderHandler { return nil }, + } + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + validMetaHeaderV3 := &block.MetaBlockV3{} + header, body, err := mp.CreateBlockProposal(validMetaHeaderV3, haveTimeTrue) + require.Nil(t, err) + require.NotNil(t, header) + require.NotNil(t, body) + }) + t.Run("successful creation, start of epoch block with empy body", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ + GetMiniBlockHeaderHandlersCalled: func() []data.MiniBlockHeaderHandler { return nil }, + } + arguments.GasComputation = &testscommon.GasComputationMock{ + ResetCalled: func() { + require.Fail(t, "should not be called") + }, + } + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + // add epoch start data to the meta processor so that IsEpochStartBlock returns true + validMetaHeaderV3 := &block.MetaBlockV3{ + EpochStart: block.EpochStart{ + LastFinalizedHeaders: make([]block.EpochStartShardData, 3), + }, + } + header, body, err := mp.CreateBlockProposal(validMetaHeaderV3, haveTimeTrue) + require.Nil(t, err) + require.NotNil(t, header) + require.NotNil(t, body) + b := body.(*block.Body) + // start of epoch block should have no mini blocks headers and no mini blocks in the body + require.Len(t, header.GetMiniBlockHeaderHandlers(), 0) + require.Len(t, b.MiniBlocks, 0) + }) + t.Run("successful creation, epoch change proposal block with empy body", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ + GetMiniBlockHeaderHandlersCalled: func() []data.MiniBlockHeaderHandler { return nil }, + } + arguments.GasComputation = &testscommon.GasComputationMock{ + ResetCalled: func() { + require.Fail(t, "should not be called") + }, + } + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + // add epoch start data to the meta processor so that IsEpochStartBlock returns true + validMetaHeaderV3 := &block.MetaBlockV3{ + EpochStart: block.EpochStart{ + LastFinalizedHeaders: make([]block.EpochStartShardData, 3), + }, + } + header, body, err := mp.CreateBlockProposal(validMetaHeaderV3, haveTimeTrue) + require.Nil(t, err) + require.NotNil(t, header) + require.NotNil(t, body) + b := body.(*block.Body) + // epoch change proposal should have no mini blocks headers and no mini blocks in the body + require.Len(t, header.GetMiniBlockHeaderHandlers(), 0) + require.Len(t, b.MiniBlocks, 0) + }) +} + func TestMetaProcessor_VerifyBlockProposal(t *testing.T) { t.Parallel() @@ -26,3 +743,815 @@ func TestMetaProcessor_VerifyBlockProposal(t *testing.T) { err = mp.VerifyBlockProposal(header, body, haveTime) require.NoError(t, err) } + +func Test_getTxCountExecutionResults(t *testing.T) { + t.Parallel() + + t.Run("nil meta block", func(t *testing.T) { + t.Parallel() + + txCount, err := blproc.GetTxCountExecutionResults(nil) + require.Nil(t, err) + require.Equal(t, uint32(0), txCount) + }) + t.Run("no execution results notarized", func(t *testing.T) { + t.Parallel() + + metaBlock := &block.MetaBlockV3{} + txCount, err := blproc.GetTxCountExecutionResults(metaBlock) + require.Nil(t, err) + require.Equal(t, uint32(0), txCount) + }) + t.Run("empty execution results notarized", func(t *testing.T) { + t.Parallel() + + metaBlock := &block.MetaBlockV3{ + ExecutionResults: []*block.MetaExecutionResult{{}, {}}, + } + txCount, err := blproc.GetTxCountExecutionResults(metaBlock) + require.Nil(t, err) + require.Equal(t, uint32(0), txCount) + }) + t.Run("invalid execution result in notarized list", func(t *testing.T) { + t.Parallel() + + var metaExecutionResult *block.BaseExecutionResult + metaBlock := &testscommon.HeaderHandlerStub{ + GetExecutionResultsHandlersCalled: func() []data.BaseExecutionResultHandler { + return []data.BaseExecutionResultHandler{ + metaExecutionResult, + } + }, + } + + txCount, err := blproc.GetTxCountExecutionResults(metaBlock) + require.Equal(t, process.ErrWrongTypeAssertion, err) + require.Equal(t, uint32(0), txCount) + }) + t.Run("execution results notarized", func(t *testing.T) { + t.Parallel() + + metaBlock := &block.MetaBlockV3{ + ExecutionResults: []*block.MetaExecutionResult{ + { + ExecutedTxCount: 5, + }, + { + ExecutedTxCount: 10, + }, + }, + } + txCount, err := blproc.GetTxCountExecutionResults(metaBlock) + require.Nil(t, err) + require.Equal(t, uint32(15), txCount) + }) +} + +func TestMetaProcessor_hasStartOfEpochExecutionResults(t *testing.T) { + t.Parallel() + + mbHeaderWithEpochStartData := block.MiniBlockHeader{ + Hash: []byte("mb hash"), + SenderShardID: core.MetachainShardId, + Type: block.RewardsBlock, + } + t.Run("nil meta block", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + hasEpochStartData, err := mp.HasStartOfEpochExecutionResults(nil) + require.Equal(t, process.ErrNilHeaderHandler, err) + require.False(t, hasEpochStartData) + }) + t.Run("no executionResults", func(t *testing.T) { + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + validMetaHeaderV3 := &block.MetaBlockV3{} + hasEpochStartData, err := mp.HasStartOfEpochExecutionResults(validMetaHeaderV3) + require.Nil(t, err) + require.False(t, hasEpochStartData) + }) + t.Run("executionResults with invalid data", func(t *testing.T) { + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + validMetaHeaderV3 := &testscommon.HeaderHandlerStub{ + GetExecutionResultsHandlersCalled: func() []data.BaseExecutionResultHandler { + return []data.BaseExecutionResultHandler{ + &block.BaseExecutionResult{}, // invalid for meta block + } + }, + } + hasEpochStartData, err := mp.HasStartOfEpochExecutionResults(validMetaHeaderV3) + require.Equal(t, process.ErrWrongTypeAssertion, err) + require.False(t, hasEpochStartData) + }) + t.Run("executionResults without epoch start data", func(t *testing.T) { + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + mbHeader := mbHeaderWithEpochStartData + mbHeader.Type = block.TxBlock + validMetaHeaderV3 := &testscommon.HeaderHandlerStub{ + GetExecutionResultsHandlersCalled: func() []data.BaseExecutionResultHandler { + return []data.BaseExecutionResultHandler{ + &block.MetaExecutionResult{MiniBlockHeaders: []block.MiniBlockHeader{mbHeader}}} + }, + } + + hasEpochStartData, err := mp.HasStartOfEpochExecutionResults(validMetaHeaderV3) + require.Nil(t, err) + require.False(t, hasEpochStartData) + }) + t.Run("executionResults with reward miniBlocks epoch start data not from meta", func(t *testing.T) { + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + mbHeader := mbHeaderWithEpochStartData + mbHeader.SenderShardID = 0 + validMetaHeaderV3 := &testscommon.HeaderHandlerStub{ + GetExecutionResultsHandlersCalled: func() []data.BaseExecutionResultHandler { + return []data.BaseExecutionResultHandler{ + &block.MetaExecutionResult{MiniBlockHeaders: []block.MiniBlockHeader{mbHeader}}} + }, + } + + hasEpochStartData, err := mp.HasStartOfEpochExecutionResults(validMetaHeaderV3) + require.Nil(t, err) + require.False(t, hasEpochStartData) + }) + t.Run("executionResults with peer miniBlocks epoch start data not from meta", func(t *testing.T) { + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + mbHeader := mbHeaderWithEpochStartData + mbHeader.SenderShardID = 0 + mbHeader.Type = block.PeerBlock + validMetaHeaderV3 := &testscommon.HeaderHandlerStub{ + GetExecutionResultsHandlersCalled: func() []data.BaseExecutionResultHandler { + return []data.BaseExecutionResultHandler{ + &block.MetaExecutionResult{MiniBlockHeaders: []block.MiniBlockHeader{mbHeader}}} + }, + } + + hasEpochStartData, err := mp.HasStartOfEpochExecutionResults(validMetaHeaderV3) + require.Nil(t, err) + require.False(t, hasEpochStartData) + }) + t.Run("executionResults with reward miniBlocks epoch start data from meta", func(t *testing.T) { + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + mbHeader := mbHeaderWithEpochStartData + mbHeader.Type = block.RewardsBlock + validMetaHeaderV3 := &testscommon.HeaderHandlerStub{ + GetExecutionResultsHandlersCalled: func() []data.BaseExecutionResultHandler { + return []data.BaseExecutionResultHandler{ + &block.MetaExecutionResult{MiniBlockHeaders: []block.MiniBlockHeader{mbHeader}}} + }, + } + + hasEpochStartData, err := mp.HasStartOfEpochExecutionResults(validMetaHeaderV3) + require.Nil(t, err) + require.True(t, hasEpochStartData) + }) + t.Run("executionResults with peer miniBlocks epoch start data from meta", func(t *testing.T) { + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + mbHeader := mbHeaderWithEpochStartData + mbHeader.Type = block.PeerBlock + validMetaHeaderV3 := &testscommon.HeaderHandlerStub{ + GetExecutionResultsHandlersCalled: func() []data.BaseExecutionResultHandler { + return []data.BaseExecutionResultHandler{ + &block.MetaExecutionResult{MiniBlockHeaders: []block.MiniBlockHeader{mbHeader}}} + }, + } + + hasEpochStartData, err := mp.HasStartOfEpochExecutionResults(validMetaHeaderV3) + require.Nil(t, err) + require.True(t, hasEpochStartData) + }) +} + +func Test_hasRewardOrPeerMiniBlocksFromSelf(t *testing.T) { + t.Parallel() + + t.Run("nil miniBlocks", func(t *testing.T) { + t.Parallel() + response := blproc.HasRewardOrPeerMiniBlocksFromMeta(nil) + require.False(t, response) + }) + t.Run("no miniBlocks", func(t *testing.T) { + t.Parallel() + response := blproc.HasRewardOrPeerMiniBlocksFromMeta([]data.MiniBlockHeaderHandler{}) + require.False(t, response) + }) + t.Run("with reward miniBlocks from different shard", func(t *testing.T) { + t.Parallel() + miniBlocks := []data.MiniBlockHeaderHandler{ + &block.MiniBlockHeader{ + SenderShardID: 1, + Type: block.RewardsBlock, + }, + } + response := blproc.HasRewardOrPeerMiniBlocksFromMeta(miniBlocks) + require.False(t, response) + }) + t.Run("only tx miniBlocks", func(t *testing.T) { + t.Parallel() + miniBlocks := []data.MiniBlockHeaderHandler{ + &block.MiniBlockHeader{ + SenderShardID: common.MetachainShardId, // although not possible in combination with txblock + Type: block.TxBlock, + }, + } + response := blproc.HasRewardOrPeerMiniBlocksFromMeta(miniBlocks) + require.False(t, response) + }) + t.Run("with reward miniBlocks from meta shard", func(t *testing.T) { + t.Parallel() + miniBlocks := []data.MiniBlockHeaderHandler{ + &block.MiniBlockHeader{ + SenderShardID: common.MetachainShardId, + Type: block.RewardsBlock, + }, + } + response := blproc.HasRewardOrPeerMiniBlocksFromMeta(miniBlocks) + require.True(t, response) + }) + t.Run("with peer miniBlocks from meta shard", func(t *testing.T) { + t.Parallel() + miniBlocks := []data.MiniBlockHeaderHandler{ + &block.MiniBlockHeader{ + SenderShardID: common.MetachainShardId, + Type: block.PeerBlock, + }, + } + response := blproc.HasRewardOrPeerMiniBlocksFromMeta(miniBlocks) + require.True(t, response) + }) +} + +func TestMetaProcessor_createProposalMiniBlocks(t *testing.T) { + t.Parallel() + miniblockSelectionSessionNoAdd := &mbSelection.MiniBlockSelectionSessionStub{ + AddMiniBlocksAndHashesCalled: func(miniBlocksAndHashes []block.MiniblockAndHash) error { + require.Fail(t, "miniBlocksAndHashes should not be called") + return nil + }, + AddReferencedHeaderCalled: func(metaBlock data.HeaderHandler, metaBlockHash []byte) { + require.Fail(t, "AddReferencedHeader should not be called") + }, + CreateAndAddMiniBlockFromTransactionsCalled: func(txHashes [][]byte) error { + require.Fail(t, "CreateAndAddMiniBlockFromTransactions should not be called") + return nil + }, + } + t.Run("no time", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.MiniBlocksSelectionSession = miniblockSelectionSessionNoAdd + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + err = mp.CreateProposalMiniBlocks(haveTimeFalse) + require.Nil(t, err) + }) + t.Run("with time and error returned by selectIncomingMiniBlocksForProposal", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.BlockTracker = &mock.BlockTrackerMock{ + ComputeLongestShardsChainsFromLastNotarizedCalled: func() ([]data.HeaderHandler, [][]byte, map[uint32][]data.HeaderHandler, error) { + return nil, nil, nil, expectedErr + }, + } + arguments.MiniBlocksSelectionSession = miniblockSelectionSessionNoAdd + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + err = mp.CreateProposalMiniBlocks(haveTimeTrue) + require.Equal(t, expectedErr, err) + }) + t.Run("with time and no error, no mini blocks/shard headers", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.MiniBlocksSelectionSession = miniblockSelectionSessionNoAdd + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + err = mp.CreateProposalMiniBlocks(haveTimeTrue) + require.Nil(t, err) + }) +} + +func TestMetaProcessor_selectIncomingMiniBlocksForProposal(t *testing.T) { + t.Parallel() + + t.Run("error from ComputeLongestShardsChainsFromLastNotarized", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.BlockTracker = &mock.BlockTrackerMock{ + ComputeLongestShardsChainsFromLastNotarizedCalled: func() ([]data.HeaderHandler, [][]byte, map[uint32][]data.HeaderHandler, error) { + return nil, nil, nil, expectedErr + }, + } + + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + err = mp.SelectIncomingMiniBlocksForProposal(haveTimeTrue) + require.Equal(t, expectedErr, err) + }) + t.Run("error from getLastCrossNotarizedShardHeaders", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.BlockTracker = &mock.BlockTrackerMock{ + ComputeLongestShardsChainsFromLastNotarizedCalled: func() ([]data.HeaderHandler, [][]byte, map[uint32][]data.HeaderHandler, error) { + return []data.HeaderHandler{}, [][]byte{}, nil, nil + }, + GetLastCrossNotarizedHeaderCalled: func(shardID uint32) (data.HeaderHandler, []byte, error) { + return nil, nil, expectedErr + }, + } + + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + err = mp.SelectIncomingMiniBlocksForProposal(haveTimeTrue) + require.Equal(t, expectedErr, err) + }) + t.Run("selection ok", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + err = mp.SelectIncomingMiniBlocksForProposal(haveTimeTrue) + require.Nil(t, err) + }) +} + +func TestMetaProcessor_selectIncomingMiniBlocks(t *testing.T) { + t.Parallel() + + t.Run("no ordered headers", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ + AddMiniBlocksAndHashesCalled: func(miniBlocksAndHashes []block.MiniblockAndHash) error { + require.Fail(t, "should not be called") + return nil + }, + AddReferencedHeaderCalled: func(metaBlock data.HeaderHandler, metaBlockHash []byte) { + require.Fail(t, "should not be called") + }, + } + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + lastShardHeaders := createLastShardHeadersNotGenesis() + var orderedHeaders []data.HeaderHandler + var orderedHeaderHashes [][]byte + + maxNumHeadersFromSameShard := uint32(2) + err = mp.SelectIncomingMiniBlocks(lastShardHeaders, orderedHeaders, orderedHeaderHashes, maxNumHeadersFromSameShard, haveTimeTrue) + require.Nil(t, err) + }) + + t.Run("time is up before processing any header", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + // ensure proofs exist but haveTime will stop immediately + pools := dataComponents.DataPool + if ph, ok := pools.(*dataRetrieverMock.PoolsHolderStub); ok { + ph.ProofsCalled = func() retriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{HasProofCalled: func(shardID uint32, headerHash []byte) bool { return true }} + } + } + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + addRefCnt := 0 + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ + AddReferencedHeaderCalled: func(metaBlock data.HeaderHandler, metaBlockHash []byte) { + addRefCnt++ + }, + } + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + lastShardHeaders := createLastShardHeadersNotGenesis() + h := &testscommon.HeaderHandlerStub{ + GetShardIDCalled: func() uint32 { return 0 }, + GetNonceCalled: func() uint64 { return 11 }, + GetMiniBlockHeadersWithDstCalled: func(destId uint32) map[string]uint32 { return map[string]uint32{"x": 1} }, + } + orderedHeaders := []data.HeaderHandler{h} + orderedHeaderHashes := [][]byte{[]byte("h1")} + + err = mp.SelectIncomingMiniBlocks(lastShardHeaders, orderedHeaders, orderedHeaderHashes, 2, haveTimeFalse) + require.Nil(t, err) + require.Equal(t, 0, addRefCnt) + }) + + t.Run("maximum shard headers allowed in one meta block reached (max=0)", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + pools := dataComponents.DataPool + if ph, ok := pools.(*dataRetrieverMock.PoolsHolderStub); ok { + ph.ProofsCalled = func() retriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{HasProofCalled: func(shardID uint32, headerHash []byte) bool { return true }} + } + } + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + called := 0 + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ + AddReferencedHeaderCalled: func(metaBlock data.HeaderHandler, metaBlockHash []byte) { called++ }, + } + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + lastShardHeaders := createLastShardHeadersNotGenesis() + h := &testscommon.HeaderHandlerStub{ + GetShardIDCalled: func() uint32 { return 0 }, + GetNonceCalled: func() uint64 { return 11 }, + GetMiniBlockHeadersWithDstCalled: func(destId uint32) map[string]uint32 { return map[string]uint32{"x": 1} }, + } + err = mp.SelectIncomingMiniBlocks(lastShardHeaders, []data.HeaderHandler{h}, [][]byte{[]byte("h1")}, 0, haveTimeTrue) + require.Nil(t, err) + require.Equal(t, 0, called) + }) + + t.Run("skip header due to nonce gap", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + pools := dataComponents.DataPool + if ph, ok := pools.(*dataRetrieverMock.PoolsHolderStub); ok { + ph.ProofsCalled = func() retriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{HasProofCalled: func(shardID uint32, headerHash []byte) bool { return true }} + } + } + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + cntAddRef := 0 + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ + AddReferencedHeaderCalled: func(metaBlock data.HeaderHandler, metaBlockHash []byte) { cntAddRef++ }, + } + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + lastShardHeaders := createLastShardHeadersNotGenesis() + // last nonce for shard 0 is 10 -> header has 12 so gap > 1 triggers continue + h := &testscommon.HeaderHandlerStub{ + GetShardIDCalled: func() uint32 { return 0 }, + GetNonceCalled: func() uint64 { return 12 }, + GetMiniBlockHeadersWithDstCalled: func(destId uint32) map[string]uint32 { return map[string]uint32{"x": 1} }, + } + err = mp.SelectIncomingMiniBlocks(lastShardHeaders, []data.HeaderHandler{h}, [][]byte{[]byte("h1")}, 2, haveTimeTrue) + require.Nil(t, err) + require.Equal(t, 0, cntAddRef) + }) + + t.Run("skip header due to per-shard limit", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + pools := dataComponents.DataPool + if ph, ok := pools.(*dataRetrieverMock.PoolsHolderStub); ok { + ph.ProofsCalled = func() retriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{HasProofCalled: func(shardID uint32, headerHash []byte) bool { return true }} + } + } + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + cntAddRef := 0 + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ + AddReferencedHeaderCalled: func(metaBlock data.HeaderHandler, metaBlockHash []byte) { cntAddRef++ }, + } + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + lastShardHeaders := createLastShardHeadersNotGenesis() + h1 := &testscommon.HeaderHandlerStub{GetShardIDCalled: func() uint32 { return 0 }, GetNonceCalled: func() uint64 { return 11 }, GetMiniBlockHeadersWithDstCalled: func(uint32) map[string]uint32 { return map[string]uint32{} }} + h2 := &testscommon.HeaderHandlerStub{GetShardIDCalled: func() uint32 { return 0 }, GetNonceCalled: func() uint64 { return 12 }, GetMiniBlockHeadersWithDstCalled: func(uint32) map[string]uint32 { return map[string]uint32{} }} + err = mp.SelectIncomingMiniBlocks(lastShardHeaders, []data.HeaderHandler{h1, h2}, [][]byte{[]byte("h1"), []byte("h2")}, 1, haveTimeTrue) + require.Nil(t, err) + // only first header should be referenced + require.Equal(t, 1, cntAddRef) + // last shard header nonce for shard 0 should remain 11 due to per-shard limit preventing second update + require.Equal(t, uint64(11), lastShardHeaders[0].Header.GetNonce()) + }) + + t.Run("skip header due to missing proof", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + pools := dataComponents.DataPool + if ph, ok := pools.(*dataRetrieverMock.PoolsHolderStub); ok { + ph.ProofsCalled = func() retriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{HasProofCalled: func(shardID uint32, headerHash []byte) bool { return false }} + } + } + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + cntAddRef := 0 + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ + AddReferencedHeaderCalled: func(metaBlock data.HeaderHandler, metaBlockHash []byte) { cntAddRef++ }, + } + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + lastShardHeaders := createLastShardHeadersNotGenesis() + h := &testscommon.HeaderHandlerStub{GetShardIDCalled: func() uint32 { return 0 }, GetNonceCalled: func() uint64 { return 11 }, GetMiniBlockHeadersWithDstCalled: func(uint32) map[string]uint32 { return map[string]uint32{} }} + err = mp.SelectIncomingMiniBlocks(lastShardHeaders, []data.HeaderHandler{h}, [][]byte{[]byte("h1")}, 2, haveTimeTrue) + require.Nil(t, err) + require.Equal(t, 0, cntAddRef) + }) + + t.Run("no cross mini blocks with dst me -> add referenced header only", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + pools := dataComponents.DataPool + if ph, ok := pools.(*dataRetrieverMock.PoolsHolderStub); ok { + ph.ProofsCalled = func() retriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{HasProofCalled: func(shardID uint32, headerHash []byte) bool { return true }} + } + } + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + cntAddRef := 0 + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ + AddReferencedHeaderCalled: func(metaBlock data.HeaderHandler, metaBlockHash []byte) { cntAddRef++ }, + } + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + lastShardHeaders := createLastShardHeadersNotGenesis() + h := &testscommon.HeaderHandlerStub{GetShardIDCalled: func() uint32 { return 0 }, GetNonceCalled: func() uint64 { return 11 }, GetMiniBlockHeadersWithDstCalled: func(uint32) map[string]uint32 { return map[string]uint32{} }} + err = mp.SelectIncomingMiniBlocks(lastShardHeaders, []data.HeaderHandler{h}, [][]byte{[]byte("h1")}, 2, haveTimeTrue) + require.Nil(t, err) + require.Equal(t, 1, cntAddRef) + // last shard header updated and marked used + require.True(t, lastShardHeaders[0].UsedInBlock) + require.Equal(t, []byte("h1"), lastShardHeaders[0].Hash) + }) + + t.Run("createMbsCrossShardDstMe returns error", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + pools := dataComponents.DataPool + if ph, ok := pools.(*dataRetrieverMock.PoolsHolderStub); ok { + ph.ProofsCalled = func() retriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{HasProofCalled: func(shardID uint32, headerHash []byte) bool { return true }} + } + } + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.TxCoordinator = &testscommon.TransactionCoordinatorMock{ + CreateMbsCrossShardDstMeCalled: func(header data.HeaderHandler, processedMiniBlocksInfo map[string]*processedMb.ProcessedMiniBlockInfo) ([]block.MiniblockAndHash, []block.MiniblockAndHash, uint32, bool, error) { + return nil, nil, 0, false, expectedErr + }, + } + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + lastShardHeaders := createLastShardHeadersNotGenesis() + h := &testscommon.HeaderHandlerStub{ + GetShardIDCalled: func() uint32 { return 0 }, + GetNonceCalled: func() uint64 { return 11 }, + GetMiniBlockHeadersWithDstCalled: func(uint32) map[string]uint32 { return map[string]uint32{"mb": 1} }, + } + err = mp.SelectIncomingMiniBlocks(lastShardHeaders, []data.HeaderHandler{h}, [][]byte{[]byte("h1")}, 2, haveTimeTrue) + require.Equal(t, expectedErr, err) + }) + + t.Run("pending mini blocks returned -> break without adding header", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + pools := dataComponents.DataPool + if ph, ok := pools.(*dataRetrieverMock.PoolsHolderStub); ok { + ph.ProofsCalled = func() retriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{HasProofCalled: func(shardID uint32, headerHash []byte) bool { return true }} + } + } + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + cntAddRef := 0 + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{AddReferencedHeaderCalled: func(metaBlock data.HeaderHandler, metaBlockHash []byte) { cntAddRef++ }} + arguments.TxCoordinator = &testscommon.TransactionCoordinatorMock{ + CreateMbsCrossShardDstMeCalled: func(header data.HeaderHandler, processedMiniBlocksInfo map[string]*processedMb.ProcessedMiniBlockInfo) ([]block.MiniblockAndHash, []block.MiniblockAndHash, uint32, bool, error) { + return nil, []block.MiniblockAndHash{{}}, 0, false, nil + }, + } + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + lastShardHeaders := createLastShardHeadersNotGenesis() + h1 := &testscommon.HeaderHandlerStub{GetShardIDCalled: func() uint32 { return 0 }, GetNonceCalled: func() uint64 { return 11 }, GetMiniBlockHeadersWithDstCalled: func(uint32) map[string]uint32 { return map[string]uint32{"mb": 1} }} + h2 := &testscommon.HeaderHandlerStub{GetShardIDCalled: func() uint32 { return 0 }, GetNonceCalled: func() uint64 { return 12 }, GetMiniBlockHeadersWithDstCalled: func(uint32) map[string]uint32 { return map[string]uint32{"mb": 1} }} + err = mp.SelectIncomingMiniBlocks(lastShardHeaders, []data.HeaderHandler{h1, h2}, [][]byte{[]byte("h1"), []byte("h2")}, 2, haveTimeTrue) + require.Nil(t, err) + require.Equal(t, 0, cntAddRef) + // ensure second header was not processed due to break after first + require.Equal(t, uint64(10), lastShardHeaders[0].Header.GetNonce()) + }) + + t.Run("success: miniblocks added and header referenced", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + pools := dataComponents.DataPool + if ph, ok := pools.(*dataRetrieverMock.PoolsHolderStub); ok { + ph.ProofsCalled = func() retriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{HasProofCalled: func(shardID uint32, headerHash []byte) bool { return true }} + } + } + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + cntAddRef := 0 + cntAddMbs := 0 + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ + AddReferencedHeaderCalled: func(metaBlock data.HeaderHandler, metaBlockHash []byte) { cntAddRef++ }, + AddMiniBlocksAndHashesCalled: func(miniBlocksAndHashes []block.MiniblockAndHash) error { cntAddMbs++; return nil }, + } + arguments.TxCoordinator = &testscommon.TransactionCoordinatorMock{ + CreateMbsCrossShardDstMeCalled: func(header data.HeaderHandler, processedMiniBlocksInfo map[string]*processedMb.ProcessedMiniBlockInfo) ([]block.MiniblockAndHash, []block.MiniblockAndHash, uint32, bool, error) { + return []block.MiniblockAndHash{{}}, nil, 3, true, nil + }, + } + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + lastShardHeaders := createLastShardHeadersNotGenesis() + h := &testscommon.HeaderHandlerStub{GetShardIDCalled: func() uint32 { return 0 }, GetNonceCalled: func() uint64 { return 11 }, GetMiniBlockHeadersWithDstCalled: func(uint32) map[string]uint32 { return map[string]uint32{"mb": 1} }} + err = mp.SelectIncomingMiniBlocks(lastShardHeaders, []data.HeaderHandler{h}, [][]byte{[]byte("h1")}, 2, haveTimeTrue) + require.Nil(t, err) + require.Equal(t, 1, cntAddMbs) + require.Equal(t, 1, cntAddRef) + // last shard header updated and marked used + require.True(t, lastShardHeaders[0].UsedInBlock) + require.Equal(t, []byte("h1"), lastShardHeaders[0].Hash) + }) +} + +func TestMetaProcessor_selectIncomingMiniBlocks_GapsAndDuplicates(t *testing.T) { + t.Parallel() + + // helper to build a MetaProcessor with proofs pool behavior + type metaSel interface { + SelectIncomingMiniBlocks(lastShardHdr map[uint32]blproc.ShardHeaderInfo, orderedHdrs []data.HeaderHandler, orderedHdrsHashes [][]byte, maxNumHeadersFromSameShard uint32, haveTime func() bool) error + } + buildMp := func(hasProofFn func(shardID uint32, headerHash []byte) bool) metaSel { + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + pools := dataComponents.DataPool + if ph, ok := pools.(*dataRetrieverMock.PoolsHolderStub); ok { + ph.ProofsCalled = func() retriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{HasProofCalled: hasProofFn} + } + } + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + return mp + } + + t.Run("inconsistent ordered headers and hashes lengths -> error", func(t *testing.T) { + t.Parallel() + + mp := buildMp(func(uint32, []byte) bool { return true }) + lastShardHeaders := createLastShardHeadersNotGenesis() + h := &testscommon.HeaderHandlerStub{GetShardIDCalled: func() uint32 { return 0 }, GetNonceCalled: func() uint64 { return 11 }, GetMiniBlockHeadersWithDstCalled: func(uint32) map[string]uint32 { return map[string]uint32{} }} + err := mp.SelectIncomingMiniBlocks(lastShardHeaders, []data.HeaderHandler{h}, [][]byte{}, 2, haveTimeTrue) + require.Equal(t, process.ErrInconsistentShardHeadersAndHashes, err) + }) + + t.Run("missing last shard header for ordered header -> error", func(t *testing.T) { + t.Parallel() + + mp := buildMp(func(uint32, []byte) bool { return true }) + lastShardHeaders := createLastShardHeadersNotGenesis() + // header from shard 99, not present in lastShardHeaders map + h := &testscommon.HeaderHandlerStub{GetShardIDCalled: func() uint32 { return 99 }, GetNonceCalled: func() uint64 { return 1 }, GetMiniBlockHeadersWithDstCalled: func(uint32) map[string]uint32 { return map[string]uint32{} }} + err := mp.SelectIncomingMiniBlocks(lastShardHeaders, []data.HeaderHandler{h}, [][]byte{[]byte("h1")}, 2, haveTimeTrue) + require.Equal(t, process.ErrMissingHeader, err) + }) + + t.Run("duplicate nonce: first has proof accepted, second skipped", func(t *testing.T) { + t.Parallel() + + cntAddRef := 0 + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + pools := dataComponents.DataPool + if ph, ok := pools.(*dataRetrieverMock.PoolsHolderStub); ok { + ph.ProofsCalled = func() retriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{HasProofCalled: func(uint32, []byte) bool { return true }} + } + } + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ + AddReferencedHeaderCalled: func(metaBlock data.HeaderHandler, metaBlockHash []byte) { cntAddRef++ }, + } + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + lastShardHeaders := createLastShardHeadersNotGenesis() + h1 := &testscommon.HeaderHandlerStub{GetShardIDCalled: func() uint32 { return 0 }, GetNonceCalled: func() uint64 { return 11 }, GetMiniBlockHeadersWithDstCalled: func(uint32) map[string]uint32 { return map[string]uint32{} }} + h2 := &testscommon.HeaderHandlerStub{GetShardIDCalled: func() uint32 { return 0 }, GetNonceCalled: func() uint64 { return 11 }, GetMiniBlockHeadersWithDstCalled: func(uint32) map[string]uint32 { return map[string]uint32{} }} + err = mp.SelectIncomingMiniBlocks(lastShardHeaders, []data.HeaderHandler{h1, h2}, [][]byte{[]byte("h1"), []byte("h2")}, 2, haveTimeTrue) + require.Nil(t, err) + require.Equal(t, 1, cntAddRef) + // last shard header updated to first hash and used + require.True(t, lastShardHeaders[0].UsedInBlock) + require.Equal(t, []byte("h1"), lastShardHeaders[0].Hash) + }) + + t.Run("duplicate nonce: first missing proof skipped, second with proof accepted", func(t *testing.T) { + t.Parallel() + + cntAddRef := 0 + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + pools := dataComponents.DataPool + if ph, ok := pools.(*dataRetrieverMock.PoolsHolderStub); ok { + ph.ProofsCalled = func() retriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{HasProofCalled: func(_ uint32, hash []byte) bool { return string(hash) == "h2" }} + } + } + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ + AddReferencedHeaderCalled: func(metaBlock data.HeaderHandler, metaBlockHash []byte) { cntAddRef++ }, + } + mp, err := blproc.NewMetaProcessor(arguments) + require.Nil(t, err) + + lastShardHeaders := createLastShardHeadersNotGenesis() + h1 := &testscommon.HeaderHandlerStub{GetShardIDCalled: func() uint32 { return 0 }, GetNonceCalled: func() uint64 { return 11 }, GetMiniBlockHeadersWithDstCalled: func(uint32) map[string]uint32 { return map[string]uint32{} }} + h2 := &testscommon.HeaderHandlerStub{GetShardIDCalled: func() uint32 { return 0 }, GetNonceCalled: func() uint64 { return 11 }, GetMiniBlockHeadersWithDstCalled: func(uint32) map[string]uint32 { return map[string]uint32{} }} + err = mp.SelectIncomingMiniBlocks(lastShardHeaders, []data.HeaderHandler{h1, h2}, [][]byte{[]byte("h1"), []byte("h2")}, 2, haveTimeTrue) + require.Nil(t, err) + require.Equal(t, 1, cntAddRef) + // last shard header updated to second hash and used + require.True(t, lastShardHeaders[0].UsedInBlock) + require.Equal(t, []byte("h2"), lastShardHeaders[0].Hash) + }) +} + +func createLastShardHeadersNotGenesis() map[uint32]blproc.ShardHeaderInfo { + shard0 := uint32(0) + shard1 := uint32(1) + shard2 := uint32(2) + + return map[uint32]blproc.ShardHeaderInfo{ + shard0: { + Header: &block.Header{ + ShardID: shard0, + Nonce: 10, + Round: 10, + }, + Hash: []byte("hash1"), + }, + shard1: { + Header: &block.Header{ + ShardID: shard1, + Nonce: 10, + Round: 10, + }, + Hash: []byte("hash2"), + }, + shard2: { + Header: &block.Header{ + ShardID: shard2, + Nonce: 10, + Round: 10, + }, + Hash: []byte("hash3"), + }, + } +} diff --git a/process/block/metablock_test.go b/process/block/metablock_test.go index 11710d7e333..9541347f405 100644 --- a/process/block/metablock_test.go +++ b/process/block/metablock_test.go @@ -198,6 +198,14 @@ func createMockMetaArguments( } gasComputation, _ := blproc.NewGasConsumption(argsGasConsumption) + shardInfoCreator, _ := blproc.NewShardInfoCreateData( + coreComponents.EnableEpochsHandler(), + dataComponents.Datapool().Headers(), + dataComponents.Datapool().Proofs(), + &mock.PendingMiniBlocksHandlerStub{}, + blockTracker, + ) + arguments := blproc.ArgMetaProcessor{ ArgBaseProcessor: blproc.ArgBaseProcessor{ CoreComponents: coreComponents, @@ -249,6 +257,7 @@ func createMockMetaArguments( EpochValidatorInfoCreator: &testscommon.EpochValidatorInfoCreatorStub{}, ValidatorStatisticsProcessor: &testscommon.ValidatorStatisticsProcessorStub{}, EpochSystemSCProcessor: &testscommon.EpochStartSystemSCStub{}, + ShardInfoCreator: shardInfoCreator, } return arguments } @@ -1403,16 +1412,16 @@ func TestMetaProcessor_CreateShardInfoShouldWorkNoHdrAddataRetrieverMockdedNotVa mp, _ := blproc.NewMetaProcessor(arguments) round := uint64(10) - shardInfo, err := mp.CreateShardInfo() + metaHdr := &block.MetaBlock{Round: round} + shardInfo, err := mp.CreateShardInfo(metaHdr) assert.Nil(t, err) assert.Equal(t, 0, len(shardInfo)) - metaHdr := &block.MetaBlock{Round: round} _, err = mp.CreateBlockBody(metaHdr, func() bool { return true }) assert.Nil(t, err) - shardInfo, err = mp.CreateShardInfo() + shardInfo, err = mp.CreateShardInfo(metaHdr) assert.Nil(t, err) assert.Equal(t, 1, len(shardInfo)) } @@ -1508,14 +1517,14 @@ func TestMetaProcessor_CreateShardInfoShouldWorkNoHdrAddedNotFinal(t *testing.T) mp.SetShardBlockFinality(0) round := uint64(40) - shardInfo, err := mp.CreateShardInfo() + metaHdr := &block.MetaBlock{Round: round} + shardInfo, err := mp.CreateShardInfo(metaHdr) assert.Nil(t, err) assert.Equal(t, 0, len(shardInfo)) - metaHdr := &block.MetaBlock{Round: round} _, err = mp.CreateBlockBody(metaHdr, haveTimeHandler) assert.Nil(t, err) - shardInfo, err = mp.CreateShardInfo() + shardInfo, err = mp.CreateShardInfo(metaHdr) assert.Nil(t, err) assert.Equal(t, 3, len(shardInfo)) } @@ -1660,14 +1669,14 @@ func TestMetaProcessor_CreateShardInfoShouldWorkHdrsAdded(t *testing.T) { mp.SetShardBlockFinality(1) round := uint64(15) - shardInfo, err := mp.CreateShardInfo() + metaHdr := &block.MetaBlock{Round: round} + shardInfo, err := mp.CreateShardInfo(metaHdr) assert.Nil(t, err) assert.Equal(t, 0, len(shardInfo)) - metaHdr := &block.MetaBlock{Round: round} _, err = mp.CreateBlockBody(metaHdr, haveTimeHandler) assert.Nil(t, err) - shardInfo, err = mp.CreateShardInfo() + shardInfo, err = mp.CreateShardInfo(metaHdr) assert.Nil(t, err) assert.Equal(t, 3, len(shardInfo)) } @@ -1813,14 +1822,14 @@ func TestMetaProcessor_CreateShardInfoEmptyBlockHDRRoundTooHigh(t *testing.T) { mp.SetShardBlockFinality(1) round := uint64(20) - shardInfo, err := mp.CreateShardInfo() + metaHdr := &block.MetaBlock{Round: round} + shardInfo, err := mp.CreateShardInfo(metaHdr) assert.Nil(t, err) assert.Equal(t, 0, len(shardInfo)) - metaHdr := &block.MetaBlock{Round: round} _, err = mp.CreateBlockBody(metaHdr, haveTimeHandler) assert.Nil(t, err) - shardInfo, err = mp.CreateShardInfo() + shardInfo, err = mp.CreateShardInfo(metaHdr) assert.Nil(t, err) assert.Equal(t, 3, len(shardInfo)) } @@ -3194,7 +3203,7 @@ func TestMetaProcessor_CreateNewHeaderValsOK(t *testing.T) { assert.Equal(t, zeroInt, metaHeader.DevFeesInEpoch) } -func TestCreateNewHeaderV3(t *testing.T) { +func TestCreateNewHeaderProposal(t *testing.T) { t.Parallel() rootHash := []byte("root") @@ -3228,14 +3237,41 @@ func TestCreateNewHeaderV3(t *testing.T) { } arguments := createMockMetaArguments(coreComponents, dataComponents, boostrapComponents, statusComponents) + dataComponents = &mock.DataComponentsMock{ + BlockChain: &testscommon.ChainHandlerStub{ + GetCurrentBlockHeaderCalled: func() data.HeaderHandler { + return &block.MetaBlockV3{ + Epoch: epoch, + LastExecutionResult: &block.MetaExecutionResultInfo{ + NotarizedInRound: 10, + ExecutionResult: &block.BaseMetaExecutionResult{ + BaseExecutionResult: &block.BaseExecutionResult{ + HeaderHash: []byte("hash2"), + HeaderNonce: 9, + HeaderRound: 9, + HeaderEpoch: epoch, + RootHash: []byte("root hash"), + GasUsed: 1000, + }, + }, + }, + } + }, + GetCurrentBlockHeaderHashCalled: func() []byte { + return []byte("hash") + }, + }, + DataPool: arguments.DataComponents.Datapool(), + Storage: arguments.DataComponents.StorageService(), + } + arguments.DataComponents = dataComponents arguments.AccountsDB[state.UserAccountsState] = &stateMock.AccountsStub{ RootHashCalled: func() ([]byte, error) { return rootHash, nil }, } - updateRoundCalled := false arguments.EpochStartTrigger = &mock.EpochStartTriggerStub{ EpochCalled: func() uint32 { return epoch @@ -3243,19 +3279,15 @@ func TestCreateNewHeaderV3(t *testing.T) { ShouldProposeEpochChangeCalled: func(round uint64, nonce uint64) bool { return true }, - UpdateRoundCalled: func(round uint64) { - updateRoundCalled = true - }, } mp, err := blproc.NewMetaProcessor(arguments) assert.Nil(t, err) - newHeader, err := mp.CreateNewHeader(round, nonce) + newHeader, err := mp.CreateNewHeaderProposal(round, nonce) require.Nil(t, err) require.IsType(t, &block.MetaBlockV3{}, newHeader) require.Equal(t, epoch, newHeader.GetEpoch()) - require.True(t, updateRoundCalled) } func TestMetaProcessor_ProcessEpochStartMetaBlock(t *testing.T) { diff --git a/process/block/pendingMb/pendingMiniBlocks.go b/process/block/pendingMb/pendingMiniBlocks.go index 5ef5eb836ff..1d055dfe8fc 100644 --- a/process/block/pendingMb/pendingMiniBlocks.go +++ b/process/block/pendingMb/pendingMiniBlocks.go @@ -7,9 +7,10 @@ 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-logger-go" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/process" - "github.com/multiversx/mx-chain-logger-go" ) var _ process.PendingMiniBlocksHandler = (*pendingMiniBlocks)(nil) @@ -68,7 +69,7 @@ func getMiniBlocksFromHeaderReadyForCrossShardExecution(header data.MetaHeaderHa miniBlocks := make([]data.MiniBlockHeaderHandler, 0) execResultHandlers := header.GetExecutionResultsHandlers() for _, execResult := range execResultHandlers { - mbs, errGetMbs := common.GetMiniBlocksHeaderHandlersFromExecResult(execResult, common.MetachainShardId) + mbs, errGetMbs := common.GetMiniBlocksHeaderHandlersFromExecResult(execResult) if errGetMbs != nil { return nil, errGetMbs } diff --git a/process/block/shardInfo.go b/process/block/shardInfo.go new file mode 100644 index 00000000000..d1bb98bd140 --- /dev/null +++ b/process/block/shardInfo.go @@ -0,0 +1,303 @@ +package block + +import ( + "encoding/hex" + "fmt" + + "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-go/common" + "github.com/multiversx/mx-chain-go/dataRetriever" + "github.com/multiversx/mx-chain-go/process" +) + +// ShardInfoCreateData is a component used to create shard info from shard headers +type ShardInfoCreateData struct { + enableEpochsHandler common.EnableEpochsHandler + headersPool dataRetriever.HeadersPool + proofsPool dataRetriever.ProofsPool + pendingMiniBlocksHandler process.PendingMiniBlocksHandler + blockTracker process.BlockTracker +} + +// NewShardInfoCreateData creates a new ShardInfoCreateData instance +func NewShardInfoCreateData( + enableEpochsHandler common.EnableEpochsHandler, + headersPool dataRetriever.HeadersPool, + proofsPool dataRetriever.ProofsPool, + pendingMiniBlocksHandler process.PendingMiniBlocksHandler, + blockTracker process.BlockTracker, +) (*ShardInfoCreateData, error) { + if check.IfNil(enableEpochsHandler) { + return nil, process.ErrNilEnableEpochsHandler + } + if check.IfNil(headersPool) { + return nil, process.ErrNilHeadersDataPool + } + if check.IfNil(proofsPool) { + return nil, process.ErrNilProofsPool + } + if check.IfNil(pendingMiniBlocksHandler) { + return nil, process.ErrNilPendingMiniBlocksHandler + } + if check.IfNil(blockTracker) { + return nil, process.ErrNilBlockTracker + } + + return &ShardInfoCreateData{ + enableEpochsHandler: enableEpochsHandler, + headersPool: headersPool, + proofsPool: proofsPool, + pendingMiniBlocksHandler: pendingMiniBlocksHandler, + blockTracker: blockTracker, + }, nil +} + +// CreateShardInfoV3 creates shard info for V3 metablock headers +func (sic *ShardInfoCreateData) CreateShardInfoV3( + metaHeader data.MetaHeaderHandler, + shardHeaders []data.HeaderHandler, + shardHeaderHashes [][]byte, +) ([]data.ShardDataProposalHandler, []data.ShardDataHandler, error) { + if check.IfNil(metaHeader) { + return nil, nil, process.ErrNilHeaderHandler + } + if !metaHeader.IsHeaderV3() { + return nil, nil, process.ErrInvalidHeader + } + + var shardInfo []data.ShardDataHandler + var shardInfoProposal []data.ShardDataProposalHandler + if len(shardHeaders) != len(shardHeaderHashes) { + return nil, nil, process.ErrInconsistentShardHeadersAndHashes + } + + for i := 0; i < len(shardHeaders); i++ { + shardDataProposal, shardData, err := sic.createShardInfoFromHeader(shardHeaders[i], shardHeaderHashes[i]) + if err != nil { + return nil, nil, err + } + shardInfo = append(shardInfo, shardData...) + shardInfoProposal = append(shardInfoProposal, shardDataProposal) + } + + return shardInfoProposal, shardInfo, nil +} + +// CreateShardInfoFromLegacyMeta creates shard info for legacy meta header +func (sic *ShardInfoCreateData) CreateShardInfoFromLegacyMeta( + metaHeader data.MetaHeaderHandler, + shardHeaders []data.ShardHeaderHandler, + shardHeaderHashes [][]byte, +) ([]data.ShardDataHandler, error) { + if check.IfNil(metaHeader) { + return nil, process.ErrNilHeaderHandler + } + if metaHeader.IsHeaderV3() { + return nil, process.ErrInvalidHeader + } + + if len(shardHeaders) != len(shardHeaderHashes) { + return nil, process.ErrInconsistentShardHeadersAndHashes + } + + shardInfo := make([]data.ShardDataHandler, 0) + for i := 0; i < len(shardHeaders); i++ { + shardData, err := sic.createShardDataFromLegacyHeader(shardHeaders[i], shardHeaderHashes[i]) + if err != nil { + return nil, err + } + shardInfo = append(shardInfo, shardData...) + } + + return shardInfo, nil +} + +func (sic *ShardInfoCreateData) createShardInfoFromHeader( + shardHeader data.HeaderHandler, + hdrHash []byte, +) (data.ShardDataProposalHandler, []data.ShardDataHandler, error) { + if check.IfNil(shardHeader) { + return nil, nil, process.ErrNilHeaderHandler + } + if len(hdrHash) == 0 { + return nil, nil, process.ErrInvalidHash + } + + hasMissingShardHdrProof := shardHeader.GetNonce() >= 1 && !sic.proofsPool.HasProof(shardHeader.GetShardID(), hdrHash) + if hasMissingShardHdrProof { + return nil, nil, fmt.Errorf("%w for shard header with hash %s", process.ErrMissingHeaderProof, hex.EncodeToString(hdrHash)) + } + + if !shardHeader.IsHeaderV3() { + shardData, err := sic.createShardDataFromLegacyHeader(shardHeader, hdrHash) + return createShardDataProposalFromHeader(shardHeader, hdrHash), shardData, err + } + + return sic.createShardDataFromV3Header(shardHeader, hdrHash) +} + +func (sic *ShardInfoCreateData) createShardDataFromLegacyHeader(shardHdr data.HeaderHandler, hdrHash []byte) ([]data.ShardDataHandler, error) { + shardData := &block.ShardData{} + shardData.TxCount = shardHdr.GetTxCount() + shardData.ShardID = shardHdr.GetShardID() + shardData.HeaderHash = hdrHash + shardData.Round = shardHdr.GetRound() + shardData.PrevHash = shardHdr.GetPrevHash() + shardData.Nonce = shardHdr.GetNonce() + shardData.PrevRandSeed = shardHdr.GetPrevRandSeed() + shardData.PubKeysBitmap = shardHdr.GetPubKeysBitmap() + if sic.enableEpochsHandler.IsFlagEnabledInEpoch(common.AndromedaFlag, shardHdr.GetEpoch()) { + shardData.Epoch = shardHdr.GetEpoch() + } + shardData.AccumulatedFees = shardHdr.GetAccumulatedFees() + shardData.DeveloperFees = shardHdr.GetDeveloperFees() + shardData.ShardMiniBlockHeaders = createShardMiniBlockHeaderFromHeader(shardHdr, sic.enableEpochsHandler) + err := sic.updateShardDataWithCrossShardInfo(shardData, shardHdr) + if err != nil { + return nil, err + } + + return []data.ShardDataHandler{shardData}, nil +} + +func (sic *ShardInfoCreateData) createShardDataFromV3Header( + shardHeader data.HeaderHandler, + hdrHash []byte, +) (data.ShardDataProposalHandler, []data.ShardDataHandler, error) { + if check.IfNil(shardHeader) { + return nil, nil, process.ErrNilHeaderHandler + } + shardDataProposal := createShardDataProposalFromHeader(shardHeader, hdrHash) + executionResults := shardHeader.GetExecutionResultsHandlers() + if len(executionResults) == 0 { + // return shard data proposal even though the shard header does not hold any execution result + return shardDataProposal, []data.ShardDataHandler{}, nil + } + + shardDataHandlers := make([]data.ShardDataHandler, len(executionResults)) + for i, execResult := range executionResults { + shardData, err := sic.createShardDataFromExecutionResult(execResult) + if err != nil { + return nil, nil, err + } + shardDataHandlers[i] = shardData + } + + return shardDataProposal, shardDataHandlers, nil +} + +func createShardDataProposalFromHeader( + header data.HeaderHandler, + hdrHash []byte, +) data.ShardDataProposalHandler { + return &block.ShardDataProposal{ + HeaderHash: hdrHash, + Round: header.GetRound(), + Nonce: header.GetNonce(), + ShardID: header.GetShardID(), + Epoch: header.GetEpoch(), + } +} + +func (sic *ShardInfoCreateData) createShardDataFromExecutionResult( + execResult data.BaseExecutionResultHandler, +) (data.ShardDataHandler, error) { + if check.IfNil(execResult) { + return nil, process.ErrNilExecutionResultHandler + } + + execResultHandler, ok := execResult.(data.ExecutionResultHandler) + if !ok { + return nil, process.ErrWrongTypeAssertion + } + + header, err := sic.headersPool.GetHeaderByHash(execResultHandler.GetHeaderHash()) + if err != nil { + return nil, err + } + + shardData := &block.ShardData{} + shardData.TxCount = uint32(execResultHandler.GetExecutedTxCount()) + shardData.ShardID = header.GetShardID() + shardData.HeaderHash = execResultHandler.GetHeaderHash() + shardData.Round = execResultHandler.GetHeaderRound() + shardData.PrevHash = header.GetPrevHash() + shardData.Nonce = execResultHandler.GetHeaderNonce() + shardData.PrevRandSeed = header.GetPrevRandSeed() + shardData.PubKeysBitmap = header.GetPubKeysBitmap() + shardData.Epoch = header.GetEpoch() + shardData.AccumulatedFees = execResultHandler.GetAccumulatedFees() + shardData.DeveloperFees = execResultHandler.GetDeveloperFees() + shardData.ShardMiniBlockHeaders = createShardMiniBlockHeaderFromExecutionResultHandler(execResultHandler) + err = sic.updateShardDataWithCrossShardInfo(shardData, header) + if err != nil { + return nil, err + } + + return shardData, nil +} + +func createShardMiniBlockHeaderFromHeader( + shardHdr data.HeaderHandler, + enableEpochsHandler common.EnableEpochsHandler, +) []block.MiniBlockHeader { + shardMiniBlockHeaders := make([]block.MiniBlockHeader, 0) + for i := 0; i < len(shardHdr.GetMiniBlockHeaderHandlers()); i++ { + if enableEpochsHandler.IsFlagEnabled(common.ScheduledMiniBlocksFlag) { + miniBlockHeader := shardHdr.GetMiniBlockHeaderHandlers()[i] + if !miniBlockHeader.IsFinal() { + log.Debug("metaProcessor.createShardInfo: do not create shard data with mini block which is not final", "mb hash", miniBlockHeader.GetHash()) + continue + } + } + + shardMiniBlockHeader := miniBlockHeaderFromMiniBlockHeaderHandler(shardHdr.GetMiniBlockHeaderHandlers()[i]) + shardMiniBlockHeaders = append(shardMiniBlockHeaders, shardMiniBlockHeader) + } + return shardMiniBlockHeaders +} + +func createShardMiniBlockHeaderFromExecutionResultHandler( + execResultHandler data.ExecutionResultHandler, +) []block.MiniBlockHeader { + mbHeaderHandlers := execResultHandler.GetMiniBlockHeadersHandlers() + shardMiniBlockHeaders := make([]block.MiniBlockHeader, 0, len(mbHeaderHandlers)) + for i := 0; i < len(mbHeaderHandlers); i++ { + shardMiniBlockHeader := miniBlockHeaderFromMiniBlockHeaderHandler(mbHeaderHandlers[i]) + shardMiniBlockHeaders = append(shardMiniBlockHeaders, shardMiniBlockHeader) + } + return shardMiniBlockHeaders +} + +func miniBlockHeaderFromMiniBlockHeaderHandler(miniBlockHeaderHandler data.MiniBlockHeaderHandler) block.MiniBlockHeader { + shardMiniBlockHeader := block.MiniBlockHeader{} + shardMiniBlockHeader.SenderShardID = miniBlockHeaderHandler.GetSenderShardID() + shardMiniBlockHeader.ReceiverShardID = miniBlockHeaderHandler.GetReceiverShardID() + shardMiniBlockHeader.Hash = miniBlockHeaderHandler.GetHash() + shardMiniBlockHeader.TxCount = miniBlockHeaderHandler.GetTxCount() + shardMiniBlockHeader.Type = block.Type(miniBlockHeaderHandler.GetTypeInt32()) + + return shardMiniBlockHeader +} + +func (sic *ShardInfoCreateData) updateShardDataWithCrossShardInfo(shardData *block.ShardData, header data.HeaderHandler) error { + // TODO: pendingMiniBlocksHandler should be updated + shardData.NumPendingMiniBlocks = uint32(len(sic.pendingMiniBlocksHandler.GetPendingMiniBlocks(header.GetShardID()))) + + // TODO: the last self notarized header should be fetched based on the nonce from the execution result + metaHeader, _, err := sic.blockTracker.GetLastSelfNotarizedHeader(header.GetShardID()) + if err != nil { + return err + } + shardData.LastIncludedMetaNonce = metaHeader.GetNonce() + + return nil +} + +// IsInterfaceNil returns true if there is no value under the interface +func (sic *ShardInfoCreateData) IsInterfaceNil() bool { + return sic == nil +} diff --git a/process/block/shardblockProposal.go b/process/block/shardblockProposal.go index 62d97947835..edfae140094 100644 --- a/process/block/shardblockProposal.go +++ b/process/block/shardblockProposal.go @@ -21,6 +21,32 @@ import ( // TODO: maybe move this to config const maxBlockProcessingTime = 3 * time.Second +// CreateNewHeaderProposal creates a new header proposal +func (sp *shardProcessor) CreateNewHeaderProposal(round uint64, nonce uint64) (data.HeaderHandler, error) { + epoch := sp.epochStartTrigger.MetaEpoch() + header := sp.versionedHeaderFactory.Create(epoch, round) + + shardHeader, ok := header.(data.ShardHeaderHandler) + if !ok { + return nil, process.ErrWrongTypeAssertion + } + if !shardHeader.IsHeaderV3() { + return nil, process.ErrInvalidHeader + } + + err := shardHeader.SetRound(round) + if err != nil { + return nil, err + } + + err = shardHeader.SetNonce(nonce) + if err != nil { + return nil, err + } + + return header, nil +} + // CreateBlockProposal creates a block proposal without executing any of the transactions func (sp *shardProcessor) CreateBlockProposal( initialHdr data.HeaderHandler, @@ -56,7 +82,7 @@ func (sp *shardProcessor) CreateBlockProposal( return nil, nil, err } - err = shardHdr.SetMetaBlockHashes(sp.miniBlocksSelectionSession.GetReferencedMetaBlockHashes()) + err = shardHdr.SetMetaBlockHashes(sp.miniBlocksSelectionSession.GetReferencedHeaderHashes()) if err != nil { return nil, nil, err } @@ -467,29 +493,6 @@ func (sp *shardProcessor) getTransactionsForMiniBlock( return mbForHeaderPtr.TxHashes, txs, nil } -func (sp *shardProcessor) checkInclusionEstimationForExecutionResults(header data.HeaderHandler) error { - prevBlockLastExecutionResult, err := process.GetPrevBlockLastExecutionResult(sp.blockChain) - if err != nil { - return err - } - - lastResultData, err := process.CreateDataForInclusionEstimation(prevBlockLastExecutionResult) - if err != nil { - return err - } - executionResults := header.GetExecutionResultsHandlers() - allowed := sp.executionResultsInclusionEstimator.Decide(lastResultData, executionResults, header.GetRound()) - if allowed != len(executionResults) { - log.Warn("number of execution results included in the header is not correct", - "expected", allowed, - "actual", len(executionResults), - ) - return process.ErrInvalidNumberOfExecutionResultsInHeader - } - - return nil -} - func computeTxTotalTxCount(miniBlockHeaders []data.MiniBlockHeaderHandler) uint32 { totalTxCount := uint32(0) for i := range miniBlockHeaders { @@ -508,43 +511,6 @@ func checkMiniBlocksAndMiniBlockHeadersConsistency(miniBlocks block.MiniBlockSli return nil } -func (sp *shardProcessor) addExecutionResultsOnHeader(shardHeader data.HeaderHandler) error { - pendingExecutionResults, err := sp.executionResultsTracker.GetPendingExecutionResults() - if err != nil { - return err - } - - lastExecutionResultHandler, err := process.GetPrevBlockLastExecutionResult(sp.blockChain) - if err != nil { - return err - } - - lastNotarizedExecutionResultInfo, err := process.CreateDataForInclusionEstimation(lastExecutionResultHandler) - if err != nil { - return err - } - - var lastExecutionResultForCurrentBlock data.LastExecutionResultHandler - numToInclude := sp.executionResultsInclusionEstimator.Decide(lastNotarizedExecutionResultInfo, pendingExecutionResults, shardHeader.GetRound()) - - executionResultsToInclude := pendingExecutionResults[:numToInclude] - lastExecutionResultForCurrentBlock = lastExecutionResultHandler - if len(executionResultsToInclude) > 0 { - lastExecutionResult := executionResultsToInclude[len(executionResultsToInclude)-1] - lastExecutionResultForCurrentBlock, err = process.CreateLastExecutionResultInfoFromExecutionResult(shardHeader.GetRound(), lastExecutionResult, sp.shardCoordinator.SelfId()) - if err != nil { - return err - } - } - - err = shardHeader.SetLastExecutionResultHandler(lastExecutionResultForCurrentBlock) - if err != nil { - return err - } - - return shardHeader.SetExecutionResultsHandlers(executionResultsToInclude) -} - func (sp *shardProcessor) createBlockBodyProposal( shardHdr data.HeaderHandler, haveTime func() bool, @@ -586,12 +552,6 @@ func (sp *shardProcessor) selectIncomingMiniBlocksForProposal( return nil, err } - referencedMetaBlocks := sp.miniBlocksSelectionSession.GetReferencedMetaBlocks() - numHeadersAdded := uint32(len(referencedMetaBlocks)) - if numHeadersAdded > 0 { - go sp.requestMetaHeadersIfNeeded(numHeadersAdded, referencedMetaBlocks[numHeadersAdded-1]) - } - miniBlockHeaderHandlers := sp.miniBlocksSelectionSession.GetMiniBlockHeaderHandlers() for _, miniBlockHeader := range miniBlockHeaderHandlers { log.Debug("mini block info", @@ -603,7 +563,7 @@ func (sp *shardProcessor) selectIncomingMiniBlocksForProposal( log.Debug("selectIncomingMiniBlocksForProposal has been finished", "num txs added", sp.miniBlocksSelectionSession.GetNumTxsAdded(), - "num referenced meta blocks", len(sp.miniBlocksSelectionSession.GetReferencedMetaBlocks())) + "num referenced meta blocks", len(sp.miniBlocksSelectionSession.GetReferencedHeaders())) return pendingMiniBlocks, nil } @@ -618,7 +578,8 @@ func (sp *shardProcessor) selectIncomingMiniBlocks( var currentMetaBlockHash []byte var pendingMiniBlocks []block.MiniblockAndHash var errCreated error - var shouldContinue bool + var createIncomingMbsResult *CrossShardIncomingMbsCreationResult + lastMeta := lastCrossNotarizedMetaHdr for i := 0; i < len(orderedMetaBlocks); i++ { if !haveTime() { log.Debug("time is up after putting cross txs with destination to current shard", @@ -627,30 +588,30 @@ func (sp *shardProcessor) selectIncomingMiniBlocks( break } - if len(sp.miniBlocksSelectionSession.GetReferencedMetaBlocks()) >= process.MaxMetaHeadersAllowedInOneShardBlock { + if len(sp.miniBlocksSelectionSession.GetReferencedHeaders()) >= process.MaxMetaHeadersAllowedInOneShardBlock { log.Debug("maximum meta headers allowed to be included in one shard block has been reached", - "meta headers added", len(sp.miniBlocksSelectionSession.GetReferencedMetaBlocks()), + "meta headers added", len(sp.miniBlocksSelectionSession.GetReferencedHeaders()), ) break } currentMetaBlock = orderedMetaBlocks[i] - if currentMetaBlock.GetNonce() > lastCrossNotarizedMetaHdr.GetNonce()+1 { + currentMetaBlockHash = orderedMetaBlocksHashes[i] + if currentMetaBlock.GetNonce() != lastMeta.GetNonce()+1 { log.Debug("skip searching", - "last meta hdr nonce", lastCrossNotarizedMetaHdr.GetNonce(), + "last meta hdr nonce", lastMeta.GetNonce(), "curr meta hdr nonce", currentMetaBlock.GetNonce()) - break + continue } - hasProofForHdr := sp.proofsPool.HasProof(core.MetachainShardId, orderedMetaBlocksHashes[i]) + hasProofForHdr := sp.proofsPool.HasProof(core.MetachainShardId, currentMetaBlockHash) if !hasProofForHdr { log.Trace("no proof for meta header", - "hash", logger.DisplayByteSlice(orderedMetaBlocksHashes[i]), + "hash", logger.DisplayByteSlice(currentMetaBlockHash), ) - break + continue } - currentMetaBlockHash = orderedMetaBlocksHashes[i] metaBlock, ok := currentMetaBlock.(data.MetaHeaderHandler) if !ok { log.Warn("selectIncomingMiniBlocks: wrong type assertion for meta block") @@ -658,57 +619,35 @@ func (sp *shardProcessor) selectIncomingMiniBlocks( } if len(currentMetaBlock.GetMiniBlockHeadersWithDst(sp.shardCoordinator.SelfId())) == 0 { - sp.miniBlocksSelectionSession.AddReferencedMetaBlock(orderedMetaBlocks[i], orderedMetaBlocksHashes[i]) + sp.miniBlocksSelectionSession.AddReferencedHeader(currentMetaBlock, currentMetaBlockHash) + lastMeta = currentMetaBlock continue } currProcessedMiniBlocksInfo := sp.processedMiniBlocksTracker.GetProcessedMiniBlocksInfo(currentMetaBlockHash) - shouldContinue, pendingMiniBlocks, errCreated = sp.createMbsCrossShardDstMe(currentMetaBlockHash, metaBlock, currProcessedMiniBlocksInfo) + createIncomingMbsResult, errCreated = sp.createMbsCrossShardDstMe(currentMetaBlockHash, metaBlock, currProcessedMiniBlocksInfo) if errCreated != nil { return nil, errCreated } - if !shouldContinue { - break + + pendingMiniBlocks = append(pendingMiniBlocks, createIncomingMbsResult.PendingMiniBlocks...) + if len(createIncomingMbsResult.AddedMiniBlocks) > 0 { + errAdd := sp.miniBlocksSelectionSession.AddMiniBlocksAndHashes(createIncomingMbsResult.AddedMiniBlocks) + if errAdd != nil { + return nil, errAdd + } + sp.miniBlocksSelectionSession.AddReferencedHeader(currentMetaBlock, currentMetaBlockHash) + lastMeta = currentMetaBlock } - sp.miniBlocksSelectionSession.AddReferencedMetaBlock(currentMetaBlock, currentMetaBlockHash) + if !createIncomingMbsResult.HeaderFinished { + break + } } return pendingMiniBlocks, nil } -func (sp *shardProcessor) createMbsCrossShardDstMe( - currentMetaBlockHash []byte, - currentMetaBlock data.MetaHeaderHandler, - miniBlockProcessingInfo map[string]*processedMb.ProcessedMiniBlockInfo, -) (bool, []block.MiniblockAndHash, error) { - currMiniBlocksAdded, pendingMiniBlocks, currNumTxsAdded, hdrFinished, errCreate := sp.txCoordinator.CreateMbsCrossShardDstMe( - currentMetaBlock, - miniBlockProcessingInfo, - ) - if errCreate != nil { - return false, nil, errCreate - } - - err := sp.miniBlocksSelectionSession.AddMiniBlocksAndHashes(currMiniBlocksAdded) - if err != nil { - return false, nil, err - } - - if !hdrFinished { - log.Debug("meta block cannot be fully processed", - "round", currentMetaBlock.GetRound(), - "nonce", currentMetaBlock.GetNonce(), - "hash", currentMetaBlockHash, - "num mbs added", len(currMiniBlocksAdded), - "num txs added", currNumTxsAdded) - - return false, pendingMiniBlocks, nil - } - - return true, pendingMiniBlocks, nil -} - func (sp *shardProcessor) createProposalMiniBlocks(haveTime func() bool) error { if !haveTime() { log.Debug("shardProcessor.createProposalMiniBlocks", "error", process.ErrTimeIsOut) diff --git a/process/block/shardblockProposal_test.go b/process/block/shardblockProposal_test.go index 9707fe98eaa..71b6c80e997 100644 --- a/process/block/shardblockProposal_test.go +++ b/process/block/shardblockProposal_test.go @@ -13,14 +13,12 @@ import ( "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/data/transaction" - "github.com/multiversx/mx-chain-go/common" - "github.com/multiversx/mx-chain-go/common/graceperiod" - "github.com/multiversx/mx-chain-go/config" - "github.com/multiversx/mx-chain-go/state" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/multiversx/mx-chain-go/common" + "github.com/multiversx/mx-chain-go/common/graceperiod" + "github.com/multiversx/mx-chain-go/config" retriever "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/dataRetriever/blockchain" "github.com/multiversx/mx-chain-go/process" @@ -29,6 +27,7 @@ import ( "github.com/multiversx/mx-chain-go/process/block/processedMb" "github.com/multiversx/mx-chain-go/process/estimator" "github.com/multiversx/mx-chain-go/process/mock" + "github.com/multiversx/mx-chain-go/state" "github.com/multiversx/mx-chain-go/storage" "github.com/multiversx/mx-chain-go/testscommon" "github.com/multiversx/mx-chain-go/testscommon/cache" @@ -68,7 +67,7 @@ func haveTimeFalse() bool { return false } -type shardProcessorTest interface { +type processorTest interface { CreateBlockProposal( initialHdr data.HeaderHandler, haveTime func() bool, @@ -233,6 +232,7 @@ func TestShardProcessor_CreateBlockProposal(t *testing.T) { arguments.BlockTracker = &mock.BlockTrackerMock{ ComputeLongestMetaChainFromLastNotarizedCalled: func() ([]data.HeaderHandler, [][]byte, error) { return []data.HeaderHandler{&block.MetaBlockV3{ + Nonce: 1, ShardInfo: []block.ShardData{ { ShardID: 1, @@ -547,8 +547,10 @@ func TestShardProcessor_CreateBlockProposal(t *testing.T) { arguments.BlockTracker = &mock.BlockTrackerMock{ ComputeLongestMetaChainFromLastNotarizedCalled: func() ([]data.HeaderHandler, [][]byte, error) { return []data.HeaderHandler{&block.MetaBlockV3{ + Nonce: 1, ShardInfo: []block.ShardData{ { + Nonce: 0, ShardID: 1, ShardMiniBlockHeaders: []block.MiniBlockHeader{ { @@ -559,6 +561,7 @@ func TestShardProcessor_CreateBlockProposal(t *testing.T) { }, // for extra coverage, should be skipped as it is empty { + Nonce: 1, ShardID: 1, ShardMiniBlockHeaders: []block.MiniBlockHeader{}, }, @@ -566,12 +569,15 @@ func TestShardProcessor_CreateBlockProposal(t *testing.T) { MiniBlockHeaders: []block.MiniBlockHeader{ {}, }, - }}, + }, &block.MetaBlockV3{Nonce: 2}, + }, [][]byte{[]byte("hash_ok"), []byte("hash_empty")}, nil }, GetLastCrossNotarizedHeaderCalled: func(shardID uint32) (data.HeaderHandler, []byte, error) { - return &block.MetaBlockV3{}, []byte("hash"), nil // dummy + return &block.MetaBlockV3{ + Nonce: 0, + }, []byte("hash"), nil // dummy }, } providedMb := &block.MiniBlock{ @@ -841,7 +847,7 @@ func TestShardProcessor_SelectIncomingMiniBlocks(t *testing.T) { coreComponents, dataComponents, bootstrapComponents, statusComponents := createComponentHolderMocks() arguments := CreateMockArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ - GetReferencedMetaBlocksCalled: func() []data.HeaderHandler { + GetReferencedHeadersCalled: func() []data.HeaderHandler { require.Fail(t, "should have not been called") return nil }, @@ -872,7 +878,7 @@ func TestShardProcessor_SelectIncomingMiniBlocks(t *testing.T) { } arguments := CreateMockArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ - GetReferencedMetaBlocksCalled: func() []data.HeaderHandler { + GetReferencedHeadersCalled: func() []data.HeaderHandler { return make([]data.HeaderHandler, process.MaxMetaHeadersAllowedInOneShardBlock) }, } @@ -1021,7 +1027,7 @@ func TestShardProcessor_SelectIncomingMiniBlocks(t *testing.T) { arguments := CreateMockArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) cntAddReferencedMetaBlockCalled := 0 arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ - AddReferencedMetaBlockCalled: func(metaBlock data.HeaderHandler, metaBlockHash []byte) { + AddReferencedHeaderCalled: func(metaBlock data.HeaderHandler, metaBlockHash []byte) { cntAddReferencedMetaBlockCalled++ }, } @@ -1060,7 +1066,7 @@ func TestShardProcessor_SelectIncomingMiniBlocks(t *testing.T) { return make(map[string]uint32) // empty }, GetNonceCalled: func() uint64 { - return 2 // same nonce + return 3 }, }, &testscommon.HeaderHandlerStub{ @@ -1071,7 +1077,7 @@ func TestShardProcessor_SelectIncomingMiniBlocks(t *testing.T) { } }, GetNonceCalled: func() uint64 { - return 2 // same nonce + return 4 // same nonce }, }, } @@ -1079,7 +1085,8 @@ func TestShardProcessor_SelectIncomingMiniBlocks(t *testing.T) { orderedMetaBlocksHashes = append(orderedMetaBlocksHashes, []byte("hash4")) _, err = sp.SelectIncomingMiniBlocks(providedLastCrossNotarizedMetaHdr, orderedMetaBlocks, orderedMetaBlocksHashes, haveTimeTrue) require.NoError(t, err) - require.Equal(t, 2, cntAddReferencedMetaBlockCalled) // should be called twice, the third hdr returns shouldContinue false + // should be called for the first 2 meta blocks, the third one does not add any mini blocks, although it has some for the shard, so it is skipped + require.Equal(t, 2, cntAddReferencedMetaBlockCalled) }) } @@ -1926,12 +1933,12 @@ func TestShardProcessor_CheckMetaHeadersValidityAndFinalityProposal(t *testing.T func checkCreateBlockProposalResult( t *testing.T, - sp shardProcessorTest, + processor processorTest, header data.HeaderHandler, haveTime func() bool, expectedError error, ) { - hdr, body, err := sp.CreateBlockProposal(header, haveTime) + hdr, body, err := processor.CreateBlockProposal(header, haveTime) require.Equal(t, expectedError, err) require.Nil(t, hdr) require.Nil(t, body) @@ -2956,6 +2963,10 @@ func TestShardProcessor_OnProposedBlock(t *testing.T) { } } arguments := CreateMockArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + cntAddRef := 0 + arguments.MiniBlocksSelectionSession = &mbSelection.MiniBlockSelectionSessionStub{ + AddReferencedHeaderCalled: func(metaBlock data.HeaderHandler, metaBlockHash []byte) { cntAddRef++ }, + } sp, err := blproc.NewShardProcessor(arguments) require.Nil(t, err) diff --git a/process/block/shardinfo_test.go b/process/block/shardinfo_test.go new file mode 100644 index 00000000000..b0f4d76f982 --- /dev/null +++ b/process/block/shardinfo_test.go @@ -0,0 +1,1127 @@ +package block + +import ( + "fmt" + "testing" + + "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/common" + "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/mock" + dataRetrieverMock "github.com/multiversx/mx-chain-go/testscommon/dataRetriever" + "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" + poolMock "github.com/multiversx/mx-chain-go/testscommon/pool" + + "github.com/stretchr/testify/require" +) + +func TestShardInfo_IsInterfaceNil(t *testing.T) { + t.Parallel() + var si *ShardInfoCreateData + require.True(t, si.IsInterfaceNil()) + si = &ShardInfoCreateData{} + require.False(t, si.IsInterfaceNil()) +} + +func TestShardInfo_NewShardInfoCreateData(t *testing.T) { + t.Parallel() + + t.Run("nil enableEpochsHandler", func(t *testing.T) { + t.Parallel() + + args := createDefaultShardInfoCreateDataArgs() + sicd, err := NewShardInfoCreateData( + nil, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, sicd) + require.Equal(t, process.ErrNilEnableEpochsHandler, err) + }) + + t.Run("nil headersPool", func(t *testing.T) { + t.Parallel() + args := createDefaultShardInfoCreateDataArgs() + sicd, err := NewShardInfoCreateData( + args.enableEpochsHandler, + nil, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, sicd) + require.Equal(t, process.ErrNilHeadersDataPool, err) + }) + + t.Run("nil proofsPool", func(t *testing.T) { + t.Parallel() + + args := createDefaultShardInfoCreateDataArgs() + + sicd, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + nil, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, sicd) + require.Equal(t, process.ErrNilProofsPool, err) + }) + + t.Run("nil pendingMiniBlocksHandler", func(t *testing.T) { + t.Parallel() + + args := createDefaultShardInfoCreateDataArgs() + + sicd, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + nil, + args.blockTracker, + ) + require.Nil(t, sicd) + require.Equal(t, process.ErrNilPendingMiniBlocksHandler, err) + }) + + t.Run("nil blockTracker", func(t *testing.T) { + t.Parallel() + + args := createDefaultShardInfoCreateDataArgs() + sicd, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + nil, + ) + require.Nil(t, sicd) + require.Equal(t, process.ErrNilBlockTracker, err) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + args := createDefaultShardInfoCreateDataArgs() + + sicd, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.NotNil(t, sicd) + require.Nil(t, err) + }) +} + +func TestShardInfoCreateData_CreateShardInfoV3(t *testing.T) { + t.Parallel() + + hdrHash0 := []byte("header hash for shard 0") + hdrHash1 := []byte("header hash for shard 1") + hdrHash2 := []byte("header hash for shard 2") + hdrHash3 := []byte("header hash for shard 2 V3") + + header0 := getHeaderV3ForShard(uint32(0), hdrHash0) + header1 := getShardHeaderForShard(uint32(1)) + header2 := getShardHeaderForShard(uint32(2)) + header3 := getHeaderV3ForShard(uint32(2), hdrHash3) + headers := []data.HeaderHandler{header0, header1, header2, header3} + + headerHashes := [][]byte{hdrHash0, hdrHash1, hdrHash2, hdrHash3} + + pool := dataRetrieverMock.NewPoolsHolderMock() + pool.Headers().AddHeader(hdrHash0, header0) + pool.Headers().AddHeader(hdrHash1, header1) + pool.Headers().AddHeader(hdrHash2, header2) + pool.Headers().AddHeader(hdrHash3, header3) + + round := uint64(10) + metaHdrV3 := &block.MetaBlockV3{Round: round} + args := createDefaultShardInfoCreateDataArgs() + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + pool.Headers(), + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + + t.Run("should fail with nil meta header", func(t *testing.T) { + t.Parallel() + + shardDataProposalHandlers, shardDataHandlers, err := sic.CreateShardInfoV3(nil, headers, headerHashes) + require.ErrorIs(t, err, process.ErrNilHeaderHandler) + require.Nil(t, shardDataProposalHandlers) + require.Nil(t, shardDataHandlers) + }) + + t.Run("should fail with legacy meta header", func(t *testing.T) { + t.Parallel() + + metaHdr := &block.MetaBlock{Round: round} + + shardDataProposalHandlers, shardDataHandlers, err := sic.CreateShardInfoV3(metaHdr, headers, headerHashes) + require.Equal(t, process.ErrInvalidHeader, err) + require.Nil(t, shardDataProposalHandlers) + require.Nil(t, shardDataHandlers) + }) + t.Run("should fail with inconsistent headers and hashes", func(t *testing.T) { + t.Parallel() + + shardDataProposalHandlers, shardDataHandlers, err := sic.CreateShardInfoV3(metaHdrV3, headers, headerHashes[:2]) + require.Equal(t, process.ErrInconsistentShardHeadersAndHashes, err) + require.Nil(t, shardDataProposalHandlers) + require.Nil(t, shardDataHandlers) + }) + t.Run("should fail when createShardInfoFromHeader errors", func(t *testing.T) { + t.Parallel() + + shardDataProposalHandlers, shardDataHandlers, err := sic.CreateShardInfoV3(metaHdrV3, headers, headerHashes) + require.Contains(t, err.Error(), process.ErrMissingHeaderProof.Error()) + require.Nil(t, shardDataProposalHandlers) + require.Nil(t, shardDataHandlers) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + args := createDefaultShardInfoCreateDataArgs() + args.pendingMiniBlocksHandler.GetPendingMiniBlocksCalled = func(shardID uint32) [][]byte { + return [][]byte{[]byte("hash1"), []byte("hash2")} + } + args.blockTracker.GetLastSelfNotarizedHeaderCalled = func(shardID uint32) (data.HeaderHandler, []byte, error) { + return &block.Header{Nonce: headers[shardID].GetNonce()}, []byte("selfNotarizedHash"), nil + } + args.enableEpochsHandler.IsFlagEnabledInEpochCalled = func(flag core.EnableEpochFlag, epoch uint32) bool { + return true + } + args.proofsPool.HasProofCalled = func(shardID uint32, headerHash []byte) bool { + return true + } + + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + pool.Headers(), + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + shardDataProposalHandlers, shardDataHandlers, err := sic.CreateShardInfoV3(metaHdrV3, headers, headerHashes) + require.Nil(t, err) + require.NotNil(t, shardDataHandlers) + require.NotNil(t, shardDataProposalHandlers) + require.Equal(t, 4, len(shardDataHandlers)) + require.Equal(t, 4, len(shardDataProposalHandlers)) + }) +} + +func TestShardInfoCreateData_CreateShardInfoFromLegacyMeta(t *testing.T) { + t.Parallel() + + pool := dataRetrieverMock.NewPoolsHolderMock() + // we will have a 3 hdrs in pool + hdrHash1 := []byte("hdr hash 1") + hdrHash2 := []byte("hdr hash 2") + hdrHash3 := []byte("hdr hash 3") + + mbHash1 := []byte("mb hash 1") + mbHash2 := []byte("mb hash 2") + mbHash3 := []byte("mb hash 3") + + miniBlockHeader1 := block.MiniBlockHeader{Hash: mbHash1} + miniBlockHeader2 := block.MiniBlockHeader{Hash: mbHash2} + miniBlockHeader3 := block.MiniBlockHeader{Hash: mbHash3} + + miniBlockHeaders1 := []block.MiniBlockHeader{miniBlockHeader1, miniBlockHeader2, miniBlockHeader3} + miniBlockHeaders2 := []block.MiniBlockHeader{miniBlockHeader1, miniBlockHeader2} + miniBlockHeaders3 := []block.MiniBlockHeader{miniBlockHeader1} + + header1 := &block.Header{ + Round: 1, + Nonce: 45, + ShardID: 0, + MiniBlockHeaders: miniBlockHeaders1} + header2 := &block.Header{ + Round: 2, + Nonce: 45, + ShardID: 1, + MiniBlockHeaders: miniBlockHeaders2} + header3 := &block.Header{ + Round: 3, + Nonce: 45, + ShardID: 2, + MiniBlockHeaders: miniBlockHeaders3} + // put the existing headers inside datapool + pool.Headers().AddHeader(hdrHash1, header1) + pool.Headers().AddHeader(hdrHash2, header2) + pool.Headers().AddHeader(hdrHash3, header3) + + headerHashes := [][]byte{hdrHash1, hdrHash2, hdrHash3} + headers := []data.ShardHeaderHandler{header1, header2, header3} + round := uint64(10) + metaHdr := &block.MetaBlock{Round: round} + + args := createDefaultShardInfoCreateDataArgs() + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + t.Run("should fail with nil meta header", func(t *testing.T) { + t.Parallel() + + shardInfo, err := sic.CreateShardInfoFromLegacyMeta(nil, headers, headerHashes) + require.Equal(t, process.ErrNilHeaderHandler, err) + require.Nil(t, shardInfo) + }) + t.Run("should fail with V3 meta header", func(t *testing.T) { + t.Parallel() + + metaHdrV3 := &block.MetaBlockV3{Round: round} + shardInfo, err := sic.CreateShardInfoFromLegacyMeta(metaHdrV3, headers, headerHashes) + require.Equal(t, process.ErrInvalidHeader, err) + require.Nil(t, shardInfo) + }) + t.Run("should fail with inconsistent headers and hashes", func(t *testing.T) { + t.Parallel() + + shardInfo, err := sic.CreateShardInfoFromLegacyMeta(metaHdr, headers, headerHashes[:1]) + require.Equal(t, process.ErrInconsistentShardHeadersAndHashes, err) + require.Nil(t, shardInfo) + + }) + t.Run("should fail when createShardDataFromLegacyHeader errors", func(t *testing.T) { + t.Parallel() + + args := createDefaultShardInfoCreateDataArgs() + args.pendingMiniBlocksHandler.GetPendingMiniBlocksCalled = func(shardID uint32) [][]byte { + return [][]byte{[]byte("hash1"), []byte("hash2")} + } + args.blockTracker.GetLastSelfNotarizedHeaderCalled = func(shardID uint32) (data.HeaderHandler, []byte, error) { + return nil, nil, errExpected + } + args.enableEpochsHandler.IsFlagEnabledInEpochCalled = func(flag core.EnableEpochFlag, epoch uint32) bool { + return true + } + args.proofsPool.HasProofCalled = func(shardID uint32, headerHash []byte) bool { + return true + } + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + pool.Headers(), + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + shardInfo, err := sic.CreateShardInfoFromLegacyMeta(metaHdr, headers, headerHashes) + require.NotNil(t, err) + require.Nil(t, shardInfo) + require.Equal(t, errExpected, err) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + args := createDefaultShardInfoCreateDataArgs() + args.pendingMiniBlocksHandler.GetPendingMiniBlocksCalled = func(shardID uint32) [][]byte { + return [][]byte{[]byte("hash1"), []byte("hash2")} + } + args.blockTracker.GetLastSelfNotarizedHeaderCalled = func(shardID uint32) (data.HeaderHandler, []byte, error) { + return &block.Header{Nonce: headers[shardID].GetNonce()}, []byte("selfNotarizedHash"), nil + } + args.enableEpochsHandler.IsFlagEnabledInEpochCalled = func(flag core.EnableEpochFlag, epoch uint32) bool { + return true + } + args.proofsPool.HasProofCalled = func(shardID uint32, headerHash []byte) bool { + return true + } + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + pool.Headers(), + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + shardInfo, err := sic.CreateShardInfoFromLegacyMeta(metaHdr, headers, headerHashes) + require.Nil(t, err) + require.NotNil(t, shardInfo) + require.Equal(t, 3, len(shardInfo)) + }) +} + +func TestShardInfoCreateData_createShardInfoFromHeader(t *testing.T) { + t.Parallel() + + t.Run("should fail with nil header", func(t *testing.T) { + t.Parallel() + args := createDefaultShardInfoCreateDataArgs() + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + + shardDataProposalHandlers, shardDataHandlers, err := sic.createShardInfoFromHeader(nil, nil) + require.Nil(t, shardDataProposalHandlers) + require.Nil(t, shardDataHandlers) + require.ErrorIs(t, err, process.ErrNilHeaderHandler) + }) + t.Run("should fail with invalid hash", func(t *testing.T) { + t.Parallel() + args := createDefaultShardInfoCreateDataArgs() + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + + shardDataProposalHandlers, shardDataHandlers, err := sic.createShardInfoFromHeader(&block.Header{}, []byte{}) + require.Nil(t, shardDataProposalHandlers) + require.Nil(t, shardDataHandlers) + require.ErrorIs(t, err, process.ErrInvalidHash) + }) + + t.Run("should fail with missing shard header proof", func(t *testing.T) { + t.Parallel() + args := createDefaultShardInfoCreateDataArgs() + args.proofsPool.HasProofCalled = func(shardID uint32, headerHash []byte) bool { + return false + } + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + + shardDataProposalHandlers, shardDataHandlers, err := sic.createShardInfoFromHeader(&block.Header{Nonce: 1}, []byte("hash")) + require.Nil(t, shardDataProposalHandlers) + require.Nil(t, shardDataHandlers) + require.ErrorIs(t, err, process.ErrMissingHeaderProof) + }) + t.Run("should work with Legacy", func(t *testing.T) { + t.Parallel() + header := getShardHeaderForShard(uint32(1)) + args := createDefaultShardInfoCreateDataArgs() + args.pendingMiniBlocksHandler.GetPendingMiniBlocksCalled = func(shardID uint32) [][]byte { + return [][]byte{[]byte("hash1"), []byte("hash2")} + } + args.blockTracker.GetLastSelfNotarizedHeaderCalled = func(shardID uint32) (data.HeaderHandler, []byte, error) { + return &block.Header{Nonce: header.GetNonce()}, []byte("selfNotarizedHash"), nil + } + args.enableEpochsHandler.IsFlagEnabledInEpochCalled = func(flag core.EnableEpochFlag, epoch uint32) bool { + return true + } + args.proofsPool.HasProofCalled = func(shardID uint32, headerHash []byte) bool { + return true + } + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + expectedProposalHandler := &block.ShardDataProposal{ + HeaderHash: []byte("hash"), + Round: 10, + Nonce: 45, + ShardID: 1, + Epoch: 0, + } + + shardDataProposalHandler, shardDataHandlers, err := sic.createShardInfoFromHeader(header, []byte("hash")) + require.Nil(t, err) + require.NotNil(t, shardDataProposalHandler) + require.NotNil(t, shardDataHandlers) + require.Equal(t, expectedProposalHandler, shardDataProposalHandler) + }) + t.Run("should work with Legacy no proof for nonce < 1", func(t *testing.T) { + t.Parallel() + header := getShardHeaderForShard(uint32(1)) + _ = header.SetNonce(0) + args := createDefaultShardInfoCreateDataArgs() + args.pendingMiniBlocksHandler.GetPendingMiniBlocksCalled = func(shardID uint32) [][]byte { + return [][]byte{[]byte("hash1"), []byte("hash2")} + } + args.blockTracker.GetLastSelfNotarizedHeaderCalled = func(shardID uint32) (data.HeaderHandler, []byte, error) { + return &block.Header{Nonce: header.GetNonce()}, []byte("selfNotarizedHash"), nil + } + args.enableEpochsHandler.IsFlagEnabledInEpochCalled = func(flag core.EnableEpochFlag, epoch uint32) bool { + return true + } + args.proofsPool.HasProofCalled = func(shardID uint32, headerHash []byte) bool { + return false + } + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + expectedProposalHandler := &block.ShardDataProposal{ + HeaderHash: []byte("hash"), + Round: 10, + Nonce: 0, + ShardID: 1, + Epoch: 0, + } + + shardDataProposalHandler, shardDataHandlers, err := sic.createShardInfoFromHeader(header, []byte("hash")) + require.Nil(t, err) + require.NotNil(t, shardDataProposalHandler) + require.NotNil(t, shardDataHandlers) + require.Equal(t, expectedProposalHandler, shardDataProposalHandler) + }) + t.Run("should work with V3", func(t *testing.T) { + t.Parallel() + header := getHeaderV3ForShard(uint32(1), []byte("header hash for shard 1")) + args := createDefaultShardInfoCreateDataArgs() + args.headersPool.GetHeaderByHashCalled = func(hash []byte) (data.HeaderHandler, error) { + return header, nil + } + + args.pendingMiniBlocksHandler.GetPendingMiniBlocksCalled = func(shardID uint32) [][]byte { + return [][]byte{[]byte("hash1"), []byte("hash2")} + } + args.blockTracker.GetLastSelfNotarizedHeaderCalled = func(shardID uint32) (data.HeaderHandler, []byte, error) { + return &block.Header{Nonce: header.GetNonce()}, []byte("selfNotarizedHash"), nil + } + args.enableEpochsHandler.IsFlagEnabledInEpochCalled = func(flag core.EnableEpochFlag, epoch uint32) bool { + return true + } + args.proofsPool.HasProofCalled = func(shardID uint32, headerHash []byte) bool { + return true + } + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + expectedProposalHandler := &block.ShardDataProposal{ + HeaderHash: []byte("hash"), + Round: 10, + Nonce: 45, + ShardID: 1, + Epoch: 7, + } + + shardDataProposalHandler, shardDataHandlers, err := sic.createShardInfoFromHeader(header, []byte("hash")) + require.Nil(t, err) + require.NotNil(t, shardDataProposalHandler) + require.NotNil(t, shardDataHandlers) + require.Equal(t, expectedProposalHandler, shardDataProposalHandler) + }) + t.Run("should work with V3 no proof for nonce < 1", func(t *testing.T) { + t.Parallel() + header := getHeaderV3ForShard(uint32(1), []byte("header hash for shard 1")) + expectedNonce := uint64(0) + _ = header.SetNonce(expectedNonce) + args := createDefaultShardInfoCreateDataArgs() + args.headersPool.GetHeaderByHashCalled = func(hash []byte) (data.HeaderHandler, error) { + return header, nil + } + args.pendingMiniBlocksHandler.GetPendingMiniBlocksCalled = func(shardID uint32) [][]byte { + return [][]byte{[]byte("hash1"), []byte("hash2")} + } + args.blockTracker.GetLastSelfNotarizedHeaderCalled = func(shardID uint32) (data.HeaderHandler, []byte, error) { + return &block.Header{Nonce: header.GetNonce()}, []byte("selfNotarizedHash"), nil + } + args.enableEpochsHandler.IsFlagEnabledInEpochCalled = func(flag core.EnableEpochFlag, epoch uint32) bool { + return true + } + args.proofsPool.HasProofCalled = func(shardID uint32, headerHash []byte) bool { + return false + } + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + expectedProposalHandler := &block.ShardDataProposal{ + HeaderHash: []byte("hash"), + Round: 10, + Nonce: 0, + ShardID: 1, + Epoch: 7, + } + + shardDataProposalHandler, shardDataHandlers, err := sic.createShardInfoFromHeader(header, []byte("hash")) + require.Nil(t, err) + require.NotNil(t, shardDataProposalHandler) + require.NotNil(t, shardDataHandlers) + require.Equal(t, expectedProposalHandler, shardDataProposalHandler) + }) +} + +func TestShardInfoCreateData_createShardDataFromLegacyHeader(t *testing.T) { + t.Parallel() + + t.Run("should fail with updateShardDataWithCrossShardInfo error", func(t *testing.T) { + t.Parallel() + header := getShardHeaderForShard(uint32(1)) + args := createDefaultShardInfoCreateDataArgs() + args.pendingMiniBlocksHandler.GetPendingMiniBlocksCalled = func(shardID uint32) [][]byte { + return [][]byte{[]byte("hash1"), []byte("hash2")} + } + args.blockTracker.GetLastSelfNotarizedHeaderCalled = func(shardID uint32) (data.HeaderHandler, []byte, error) { + return nil, nil, fmt.Errorf("GetLastSelfNotarizedHeader error") + } + args.enableEpochsHandler.IsFlagEnabledInEpochCalled = func(flag core.EnableEpochFlag, epoch uint32) bool { + return false + } + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + shardDataList, err := sic.createShardDataFromLegacyHeader(header, []byte("headerHash")) + require.Contains(t, err.Error(), "GetLastSelfNotarizedHeader error") + require.Nil(t, shardDataList) + }) + + t.Run("should work with enable epoch flag disabled", func(t *testing.T) { + t.Parallel() + header := getShardHeaderForShard(uint32(1)) + args := createDefaultShardInfoCreateDataArgs() + args.pendingMiniBlocksHandler.GetPendingMiniBlocksCalled = func(shardID uint32) [][]byte { + return [][]byte{[]byte("hash1"), []byte("hash2")} + } + args.blockTracker.GetLastSelfNotarizedHeaderCalled = func(shardID uint32) (data.HeaderHandler, []byte, error) { + return &block.Header{Nonce: header.GetNonce()}, []byte("selfNotarizedHash"), nil + } + args.enableEpochsHandler.IsFlagEnabledInEpochCalled = func(flag core.EnableEpochFlag, epoch uint32) bool { + return false + } + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + shardDataList, err := sic.createShardDataFromLegacyHeader(header, []byte("headerHash")) + require.Nil(t, err) + require.NotNil(t, shardDataList) + require.Equal(t, 1, len(shardDataList)) + require.Equal(t, header.GetNonce(), shardDataList[0].GetNonce()) + require.Equal(t, uint32(0), shardDataList[0].(*block.ShardData).GetEpoch()) + }) + t.Run("should work with enable epoch flag enabled", func(t *testing.T) { + t.Parallel() + header := getShardHeaderForShard(uint32(1)) + args := createDefaultShardInfoCreateDataArgs() + args.pendingMiniBlocksHandler.GetPendingMiniBlocksCalled = func(shardID uint32) [][]byte { + return [][]byte{[]byte("hash1"), []byte("hash2")} + } + args.blockTracker.GetLastSelfNotarizedHeaderCalled = func(shardID uint32) (data.HeaderHandler, []byte, error) { + return &block.Header{Nonce: header.GetNonce()}, []byte("selfNotarizedHash"), nil + } + args.enableEpochsHandler.IsFlagEnabledInEpochCalled = func(flag core.EnableEpochFlag, epoch uint32) bool { + return true + } + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + shardDataList, err := sic.createShardDataFromLegacyHeader(header, []byte("headerHash")) + require.Nil(t, err) + require.NotNil(t, shardDataList) + require.Equal(t, 1, len(shardDataList)) + require.Equal(t, header.GetNonce(), shardDataList[0].GetNonce()) + require.Equal(t, header.GetEpoch(), shardDataList[0].(*block.ShardData).GetEpoch()) + }) +} +func TestShardInfoCreateData_createShardDataFromV3Header(t *testing.T) { + t.Parallel() + + t.Run("should fail with nil header", func(t *testing.T) { + t.Parallel() + args := createDefaultShardInfoCreateDataArgs() + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + shardDataProposalHandler, shardDataHandlers, err := sic.createShardDataFromV3Header(nil, nil) + require.Nil(t, shardDataHandlers) + require.Nil(t, shardDataProposalHandler) + require.ErrorIs(t, err, process.ErrNilHeaderHandler) + }) + t.Run("should return early if no execution results", func(t *testing.T) { + t.Parallel() + header := &block.HeaderV3{ + Nonce: 0, + } + args := createDefaultShardInfoCreateDataArgs() + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + shardDataProposalHandler, shardDataHandlers, err := sic.createShardDataFromV3Header(header, []byte("headerHash")) + require.Nil(t, err) + require.NotNil(t, shardDataHandlers) + require.NotNil(t, shardDataProposalHandler) + require.Equal(t, 0, len(shardDataHandlers)) + }) + t.Run("should fail with createShardDataFromExecutionResult error", func(t *testing.T) { + t.Parallel() + expectedNonce := uint64(12345) + header := getHeaderV3ForShard(uint32(1), []byte("header hash for shard 1")) + args := createDefaultShardInfoCreateDataArgs() + // GetHeaderByHash error will fail createShardDataFromExecutionResult + args.headersPool.GetHeaderByHashCalled = func(hash []byte) (data.HeaderHandler, error) { + return nil, fmt.Errorf("GetHeaderByHash error") + } + + args.pendingMiniBlocksHandler.GetPendingMiniBlocksCalled = func(shardID uint32) [][]byte { + return [][]byte{[]byte("hash1"), []byte("hash2")} + } + args.blockTracker.GetLastSelfNotarizedHeaderCalled = func(shardID uint32) (data.HeaderHandler, []byte, error) { + return &block.Header{Nonce: expectedNonce}, []byte("selfNotarizedHash"), nil + } + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + shardDataProposalHandler, shardDataHandlers, err := sic.createShardDataFromV3Header(header, []byte("headerHash")) + require.Nil(t, shardDataHandlers) + require.Nil(t, shardDataProposalHandler) + require.Equal(t, fmt.Errorf("GetHeaderByHash error"), err) + }) + + t.Run("should work", func(t *testing.T) { + t.Parallel() + expectedNonce := uint64(12345) + header := getHeaderV3ForShard(uint32(1), []byte("header hash for shard 1")) + args := createDefaultShardInfoCreateDataArgs() + args.headersPool.GetHeaderByHashCalled = func(hash []byte) (data.HeaderHandler, error) { + return header, nil + } + args.pendingMiniBlocksHandler.GetPendingMiniBlocksCalled = func(shardID uint32) [][]byte { + return [][]byte{[]byte("hash1"), []byte("hash2")} + } + args.blockTracker.GetLastSelfNotarizedHeaderCalled = func(shardID uint32) (data.HeaderHandler, []byte, error) { + return &block.Header{Nonce: expectedNonce}, []byte("selfNotarizedHash"), nil + } + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + shardDataProposalHandler, shardDataHandlers, err := sic.createShardDataFromV3Header(header, []byte("headerHash")) + require.Nil(t, err) + require.NotNil(t, shardDataHandlers) + require.Equal(t, 1, len(shardDataHandlers)) + require.Equal(t, expectedNonce, shardDataHandlers[0].GetNonce()) + require.NotNil(t, shardDataProposalHandler) + require.Equal(t, int(header.GetNonce()), int(shardDataProposalHandler.GetNonce())) + }) +} + +func TestShardInfoCreateData_createShardDataFromExecutionResult(t *testing.T) { + t.Parallel() + + t.Run("should fail with nil execution result", func(t *testing.T) { + t.Parallel() + args := createDefaultShardInfoCreateDataArgs() + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + shardData, err := sic.createShardDataFromExecutionResult(nil) + require.ErrorIs(t, err, process.ErrNilExecutionResultHandler) + require.Nil(t, shardData) + }) + + t.Run("should fail with wrong type of execution result", func(t *testing.T) { + t.Parallel() + args := createDefaultShardInfoCreateDataArgs() + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + shardData, err := sic.createShardDataFromExecutionResult(&block.BaseExecutionResult{}) + require.ErrorIs(t, err, process.ErrWrongTypeAssertion) + require.Nil(t, shardData) + }) + + t.Run("should fail when GetHeaderByHash errors", func(t *testing.T) { + t.Parallel() + args := createDefaultShardInfoCreateDataArgs() + args.headersPool.GetHeaderByHashCalled = func(hash []byte) (data.HeaderHandler, error) { + return nil, fmt.Errorf("GetHeaderByHash error") + } + + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + execResult := getExecutionResultForShard(uint32(1), []byte("header hash for shard 1")) + shardData, err := sic.createShardDataFromExecutionResult(execResult) + require.Equal(t, fmt.Errorf("GetHeaderByHash error"), err) + require.Nil(t, shardData) + }) + + t.Run("should fail when updateShardDataWithCrossShardInfo fails", func(t *testing.T) { + t.Parallel() + args := createDefaultShardInfoCreateDataArgs() + args.headersPool.GetHeaderByHashCalled = func(hash []byte) (data.HeaderHandler, error) { + return getShardHeaderForShard(uint32(1)), nil + } + + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + execResult := getExecutionResultForShard(uint32(1), []byte("header hash for shard 1")) + shardData, err := sic.createShardDataFromExecutionResult(execResult) + require.Equal(t, process.ErrNotarizedHeadersSliceIsNil, err) + require.Nil(t, shardData) + }) + + t.Run("should work", func(t *testing.T) { + t.Parallel() + expectedNonce := uint64(1) + header := getShardHeaderForShard(uint32(1)) + args := createDefaultShardInfoCreateDataArgs() + args.headersPool.GetHeaderByHashCalled = func(hash []byte) (data.HeaderHandler, error) { + return header, nil + } + args.pendingMiniBlocksHandler.GetPendingMiniBlocksCalled = func(shardID uint32) [][]byte { + return [][]byte{[]byte("hash1"), []byte("hash2")} + } + args.blockTracker.GetLastSelfNotarizedHeaderCalled = func(shardID uint32) (data.HeaderHandler, []byte, error) { + return &block.Header{Nonce: expectedNonce}, []byte("selfNotarizedHash"), nil + } + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + execResult := getExecutionResultForShard(uint32(1), []byte("header hash for shard 1")) + shardData, err := sic.createShardDataFromExecutionResult(execResult) + require.Nil(t, err) + require.NotNil(t, shardData) + require.Equal(t, uint32(2), shardData.GetNumPendingMiniBlocks()) + require.Equal(t, uint32(execResult.GetExecutedTxCount()), shardData.GetTxCount()) + require.Equal(t, uint32(1), shardData.GetShardID()) + require.Equal(t, execResult.GetAccumulatedFees(), shardData.GetAccumulatedFees()) + require.Equal(t, execResult.GetHeaderHash(), shardData.GetHeaderHash()) + require.Equal(t, execResult.GetHeaderRound(), shardData.GetRound()) + require.Equal(t, header.GetPrevHash(), shardData.GetPrevHash()) + require.Equal(t, execResult.GetHeaderNonce(), shardData.GetNonce()) + require.Equal(t, header.GetPrevRandSeed(), shardData.GetPrevRandSeed()) + require.Equal(t, header.GetPubKeysBitmap(), shardData.GetPubKeysBitmap()) + require.Equal(t, execResult.GetAccumulatedFees(), shardData.GetAccumulatedFees()) + require.Equal(t, execResult.GetDeveloperFees(), shardData.GetDeveloperFees()) + require.Equal(t, header.GetEpoch(), shardData.(*block.ShardData).GetEpoch()) + }) +} + +func TestShardInfoCreateData_miniBlockHeaderFromMiniBlockHeader(t *testing.T) { + t.Parallel() + + t.Run("ScheduledMiniBlocksFlag disabled", func(t *testing.T) { + t.Parallel() + headerHandler := getShardHeaderForShard(uint32(1)) + enableEpochsHandler := enableEpochsHandlerMock.NewEnableEpochsHandlerStub() + + miniblockHeaders := createShardMiniBlockHeaderFromHeader(headerHandler, enableEpochsHandler) + require.NotNil(t, miniblockHeaders) + require.Equal(t, 3, len(miniblockHeaders)) + checkMiniBlockHeadersComparable(t, headerHandler.GetMiniBlockHeaderHandlers()[0], miniblockHeaders[0]) + checkMiniBlockHeadersComparable(t, headerHandler.GetMiniBlockHeaderHandlers()[1], miniblockHeaders[1]) + checkMiniBlockHeadersComparable(t, headerHandler.GetMiniBlockHeaderHandlers()[2], miniblockHeaders[2]) + }) + t.Run("ScheduledMiniBlocksFlag enabled, all miniblocks final", func(t *testing.T) { + t.Parallel() + headerHandler := getShardHeaderForShard(uint32(1)) + enableEpochsHandler := enableEpochsHandlerMock.NewEnableEpochsHandlerStub() + enableEpochsHandler.IsFlagEnabledCalled = func(flag core.EnableEpochFlag) bool { + return flag == common.ScheduledMiniBlocksFlag + } + + miniblockHeaders := createShardMiniBlockHeaderFromHeader(headerHandler, enableEpochsHandler) + require.NotNil(t, miniblockHeaders) + require.Equal(t, 3, len(miniblockHeaders)) + checkMiniBlockHeadersComparable(t, headerHandler.GetMiniBlockHeaderHandlers()[0], miniblockHeaders[0]) + checkMiniBlockHeadersComparable(t, headerHandler.GetMiniBlockHeaderHandlers()[1], miniblockHeaders[1]) + checkMiniBlockHeadersComparable(t, headerHandler.GetMiniBlockHeaderHandlers()[2], miniblockHeaders[2]) + }) + t.Run("ScheduledMiniBlocksFlag enabled, not all miniblocks final", func(t *testing.T) { + t.Parallel() + headerHandler := getShardHeaderForShard(uint32(1)) + enableEpochsHandler := enableEpochsHandlerMock.NewEnableEpochsHandlerStub() + enableEpochsHandler.IsFlagEnabledCalled = func(flag core.EnableEpochFlag) bool { + return flag == common.ScheduledMiniBlocksFlag + } + _ = headerHandler.GetMiniBlockHeaderHandlers()[1].SetConstructionState(int32(block.Proposed)) + require.False(t, headerHandler.GetMiniBlockHeaderHandlers()[1].IsFinal()) + + miniblockHeaders := createShardMiniBlockHeaderFromHeader(headerHandler, enableEpochsHandler) + require.NotNil(t, miniblockHeaders) + require.Equal(t, 2, len(miniblockHeaders)) + checkMiniBlockHeadersComparable(t, headerHandler.GetMiniBlockHeaderHandlers()[0], miniblockHeaders[0]) + checkMiniBlockHeadersComparable(t, headerHandler.GetMiniBlockHeaderHandlers()[2], miniblockHeaders[1]) + }) + t.Run("ScheduledMiniBlocksFlag enabled, no final miniblocks", func(t *testing.T) { + t.Parallel() + headerHandler := getShardHeaderForShard(uint32(1)) + enableEpochsHandler := enableEpochsHandlerMock.NewEnableEpochsHandlerStub() + enableEpochsHandler.IsFlagEnabledCalled = func(flag core.EnableEpochFlag) bool { + return flag == common.ScheduledMiniBlocksFlag + } + for i := 0; i < len(headerHandler.GetMiniBlockHeaderHandlers()); i++ { + _ = headerHandler.GetMiniBlockHeaderHandlers()[i].SetConstructionState(int32(block.Proposed)) + require.False(t, headerHandler.GetMiniBlockHeaderHandlers()[i].IsFinal()) + } + + miniblockHeaders := createShardMiniBlockHeaderFromHeader(headerHandler, enableEpochsHandler) + require.NotNil(t, miniblockHeaders) + require.Equal(t, 0, len(miniblockHeaders)) + }) +} + +func TestShardInfoCreateData_createShardMiniBlockHeaderFromExecutionResultHandler(t *testing.T) { + t.Parallel() + + execResultHandler := getExecutionResultForShard(uint32(1), []byte("header hash for shard 1")) + shardMiniBlockHeaders := createShardMiniBlockHeaderFromExecutionResultHandler(execResultHandler) + require.NotNil(t, shardMiniBlockHeaders) + require.Equal(t, 3, len(shardMiniBlockHeaders)) + for i := 0; i < len(shardMiniBlockHeaders); i++ { + require.Equal(t, execResultHandler.MiniBlockHeaders[i].Hash, shardMiniBlockHeaders[i].Hash) + require.Equal(t, execResultHandler.MiniBlockHeaders[i].Type, shardMiniBlockHeaders[i].Type) + require.Equal(t, execResultHandler.MiniBlockHeaders[i].TxCount, shardMiniBlockHeaders[i].TxCount) + require.Equal(t, execResultHandler.MiniBlockHeaders[i].SenderShardID, shardMiniBlockHeaders[i].SenderShardID) + require.Equal(t, execResultHandler.MiniBlockHeaders[i].ReceiverShardID, shardMiniBlockHeaders[i].ReceiverShardID) + } +} + +// TODO modify when the function is updated +func TestShardInfoCreateData_updateShardDataWithCrossShardInfo(t *testing.T) { + t.Parallel() + header := block.Header{ShardID: 1} + shardData := &block.ShardData{} + + t.Run("should fail with GetLastSelfNotarizedHeader error", func(t *testing.T) { + t.Parallel() + args := createDefaultShardInfoCreateDataArgs() + args.blockTracker.GetLastSelfNotarizedHeaderCalled = func(shardID uint32) (data.HeaderHandler, []byte, error) { + return nil, nil, fmt.Errorf("GetLastSelfNotarizedHeader error") + } + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + err = sic.updateShardDataWithCrossShardInfo(shardData, &header) + require.Contains(t, err.Error(), "GetLastSelfNotarizedHeader error") + }) + + t.Run("should work with no data", func(t *testing.T) { + t.Parallel() + expectedNonce := uint64(12345) + args := createDefaultShardInfoCreateDataArgs() + args.pendingMiniBlocksHandler.GetPendingMiniBlocksCalled = func(shardID uint32) [][]byte { + return [][]byte{[]byte("hash1"), []byte("hash2")} + } + args.blockTracker.GetLastSelfNotarizedHeaderCalled = func(shardID uint32) (data.HeaderHandler, []byte, error) { + return &block.Header{Nonce: expectedNonce}, []byte("selfNotarizedHash"), nil + } + sic, err := NewShardInfoCreateData( + args.enableEpochsHandler, + args.headersPool, + args.proofsPool, + args.pendingMiniBlocksHandler, + args.blockTracker, + ) + require.Nil(t, err) + err = sic.updateShardDataWithCrossShardInfo(shardData, &header) + require.NotNil(t, shardData) + require.Nil(t, err) + require.Equal(t, uint32(2), shardData.NumPendingMiniBlocks) + require.Equal(t, expectedNonce, shardData.LastIncludedMetaNonce) + }) +} + +type shardInfoCreateDataTestArgs struct { + headersPool *poolMock.HeadersPoolStub + proofsPool *dataRetrieverMock.ProofsPoolMock + pendingMiniBlocksHandler *mock.PendingMiniBlocksHandlerStub + blockTracker *mock.BlockTrackerMock + enableEpochsHandler *enableEpochsHandlerMock.EnableEpochsHandlerStub +} + +func createDefaultShardInfoCreateDataArgs() *shardInfoCreateDataTestArgs { + return &shardInfoCreateDataTestArgs{ + headersPool: &poolMock.HeadersPoolStub{}, + proofsPool: &dataRetrieverMock.ProofsPoolMock{}, + pendingMiniBlocksHandler: &mock.PendingMiniBlocksHandlerStub{}, + blockTracker: &mock.BlockTrackerMock{}, + enableEpochsHandler: enableEpochsHandlerMock.NewEnableEpochsHandlerStub(), + } +} + +func getMiniBlockHeadersForShard(shardID uint32) []block.MiniBlockHeader { + mbHash1 := []byte(fmt.Sprintf("mb hash 1 for shard %d", shardID)) + mbHash2 := []byte(fmt.Sprintf("mb hash 2 for shard %d", shardID)) + mbHash3 := []byte(fmt.Sprintf("mb hash 3 for shard %d", shardID)) + + miniBlockHeader1 := block.MiniBlockHeader{ + Hash: mbHash1, + Type: block.TxBlock, + TxCount: 10, + SenderShardID: shardID, + ReceiverShardID: 2, + } + miniBlockHeader2 := block.MiniBlockHeader{ + Hash: mbHash2, + Type: block.InvalidBlock, + TxCount: 1, + SenderShardID: shardID, + ReceiverShardID: 2, + } + miniBlockHeader3 := block.MiniBlockHeader{ + Hash: mbHash3, + Type: block.SmartContractResultBlock, + TxCount: 5, + SenderShardID: shardID, + ReceiverShardID: 0, + } + + miniBlockHeaders := []block.MiniBlockHeader{miniBlockHeader1, miniBlockHeader2, miniBlockHeader3} + return miniBlockHeaders +} + +func getShardHeaderForShard(shardID uint32) data.HeaderHandler { + prevHash := []byte(fmt.Sprintf("prevHash for shard %d", shardID)) + prevRandSeed := []byte(fmt.Sprintf("prevRandSeed for shard %d", shardID)) + currRandSeed := []byte(fmt.Sprintf("currRandSeed for shard %d", shardID)) + return &block.HeaderV2{ + Header: &block.Header{ + Round: 10, + Nonce: 45, + ShardID: shardID, + PrevRandSeed: prevRandSeed, + RandSeed: currRandSeed, + PrevHash: prevHash, + MiniBlockHeaders: getMiniBlockHeadersForShard(shardID), + }, + } +} + +func getHeaderV3ForShard(shardID uint32, hash []byte) data.HeaderHandler { + prevHash := []byte(fmt.Sprintf("prevHash for shard %d", shardID)) + prevRandSeed := []byte(fmt.Sprintf("prevRandSeed for shard %d", shardID)) + currRandSeed := []byte(fmt.Sprintf("currRandSeed for shard %d", shardID)) + return &block.HeaderV3{ + Epoch: 7, + Round: 10, + Nonce: 45, + ShardID: shardID, + PrevRandSeed: prevRandSeed, + RandSeed: currRandSeed, + PrevHash: prevHash, + MiniBlockHeaders: getMiniBlockHeadersForShard(shardID), + ExecutionResults: []*block.ExecutionResult{getExecutionResultForShard(shardID, hash)}, + } +} + +func getExecutionResultForShard(shardID uint32, hash []byte) *block.ExecutionResult { + return &block.ExecutionResult{ + ExecutedTxCount: 100, + MiniBlockHeaders: getMiniBlockHeadersForShard(shardID), + BaseExecutionResult: &block.BaseExecutionResult{ + HeaderHash: hash, + HeaderNonce: 12345, + HeaderEpoch: 7, + HeaderRound: 15, + RootHash: []byte(fmt.Sprintf("root hash for shard %d", shardID)), + GasUsed: 5000, + }, + } +} + +// compares only the fields that are set in createShardMiniBlockHeaderFromExecutionResultHandler +func checkMiniBlockHeadersComparable(t *testing.T, mbHeader1 data.MiniBlockHeaderHandler, mbHeader2 block.MiniBlockHeader) { + require.Equal(t, mbHeader1.GetHash(), mbHeader2.Hash) + require.Equal(t, block.Type(mbHeader1.GetTypeInt32()), mbHeader2.Type) + require.Equal(t, mbHeader1.GetTxCount(), mbHeader2.TxCount) + require.Equal(t, mbHeader1.GetSenderShardID(), mbHeader2.SenderShardID) + require.Equal(t, mbHeader1.GetReceiverShardID(), mbHeader2.ReceiverShardID) +} diff --git a/process/common.go b/process/common.go index 4572a534af2..f7f7a27e8f7 100644 --- a/process/common.go +++ b/process/common.go @@ -1230,9 +1230,15 @@ func CreateDataForInclusionEstimation( var notarizedInRound uint64 switch lastExecutionResult := handler.(type) { case *block.ExecutionResultInfo: + if check.IfNil(lastExecutionResult.GetExecutionResult()) { + return nil, ErrNilBaseExecutionResult + } notarizedInRound = lastExecutionResult.GetNotarizedInRound() proposedInRound = lastExecutionResult.GetExecutionResult().HeaderRound case *block.MetaExecutionResultInfo: + if check.IfNil(lastExecutionResult.GetExecutionResult()) { + return nil, ErrNilBaseExecutionResult + } notarizedInRound = lastExecutionResult.GetNotarizedInRound() proposedInRound = lastExecutionResult.GetExecutionResult().GetHeaderRound() default: diff --git a/process/errors.go b/process/errors.go index 90062582649..f55a9eaf6df 100644 --- a/process/errors.go +++ b/process/errors.go @@ -1412,3 +1412,12 @@ var ErrZeroLimit = errors.New("zero limit") // ErrInvalidBlockType signals that an invalid block type has been provided var ErrInvalidBlockType = errors.New("invalid block type") + +// ErrNilEpochStartData signals that nil epoch start data has been provided +var ErrNilEpochStartData = errors.New("nil epoch start data") + +// ErrInconsistentShardHeadersAndHashes signals that shard headers are inconsistent with their hashes +var ErrInconsistentShardHeadersAndHashes = errors.New("inconsistent shard headers and hashes") + +// ErrNilShardInfoCreator signals that a nil shard info creator has been provided +var ErrNilShardInfoCreator = errors.New("nil shard info creator") diff --git a/process/interface.go b/process/interface.go index 5cbb857dc2f..e4ec08a346c 100644 --- a/process/interface.go +++ b/process/interface.go @@ -519,8 +519,7 @@ type VirtualMachinesContainerFactory interface { // EpochStartTriggerHandler defines that actions which are needed by processor for start of epoch type EpochStartTriggerHandler interface { Update(round uint64, nonce uint64) - UpdateRound(round uint64) - SetEpochChange() + SetEpochChange(round uint64) ShouldProposeEpochChange(round uint64, nonce uint64) bool IsEpochStart() bool Epoch() uint32 @@ -1539,8 +1538,8 @@ type GasComputation interface { txHashes [][]byte, transactions []data.TransactionHandler, ) (addedTxHashes [][]byte, pendingMiniBlocksAdded []data.MiniBlockHeaderHandler, err error) - RevertIncomingMiniBlocks(miniBlockHashes [][]byte) GetBandwidthForTransactions() uint64 + RevertIncomingMiniBlocks(miniBlockHashes [][]byte) TotalGasConsumed() uint64 DecreaseIncomingLimit() DecreaseOutgoingLimit() @@ -1587,3 +1586,10 @@ type InclusionEstimator interface { Decide(lastNotarised *estimator.LastExecutionResultForInclusion, pending []data.BaseExecutionResultHandler, currentHeaderRound uint64) (allowed int) IsInterfaceNil() bool } + +// ShardInfoCreator defines the functionality to create shard info +type ShardInfoCreator interface { + CreateShardInfoV3(metaHeader data.MetaHeaderHandler, shardHeaders []data.HeaderHandler, shardHeaderHashes [][]byte) ([]data.ShardDataProposalHandler, []data.ShardDataHandler, error) + CreateShardInfoFromLegacyMeta(metaHeader data.MetaHeaderHandler, shardHeaders []data.ShardHeaderHandler, shardHeaderHashes [][]byte) ([]data.ShardDataHandler, error) + IsInterfaceNil() bool +} diff --git a/process/mock/blockTrackerMock.go b/process/mock/blockTrackerMock.go index 5c6d425c363..5fcf2d9e135 100644 --- a/process/mock/blockTrackerMock.go +++ b/process/mock/blockTrackerMock.go @@ -9,6 +9,7 @@ import ( "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-go/process" "github.com/multiversx/mx-chain-go/process/track" "github.com/multiversx/mx-chain-go/sharding" @@ -244,6 +245,10 @@ func (btm *BlockTrackerMock) ComputeLongestMetaChainFromLastNotarized() ([]data. // ComputeLongestShardsChainsFromLastNotarized - func (btm *BlockTrackerMock) ComputeLongestShardsChainsFromLastNotarized() ([]data.HeaderHandler, [][]byte, map[uint32][]data.HeaderHandler, error) { + if btm.ComputeLongestShardsChainsFromLastNotarizedCalled != nil { + return btm.ComputeLongestShardsChainsFromLastNotarizedCalled() + } + hdrsMap := make(map[uint32][]data.HeaderHandler) hdrsHashesMap := make(map[uint32][][]byte) diff --git a/process/mock/endOfEpochTriggerStub.go b/process/mock/endOfEpochTriggerStub.go index a190b63b492..3b66a6dd411 100644 --- a/process/mock/endOfEpochTriggerStub.go +++ b/process/mock/endOfEpochTriggerStub.go @@ -21,21 +21,13 @@ type EpochStartTriggerStub struct { EpochFinalityAttestingRoundCalled func() uint64 EpochStartMetaHdrHashCalled func() []byte ShouldProposeEpochChangeCalled func(round uint64, nonce uint64) bool - UpdateRoundCalled func(round uint64) - SetEpochChangeCalled func() -} - -// UpdateRound - -func (e *EpochStartTriggerStub) UpdateRound(round uint64) { - if e.UpdateRoundCalled != nil { - e.UpdateRoundCalled(round) - } + SetEpochChangeCalled func(round uint64) } // SetEpochChange - -func (e *EpochStartTriggerStub) SetEpochChange() { +func (e *EpochStartTriggerStub) SetEpochChange(round uint64) { if e.SetEpochChangeCalled != nil { - e.SetEpochChangeCalled() + e.SetEpochChangeCalled(round) } } diff --git a/process/peer/process.go b/process/peer/process.go index 24c89a08dc6..86946711317 100644 --- a/process/peer/process.go +++ b/process/peer/process.go @@ -1089,7 +1089,9 @@ func (vs *validatorStatistics) updateValidatorInfoOnSuccessfulBlock( log.Debug("updateValidatorInfoOnSuccessfulBlock", "leaderAccumulatedFees in current block", leaderAccumulatedFees.String(), "leader fees in Epoch", peerAcc.GetAccumulatedFees().String(), - "leader", core.GetTrimmedPk(string(peerAcc.AddressBytes()))) + "newRating", newRating, + "leader", core.GetTrimmedPk(string(peerAcc.AddressBytes())), + ) case validatorSuccess: peerAcc.IncreaseValidatorSuccessRate(1) newRating = vs.rater.ComputeIncreaseValidator(shardId, peerAcc.GetTempRating()) diff --git a/process/track/blockNotarizer.go b/process/track/blockNotarizer.go index 0d6b581fd6e..6520d3ff810 100644 --- a/process/track/blockNotarizer.go +++ b/process/track/blockNotarizer.go @@ -9,6 +9,7 @@ import ( "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" + "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/sharding" ) diff --git a/testscommon/epochStartTriggerStub.go b/testscommon/epochStartTriggerStub.go index 4a2de6e9ecc..2578dd62d59 100644 --- a/testscommon/epochStartTriggerStub.go +++ b/testscommon/epochStartTriggerStub.go @@ -21,21 +21,13 @@ type EpochStartTriggerStub struct { EpochFinalityAttestingRoundCalled func() uint64 EpochStartMetaHdrHashCalled func() []byte ShouldProposeEpochChangeCalled func(round uint64, nonce uint64) bool - UpdateRoundCalled func(round uint64) - SetEpochChangeCalled func() -} - -// UpdateRound - -func (e *EpochStartTriggerStub) UpdateRound(round uint64) { - if e.UpdateRoundCalled != nil { - e.UpdateRoundCalled(round) - } + SetEpochChangeCalled func(round uint64) } // SetEpochChange - -func (e *EpochStartTriggerStub) SetEpochChange() { +func (e *EpochStartTriggerStub) SetEpochChange(round uint64) { if e.SetEpochChangeCalled != nil { - e.SetEpochChangeCalled() + e.SetEpochChangeCalled(round) } } diff --git a/testscommon/gasComputationMock.go b/testscommon/gasComputationMock.go index 2ab7c353b8d..9cfcdbbd341 100644 --- a/testscommon/gasComputationMock.go +++ b/testscommon/gasComputationMock.go @@ -14,7 +14,6 @@ type GasComputationMock struct { txHashes [][]byte, transactions []data.TransactionHandler, ) ([][]byte, []data.MiniBlockHeaderHandler, error) - RevertIncomingMiniBlocksCalled func(miniBlockHashes [][]byte) GetBandwidthForTransactionsCalled func() uint64 TotalGasConsumedCalled func() uint64 DecreaseIncomingLimitCalled func() @@ -24,6 +23,7 @@ type GasComputationMock struct { ResetIncomingLimitCalled func() ResetOutgoingLimitCalled func() ResetCalled func() + RevertIncomingMiniBlocksCalled func(miniBlockHashes [][]byte) } // CheckIncomingMiniBlocks - diff --git a/testscommon/headerHandlerStub.go b/testscommon/headerHandlerStub.go index d52cded1fff..e9bea524507 100644 --- a/testscommon/headerHandlerStub.go +++ b/testscommon/headerHandlerStub.go @@ -68,6 +68,11 @@ type HeaderHandlerStub struct { SetTxCountCalled func(count uint32) error SetMetaBlockHashesCalled func(hashes [][]byte) error SetEpochStartHandlerCalled func(epochStartHandler data.EpochStartHandler) error + SetRoundCalled func(round uint64) error + SetNonceCalled func(nonce uint64) error + SetShardInfoHandlersCalled func(shardInfo []data.ShardDataHandler) error + GetShardInfoProposalHandlersCalled func() []data.ShardDataProposalHandler + SetShardInfoProposalHandlersCalled func(shardInfo []data.ShardDataProposalHandler) error } // SetEpochStartHandler - @@ -225,8 +230,11 @@ func (hhs *HeaderHandlerStub) GetReserved() []byte { } // SetNonce - -func (hhs *HeaderHandlerStub) SetNonce(_ uint64) error { - panic("implement me") +func (hhs *HeaderHandlerStub) SetNonce(nonce uint64) error { + if hhs.SetNonceCalled != nil { + return hhs.SetNonceCalled(nonce) + } + return nil } // SetEpoch - @@ -238,8 +246,11 @@ func (hhs *HeaderHandlerStub) SetEpoch(epoch uint32) error { } // SetRound - -func (hhs *HeaderHandlerStub) SetRound(_ uint64) error { - panic("implement me") +func (hhs *HeaderHandlerStub) SetRound(round uint64) error { + if hhs.SetRoundCalled != nil { + return hhs.SetRoundCalled(round) + } + return nil } // SetTimeStamp - @@ -435,8 +446,11 @@ func (hhs *HeaderHandlerStub) SetDevFeesInEpoch(_ *big.Int) error { } // SetShardInfoHandlers - -func (hhs *HeaderHandlerStub) SetShardInfoHandlers(_ []data.ShardDataHandler) error { - panic("implement me") +func (hhs *HeaderHandlerStub) SetShardInfoHandlers(shardInfo []data.ShardDataHandler) error { + if hhs.SetShardInfoHandlersCalled != nil { + return hhs.SetShardInfoHandlersCalled(shardInfo) + } + return nil } // SetAccumulatedFeesInEpoch - @@ -554,3 +568,19 @@ func (hhs *HeaderHandlerStub) GetGasLimit() uint32 { return 0 } + +// GetShardInfoProposalHandlers - +func (hhs *HeaderHandlerStub) GetShardInfoProposalHandlers() []data.ShardDataProposalHandler { + if hhs.GetShardInfoProposalHandlersCalled != nil { + return hhs.GetShardInfoProposalHandlersCalled() + } + return nil +} + +// SetShardInfoProposalHandlers - +func (hhs *HeaderHandlerStub) SetShardInfoProposalHandlers(shardInfo []data.ShardDataProposalHandler) error { + if hhs.SetShardInfoProposalHandlersCalled != nil { + return hhs.SetShardInfoProposalHandlersCalled(shardInfo) + } + return nil +} diff --git a/testscommon/mbSelection/mbSelectionSessionStub.go b/testscommon/mbSelection/mbSelectionSessionStub.go index 31ee247f893..fd3ac8f0d6a 100644 --- a/testscommon/mbSelection/mbSelectionSessionStub.go +++ b/testscommon/mbSelection/mbSelectionSessionStub.go @@ -11,10 +11,10 @@ type MiniBlockSelectionSessionStub struct { GetMiniBlockHeaderHandlersCalled func() []data.MiniBlockHeaderHandler GetMiniBlocksCalled func() block.MiniBlockSlice GetMiniBlockHashesCalled func() [][]byte - AddReferencedMetaBlockCalled func(metaBlock data.HeaderHandler, metaBlockHash []byte) - GetReferencedMetaBlockHashesCalled func() [][]byte - GetReferencedMetaBlocksCalled func() []data.HeaderHandler - GetLastMetaBlockCalled func() data.HeaderHandler + AddReferencedHeaderCalled func(metaBlock data.HeaderHandler, metaBlockHash []byte) + GetReferencedHeaderHashesCalled func() [][]byte + GetReferencedHeadersCalled func() []data.HeaderHandler + GetLastHeaderCalled func() data.HeaderHandler GetGasProvidedCalled func() uint64 GetNumTxsAddedCalled func() uint32 AddMiniBlocksAndHashesCalled func(miniBlocksAndHashes []block.MiniblockAndHash) error @@ -52,33 +52,33 @@ func (mbss *MiniBlockSelectionSessionStub) GetMiniBlockHashes() [][]byte { return nil } -// AddReferencedMetaBlock - -func (mbss *MiniBlockSelectionSessionStub) AddReferencedMetaBlock(metaBlock data.HeaderHandler, metaBlockHash []byte) { - if mbss.AddReferencedMetaBlockCalled != nil { - mbss.AddReferencedMetaBlockCalled(metaBlock, metaBlockHash) +// AddReferencedHeader - +func (mbss *MiniBlockSelectionSessionStub) AddReferencedHeader(metaBlock data.HeaderHandler, metaBlockHash []byte) { + if mbss.AddReferencedHeaderCalled != nil { + mbss.AddReferencedHeaderCalled(metaBlock, metaBlockHash) } } -// GetReferencedMetaBlockHashes - -func (mbss *MiniBlockSelectionSessionStub) GetReferencedMetaBlockHashes() [][]byte { - if mbss.GetReferencedMetaBlockHashesCalled != nil { - return mbss.GetReferencedMetaBlockHashesCalled() +// GetReferencedHeaderHashes - +func (mbss *MiniBlockSelectionSessionStub) GetReferencedHeaderHashes() [][]byte { + if mbss.GetReferencedHeaderHashesCalled != nil { + return mbss.GetReferencedHeaderHashesCalled() } return nil } -// GetReferencedMetaBlocks - -func (mbss *MiniBlockSelectionSessionStub) GetReferencedMetaBlocks() []data.HeaderHandler { - if mbss.GetReferencedMetaBlocksCalled != nil { - return mbss.GetReferencedMetaBlocksCalled() +// GetReferencedHeaders - +func (mbss *MiniBlockSelectionSessionStub) GetReferencedHeaders() []data.HeaderHandler { + if mbss.GetReferencedHeadersCalled != nil { + return mbss.GetReferencedHeadersCalled() } return nil } -// GetLastMetaBlock - -func (mbss *MiniBlockSelectionSessionStub) GetLastMetaBlock() data.HeaderHandler { - if mbss.GetLastMetaBlockCalled != nil { - return mbss.GetLastMetaBlockCalled() +// GetLastHeader - +func (mbss *MiniBlockSelectionSessionStub) GetLastHeader() data.HeaderHandler { + if mbss.GetLastHeaderCalled != nil { + return mbss.GetLastHeaderCalled() } return nil } diff --git a/testscommon/processMocks/shardInfoCreatorMock.go b/testscommon/processMocks/shardInfoCreatorMock.go new file mode 100644 index 00000000000..67c612a8bf7 --- /dev/null +++ b/testscommon/processMocks/shardInfoCreatorMock.go @@ -0,0 +1,40 @@ +package processMocks + +import ( + "github.com/multiversx/mx-chain-core-go/data" +) + +// ShardInfoCreatorMock is a mock implementation of ShardInfoCreator interface +type ShardInfoCreatorMock struct { + CreateShardInfoV3Called func(metaHeader data.MetaHeaderHandler, shardHeaders []data.HeaderHandler, shardHeaderHashes [][]byte) ([]data.ShardDataProposalHandler, []data.ShardDataHandler, error) + CreateShardInfoFromLegacyMetaCalled func(metaHeader data.MetaHeaderHandler, shardHeaders []data.ShardHeaderHandler, shardHeaderHashes [][]byte) ([]data.ShardDataHandler, error) +} + +// CreateShardInfoV3 - +func (scm *ShardInfoCreatorMock) CreateShardInfoV3( + metaHeader data.MetaHeaderHandler, + shardHeaders []data.HeaderHandler, + shardHeaderHashes [][]byte, +) ([]data.ShardDataProposalHandler, []data.ShardDataHandler, error) { + if scm.CreateShardInfoV3Called != nil { + return scm.CreateShardInfoV3Called(metaHeader, shardHeaders, shardHeaderHashes) + } + return nil, nil, nil +} + +// CreateShardInfoFromLegacyMeta - +func (scm *ShardInfoCreatorMock) CreateShardInfoFromLegacyMeta( + metaHeader data.MetaHeaderHandler, + shardHeaders []data.ShardHeaderHandler, + shardHeaderHashes [][]byte, +) ([]data.ShardDataHandler, error) { + if scm.CreateShardInfoFromLegacyMetaCalled != nil { + return scm.CreateShardInfoFromLegacyMetaCalled(metaHeader, shardHeaders, shardHeaderHashes) + } + return nil, nil +} + +// IsInterfaceNil - +func (scm *ShardInfoCreatorMock) IsInterfaceNil() bool { + return scm == nil +} diff --git a/txcache/selectionTracker.go b/txcache/selectionTracker.go index c99909411a1..a62814c3113 100644 --- a/txcache/selectionTracker.go +++ b/txcache/selectionTracker.go @@ -7,9 +7,10 @@ import ( "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-go/common" logger "github.com/multiversx/mx-chain-logger-go" "golang.org/x/exp/slices" + + "github.com/multiversx/mx-chain-go/common" ) type selectionTracker struct {