diff --git a/cmd/sovereignnode/chainSimulator/common/common.go b/cmd/sovereignnode/chainSimulator/common/common.go new file mode 100644 index 00000000000..6b1781b4a7a --- /dev/null +++ b/cmd/sovereignnode/chainSimulator/common/common.go @@ -0,0 +1,39 @@ +package common + +import ( + "github.com/multiversx/mx-chain-core-go/data" + "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/factory" + "github.com/multiversx/mx-chain-go/factory/runType" + "github.com/multiversx/mx-chain-go/node/chainSimulator/process" + sovRunType "github.com/multiversx/mx-chain-go/sovereignnode/runType" +) + +// GetCurrentSovereignHeader returns current sovereign chain block handler from blockchain hook +func GetCurrentSovereignHeader(nodeHandler process.NodeHandler) data.SovereignChainHeaderHandler { + return nodeHandler.GetChainHandler().GetCurrentBlockHeader().(data.SovereignChainHeaderHandler) +} + +// CreateSovereignRunTypeComponents will create sovereign run type components +func CreateSovereignRunTypeComponents(args runType.ArgsRunTypeComponents, sovereignExtraConfig config.SovereignConfig) (factory.RunTypeComponentsHolder, error) { + argsSovRunType, err := sovRunType.CreateSovereignArgsRunTypeComponents(args, sovereignExtraConfig) + if err != nil { + return nil, err + } + + sovereignComponentsFactory, err := runType.NewSovereignRunTypeComponentsFactory(*argsSovRunType) + if err != nil { + return nil, err + } + + managedRunTypeComponents, err := runType.NewManagedRunTypeComponents(sovereignComponentsFactory) + if err != nil { + return nil, err + } + err = managedRunTypeComponents.Create() + if err != nil { + return nil, err + } + + return managedRunTypeComponents, nil +} diff --git a/cmd/sovereignnode/chainSimulator/sovereignChainSimulator.go b/cmd/sovereignnode/chainSimulator/sovereignChainSimulator.go index 3f05a1cd5a9..a7cbc539e23 100644 --- a/cmd/sovereignnode/chainSimulator/sovereignChainSimulator.go +++ b/cmd/sovereignnode/chainSimulator/sovereignChainSimulator.go @@ -12,9 +12,9 @@ import ( "github.com/multiversx/mx-chain-go/node" "github.com/multiversx/mx-chain-go/node/chainSimulator" "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/block/sovereign/incomingHeader" + sovCommon "github.com/multiversx/mx-chain-go/sovereignnode/chainSimulator/common" sovereignConfig "github.com/multiversx/mx-chain-go/sovereignnode/config" - "github.com/multiversx/mx-chain-go/sovereignnode/incomingHeader" - sovRunType "github.com/multiversx/mx-chain-go/sovereignnode/runType" ) const ( @@ -54,11 +54,13 @@ func NewSovereignChainSimulator(args ArgsSovereignChainSimulator) (chainSimulato args.CreateRunTypeCoreComponents = func() (factory.RunTypeCoreComponentsHolder, error) { return createSovereignRunTypeCoreComponents() } - args.CreateIncomingHeaderSubscriber = func(config *config.NotifierConfig, dataPool dataRetriever.PoolsHolder, mainChainNotarizationStartRound uint64, runTypeComponents factory.RunTypeComponentsHolder) (process.IncomingHeaderSubscriber, error) { + args.CreateIncomingHeaderSubscriber = func(config config.WebSocketConfig, dataPool dataRetriever.PoolsHolder, mainChainNotarizationStartRound uint64, runTypeComponents factory.RunTypeComponentsHolder) (process.IncomingHeaderSubscriber, error) { return incomingHeader.CreateIncomingHeaderProcessor(config, dataPool, mainChainNotarizationStartRound, runTypeComponents) } - args.CreateRunTypeComponents = func(argsRunType runType.ArgsRunTypeComponents) (factory.RunTypeComponentsHolder, error) { - return createSovereignRunTypeComponents(argsRunType, *configs.SovereignExtraConfig) + if args.CreateRunTypeComponents == nil { + args.CreateRunTypeComponents = func(args runType.ArgsRunTypeComponents) (factory.RunTypeComponentsHolder, error) { + return sovCommon.CreateSovereignRunTypeComponents(args, *configs.SovereignExtraConfig) + } } args.NodeFactory = node.NewSovereignNodeFactory(configs.SovereignExtraConfig.GenesisConfig.NativeESDT) args.ChainProcessorFactory = NewSovereignChainHandlerFactory() @@ -105,26 +107,3 @@ func createSovereignRunTypeCoreComponents() (factory.RunTypeCoreComponentsHolder return managedRunTypeCoreComponents, nil } - -func createSovereignRunTypeComponents(args runType.ArgsRunTypeComponents, sovereignExtraConfig config.SovereignConfig) (factory.RunTypeComponentsHolder, error) { - argsSovRunType, err := sovRunType.CreateSovereignArgsRunTypeComponents(args, sovereignExtraConfig) - if err != nil { - return nil, err - } - - sovereignComponentsFactory, err := runType.NewSovereignRunTypeComponentsFactory(*argsSovRunType) - if err != nil { - return nil, err - } - - managedRunTypeComponents, err := runType.NewManagedRunTypeComponents(sovereignComponentsFactory) - if err != nil { - return nil, err - } - err = managedRunTypeComponents.Create() - if err != nil { - return nil, err - } - - return managedRunTypeComponents, nil -} diff --git a/cmd/sovereignnode/chainSimulator/tests/epochChange/epochChange_test.go b/cmd/sovereignnode/chainSimulator/tests/epochChange/epochChange_test.go index 03db19cfc67..5d63b703028 100644 --- a/cmd/sovereignnode/chainSimulator/tests/epochChange/epochChange_test.go +++ b/cmd/sovereignnode/chainSimulator/tests/epochChange/epochChange_test.go @@ -15,13 +15,21 @@ import ( "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/dataRetriever" + "github.com/multiversx/mx-chain-go/dataRetriever/requestHandlers" + "github.com/multiversx/mx-chain-go/factory" + "github.com/multiversx/mx-chain-go/factory/runType" chainSim "github.com/multiversx/mx-chain-go/integrationTests/chainSimulator" "github.com/multiversx/mx-chain-go/integrationTests/chainSimulator/staking" "github.com/multiversx/mx-chain-go/node/chainSimulator" "github.com/multiversx/mx-chain-go/node/chainSimulator/components/api" "github.com/multiversx/mx-chain-go/node/chainSimulator/process" + proc "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/process/headerCheck" sovereignChainSimulator "github.com/multiversx/mx-chain-go/sovereignnode/chainSimulator" + "github.com/multiversx/mx-chain-go/sovereignnode/chainSimulator/common" + "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/components" + testsFactory "github.com/multiversx/mx-chain-go/testscommon/factory" ) const ( @@ -41,6 +49,27 @@ func TestSovereignChainSimulator_EpochChange(t *testing.T) { Value: 50, // do not lower this value so that each validator can participate in consensus as leader to get rewards } + sovConfig := config.SovereignConfig{} + + sovRequestHandler := &testscommon.ExtendedShardHeaderRequestHandlerStub{ + RequestHandlerStub: testscommon.RequestHandlerStub{ + RequestMiniBlockHandlerCalled: func(destShardID uint32, miniblockHash []byte) { + require.Fail(t, "should not request miniBlock") + }, + RequestMiniBlocksHandlerCalled: func(destShardID uint32, miniblocksHashes [][]byte) { + require.Fail(t, "should not request miniBlocks") + }, + RequestRewardTxHandlerCalled: func(destShardID uint32, txHashes [][]byte) { + require.Fail(t, "should not request reward txs") + }, + }, + } + sovRequestHandlerFactory := &testsFactory.RequestHandlerFactoryMock{ + CreateRequestHandlerCalled: func(args requestHandlers.RequestHandlerArgs) (proc.RequestHandler, error) { + return sovRequestHandler, nil + }, + } + var protocolSustainabilityAddress string cs, err := sovereignChainSimulator.NewSovereignChainSimulator(sovereignChainSimulator.ArgsSovereignChainSimulator{ SovereignConfigPath: sovereignConfigPath, @@ -68,6 +97,16 @@ func TestSovereignChainSimulator_EpochChange(t *testing.T) { protocolSustainabilityAddress = cfg.EconomicsConfig.RewardsSettings.RewardsConfigByEpoch[0].ProtocolSustainabilityAddress cfg.EpochConfig.EnableEpochs = newCfg + sovConfig = cfg.GeneralConfig.SovereignConfig + }, + CreateRunTypeComponents: func(args runType.ArgsRunTypeComponents) (factory.RunTypeComponentsHolder, error) { + runTypeComps, err := common.CreateSovereignRunTypeComponents(args, sovConfig) + require.Nil(t, err) + + runTypeCompsHolder := components.GetRunTypeComponentsStub(runTypeComps) + runTypeCompsHolder.RequestHandlerFactory = sovRequestHandlerFactory + + return runTypeCompsHolder, nil }, }, }) @@ -235,15 +274,11 @@ func checkProtocolSustainabilityAddressBalanceIncreased( } func getAllFeesInEpoch(nodeHandler process.NodeHandler) (*big.Int, *big.Int) { - sovHdr := getCurrSovHdr(nodeHandler) + sovHdr := common.GetCurrentSovereignHeader(nodeHandler) return sovHdr.GetAccumulatedFeesInEpoch(), sovHdr.GetDevFeesInEpoch() } func getAllFees(nodeHandler process.NodeHandler) (*big.Int, *big.Int) { - sovHdr := getCurrSovHdr(nodeHandler) + sovHdr := common.GetCurrentSovereignHeader(nodeHandler) return sovHdr.GetAccumulatedFees(), sovHdr.GetDeveloperFees() } - -func getCurrSovHdr(nodeHandler process.NodeHandler) data.SovereignChainHeaderHandler { - return nodeHandler.GetChainHandler().GetCurrentBlockHeader().(data.SovereignChainHeaderHandler) -} diff --git a/cmd/sovereignnode/chainSimulator/tests/esdt/issueSc_test.go b/cmd/sovereignnode/chainSimulator/tests/esdt/issueSc_test.go index c3c1d8c94b8..5a9dadfec58 100644 --- a/cmd/sovereignnode/chainSimulator/tests/esdt/issueSc_test.go +++ b/cmd/sovereignnode/chainSimulator/tests/esdt/issueSc_test.go @@ -46,9 +46,12 @@ func TestSovereignChainSimulator_SmartContract_IssueToken(t *testing.T) { PathToInitialConfig: defaultPathToInitialConfig, GenesisTimestamp: time.Now().Unix(), RoundDurationInMillis: uint64(6000), - RoundsPerEpoch: core.OptionalUint64{}, - ApiInterface: api.NewNoApiInterface(), - MinNodesPerShard: 2, + RoundsPerEpoch: core.OptionalUint64{ + HasValue: true, + Value: 25, + }, + ApiInterface: api.NewNoApiInterface(), + MinNodesPerShard: 2, AlterConfigsFunction: func(cfg *config.Configs) { cfg.SystemSCConfig.ESDTSystemSCConfig.BaseIssuingCost = issuePrice }, @@ -59,10 +62,10 @@ func TestSovereignChainSimulator_SmartContract_IssueToken(t *testing.T) { defer cs.Close() - time.Sleep(time.Second) // wait for VM to be ready for processing queries + err = cs.GenerateBlocksUntilEpochIsReached(6) + require.Nil(t, err) nodeHandler := cs.GetNodeHandler(core.SovereignChainShardId) - systemScAddress := chainSim.GetSysAccBytesAddress(t, nodeHandler) wallet, err := cs.GenerateAndMintWalletAddress(core.SovereignChainShardId, big.NewInt(0).Mul(chainSim.OneEGLD, big.NewInt(100))) diff --git a/cmd/sovereignnode/chainSimulator/tests/processBlock/cleanupHeaders_test.go b/cmd/sovereignnode/chainSimulator/tests/processBlock/cleanupHeaders_test.go index 3e396122dbc..c551120426e 100644 --- a/cmd/sovereignnode/chainSimulator/tests/processBlock/cleanupHeaders_test.go +++ b/cmd/sovereignnode/chainSimulator/tests/processBlock/cleanupHeaders_test.go @@ -144,9 +144,7 @@ func checkCrossNotarizedHeader(t *testing.T, round int, crossNotarizedHeader dat if round == 1 { // first cross header is dummy with nonce 0 require.Equal(t, uint64(0), crossNotarizedHeader.GetNonce()) - } else if round == 2 { // first cross notarized header is 10000000 + } else { // cross header with nonce is notarized require.Equal(t, headerNonce, crossNotarizedHeader.GetNonce()) - } else { // cross header with nonce-1 is notarized - require.Equal(t, headerNonce-1, crossNotarizedHeader.GetNonce()) } } diff --git a/cmd/sovereignnode/chainSimulator/tests/processBlock/incomingHeader_test.go b/cmd/sovereignnode/chainSimulator/tests/processBlock/incomingHeader_test.go index f0aaf6e3da1..cf32a0efdc6 100644 --- a/cmd/sovereignnode/chainSimulator/tests/processBlock/incomingHeader_test.go +++ b/cmd/sovereignnode/chainSimulator/tests/processBlock/incomingHeader_test.go @@ -8,19 +8,29 @@ import ( "time" "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/data" coreAPI "github.com/multiversx/mx-chain-core-go/data/api" "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/data/sovereign" "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/stretchr/testify/require" + "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/dataRetriever/requestHandlers" + "github.com/multiversx/mx-chain-go/factory" + "github.com/multiversx/mx-chain-go/factory/runType" chainSim "github.com/multiversx/mx-chain-go/integrationTests/chainSimulator" "github.com/multiversx/mx-chain-go/node/chainSimulator" "github.com/multiversx/mx-chain-go/node/chainSimulator/components/api" "github.com/multiversx/mx-chain-go/node/chainSimulator/configs" "github.com/multiversx/mx-chain-go/node/chainSimulator/process" + proc "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/block/sovereign/incomingHeader" sovereignChainSimulator "github.com/multiversx/mx-chain-go/sovereignnode/chainSimulator" - "github.com/multiversx/mx-chain-go/sovereignnode/dataCodec" + "github.com/multiversx/mx-chain-go/sovereignnode/chainSimulator/common" + "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/components" + testFactory "github.com/multiversx/mx-chain-go/testscommon/factory" ) const ( @@ -31,6 +41,12 @@ const ( hashSize = 32 ) +type sovChainBlockTracer interface { + proc.BlockTracker + ComputeLongestExtendedShardChainFromLastNotarized() ([]data.HeaderHandler, [][]byte, error) + IsGenesisLastCrossNotarizedHeader() bool +} + // This test will simulate an incoming header. // At the end of the test the amount of tokens needs to be in the receiver account func TestSovereignChainSimulator_IncomingHeader(t *testing.T) { @@ -74,11 +90,11 @@ func TestSovereignChainSimulator_IncomingHeader(t *testing.T) { txsEvent = nil } - incomingHeader, headerHash := createIncomingHeader(nodeHandler, &headerNonce, prevHeader, txsEvent) - err = nodeHandler.GetIncomingHeaderSubscriber().AddHeader(headerHash, incomingHeader) + incomingHdr, headerHash := createIncomingHeader(nodeHandler, &headerNonce, prevHeader, txsEvent) + err = nodeHandler.GetIncomingHeaderSubscriber().AddHeader(headerHash, incomingHdr) require.Nil(t, err) - prevHeader = incomingHeader.Header + prevHeader = incomingHdr.Header err = cs.GenerateBlocks(1) require.Nil(t, err) @@ -91,16 +107,372 @@ func TestSovereignChainSimulator_IncomingHeader(t *testing.T) { require.Equal(t, amountToTransfer, esdts[token].Value.String()) } +// In this test we simulate: +// - a sovereign chain with the same round time as mainnet +// - for each generated block in sovereign chain, we receive an incoming header from mainnet +func TestSovereignChainSimulator_AddIncomingHeaderCase1(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + startRound := uint64(101) + cs, err := sovereignChainSimulator.NewSovereignChainSimulator(sovereignChainSimulator.ArgsSovereignChainSimulator{ + SovereignConfigPath: sovereignConfigPath, + ArgsChainSimulator: &chainSimulator.ArgsChainSimulator{ + BypassTxSignatureCheck: false, + TempDir: t.TempDir(), + PathToInitialConfig: defaultPathToInitialConfig, + GenesisTimestamp: time.Now().Unix(), + RoundDurationInMillis: uint64(6000), + RoundsPerEpoch: core.OptionalUint64{ + Value: 20, + HasValue: true, + }, + ApiInterface: api.NewNoApiInterface(), + MinNodesPerShard: 2, + AlterConfigsFunction: func(cfg *config.Configs) { + cfg.GeneralConfig.SovereignConfig.MainChainNotarization.MainChainNotarizationStartRound = startRound + }, + }, + }) + require.Nil(t, err) + require.NotNil(t, cs) + + defer cs.Close() + + nodeHandler := cs.GetNodeHandler(core.SovereignChainShardId) + + incomingHdrNonce := startRound - 5 + prevIncomingHeader := createHeaderV2(incomingHdrNonce, generateRandomHash(), generateRandomHash()) + + sovBlockTracker := getSovereignBlockTracker(t, nodeHandler) + + var previousExtendedHeaderHash []byte + var prevSovHdr data.SovereignChainHeaderHandler + var previousExtendedHeader data.HeaderHandler + + for currIncomingHeaderRound := startRound - 5; currIncomingHeaderRound < startRound+200; currIncomingHeaderRound++ { + + // Handlers are notified on go routines; wait a bit so that pools are updated + incomingHdr := addIncomingHeader(t, nodeHandler, &incomingHdrNonce, prevIncomingHeader) + time.Sleep(time.Millisecond * 10) + + // We just received header in pool and notified all subscribed components, header has not been processed + committed. + // We check how leader will compute the longest incoming header chain + extendedHeaderHash := getExtendedHeaderHash(t, nodeHandler, incomingHdr) + longestChain, longestChainHdrHashes, err := sovBlockTracker.ComputeLongestExtendedShardChainFromLastNotarized() + require.Nil(t, err) + + if currIncomingHeaderRound < startRound { + require.Empty(t, longestChain) + require.Empty(t, longestChainHdrHashes) + + } else { + currentExtendedHeader := getExtendedHeader(t, nodeHandler, incomingHdr) + + // On sovereign epoch start block processing we do not process incoming headers. + // This means that we accumulate incoming headers in pool and in next sovereign header we need to include + // accumulating headers + if prevSovHdr.IsStartOfEpochBlock() { + require.Equal(t, []data.HeaderHandler{previousExtendedHeader, currentExtendedHeader}, longestChain) + require.Equal(t, [][]byte{previousExtendedHeaderHash, extendedHeaderHash}, longestChainHdrHashes) + } else { + require.Equal(t, []data.HeaderHandler{currentExtendedHeader}, longestChain) + require.Equal(t, [][]byte{extendedHeaderHash}, longestChainHdrHashes) + } + } + + // Process + commit sovereign block with received incoming header + err = cs.GenerateBlocks(1) + require.Nil(t, err) + + currentSovHeader := common.GetCurrentSovereignHeader(nodeHandler) + lastCrossNotarizedHeader, _, err := sovBlockTracker.GetLastCrossNotarizedHeader(core.MainChainShardId) + require.Nil(t, err) + + // Check tracker and blockchain hook state for incoming processed data + if currIncomingHeaderRound <= 99 { + require.Zero(t, lastCrossNotarizedHeader.GetRound()) + require.True(t, sovBlockTracker.IsGenesisLastCrossNotarizedHeader()) + require.Empty(t, currentSovHeader.GetExtendedShardHeaderHashes()) + } else if currIncomingHeaderRound == 100 { // pre-genesis incoming header is notarized + require.Equal(t, uint64(100), lastCrossNotarizedHeader.GetRound()) + require.False(t, sovBlockTracker.IsGenesisLastCrossNotarizedHeader()) + require.Empty(t, currentSovHeader.GetExtendedShardHeaderHashes()) + } else { // since genesis main-chain header, each incoming header is instantly notarized (0 block finality) + if currentSovHeader.IsStartOfEpochBlock() { // epoch start block, no incoming header process is added to sovereign block + require.Equal(t, currIncomingHeaderRound-1, lastCrossNotarizedHeader.GetRound()) + require.False(t, sovBlockTracker.IsGenesisLastCrossNotarizedHeader()) + require.Empty(t, currentSovHeader.GetExtendedShardHeaderHashes()) + } else if prevSovHdr.IsStartOfEpochBlock() { // prev sov block was epoch start, should have 2 accumulated incoming headers + require.Equal(t, currIncomingHeaderRound, lastCrossNotarizedHeader.GetRound()) + require.False(t, sovBlockTracker.IsGenesisLastCrossNotarizedHeader()) + require.Equal(t, [][]byte{previousExtendedHeaderHash, extendedHeaderHash}, currentSovHeader.GetExtendedShardHeaderHashes()) + } else { // normal processing, in each sovereign block, there is an extended header hash + require.Equal(t, currIncomingHeaderRound, lastCrossNotarizedHeader.GetRound()) + require.False(t, sovBlockTracker.IsGenesisLastCrossNotarizedHeader()) + require.Equal(t, [][]byte{extendedHeaderHash}, currentSovHeader.GetExtendedShardHeaderHashes()) + } + } + + prevSovHdr = currentSovHeader + prevIncomingHeader = incomingHdr.Header + previousExtendedHeaderHash = extendedHeaderHash + previousExtendedHeader = getExtendedHeader(t, nodeHandler, incomingHdr) + } + + require.Equal(t, uint32(9), nodeHandler.GetCoreComponents().EpochNotifier().CurrentEpoch()) +} + +// In this test we simulate: +// - a sovereign chain with a lower round time than mainnet +// - we will receive a mainnet block at every 3 generated sovereign blocks +func TestSovereignChainSimulator_AddIncomingHeaderCase2(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + startRound := uint64(101) + cs, err := sovereignChainSimulator.NewSovereignChainSimulator(sovereignChainSimulator.ArgsSovereignChainSimulator{ + SovereignConfigPath: sovereignConfigPath, + ArgsChainSimulator: &chainSimulator.ArgsChainSimulator{ + BypassTxSignatureCheck: false, + TempDir: t.TempDir(), + PathToInitialConfig: defaultPathToInitialConfig, + GenesisTimestamp: time.Now().Unix(), + RoundDurationInMillis: uint64(6000), + RoundsPerEpoch: core.OptionalUint64{ + Value: 20, + HasValue: true, + }, + ApiInterface: api.NewNoApiInterface(), + MinNodesPerShard: 2, + AlterConfigsFunction: func(cfg *config.Configs) { + cfg.GeneralConfig.SovereignConfig.MainChainNotarization.MainChainNotarizationStartRound = startRound + }, + }, + }) + require.Nil(t, err) + require.NotNil(t, cs) + + defer cs.Close() + + nodeHandler := cs.GetNodeHandler(core.SovereignChainShardId) + + sovBlockTracker := getSovereignBlockTracker(t, nodeHandler) + require.True(t, sovBlockTracker.IsGenesisLastCrossNotarizedHeader()) + + incomingHdrNonce := startRound - 1 + prevHeader := createHeaderV2(incomingHdrNonce, generateRandomHash(), generateRandomHash()) + incomingHdr := addIncomingHeader(t, nodeHandler, &incomingHdrNonce, prevHeader) + require.Nil(t, err) + + lastCrossNotarizedRound := uint64(100) + // Pre genesis header was added before, but no extended header will be notarized in the next 3 sovereign blocks + for i := 0; i < 3; i++ { + err = cs.GenerateBlocks(1) + require.Nil(t, err) + + currentSovBlock := common.GetCurrentSovereignHeader(nodeHandler) + require.Empty(t, currentSovBlock.GetExtendedShardHeaderHashes()) + + checkLastCrossNotarizedRound(t, sovBlockTracker, lastCrossNotarizedRound) + } + + // From now on, every 3 sovereign blocks we add one incoming header + for i := 0; i < 50; i++ { + if i%3 == 0 { + prevHeader = incomingHdr.Header + incomingHdr = addIncomingHeader(t, nodeHandler, &incomingHdrNonce, prevHeader) + lastCrossNotarizedRound++ + } + + err = cs.GenerateBlocks(1) + require.Nil(t, err) + + checkLastCrossNotarizedRound(t, sovBlockTracker, lastCrossNotarizedRound) + + currentSovBlock := common.GetCurrentSovereignHeader(nodeHandler) + if i%3 == 0 { + extendedHeaderHash := getExtendedHeaderHash(t, nodeHandler, incomingHdr) + require.Equal(t, [][]byte{extendedHeaderHash}, currentSovBlock.GetExtendedShardHeaderHashes()) + } else { + require.Empty(t, currentSovBlock.GetExtendedShardHeaderHashes()) + } + } + +} + +// In this test we simulate: +// - a sovereign chain with a higher round time than mainnet +// - we will receive 3 mainnet block for every generated sovereign blocks +func TestSovereignChainSimulator_AddIncomingHeaderCase3(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + startRound := uint64(101) + sovConfig := config.SovereignConfig{} + + sovRequestHandler := &testscommon.ExtendedShardHeaderRequestHandlerStub{ + RequestExtendedShardHeaderCalled: func(hash []byte) { + require.Fail(t, "should not request any extended header") + }, + RequestExtendedShardHeaderByNonceCalled: func(nonce uint64) { + require.Fail(t, "should not request any extended header") + }, + } + sovRequestHandlerFactory := &testFactory.RequestHandlerFactoryMock{ + CreateRequestHandlerCalled: func(args requestHandlers.RequestHandlerArgs) (proc.RequestHandler, error) { + return sovRequestHandler, nil + }, + } + + cs, err := sovereignChainSimulator.NewSovereignChainSimulator(sovereignChainSimulator.ArgsSovereignChainSimulator{ + SovereignConfigPath: sovereignConfigPath, + ArgsChainSimulator: &chainSimulator.ArgsChainSimulator{ + BypassTxSignatureCheck: false, + TempDir: t.TempDir(), + PathToInitialConfig: defaultPathToInitialConfig, + GenesisTimestamp: time.Now().Unix(), + RoundDurationInMillis: uint64(6000), + RoundsPerEpoch: core.OptionalUint64{ + Value: 20, + HasValue: true, + }, + ApiInterface: api.NewNoApiInterface(), + MinNodesPerShard: 1, + AlterConfigsFunction: func(cfg *config.Configs) { + cfg.GeneralConfig.SovereignConfig.MainChainNotarization.MainChainNotarizationStartRound = startRound + sovConfig = cfg.GeneralConfig.SovereignConfig + }, + CreateRunTypeComponents: func(args runType.ArgsRunTypeComponents) (factory.RunTypeComponentsHolder, error) { + runTypeComps, err := common.CreateSovereignRunTypeComponents(args, sovConfig) + require.Nil(t, err) + + runTypeCompsHolder := components.GetRunTypeComponentsStub(runTypeComps) + runTypeCompsHolder.RequestHandlerFactory = sovRequestHandlerFactory + + return runTypeCompsHolder, nil + }, + }, + }) + require.Nil(t, err) + require.NotNil(t, cs) + + defer cs.Close() + + nodeHandler := cs.GetNodeHandler(core.SovereignChainShardId) + + sovBlockTracker := getSovereignBlockTracker(t, nodeHandler) + require.True(t, sovBlockTracker.IsGenesisLastCrossNotarizedHeader()) + + incomingHdrNonce := startRound - 3 + prevHeader := createHeaderV2(incomingHdrNonce, generateRandomHash(), generateRandomHash()) + + // Fill pool with incoming headers up until pre-genesis + for i := 0; i < 3; i++ { + incomingHdr := addIncomingHeader(t, nodeHandler, &incomingHdrNonce, prevHeader) + prevHeader = incomingHdr.Header + } + + err = cs.GenerateBlocks(1) + require.Nil(t, err) + + // We process one sovereign block, no incoming header should be added yet + lastCrossNotarizedRound := uint64(100) + checkLastCrossNotarizedRound(t, sovBlockTracker, lastCrossNotarizedRound) + + currentSovBlock := common.GetCurrentSovereignHeader(nodeHandler) + require.Empty(t, currentSovBlock.GetExtendedShardHeaderHashes()) + + prevSovBlock := currentSovBlock + extendedHeaderHashes := make([][]byte, 0) + // From now on, we generate 3 incoming headers per sovereign block + for i := 1; i < 300; i++ { + incomingHdr := addIncomingHeader(t, nodeHandler, &incomingHdrNonce, prevHeader) + extendedHeaderHashes = append(extendedHeaderHashes, getExtendedHeaderHash(t, nodeHandler, incomingHdr)) + + if i%3 == 0 { + time.Sleep(time.Millisecond * 50) + + err = cs.GenerateBlocks(1) + require.Nil(t, err) + + currentSovBlock = common.GetCurrentSovereignHeader(nodeHandler) + + if currentSovBlock.IsStartOfEpochBlock() { + require.Empty(t, currentSovBlock.GetExtendedShardHeaderHashes()) + } else if prevSovBlock.IsStartOfEpochBlock() { + require.Len(t, currentSovBlock.GetExtendedShardHeaderHashes(), 6) + require.Equal(t, extendedHeaderHashes, currentSovBlock.GetExtendedShardHeaderHashes()) + + lastCrossNotarizedRound += 6 + extendedHeaderHashes = make([][]byte, 0) + } else { + require.Len(t, currentSovBlock.GetExtendedShardHeaderHashes(), 3) + require.Equal(t, extendedHeaderHashes, currentSovBlock.GetExtendedShardHeaderHashes()) + + lastCrossNotarizedRound += 3 + extendedHeaderHashes = make([][]byte, 0) + } + + } + + checkLastCrossNotarizedRound(t, sovBlockTracker, lastCrossNotarizedRound) + prevHeader = incomingHdr.Header + prevSovBlock = currentSovBlock + } + + require.Equal(t, uint32(4), nodeHandler.GetCoreComponents().EpochNotifier().CurrentEpoch()) + +} + +func getExtendedHeader(t *testing.T, nodeHandler process.NodeHandler, incomingHdr *sovereign.IncomingHeader) data.HeaderHandler { + extendedHeader, err := nodeHandler.GetIncomingHeaderSubscriber().CreateExtendedHeader(incomingHdr) + require.Nil(t, err) + + return extendedHeader +} + +func getExtendedHeaderHash(t *testing.T, nodeHandler process.NodeHandler, incomingHdr *sovereign.IncomingHeader) []byte { + extendedHeader := getExtendedHeader(t, nodeHandler, incomingHdr) + extendedHeaderHash, err := core.CalculateHash(nodeHandler.GetCoreComponents().InternalMarshalizer(), nodeHandler.GetCoreComponents().Hasher(), extendedHeader) + require.Nil(t, err) + + return extendedHeaderHash +} + func createIncomingHeader(nodeHandler process.NodeHandler, headerNonce *uint64, prevHeader *block.HeaderV2, txsEvent []*transaction.Event) (*sovereign.IncomingHeader, []byte) { *headerNonce++ prevHeaderHash, _ := core.CalculateHash(nodeHandler.GetCoreComponents().InternalMarshalizer(), nodeHandler.GetCoreComponents().Hasher(), prevHeader) - incomingHeader := &sovereign.IncomingHeader{ + incomingHdr := &sovereign.IncomingHeader{ Header: createHeaderV2(*headerNonce, prevHeaderHash, prevHeader.GetRandSeed()), IncomingEvents: txsEvent, } - headerHash, _ := core.CalculateHash(nodeHandler.GetCoreComponents().InternalMarshalizer(), nodeHandler.GetCoreComponents().Hasher(), incomingHeader.Header) + headerHash, _ := core.CalculateHash(nodeHandler.GetCoreComponents().InternalMarshalizer(), nodeHandler.GetCoreComponents().Hasher(), incomingHdr.Header) + + return incomingHdr, headerHash +} + +func addIncomingHeader(t *testing.T, nodeHandler process.NodeHandler, headerNonce *uint64, prevHeader *block.HeaderV2) *sovereign.IncomingHeader { + incomingHdr, incomingHeaderHash := createSimpleIncomingHeader(nodeHandler, headerNonce, prevHeader) + err := nodeHandler.GetIncomingHeaderSubscriber().AddHeader(incomingHeaderHash, incomingHdr) + require.Nil(t, err) + + return incomingHdr +} + +func createSimpleIncomingHeader(nodeHandler process.NodeHandler, headerNonce *uint64, prevHeader *block.HeaderV2) (*sovereign.IncomingHeader, []byte) { + prevHeaderHash, _ := core.CalculateHash(nodeHandler.GetCoreComponents().InternalMarshalizer(), nodeHandler.GetCoreComponents().Hasher(), prevHeader) + incomingHdr := &sovereign.IncomingHeader{ + Header: createHeaderV2(*headerNonce, prevHeaderHash, prevHeader.GetRandSeed()), + IncomingEvents: []*transaction.Event{}, + } + headerHash, _ := core.CalculateHash(nodeHandler.GetCoreComponents().InternalMarshalizer(), nodeHandler.GetCoreComponents().Hasher(), incomingHdr.Header) + *headerNonce++ - return incomingHeader, headerHash + return incomingHdr, headerHash } func createHeaderV2(nonce uint64, prevHash []byte, prevRandSeed []byte) *block.HeaderV2 { @@ -122,7 +494,7 @@ func generateRandomHash() []byte { return randomBytes } -func createTransactionsEvent(dataCodecHandler dataCodec.SovereignDataCodec, receiver []byte, token string, amountToTransfer string) []*transaction.Event { +func createTransactionsEvent(dataCodecHandler incomingHeader.SovereignDataCodec, receiver []byte, token string, amountToTransfer string) []*transaction.Event { tokenData, _ := dataCodecHandler.SerializeTokenData(createTokenData(amountToTransfer)) eventData, _ := dataCodecHandler.SerializeEventData(createEventData()) @@ -159,3 +531,17 @@ func createEventData() sovereign.EventData { TransferData: nil, } } + +func getSovereignBlockTracker(t *testing.T, nodeHandler process.NodeHandler) sovChainBlockTracer { + sovBlockTracker, castOk := nodeHandler.GetProcessComponents().BlockTracker().(sovChainBlockTracer) + require.True(t, castOk) + + return sovBlockTracker +} + +func checkLastCrossNotarizedRound(t *testing.T, sovBlockTracker sovChainBlockTracer, lastCrossNotarizedRound uint64) { + lastCrossNotarizedHeader, _, err := sovBlockTracker.GetLastCrossNotarizedHeader(core.MainChainShardId) + require.Nil(t, err) + require.Equal(t, lastCrossNotarizedRound, lastCrossNotarizedHeader.GetRound()) + require.False(t, sovBlockTracker.IsGenesisLastCrossNotarizedHeader()) +} diff --git a/cmd/sovereignnode/config/sovereignConfig.toml b/cmd/sovereignnode/config/sovereignConfig.toml index fe8b38e0aa7..9b904591c7d 100644 --- a/cmd/sovereignnode/config/sovereignConfig.toml +++ b/cmd/sovereignnode/config/sovereignConfig.toml @@ -45,6 +45,12 @@ Hasher = "sha256" [NotifierConfig] + # This flag indicates whether the node will establish a WebSocket receiver connection from a light node or observer. + # Running an additional main chain light node as a notifier requires extra hardware resources. + # When disabled, the node will rely on and trust incoming headers from the main chain proposed by other leaders. + # Disabling this flag can be useful in scenarios where additional validation infrastructure isn't necessary. + Enabled = true + SubscribedEvents = [ { Identifier = "deposit", Addresses = ["erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"] }, { Identifier = "execute", Addresses = ["erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"] } diff --git a/cmd/sovereignnode/dataCodec/dataCodec_test.go b/cmd/sovereignnode/dataCodec/dataCodec_test.go index 5bd83907614..cd3bb9af411 100644 --- a/cmd/sovereignnode/dataCodec/dataCodec_test.go +++ b/cmd/sovereignnode/dataCodec/dataCodec_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/multiversx/mx-chain-go/errors" + "github.com/multiversx/mx-chain-go/process/block/sovereign/incomingHeader" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data/sovereign" @@ -13,7 +14,7 @@ import ( "github.com/stretchr/testify/require" ) -func createDataCodec() SovereignDataCodec { +func createDataCodec() incomingHeader.SovereignDataCodec { serializer, _ := abi.NewSerializer(abi.ArgsNewSerializer{ PartsSeparator: "@", }) diff --git a/cmd/sovereignnode/dataCodec/interface.go b/cmd/sovereignnode/dataCodec/interface.go index 0928ccf7cc6..8681540a76e 100644 --- a/cmd/sovereignnode/dataCodec/interface.go +++ b/cmd/sovereignnode/dataCodec/interface.go @@ -26,13 +26,3 @@ type TokenDataEncoder interface { type OperationDataEncoder interface { SerializeOperation(operation sovereign.Operation) ([]byte, error) } - -// SovereignDataCodec is the interface for serializing/deserializing data -type SovereignDataCodec interface { - SerializeEventData(eventData sovereign.EventData) ([]byte, error) - DeserializeEventData(data []byte) (*sovereign.EventData, error) - SerializeTokenData(tokenData sovereign.EsdtTokenData) ([]byte, error) - DeserializeTokenData(data []byte) (*sovereign.EsdtTokenData, error) - SerializeOperation(operation sovereign.Operation) ([]byte, error) - IsInterfaceNil() bool -} diff --git a/cmd/sovereignnode/incomingHeader/interface.go b/cmd/sovereignnode/incomingHeader/interface.go deleted file mode 100644 index 3551d7350da..00000000000 --- a/cmd/sovereignnode/incomingHeader/interface.go +++ /dev/null @@ -1,21 +0,0 @@ -package incomingHeader - -import "github.com/multiversx/mx-chain-core-go/data" - -// HeadersPool should be able to add new headers in pool -type HeadersPool interface { - AddHeaderInShard(headerHash []byte, header data.HeaderHandler, shardID uint32) - IsInterfaceNil() bool -} - -// TransactionPool should be able to add a new transaction in the pool -type TransactionPool interface { - AddData(key []byte, data interface{}, sizeInBytes int, cacheId string) - IsInterfaceNil() bool -} - -// TopicsChecker should be able to check the topics validity -type TopicsChecker interface { - CheckValidity(topics [][]byte) error - IsInterfaceNil() bool -} diff --git a/cmd/sovereignnode/notifier/errors.go b/cmd/sovereignnode/notifier/errors.go new file mode 100644 index 00000000000..10397cdbd75 --- /dev/null +++ b/cmd/sovereignnode/notifier/errors.go @@ -0,0 +1,5 @@ +package notifier + +import "errors" + +var errNilSovereignNotifier = errors.New("nil sovereign notifier provided") diff --git a/cmd/sovereignnode/notifier/interface.go b/cmd/sovereignnode/notifier/interface.go new file mode 100644 index 00000000000..345c9cdf2c6 --- /dev/null +++ b/cmd/sovereignnode/notifier/interface.go @@ -0,0 +1,8 @@ +package notifier + +// SovereignNotifierBootstrapper defines a sovereign notifier bootstrapper +type SovereignNotifierBootstrapper interface { + Start() + Close() error + IsInterfaceNil() bool +} diff --git a/cmd/sovereignnode/notifier/notifierBootstrapper.go b/cmd/sovereignnode/notifier/notifierBootstrapper.go new file mode 100644 index 00000000000..a813b68c7a1 --- /dev/null +++ b/cmd/sovereignnode/notifier/notifierBootstrapper.go @@ -0,0 +1,177 @@ +package notifier + +import ( + "context" + "os" + "syscall" + "time" + + "github.com/multiversx/mx-chain-core-go/core/check" + logger "github.com/multiversx/mx-chain-logger-go" + notifierProcess "github.com/multiversx/mx-chain-sovereign-notifier-go/process" + + "github.com/multiversx/mx-chain-go/errors" + "github.com/multiversx/mx-chain-go/process" +) + +const roundsThreshold = process.MaxRoundsWithoutNewBlockReceived + 1 + +var log = logger.GetOrCreate("notifier-bootstrap") + +// ArgsNotifierBootstrapper defines args needed to create a new notifier bootstrapper +type ArgsNotifierBootstrapper struct { + IncomingHeaderHandler process.IncomingHeaderSubscriber + SovereignNotifier notifierProcess.SovereignNotifier + ForkDetector process.ForkDetector + Bootstrapper process.Bootstrapper + SigStopNode chan os.Signal + RoundDuration uint64 +} + +type notifierBootstrapper struct { + incomingHeaderHandler process.IncomingHeaderSubscriber + sovereignNotifier notifierProcess.SovereignNotifier + forkDetector process.ForkDetector + sigStopNode chan os.Signal + + syncedRoundsChan chan int32 + cancelFunc func() + roundDuration uint64 +} + +// NewNotifierBootstrapper creates a ws receiver connection registration bootstrapper +func NewNotifierBootstrapper(args ArgsNotifierBootstrapper) (*notifierBootstrapper, error) { + if err := checkArgs(args); err != nil { + return nil, err + } + + nb := ¬ifierBootstrapper{ + incomingHeaderHandler: args.IncomingHeaderHandler, + sovereignNotifier: args.SovereignNotifier, + forkDetector: args.ForkDetector, + syncedRoundsChan: make(chan int32, 1), + cancelFunc: nil, + roundDuration: args.RoundDuration, + sigStopNode: args.SigStopNode, + } + + args.Bootstrapper.AddSyncStateListener(nb.receivedSyncState) + + return nb, nil +} + +func checkArgs(args ArgsNotifierBootstrapper) error { + if check.IfNil(args.IncomingHeaderHandler) { + return errors.ErrNilIncomingHeaderSubscriber + } + if check.IfNil(args.SovereignNotifier) { + return errNilSovereignNotifier + } + if check.IfNil(args.ForkDetector) { + return errors.ErrNilForkDetector + } + if check.IfNil(args.Bootstrapper) { + return process.ErrNilBootstrapper + } + if args.RoundDuration == 0 { + return errors.ErrInvalidRoundDuration + } + + return nil +} + +func (nb *notifierBootstrapper) receivedSyncState(isNodeSynchronized bool) { + if isNodeSynchronized && nb.forkDetector.GetHighestFinalBlockNonce() != 0 { + nb.writeRoundsDeltaOnChan(1) + } else if !isNodeSynchronized { + nb.writeRoundsDeltaOnChan(-1) + } +} + +func (nb *notifierBootstrapper) writeRoundsDeltaOnChan(delta int32) { + select { + case nb.syncedRoundsChan <- delta: + default: + } +} + +// Start will start waiting on a go routine to be notified via syncedRoundsChan when the sovereign node is synced. +// Meanwhile, it will print the current node state in log. When node is fully synced, it will register the incoming header +// processor to the websocket listener and exit the waiting loop. +func (nb *notifierBootstrapper) Start() { + var ctx context.Context + ctx, nb.cancelFunc = context.WithCancel(context.Background()) + go nb.checkNodeState(ctx) +} + +func (nb *notifierBootstrapper) checkNodeState(ctx context.Context) { + timeToWaitReSync := uint64(roundsThreshold) * nb.roundDuration + ticker := time.NewTicker(time.Duration(timeToWaitReSync) * time.Millisecond) + defer ticker.Stop() + + var syncedRounds uint32 + + for { + select { + case <-ctx.Done(): + log.Debug("notifierBootstrapper.checkNodeState: worker's go routine is stopping...") + return + case delta := <-nb.syncedRoundsChan: + syncedRounds = updateSyncedRounds(syncedRounds, delta) + if syncedRounds < uint32(roundsThreshold) { + log.Debug("notifierBootstrapper.checkNodeState", "syncedRounds", syncedRounds) + continue + } + + err := nb.sovereignNotifier.RegisterHandler(nb.incomingHeaderHandler) + if err != nil { + log.Error("notifierBootstrapper: sovereignNotifier.RegisterHandler", "err", err) + nb.sigStopNode <- syscall.SIGTERM + } else { + log.Info("notifierBootstrapper.checkNodeState", "is node synced", true) + } + + return + case <-ticker.C: + log.Debug("notifierBootstrapper.checkNodeState", "is node synced", false) + } + } +} + +func updateSyncedRounds(syncedRounds uint32, delta int32) uint32 { + if delta > 0 { + syncedRounds += uint32(delta) + } else if syncedRounds > 0 { + syncedRounds-- + } + + return syncedRounds +} + +// Close cancels current context and empties channel reads +func (nb *notifierBootstrapper) Close() error { + if nb.cancelFunc != nil { + nb.cancelFunc() + } + + nrReads := emptyChannel(nb.syncedRoundsChan) + log.Debug("notifierBootstrapper: emptied channel", "syncedRoundsChan nrReads", nrReads) + return nil +} + +func emptyChannel(ch chan int32) int { + readsCnt := 0 + for { + select { + case <-ch: + readsCnt++ + default: + return readsCnt + } + } +} + +// IsInterfaceNil checks if the underlying pointer is nil +func (nb *notifierBootstrapper) IsInterfaceNil() bool { + return nb == nil +} diff --git a/cmd/sovereignnode/notifier/notifierBootstrapper_test.go b/cmd/sovereignnode/notifier/notifierBootstrapper_test.go new file mode 100644 index 00000000000..203c11ba08a --- /dev/null +++ b/cmd/sovereignnode/notifier/notifierBootstrapper_test.go @@ -0,0 +1,296 @@ +package notifier + +import ( + "context" + "errors" + "math/rand" + "os" + "reflect" + "runtime" + "sync" + "sync/atomic" + "syscall" + "testing" + "time" + + notifierProcess "github.com/multiversx/mx-chain-sovereign-notifier-go/process" + "github.com/multiversx/mx-chain-sovereign-notifier-go/testscommon" + "github.com/stretchr/testify/require" + + errorsMx "github.com/multiversx/mx-chain-go/errors" + "github.com/multiversx/mx-chain-go/integrationTests/mock" + "github.com/multiversx/mx-chain-go/process" + processMocks "github.com/multiversx/mx-chain-go/process/mock" + "github.com/multiversx/mx-chain-go/testscommon/sovereign" +) + +func createArgs() ArgsNotifierBootstrapper { + return ArgsNotifierBootstrapper{ + IncomingHeaderHandler: &sovereign.IncomingHeaderSubscriberStub{}, + SovereignNotifier: &testscommon.SovereignNotifierStub{}, + ForkDetector: &mock.ForkDetectorStub{}, + Bootstrapper: &processMocks.BootstrapperStub{}, + RoundDuration: 100, + } +} + +func getFunctionName(i interface{}) string { + return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name() +} + +func TestNewNotifierBootstrapper(t *testing.T) { + t.Parallel() + + t.Run("nil incoming header processor", func(t *testing.T) { + args := createArgs() + args.IncomingHeaderHandler = nil + nb, err := NewNotifierBootstrapper(args) + require.Nil(t, nb) + require.Equal(t, errorsMx.ErrNilIncomingHeaderSubscriber, err) + }) + t.Run("nil sovereign notifier", func(t *testing.T) { + args := createArgs() + args.SovereignNotifier = nil + nb, err := NewNotifierBootstrapper(args) + require.Nil(t, nb) + require.Equal(t, errNilSovereignNotifier, err) + }) + t.Run("nil fork detector", func(t *testing.T) { + args := createArgs() + args.ForkDetector = nil + nb, err := NewNotifierBootstrapper(args) + require.Nil(t, nb) + require.Equal(t, errorsMx.ErrNilForkDetector, err) + }) + t.Run("nil bootstrapper", func(t *testing.T) { + args := createArgs() + args.Bootstrapper = nil + nb, err := NewNotifierBootstrapper(args) + require.Nil(t, nb) + require.Equal(t, process.ErrNilBootstrapper, err) + }) + t.Run("zero value round duration", func(t *testing.T) { + args := createArgs() + args.RoundDuration = 0 + nb, err := NewNotifierBootstrapper(args) + require.Nil(t, nb) + require.Equal(t, errorsMx.ErrInvalidRoundDuration, err) + }) + t.Run("should work", func(t *testing.T) { + args := createArgs() + nb, err := NewNotifierBootstrapper(args) + require.Nil(t, err) + require.False(t, nb.IsInterfaceNil()) + }) +} + +func TestNotifierBootstrapper_Start(t *testing.T) { + t.Parallel() + + args := createArgs() + + wasRegisteredToStateSync := false + args.Bootstrapper = &processMocks.BootstrapperStub{ + AddSyncStateListenerCalled: func(f func(bool)) { + require.Contains(t, getFunctionName(f), "(*notifierBootstrapper).receivedSyncState") + wasRegisteredToStateSync = true + }, + } + + registerCalledCt := atomic.Int64{} + args.SovereignNotifier = &testscommon.SovereignNotifierStub{ + RegisterHandlerCalled: func(handler notifierProcess.IncomingHeaderSubscriber) error { + require.Equal(t, args.IncomingHeaderHandler, handler) + registerCalledCt.Add(1) + return nil + }, + } + + getHighestNonceCalledCt := atomic.Int64{} + args.ForkDetector = &mock.ForkDetectorStub{ + GetHighestFinalBlockNonceCalled: func() uint64 { + defer func() { + getHighestNonceCalledCt.Add(1) + }() + + return uint64(getHighestNonceCalledCt.Load()) + }, + } + + nb, _ := NewNotifierBootstrapper(args) + require.True(t, wasRegisteredToStateSync) + + nb.Start() + + defer func() { + err := nb.Close() + require.Nil(t, err) + }() + + time.Sleep(time.Millisecond * 50) + require.Zero(t, registerCalledCt.Load()) + require.Zero(t, getHighestNonceCalledCt.Load()) + + nb.receivedSyncState(false) + time.Sleep(time.Millisecond * 50) + require.Zero(t, registerCalledCt.Load()) + require.Zero(t, getHighestNonceCalledCt.Load()) + + nb.receivedSyncState(true) + time.Sleep(time.Millisecond * 50) + require.Zero(t, registerCalledCt.Load()) + require.Equal(t, int64(1), getHighestNonceCalledCt.Load()) + + nb.receivedSyncState(true) + time.Sleep(time.Millisecond * 50) + require.Zero(t, registerCalledCt.Load()) + require.Equal(t, int64(2), getHighestNonceCalledCt.Load()) + + for i := int64(0); i < 10; i++ { + nb.receivedSyncState(false) + time.Sleep(time.Millisecond * 50) + require.Zero(t, registerCalledCt.Load()) + require.Equal(t, int64(2), getHighestNonceCalledCt.Load()) + } + for i := int64(1); i < roundsThreshold; i++ { + nb.receivedSyncState(true) + time.Sleep(time.Millisecond * 50) + require.Zero(t, registerCalledCt.Load()) + require.Equal(t, i+2, getHighestNonceCalledCt.Load()) + } + + for i := roundsThreshold; i < roundsThreshold+10; i++ { + nb.receivedSyncState(true) + time.Sleep(time.Millisecond * 50) + require.Equal(t, int64(1), registerCalledCt.Load()) + require.Equal(t, int64(i+2), getHighestNonceCalledCt.Load()) + } +} + +func TestNotifierBootstrapper_Start_ConcurrencyTest(t *testing.T) { + t.Parallel() + + args := createArgs() + + getHighestNonceCalledCt := atomic.Int64{} + args.ForkDetector = &mock.ForkDetectorStub{ + GetHighestFinalBlockNonceCalled: func() uint64 { + defer func() { + getHighestNonceCalledCt.Add(1) + }() + + return uint64(getHighestNonceCalledCt.Load()) + }, + } + + nb, _ := NewNotifierBootstrapper(args) + nb.Start() + + defer func() { + err := nb.Close() + require.Nil(t, err) + }() + + numGoRoutines := 1000 + wg := sync.WaitGroup{} + wg.Add(numGoRoutines) + + for i := 0; i < numGoRoutines; i++ { + go func(idx int) { + isSynced := false + if rand.Int31n(100) < 51 { + isSynced = true + } + + nb.receivedSyncState(isSynced) + wg.Done() + }(i) + } + + wg.Wait() + require.NotZero(t, getHighestNonceCalledCt.Load()) + require.True(t, getHighestNonceCalledCt.Load() < int64(numGoRoutines)) +} + +func TestNotifierBootstrapper_StartWithRegisterFailing(t *testing.T) { + t.Parallel() + + sigStopNodeMock := make(chan os.Signal, 1) + + args := createArgs() + args.SigStopNode = sigStopNodeMock + args.RoundDuration = 10 + + registerCalledCt := atomic.Int64{} + args.SovereignNotifier = &testscommon.SovereignNotifierStub{ + RegisterHandlerCalled: func(handler notifierProcess.IncomingHeaderSubscriber) error { + require.Equal(t, args.IncomingHeaderHandler, handler) + + defer func() { + registerCalledCt.Add(1) + }() + + return errors.New("local error") + }, + } + + args.ForkDetector = &mock.ForkDetectorStub{ + GetHighestFinalBlockNonceCalled: func() uint64 { + return 1 + }, + } + + nb, _ := NewNotifierBootstrapper(args) + nb.syncedRoundsChan <- roundsThreshold - 1 + + nb.Start() + + defer func() { + err := nb.Close() + require.Nil(t, err) + }() + + time.Sleep(time.Millisecond * 200) + require.Zero(t, registerCalledCt.Load()) + + nb.receivedSyncState(true) + time.Sleep(time.Millisecond * 50) + require.Equal(t, int64(1), registerCalledCt.Load()) + + select { + case sig := <-sigStopNodeMock: + require.Equal(t, syscall.SIGTERM, sig) + case <-time.After(time.Millisecond * 100): // Timeout to avoid hanging + t.Error("expected SIGTERM signal on sigStopNodeMock, but none received") + } + + // Once registration fails, the waiting is done, no other register is called + for i := 0; i < 10; i++ { + nb.receivedSyncState(true) + time.Sleep(time.Millisecond * 50) + require.Equal(t, int64(1), registerCalledCt.Load()) + } +} + +func TestCheckNodeState_CtxDone(t *testing.T) { + t.Parallel() + + args := createArgs() + nb, _ := NewNotifierBootstrapper(args) + + ctx, cancel := context.WithCancel(context.Background()) + doneChan := make(chan struct{}) + + go func() { + nb.checkNodeState(ctx) + close(doneChan) + }() + + cancel() + + select { + case <-doneChan: + case <-time.After(time.Second): + require.Fail(t, "checkNodeState did not exit on ctx.Done() as expected") + } +} diff --git a/cmd/sovereignnode/runType/argRunTypeComponentsCreator.go b/cmd/sovereignnode/runType/argRunTypeComponentsCreator.go index 1ea83750710..5ef7a220f20 100644 --- a/cmd/sovereignnode/runType/argRunTypeComponentsCreator.go +++ b/cmd/sovereignnode/runType/argRunTypeComponentsCreator.go @@ -5,9 +5,8 @@ import ( "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/factory/runType" + "github.com/multiversx/mx-chain-go/process/block/sovereign/incomingHeader" "github.com/multiversx/mx-chain-go/sovereignnode/dataCodec" - "github.com/multiversx/mx-chain-go/sovereignnode/incomingHeader" - "github.com/multiversx/mx-sdk-abi-go/abi" ) diff --git a/cmd/sovereignnode/sovereignNodeRunner.go b/cmd/sovereignnode/sovereignNodeRunner.go index 66d6c276467..53fe17cea58 100644 --- a/cmd/sovereignnode/sovereignNodeRunner.go +++ b/cmd/sovereignnode/sovereignNodeRunner.go @@ -23,6 +23,8 @@ import ( "github.com/multiversx/mx-chain-core-go/core/throttler" "github.com/multiversx/mx-chain-core-go/data/endProcess" outportCore "github.com/multiversx/mx-chain-core-go/data/outport" + "github.com/multiversx/mx-chain-go/process/block/sovereign/incomingHeader" + "github.com/multiversx/mx-chain-go/sovereignnode/notifier" logger "github.com/multiversx/mx-chain-logger-go" "github.com/multiversx/mx-chain-sovereign-bridge-go/cert" factoryBridge "github.com/multiversx/mx-chain-sovereign-bridge-go/client" @@ -71,7 +73,6 @@ import ( "github.com/multiversx/mx-chain-go/process/interceptors" "github.com/multiversx/mx-chain-go/sharding/nodesCoordinator" sovereignConfig "github.com/multiversx/mx-chain-go/sovereignnode/config" - "github.com/multiversx/mx-chain-go/sovereignnode/incomingHeader" sovRunType "github.com/multiversx/mx-chain-go/sovereignnode/runType" "github.com/multiversx/mx-chain-go/state/syncer" "github.com/multiversx/mx-chain-go/storage/cache" @@ -433,7 +434,7 @@ func (snr *sovereignNodeRunner) executeOneComponentCreationCycle( log.Debug("creating process components") incomingHeaderHandler, err := incomingHeader.CreateIncomingHeaderProcessor( - &configs.SovereignExtraConfig.NotifierConfig, + configs.SovereignExtraConfig.NotifierConfig.WebSocketConfig, managedDataComponents.Datapool(), configs.SovereignExtraConfig.MainChainNotarization.MainChainNotarizationStartRound, managedRunTypeComponents, @@ -524,14 +525,20 @@ func (snr *sovereignNodeRunner) executeOneComponentCreationCycle( managedProcessComponents, managedStatusCoreComponents, ) - if err != nil { return true, err } - sovereignWsReceiver, err := createSovereignWsReceiver( + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + + notifierServices, err := createNotifierWSReceiverServicesIfNeeded( &configs.SovereignExtraConfig.NotifierConfig, incomingHeaderHandler, + managedCoreComponents.GenesisNodesSetup().GetRoundDuration(), + managedProcessComponents.ForkDetector(), + managedConsensusComponents.Bootstrapper(), + sigs, ) if err != nil { return true, err @@ -539,14 +546,18 @@ func (snr *sovereignNodeRunner) executeOneComponentCreationCycle( log.Debug("creating node structure") - extraOptionNotifierReceiver := func(n *node.Node) error { - n.AddClosableComponent(sovereignWsReceiver) + extraOptionsNotifier := func(n *node.Node) error { + for _, notifierService := range notifierServices { + n.AddClosableComponent(notifierService) + } + return nil } extraOptionOutGoingBridgeSender := func(n *node.Node) error { n.AddClosableComponent(outGoingBridgeOpHandler) return nil } + nodeHandler, err := node.CreateNode( configs.GeneralConfig, managedRunTypeComponents, @@ -564,7 +575,7 @@ func (snr *sovereignNodeRunner) executeOneComponentCreationCycle( flagsConfig.BootstrapRoundIndex, configs.ImportDbConfig.IsImportDBMode, node.NewSovereignNodeFactory(configs.GeneralConfig.SovereignConfig.GenesisConfig.NativeESDT), - extraOptionNotifierReceiver, + extraOptionsNotifier, extraOptionOutGoingBridgeSender, ) if err != nil { @@ -601,9 +612,6 @@ func (snr *sovereignNodeRunner) executeOneComponentCreationCycle( statusHandler.SetStringValue(common.MetricAreVMQueriesReady, strconv.FormatBool(true)) }(managedStatusCoreComponents.AppStatusHandler()) - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) - err = waitForSignal( sigs, managedCoreComponents.ChanStopNodeProcess(), @@ -1882,26 +1890,50 @@ func createWhiteListerVerifiedTxs(generalConfig *config.Config) (process.WhiteLi return interceptors.NewWhiteListDataVerifier(whiteListCacheVerified) } -func createSovereignWsReceiver( +func createNotifierWSReceiverServicesIfNeeded( config *config.NotifierConfig, incomingHeaderHandler process.IncomingHeaderSubscriber, -) (notifierProcess.WSClient, error) { - argsNotifier := factory.ArgsCreateSovereignNotifier{ - MarshallerType: config.WebSocketConfig.MarshallerType, - SubscribedEvents: getNotifierSubscribedEvents(config.SubscribedEvents), - HasherType: config.WebSocketConfig.HasherType, + roundDuration uint64, + forkDetector process.ForkDetector, + bootstrapper process.Bootstrapper, + sigStopNode chan os.Signal, +) ([]mainFactory.Closer, error) { + if !config.Enabled { + log.Info("running without any notifier attached") + return make([]mainFactory.Closer, 0), nil } - sovereignNotifier, err := factory.CreateSovereignNotifier(argsNotifier) + sovereignNotifier, err := createSovereignNotifier(config) if err != nil { return nil, err } - err = sovereignNotifier.RegisterHandler(incomingHeaderHandler) + sovereignWsReceiver, err := createSovereignWsReceiver( + config, + sovereignNotifier, + ) + if err != nil { + return nil, err + } + sovereignNotifierBootstrapper, err := startSovereignNotifierBootstrapper( + incomingHeaderHandler, + sovereignNotifier, + roundDuration, + forkDetector, + bootstrapper, + sigStopNode, + ) if err != nil { return nil, err } + return []mainFactory.Closer{sovereignWsReceiver, sovereignNotifierBootstrapper}, nil +} + +func createSovereignWsReceiver( + config *config.NotifierConfig, + sovereignNotifier notifierProcess.SovereignNotifier, +) (notifierProcess.WSClient, error) { argsWsReceiver := factory.ArgsWsClientReceiverNotifier{ WebSocketConfig: notifierCfg.WebSocketConfig{ Url: config.WebSocketConfig.Url, @@ -1919,6 +1951,42 @@ func createSovereignWsReceiver( return factory.CreateWsClientReceiverNotifier(argsWsReceiver) } +func createSovereignNotifier(config *config.NotifierConfig) (notifierProcess.SovereignNotifier, error) { + argsNotifier := factory.ArgsCreateSovereignNotifier{ + MarshallerType: config.WebSocketConfig.MarshallerType, + SubscribedEvents: getNotifierSubscribedEvents(config.SubscribedEvents), + HasherType: config.WebSocketConfig.HasherType, + } + + return factory.CreateSovereignNotifier(argsNotifier) +} + +func startSovereignNotifierBootstrapper( + incomingHeaderHandler process.IncomingHeaderSubscriber, + sovereignNotifier notifierProcess.SovereignNotifier, + roundDuration uint64, + forkDetector process.ForkDetector, + bootstrapper process.Bootstrapper, + sigStopNode chan os.Signal, +) (notifier.SovereignNotifierBootstrapper, error) { + args := notifier.ArgsNotifierBootstrapper{ + IncomingHeaderHandler: incomingHeaderHandler, + SovereignNotifier: sovereignNotifier, + ForkDetector: forkDetector, + Bootstrapper: bootstrapper, + RoundDuration: roundDuration, + SigStopNode: sigStopNode, + } + + notifierBootstrapper, err := notifier.NewNotifierBootstrapper(args) + if err != nil { + return nil, err + } + + notifierBootstrapper.Start() + return notifierBootstrapper, nil +} + func getNotifierSubscribedEvents(events []config.SubscribedEvent) []notifierCfg.SubscribedEvent { ret := make([]notifierCfg.SubscribedEvent, len(events)) diff --git a/cmd/sovereignnode/systemTestDemo/go.mod b/cmd/sovereignnode/systemTestDemo/go.mod index 2690f122736..1a98734fa3c 100644 --- a/cmd/sovereignnode/systemTestDemo/go.mod +++ b/cmd/sovereignnode/systemTestDemo/go.mod @@ -3,20 +3,21 @@ module github.com/multiversx/mx-chain-go/cmd/sovereignnode/sovereignChecker go 1.20 require ( - github.com/multiversx/mx-chain-communication-go v1.0.8 - github.com/multiversx/mx-chain-core-go v1.2.17-0.20231204120455-9672e1a91430 - github.com/multiversx/mx-chain-crypto-go v1.2.8 - github.com/multiversx/mx-chain-logger-go v1.0.13 - github.com/multiversx/mx-chain-sovereign-bridge-go v0.0.0-20231218134309-eb39c56a1539 - github.com/multiversx/mx-sdk-go v1.3.8 + github.com/multiversx/mx-chain-communication-go v1.1.1 + github.com/multiversx/mx-chain-core-go v1.2.24-0.20241111110328-b8a9c02d5583 + github.com/multiversx/mx-chain-crypto-go v1.2.12 + github.com/multiversx/mx-chain-logger-go v1.0.15 + github.com/multiversx/mx-chain-sovereign-bridge-go v0.0.0-20240116102202-4cf6fbbd95a3 + github.com/multiversx/mx-sdk-go v1.4.1 github.com/urfave/cli v1.22.14 - google.golang.org/grpc v1.59.0 + google.golang.org/grpc v1.60.1 ) require ( github.com/btcsuite/btcd/btcutil v1.1.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/denisbrodbeck/machineid v1.0.1 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect @@ -25,19 +26,22 @@ require ( github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/golang-lru v0.6.0 // indirect github.com/mr-tron/base58 v1.2.0 // indirect - github.com/multiversx/concurrent-map v0.1.4 // indirect - github.com/multiversx/mx-chain-go v1.6.3 // indirect - github.com/multiversx/mx-chain-storage-go v1.0.13 // indirect - github.com/multiversx/mx-chain-vm-common-go v1.5.5 // indirect + github.com/multiversx/mx-chain-go v1.7.12 // indirect + github.com/multiversx/mx-chain-storage-go v1.0.16 // indirect + github.com/multiversx/mx-chain-vm-common-go v1.5.12 // indirect github.com/pborman/uuid v1.2.1 // indirect github.com/pelletier/go-toml v1.9.3 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect + github.com/tklauser/go-sysconf v0.3.4 // indirect + github.com/tklauser/numcpus v0.2.1 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect - golang.org/x/crypto v0.12.0 // indirect - golang.org/x/net v0.14.0 // indirect - golang.org/x/sys v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect + github.com/yusufpapurcu/wmi v1.2.2 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect google.golang.org/protobuf v1.31.0 // indirect ) diff --git a/cmd/sovereignnode/systemTestDemo/go.sum b/cmd/sovereignnode/systemTestDemo/go.sum index 29c20025b04..6ff140026ef 100644 --- a/cmd/sovereignnode/systemTestDemo/go.sum +++ b/cmd/sovereignnode/systemTestDemo/go.sum @@ -43,6 +43,8 @@ github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmV github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= @@ -96,26 +98,24 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/multiversx/concurrent-map v0.1.4 h1:hdnbM8VE4b0KYJaGY5yJS2aNIW9TFFsUYwbO0993uPI= -github.com/multiversx/concurrent-map v0.1.4/go.mod h1:8cWFRJDOrWHOTNSqgYCUvwT7c7eFQ4U2vKMOp4A/9+o= -github.com/multiversx/mx-chain-communication-go v1.0.8 h1:sTx4Vmx+QCpngUFq/LF/Ka8bevlK2vMxfclE284twfc= -github.com/multiversx/mx-chain-communication-go v1.0.8/go.mod h1:+oaUowpq+SqrEmAsMPGwhz44g7L81loWb6AiNQU9Ms4= -github.com/multiversx/mx-chain-core-go v1.2.17-0.20231204120455-9672e1a91430 h1:e3TK2DnUcKBtbgFpe/yjPjPMgAW87F4j5w0H8SKL8js= -github.com/multiversx/mx-chain-core-go v1.2.17-0.20231204120455-9672e1a91430/go.mod h1:I/hmkp0dO04sW9mLSD/gXGaw48U5rBl4yNo5YYgSGN0= -github.com/multiversx/mx-chain-crypto-go v1.2.8 h1:wOgVlUaO5X4L8iEbFjcQcL8SZvv6WZ7LqH73BiRPhxU= -github.com/multiversx/mx-chain-crypto-go v1.2.8/go.mod h1:fkaWKp1rbQN9wPKya5jeoRyC+c/SyN/NfggreyeBw+8= -github.com/multiversx/mx-chain-go v1.6.3 h1:U5Z7oscke09d7BwZ/Va8ozovKVchr5oPHL9pZon5+hM= -github.com/multiversx/mx-chain-go v1.6.3/go.mod h1:zQ55L8EtAIKj9j5yWtBtn5gXdeodzT47pedzd4v2cA0= -github.com/multiversx/mx-chain-logger-go v1.0.13 h1:eru/TETo0MkO4ZTnXsQDKf4PBRpAXmqjT02klNT/JnY= -github.com/multiversx/mx-chain-logger-go v1.0.13/go.mod h1:MZJhTAtZTJxT+yK2EHc4ZW3YOHUc1UdjCD0iahRNBZk= -github.com/multiversx/mx-chain-sovereign-bridge-go v0.0.0-20231218134309-eb39c56a1539 h1:1VHainKn4nsuyGXeJh1XqyVeSSoyJWR1ffofJgyUhEQ= -github.com/multiversx/mx-chain-sovereign-bridge-go v0.0.0-20231218134309-eb39c56a1539/go.mod h1:TBRfsAfNqPwjbxspzXC8iMqfbMt3C4t5ZvbXZVw5Dvc= -github.com/multiversx/mx-chain-storage-go v1.0.13 h1:i41VPDJZ0pn5gf18zTXrac5xeiolUOztNuzL3wEXRuI= -github.com/multiversx/mx-chain-storage-go v1.0.13/go.mod h1:sJ2q49tgjxNpMpsHysjABqCAB0FLBmDblbjBkQ8XfmA= -github.com/multiversx/mx-chain-vm-common-go v1.5.5 h1:NoG73lvcHSeUcoFlYybG8ceGuJ6KptD3QJjUNEnGDVk= -github.com/multiversx/mx-chain-vm-common-go v1.5.5/go.mod h1:sqkKMCnwkWl8DURdb9q7pctK8IANghdHY1KJLE0ox2c= -github.com/multiversx/mx-sdk-go v1.3.8 h1:3hnTZXJdLhSLv0bkfvKGZnW+GAcVA91KuVC6SNH1OkA= -github.com/multiversx/mx-sdk-go v1.3.8/go.mod h1:cJ18WRQ2ZEDH7jUMMS9AbYUXZ66muMTOVcipsNddv/8= +github.com/multiversx/mx-chain-communication-go v1.1.1 h1:y4DoQeQOJTaSUsRzczQFazf8JYQmInddypApqA3AkwM= +github.com/multiversx/mx-chain-communication-go v1.1.1/go.mod h1:WK6bP4pGEHGDDna/AYRIMtl6G9OA0NByI1Lw8PmOnRM= +github.com/multiversx/mx-chain-core-go v1.2.24-0.20241111110328-b8a9c02d5583 h1:EEYn25eF4DV1UTcCjOarSH1voitQ+YCnFmbDgJzLqB8= +github.com/multiversx/mx-chain-core-go v1.2.24-0.20241111110328-b8a9c02d5583/go.mod h1:P/YBoFnt25XUaCQ7Q/SD15vhnc9yV5JDhHxyFO9P8Z0= +github.com/multiversx/mx-chain-crypto-go v1.2.12 h1:zWip7rpUS4CGthJxfKn5MZfMfYPjVjIiCID6uX5BSOk= +github.com/multiversx/mx-chain-crypto-go v1.2.12/go.mod h1:HzcPpCm1zanNct/6h2rIh+MFrlXbjA5C8+uMyXj3LI4= +github.com/multiversx/mx-chain-go v1.7.12 h1:cQ3g5sFZEcQmIRwi/wt+K/3d5nIwCMQRC1ZnJDRuRY4= +github.com/multiversx/mx-chain-go v1.7.12/go.mod h1:HwklJGQfMpv/yyF4oLpxjwdKCawspv1JjdgezlWBpRQ= +github.com/multiversx/mx-chain-logger-go v1.0.15 h1:HlNdK8etyJyL9NQ+6mIXyKPEBo+wRqOwi3n+m2QIHXc= +github.com/multiversx/mx-chain-logger-go v1.0.15/go.mod h1:t3PRKaWB1M+i6gUfD27KXgzLJJC+mAQiN+FLlL1yoGQ= +github.com/multiversx/mx-chain-sovereign-bridge-go v0.0.0-20240116102202-4cf6fbbd95a3 h1:8x/cqQ7IQvYEiOy9l2DmUvJArVRz1OfeMyOzJAbyDxs= +github.com/multiversx/mx-chain-sovereign-bridge-go v0.0.0-20240116102202-4cf6fbbd95a3/go.mod h1:/U8wy9SMizv5oXD6suxWRkusSx2SvLRARS4R4HuaXAA= +github.com/multiversx/mx-chain-storage-go v1.0.16 h1:l2lJq+EAN3YwLbjJrnoKfFd1/1Xmo9DcAUECND2obLs= +github.com/multiversx/mx-chain-storage-go v1.0.16/go.mod h1:uM/z7YyqTOD3wgyH8TfapyEl5sb+7x/Jaxne4cfG4HI= +github.com/multiversx/mx-chain-vm-common-go v1.5.12 h1:Q8F6DE7XhgHtWgg2rozSv4Tv5fE3ENkJz6mjRoAfht8= +github.com/multiversx/mx-chain-vm-common-go v1.5.12/go.mod h1:Sv6iS1okB6gy3HAsW6KHYtAxShNAfepKLtu//AURI8c= +github.com/multiversx/mx-sdk-go v1.4.1 h1:/zk7LDmnl1ovkmjkDejLRUtbjO7kmVQw8AlAyGNITBU= +github.com/multiversx/mx-sdk-go v1.4.1/go.mod h1:2kTQLFck47wtHpzdWrM3mrLlTypE5zn39JCbzN16cxs= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -144,6 +144,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -157,6 +159,10 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= +github.com/tklauser/go-sysconf v0.3.4 h1:HT8SVixZd3IzLdfs/xlpq0jeSfTX57g1v6wB1EuzV7M= +github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek= +github.com/tklauser/numcpus v0.2.1 h1:ct88eFm+Q7m2ZfXJdan1xYoXKlmwsfP+k88q05KvlZc= +github.com/tklauser/numcpus v0.2.1/go.mod h1:9aU+wOc6WjUIZEwWMP62PL/41d65P+iks1gBkr4QyP8= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= @@ -165,13 +171,15 @@ github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= +github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -185,8 +193,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -195,6 +203,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -204,13 +213,14 @@ golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -218,8 +228,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -230,10 +240,10 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= -google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 h1:6GQBEOdGkX6MMTLT9V+TjtIRZCw9VPD5Z+yHY9wMgS0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= +google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= +google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/cmd/sovereignnode/systemTestDemo/mockNotifier/disabledGRPCServer.go b/cmd/sovereignnode/systemTestDemo/mockNotifier/disabledGRPCServer.go new file mode 100644 index 00000000000..c280b68ec8e --- /dev/null +++ b/cmd/sovereignnode/systemTestDemo/mockNotifier/disabledGRPCServer.go @@ -0,0 +1,13 @@ +package main + +type disabledGRPCServer struct { +} + +// NewDisabledGRPCServer - +func NewDisabledGRPCServer() *disabledGRPCServer { + return &disabledGRPCServer{} +} + +// Stop - +func (dgs *disabledGRPCServer) Stop() { +} diff --git a/cmd/sovereignnode/systemTestDemo/mockNotifier/disabledMockGRPCServer.go b/cmd/sovereignnode/systemTestDemo/mockNotifier/disabledMockGRPCServer.go new file mode 100644 index 00000000000..7b51ad53d62 --- /dev/null +++ b/cmd/sovereignnode/systemTestDemo/mockNotifier/disabledMockGRPCServer.go @@ -0,0 +1,19 @@ +package main + +type disabledMockServer struct { +} + +// NewDisabledMockServer - +func NewDisabledMockServer() *disabledMockServer { + return &disabledMockServer{} +} + +// ExtractRandomBridgeTopicsForConfirmation - +func (dms *disabledMockServer) ExtractRandomBridgeTopicsForConfirmation() ([]*ConfirmedBridgeOp, error) { + return make([]*ConfirmedBridgeOp, 0), nil +} + +// IsInterfaceNil - +func (dms *disabledMockServer) IsInterfaceNil() bool { + return dms == nil +} diff --git a/cmd/sovereignnode/systemTestDemo/mockNotifier/flags.go b/cmd/sovereignnode/systemTestDemo/mockNotifier/flags.go index 0ff7f86509a..d60925cfbc3 100644 --- a/cmd/sovereignnode/systemTestDemo/mockNotifier/flags.go +++ b/cmd/sovereignnode/systemTestDemo/mockNotifier/flags.go @@ -14,4 +14,18 @@ var ( " log level.", Value: "*:" + logger.LogTrace.String(), } + grpcEnabled = cli.BoolFlag{ + Name: "grpc-enabled", + Usage: "Boolean option for enabling GRPC server.", + } + sovereignBridgeCertificateFile = cli.StringFlag{ + Name: "certificate", + Usage: "The path for sovereign grpc bridge service certificate file.", + Value: "~/MultiversX/testnet/node/config/certificate.crt", + } + sovereignBridgeCertificatePkFile = cli.StringFlag{ + Name: "certificate-pk", + Usage: "The path for sovereign grpc bridge private key certificate file.", + Value: "~/MultiversX/testnet/node/config/private_key.pem", + } ) diff --git a/cmd/sovereignnode/systemTestDemo/mockNotifier/interface.go b/cmd/sovereignnode/systemTestDemo/mockNotifier/interface.go new file mode 100644 index 00000000000..a4b94657ca4 --- /dev/null +++ b/cmd/sovereignnode/systemTestDemo/mockNotifier/interface.go @@ -0,0 +1,11 @@ +package main + +// MockServer holds the mock server actions +type MockServer interface { + ExtractRandomBridgeTopicsForConfirmation() ([]*ConfirmedBridgeOp, error) +} + +// GRPCServerMock holds the grpc server actions +type GRPCServerMock interface { + Stop() +} diff --git a/cmd/sovereignnode/systemTestDemo/mockNotifier/notifier.go b/cmd/sovereignnode/systemTestDemo/mockNotifier/notifier.go index b56a9ffcb75..a1add658f52 100644 --- a/cmd/sovereignnode/systemTestDemo/mockNotifier/notifier.go +++ b/cmd/sovereignnode/systemTestDemo/mockNotifier/notifier.go @@ -2,18 +2,19 @@ package main import ( "crypto/rand" + "encoding/binary" "encoding/hex" "fmt" "math/big" "net" "os" + "strings" "time" "github.com/multiversx/mx-chain-communication-go/websocket/data" factoryHost "github.com/multiversx/mx-chain-communication-go/websocket/factory" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data/block" - "github.com/multiversx/mx-chain-core-go/data/esdt" "github.com/multiversx/mx-chain-core-go/data/outport" "github.com/multiversx/mx-chain-core-go/data/sovereign" "github.com/multiversx/mx-chain-core-go/data/transaction" @@ -30,12 +31,6 @@ import ( // from this branch: sovereign-stress-test-branch. // 2. Keep the config in variables.sh with at least 3 validators. // -// If you need to simulate bridge outgoing txs with notifier confirmation, but don't have yet any SC deployed in sovereign -// shard, you can simply add the following lines in `sovereignChainBlock.go`, func: `createAndSetOutGoingMiniBlock` -// + bridgeOp1 := []byte("bridgeOp@123@rcv1@token1@val1" + hex.EncodeToString(headerHandler.GetRandSeed())) -// + bridgeOp2 := []byte("bridgeOp@124@rcv2@token2@val2" + hex.EncodeToString(headerHandler.GetRandSeed())) -// + outGoingOperations = [][]byte{bridgeOp1, bridgeOp2} -// // If you are running with a local testnet and need the necessary certificate files to mock bridge operations, you // can find them(certificate.crt + private_key.pem) within testnet environment setup at ~MultiversX/testnet/node/config @@ -50,6 +45,9 @@ func main() { "The blocks are sent with an arbitrary period between them." app.Flags = []cli.Flag{ logLevel, + grpcEnabled, + sovereignBridgeCertificateFile, + sovereignBridgeCertificatePkFile, } app.Authors = []cli.Author{ { @@ -78,7 +76,7 @@ func startMockNotifier(ctx *cli.Context) error { return err } - mockedGRPCServer, grpcServerConn, err := createAndStartGRPCServer() + mockedGRPCServer, grpcServerConn, err := createAndStartGRPCServer(ctx) if err != nil { log.Error("cannot create grpc server", "error", err) return err @@ -95,7 +93,7 @@ func startMockNotifier(ctx *cli.Context) error { return err } - nonce := uint64(10) + nonce := uint64(3) prevHash := generateRandomHash() prevRandSeed := generateRandomHash() for { @@ -120,7 +118,7 @@ func startMockNotifier(ctx *cli.Context) error { err = sendOutportBlock(outportBlock, host) log.LogIfError(err) - time.Sleep(3000 * time.Millisecond) + time.Sleep(2000 * time.Millisecond) err = sendFinalizedBlock(headerHash, host) log.LogIfError(err) @@ -155,15 +153,19 @@ func createWSHost() (factoryHost.FullDuplexHost, error) { return factoryHost.CreateWebSocketHost(args) } -func createAndStartGRPCServer() (*mockServer, *grpc.Server, error) { +func createAndStartGRPCServer(ctx *cli.Context) (MockServer, GRPCServerMock, error) { + if !ctx.Bool(grpcEnabled.Name) { + return NewDisabledMockServer(), NewDisabledGRPCServer(), nil + } + listener, err := net.Listen("tcp", grpcAddress) if err != nil { return nil, nil, err } tlsConfig, err := cert.LoadTLSServerConfig(cert.FileCfg{ - CertFile: "certificate.crt", - PkFile: "private_key.pem", + CertFile: getAbsolutePath(ctx.GlobalString(sovereignBridgeCertificateFile.Name)), + PkFile: getAbsolutePath(ctx.GlobalString(sovereignBridgeCertificatePkFile.Name)), }) if err != nil { return nil, nil, err @@ -189,6 +191,19 @@ func createAndStartGRPCServer() (*mockServer, *grpc.Server, error) { return mockedServer, grpcServer, nil } +func getAbsolutePath(path string) string { + if !strings.HasPrefix(path, "~") { + return path + } + + homeDir, err := os.UserHomeDir() + if err != nil { + log.Error("Error getting home directory: " + err.Error()) + return "" + } + return strings.Replace(path, "~", homeDir, 1) +} + func generateRandomHash() []byte { randomBytes := make([]byte, hashSize) _, _ = rand.Read(randomBytes) @@ -244,12 +259,7 @@ func createLogs(subscribedAddr []byte, ct uint64) ([]*outport.LogData, error) { return nil, err } - nonce := big.NewInt(int64(ct)).Bytes() - gasLimit := big.NewInt(69327).Bytes() - dummyFuncWithArgsAndGas := []byte("@0a@@66756e6332@61726731@") - - logData := append(nonce, dummyFuncWithArgsAndGas...) - logData = append(logData, gasLimit...) + eventData := createEventData(ct, subscribedAddr) return []*outport.LogData{ { @@ -260,7 +270,7 @@ func createLogs(subscribedAddr []byte, ct uint64) ([]*outport.LogData, error) { Address: subscribedAddr, Identifier: []byte("deposit"), Topics: topics, - Data: logData, + Data: eventData, }, }, }, @@ -276,8 +286,8 @@ func createOutGoingBridgeOpsConfirmationLogs(confirmedBridgeOps []*ConfirmedBrid Events: []*transaction.Event{ { Address: subscribedAddr, - Identifier: []byte("executedBridgeOp"), - Topics: [][]byte{confirmedBridgeOp.HashOfHashes, confirmedBridgeOp.BridgeOpHash}, + Identifier: []byte("execute"), + Topics: [][]byte{[]byte("executedBridgeOp"), confirmedBridgeOp.HashOfHashes, confirmedBridgeOp.BridgeOpHash}, }, }, }, @@ -289,43 +299,86 @@ func createOutGoingBridgeOpsConfirmationLogs(confirmedBridgeOps []*ConfirmedBrid func createTransferTopics(addr []byte, ct int64) ([][]byte, error) { nftTransferNonce := big.NewInt(ct%2 + 1) - nftTransferValue := big.NewInt(100) - nftMetaData, err := createNFTMetaData(nftTransferValue, nftTransferNonce.Uint64(), addr) - if err != nil { - return nil, err - } - + nftMetaData := createESDTTokenData(core.NonFungibleV2, big.NewInt(1).Bytes(), "hash", "name", "attributes", addr, big.NewInt(1000).Bytes(), "uri1", "uri2") transferNFT := [][]byte{ []byte("ASH-a642d1"), // id nftTransferNonce.Bytes(), // nonce != 0 nftMetaData, // meta data } + + tokenMetaData := createESDTTokenData(core.Fungible, big.NewInt(50+ct).Bytes(), "", "", "", addr, big.NewInt(0).Bytes()) transferESDT := [][]byte{ - []byte("WEGLD-bd4d79"), // id - big.NewInt(0).Bytes(), // nonce = 0 - big.NewInt(50 + ct).Bytes(), // value + []byte("WEGLD-bd4d79"), // id + big.NewInt(0).Bytes(), // nonce = 0 + tokenMetaData, // meta data } - topic := append([][]byte{addr}, transferNFT...) - topic = append(topic, transferESDT...) - return topic, nil + topics := make([][]byte, 0) + topics = append(topics, []byte("deposit")) + topics = append(topics, addr) + topics = append(topics, transferNFT...) + topics = append(topics, transferESDT...) + return topics, nil } -func createNFTMetaData(value *big.Int, nonce uint64, creator []byte) ([]byte, error) { - esdtData := &esdt.ESDigitalToken{ - Type: uint32(core.NonFungible), - Value: value, - TokenMetaData: &esdt.MetaData{ - URIs: [][]byte{[]byte("uri1"), []byte("uri2"), []byte("uri3")}, - Nonce: nonce, - Hash: []byte("NFT hash"), - Name: []byte("name nft"), - Attributes: []byte("attributes"), - Creator: creator, - }, +func createESDTTokenData( + esdtType core.ESDTType, + amount []byte, + hash string, + name string, + attributes string, + creator []byte, + royalties []byte, + uris ...string, +) []byte { + esdtTokenData := make([]byte, 0) + esdtTokenData = append(esdtTokenData, uint8(esdtType)) // esdt type + esdtTokenData = append(esdtTokenData, numberToBytes(uint64(len(amount)), lenItemSize)...) // length of amount + esdtTokenData = append(esdtTokenData, amount...) // amount + esdtTokenData = append(esdtTokenData, []byte{0x00}...) // not frozen + esdtTokenData = append(esdtTokenData, numberToBytes(uint64(len(hash)), lenItemSize)...) // length of hash + esdtTokenData = append(esdtTokenData, hash...) // hash + esdtTokenData = append(esdtTokenData, numberToBytes(uint64(len(name)), lenItemSize)...) // length of name + esdtTokenData = append(esdtTokenData, name...) // name + esdtTokenData = append(esdtTokenData, numberToBytes(uint64(len(attributes)), lenItemSize)...) // length of attributes + esdtTokenData = append(esdtTokenData, attributes...) // attributes + esdtTokenData = append(esdtTokenData, creator...) // creator + esdtTokenData = append(esdtTokenData, numberToBytes(uint64(len(royalties)), lenItemSize)...) // length of royalties + esdtTokenData = append(esdtTokenData, royalties...) // royalties + esdtTokenData = append(esdtTokenData, numberToBytes(uint64(len(uris)), lenItemSize)...) // number of uris + for _, uri := range uris { + esdtTokenData = append(esdtTokenData, numberToBytes(uint64(len(uri)), lenItemSize)...) // length of uri + esdtTokenData = append(esdtTokenData, []byte(uri)...) // uri } - return marshaller.Marshal(esdtData) + return esdtTokenData +} + +func createEventData(nonce uint64, addr []byte) []byte { + gasLimit := uint64(10000000) + function := []byte("func") + args := [][]byte{[]byte("arg1")} + + eventData := make([]byte, 0) + eventData = append(eventData, numberToBytes(nonce, u64Size)...) // event nonce + eventData = append(eventData, addr...) // original sender + eventData = append(eventData, []byte{0x01}...) // has transfer data + eventData = append(eventData, numberToBytes(gasLimit, u64Size)...) // gas limit bytes + eventData = append(eventData, numberToBytes(uint64(len(function)), lenItemSize)...) // length of function + eventData = append(eventData, function...) // function + eventData = append(eventData, numberToBytes(uint64(len(args)), lenItemSize)...) // number of arguments + for _, arg := range args { + eventData = append(eventData, numberToBytes(uint64(len(arg)), lenItemSize)...) // length of current argument + eventData = append(eventData, arg...) // current argument + } + + return eventData +} + +func numberToBytes(number uint64, size int) []byte { + result := make([]byte, 8) + binary.BigEndian.PutUint64(result, number) + return result[8-size:] } func createBlockData(headerV2 *block.HeaderV2) (*outport.BlockData, error) { diff --git a/cmd/sovereignnode/systemTestDemo/mockNotifier/vars.go b/cmd/sovereignnode/systemTestDemo/mockNotifier/vars.go index d25a9c1dfe7..93e3aeb536d 100644 --- a/cmd/sovereignnode/systemTestDemo/mockNotifier/vars.go +++ b/cmd/sovereignnode/systemTestDemo/mockNotifier/vars.go @@ -8,8 +8,10 @@ import ( ) const ( - addressLen = 32 - hashSize = 32 + addressLen = 32 + hashSize = 32 + lenItemSize = 4 + u64Size = 8 ) var ( @@ -18,7 +20,7 @@ var ( marshaller, _ = factory.NewMarshalizer("gogo protobuf") pubKeyConverter, _ = pubkeyConverter.NewBech32PubkeyConverter(addressLen, "erd") - wsURL = "ws://localhost:22111" + wsURL = "localhost:22111" grpcAddress = ":8085" subscribedAddress = "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" ) diff --git a/common/enablers/enableEpochsHandler.go b/common/enablers/enableEpochsHandler.go index 65b4a8bc274..e412aa5408f 100644 --- a/common/enablers/enableEpochsHandler.go +++ b/common/enablers/enableEpochsHandler.go @@ -858,16 +858,6 @@ func (handler *enableEpochsHandler) GetCurrentEpoch() uint32 { return currentEpoch } -// StakingV4Step2EnableEpoch returns the epoch when stakingV4 becomes active -func (handler *enableEpochsHandler) StakingV4Step2EnableEpoch() uint32 { - return handler.enableEpochsConfig.StakingV4Step2EnableEpoch -} - -// StakingV4Step1EnableEpoch returns the epoch when stakingV4 phase1 becomes active -func (handler *enableEpochsHandler) StakingV4Step1EnableEpoch() uint32 { - return handler.enableEpochsConfig.StakingV4Step1EnableEpoch -} - // IsInterfaceNil returns true if there is no value under the interface func (handler *enableEpochsHandler) IsInterfaceNil() bool { return handler == nil diff --git a/config/sovereignConfig.go b/config/sovereignConfig.go index 0461bd8e0ba..04ad37b9cd0 100644 --- a/config/sovereignConfig.go +++ b/config/sovereignConfig.go @@ -38,6 +38,7 @@ type OutGoingBridgeCertificate struct { // NotifierConfig holds sovereign notifier configuration type NotifierConfig struct { + Enabled bool `toml:"Enabled"` SubscribedEvents []SubscribedEvent `toml:"SubscribedEvents"` WebSocketConfig WebSocketConfig `toml:"WebSocket"` } diff --git a/dataRetriever/requestHandlers/requestHandler.go b/dataRetriever/requestHandlers/requestHandler.go index 95f6290f130..6fd1ced8a68 100644 --- a/dataRetriever/requestHandlers/requestHandler.go +++ b/dataRetriever/requestHandlers/requestHandler.go @@ -10,11 +10,12 @@ 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/core/partitioning" + "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/factory" - "github.com/multiversx/mx-chain-logger-go" ) var _ epochStart.RequestHandler = (*resolverRequestHandler)(nil) diff --git a/epochStart/bootstrap/bootStrapShardProcessor.go b/epochStart/bootstrap/bootStrapShardProcessor.go index 103e616cead..17de07503c9 100644 --- a/epochStart/bootstrap/bootStrapShardProcessor.go +++ b/epochStart/bootstrap/bootStrapShardProcessor.go @@ -22,6 +22,7 @@ import ( "github.com/multiversx/mx-chain-go/sharding/nodesCoordinator" "github.com/multiversx/mx-chain-go/storage/cache" "github.com/multiversx/mx-chain-go/trie/factory" + updateSync "github.com/multiversx/mx-chain-go/update/sync" ) type bootStrapShardProcessor struct { @@ -447,3 +448,7 @@ func (bp *bootStrapShardProcessor) createStorageEpochStartMetaSyncer(args ArgsNe func (bp *bootStrapShardProcessor) createEpochStartInterceptorsContainers(args bootStrapFactory.ArgsEpochStartInterceptorContainer) (process.InterceptorsContainer, process.InterceptorsContainer, error) { return bootStrapFactory.NewEpochStartInterceptorsContainer(args) } + +func (bp *bootStrapShardProcessor) createCrossHeaderRequester() (updateSync.CrossHeaderRequester, error) { + return updateSync.NewMetaHeaderRequester(bp.requestHandler) +} diff --git a/epochStart/bootstrap/bootStrapSovereignShardProcessor.go b/epochStart/bootstrap/bootStrapSovereignShardProcessor.go index eb3bbc61bed..9dc0d6fbd29 100644 --- a/epochStart/bootstrap/bootStrapSovereignShardProcessor.go +++ b/epochStart/bootstrap/bootStrapSovereignShardProcessor.go @@ -2,6 +2,7 @@ package bootstrap import ( "context" + "fmt" "time" "github.com/multiversx/mx-chain-core-go/core" @@ -15,10 +16,12 @@ import ( "github.com/multiversx/mx-chain-go/epochStart/bootstrap/disabled" bootStrapFactory "github.com/multiversx/mx-chain-go/epochStart/bootstrap/factory" "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/block/sovereign/incomingHeader" "github.com/multiversx/mx-chain-go/process/factory/interceptorscontainer" "github.com/multiversx/mx-chain-go/sharding/nodesCoordinator" "github.com/multiversx/mx-chain-go/storage/cache" "github.com/multiversx/mx-chain-go/trie/factory" + updateSync "github.com/multiversx/mx-chain-go/update/sync" ) type sovereignBootStrapShardProcessor struct { @@ -158,8 +161,14 @@ func (sbp *sovereignBootStrapShardProcessor) baseSyncHeaders( meta data.MetaHeaderHandler, timeToWaitForRequestedData time.Duration, ) (map[string]data.HeaderHandler, error) { - hashesToRequest := make([][]byte, 0, 1) - shardIds := make([]uint32, 0, 1) + hashesToRequest := make([][]byte, 0, 2) + shardIds := make([]uint32, 0, 2) + + for _, epochStartData := range meta.GetEpochStartHandler().GetLastFinalizedHeaderHandlers() { + hashesToRequest = append(hashesToRequest, epochStartData.GetHeaderHash()) + shardIds = append(shardIds, epochStartData.GetShardID()) + } + if meta.GetEpoch() > sbp.startEpoch+1 { // no need to request genesis block hashesToRequest = append(hashesToRequest, meta.GetEpochStartHandler().GetEconomicsHandler().GetPrevEpochStartHash()) shardIds = append(shardIds, core.SovereignChainShardId) @@ -259,9 +268,19 @@ func (sbp *sovereignBootStrapShardProcessor) createEpochStartInterceptorsContain return nil, nil, err } + incomingHeaderProcessor, err := incomingHeader.CreateIncomingHeaderProcessor( + sbp.generalConfig.SovereignConfig.NotifierConfig.WebSocketConfig, + sbp.dataPool, + sbp.generalConfig.SovereignConfig.MainChainNotarization.MainChainNotarizationStartRound, + sbp.runTypeComponents, + ) + if err != nil { + return nil, nil, err + } + interceptorsContainerFactory, err := interceptorscontainer.NewSovereignShardInterceptorsContainerFactory(interceptorscontainer.ArgsSovereignShardInterceptorsContainerFactory{ ShardContainer: sp, - IncomingHeaderSubscriber: disabled.NewIncomingHeaderSubscriber(), + IncomingHeaderSubscriber: incomingHeaderProcessor, }) if err != nil { return nil, nil, err @@ -269,3 +288,12 @@ func (sbp *sovereignBootStrapShardProcessor) createEpochStartInterceptorsContain return interceptorsContainerFactory.Create() } + +func (bp *sovereignBootStrapShardProcessor) createCrossHeaderRequester() (updateSync.CrossHeaderRequester, error) { + extendedHeaderRequester, castOk := bp.requestHandler.(updateSync.ExtendedShardHeaderRequestHandler) + if !castOk { + return nil, fmt.Errorf("%w in sovereignBootStrapShardProcessor.createCrossHeaderRequester for extendedHeaderRequester", process.ErrWrongTypeAssertion) + } + + return updateSync.NewExtendedHeaderRequester(extendedHeaderRequester) +} diff --git a/epochStart/bootstrap/bootStrapSovereignShardProcessor_test.go b/epochStart/bootstrap/bootStrapSovereignShardProcessor_test.go index 947724043e8..0cab9273ed4 100644 --- a/epochStart/bootstrap/bootStrapSovereignShardProcessor_test.go +++ b/epochStart/bootstrap/bootStrapSovereignShardProcessor_test.go @@ -123,6 +123,7 @@ func TestBootStrapSovereignShardProcessor_syncHeadersFrom(t *testing.T) { sovProc := createSovBootStrapProc() prevEpochStartHash := []byte("prevEpochStartHash") + lastCrossChainHeaderHash := []byte("lastCrossChainHeaderHash") sovHdr := &block.SovereignChainHeader{ Header: &block.Header{ Epoch: 4, @@ -131,6 +132,10 @@ func TestBootStrapSovereignShardProcessor_syncHeadersFrom(t *testing.T) { Economics: block.Economics{ PrevEpochStartHash: prevEpochStartHash, }, + LastFinalizedCrossChainHeader: block.EpochStartCrossChainData{ + ShardID: core.MainChainShardId, + HeaderHash: lastCrossChainHeaderHash, + }, }, } @@ -140,8 +145,8 @@ func TestBootStrapSovereignShardProcessor_syncHeadersFrom(t *testing.T) { headersSyncedCt := 0 sovProc.headersSyncer = &epochStartMocks.HeadersByHashSyncerStub{ SyncMissingHeadersByHashCalled: func(shardIDs []uint32, headersHashes [][]byte, ctx context.Context) error { - require.Equal(t, []uint32{core.SovereignChainShardId}, shardIDs) - require.Equal(t, [][]byte{prevEpochStartHash}, headersHashes) + require.Equal(t, []uint32{core.MainChainShardId, core.SovereignChainShardId}, shardIDs) + require.Equal(t, [][]byte{lastCrossChainHeaderHash, prevEpochStartHash}, headersHashes) headersSyncedCt++ return nil }, @@ -242,6 +247,8 @@ func TestBootStrapSovereignShardProcessor_createEpochStartInterceptorsContainers t.Parallel() sovProc := createSovBootStrapProc() + sovProc.dataPool = dataRetrieverMock.NewPoolsHolderMock() + args := factoryInterceptors.ArgsEpochStartInterceptorContainer{ CoreComponents: sovProc.coreComponentsHolder, CryptoComponents: sovProc.cryptoComponentsHolder, @@ -286,3 +293,20 @@ func TestBootStrapSovereignShardProcessor_createEpochStartInterceptorsContainers require.Empty(t, allKeys) require.Zero(t, fullContainer.Len()) } + +func TestBootStrapSovereignShardProcessor_createCrossHeaderRequester(t *testing.T) { + t.Parallel() + + sovProc := createSovBootStrapProc() + sovProc.dataPool = dataRetrieverMock.NewPoolsHolderMock() + + requester, err := sovProc.createCrossHeaderRequester() + require.Nil(t, requester) + require.ErrorIs(t, err, process.ErrWrongTypeAssertion) + require.ErrorContains(t, err, "extendedHeaderRequester") + + sovProc.requestHandler = &testscommon.ExtendedShardHeaderRequestHandlerStub{} + requester, err = sovProc.createCrossHeaderRequester() + require.Nil(t, err) + require.Equal(t, "*sync.extendedHeaderRequester", fmt.Sprintf("%T", requester)) +} diff --git a/epochStart/bootstrap/disabled/incomingHeaderSubscriber.go b/epochStart/bootstrap/disabled/incomingHeaderSubscriber.go deleted file mode 100644 index d9b6a589460..00000000000 --- a/epochStart/bootstrap/disabled/incomingHeaderSubscriber.go +++ /dev/null @@ -1,30 +0,0 @@ -package disabled - -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/sovereign" -) - -type incomingHeaderSubscriber struct { -} - -// NewIncomingHeaderSubscriber creates a disabled incoming header subscriber -func NewIncomingHeaderSubscriber() *incomingHeaderSubscriber { - return &incomingHeaderSubscriber{} -} - -// AddHeader does nothing -func (ihs *incomingHeaderSubscriber) AddHeader(_ []byte, _ sovereign.IncomingHeaderHandler) error { - return nil -} - -// CreateExtendedHeader returns an empty shard extended header -func (ihs *incomingHeaderSubscriber) CreateExtendedHeader(_ sovereign.IncomingHeaderHandler) (data.ShardHeaderExtendedHandler, error) { - return &block.ShardHeaderExtended{}, nil -} - -// IsInterfaceNil checks if the underlying pointer is nil -func (ihs *incomingHeaderSubscriber) IsInterfaceNil() bool { - return ihs == nil -} diff --git a/epochStart/bootstrap/interface.go b/epochStart/bootstrap/interface.go index 14c5a042748..03fe3b36b9d 100644 --- a/epochStart/bootstrap/interface.go +++ b/epochStart/bootstrap/interface.go @@ -5,15 +5,18 @@ import ( "time" "github.com/multiversx/mx-chain-go/dataRetriever" + sovereignBlock "github.com/multiversx/mx-chain-go/dataRetriever/dataPool/sovereign" requesterscontainer "github.com/multiversx/mx-chain-go/dataRetriever/factory/requestersContainer" storageRequestFactory "github.com/multiversx/mx-chain-go/dataRetriever/factory/storageRequestersContainer/factory" "github.com/multiversx/mx-chain-go/dataRetriever/requestHandlers" "github.com/multiversx/mx-chain-go/epochStart" bootStrapFactory "github.com/multiversx/mx-chain-go/epochStart/bootstrap/factory" "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/block/sovereign" "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/sharding/nodesCoordinator" syncerFactory "github.com/multiversx/mx-chain-go/state/syncer/factory" + updateSync "github.com/multiversx/mx-chain-go/update/sync" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data" @@ -92,6 +95,9 @@ type RunTypeComponentsHolder interface { RequestersContainerFactoryCreator() requesterscontainer.RequesterContainerFactoryCreator ValidatorAccountsSyncerFactoryHandler() syncerFactory.ValidatorAccountsSyncerFactoryHandler ShardRequestersContainerCreatorHandler() storageRequestFactory.ShardRequestersContainerCreatorHandler + OutGoingOperationsPoolHandler() sovereignBlock.OutGoingOperationsPool + DataCodecHandler() sovereign.DataCodecHandler + TopicsCheckerHandler() sovereign.TopicsCheckerHandler IsInterfaceNil() bool } @@ -117,6 +123,7 @@ type bootStrapShardProcessorHandler interface { createEpochStartMetaSyncer() (epochStart.StartOfEpochMetaSyncer, error) createStorageEpochStartMetaSyncer(args ArgsNewEpochStartMetaSyncer) (epochStart.StartOfEpochMetaSyncer, error) createEpochStartInterceptorsContainers(args bootStrapFactory.ArgsEpochStartInterceptorContainer) (process.InterceptorsContainer, process.InterceptorsContainer, error) + createCrossHeaderRequester() (updateSync.CrossHeaderRequester, error) } type epochStartTopicProviderHandler interface { diff --git a/epochStart/bootstrap/process.go b/epochStart/bootstrap/process.go index 05b0e1ca7a1..2bbf4725f01 100644 --- a/epochStart/bootstrap/process.go +++ b/epochStart/bootstrap/process.go @@ -585,11 +585,17 @@ func (e *epochStartBootstrap) createSyncers() error { return err } + crossHeaderRequester, err := e.bootStrapShardProcessor.createCrossHeaderRequester() + if err != nil { + return err + } + syncMissingHeadersArgs := updateSync.ArgsNewMissingHeadersByHashSyncer{ - Storage: disabled.CreateMemUnit(), - Cache: e.dataPool.Headers(), - Marshalizer: e.coreComponentsHolder.InternalMarshalizer(), - RequestHandler: e.requestHandler, + Storage: disabled.CreateMemUnit(), + Cache: e.dataPool.Headers(), + Marshalizer: e.coreComponentsHolder.InternalMarshalizer(), + RequestHandler: e.requestHandler, + CrossHeaderRequester: crossHeaderRequester, } e.headersSyncer, err = updateSync.NewMissingheadersByHashSyncer(syncMissingHeadersArgs) if err != nil { diff --git a/epochStart/bootstrap/shardSovereignStorageHandler.go b/epochStart/bootstrap/shardSovereignStorageHandler.go index 59869405ab7..4122a3104d2 100644 --- a/epochStart/bootstrap/shardSovereignStorageHandler.go +++ b/epochStart/bootstrap/shardSovereignStorageHandler.go @@ -1,12 +1,15 @@ package bootstrap import ( + "encoding/hex" + "errors" "fmt" "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/dataRetriever" + "github.com/multiversx/mx-chain-go/epochStart" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/process/block/bootstrapStorage" ) @@ -50,9 +53,14 @@ func (ssh *sovereignShardStorageHandler) SaveDataToStorage(components *Component return err } + lastCrossNotarizedHeaders, err := ssh.saveLastCrossChainNotarizedHeaders(components.EpochStartMetaBlock, components.Headers) + if err != nil { + return err + } + bootStrapData := bootstrapStorage.BootstrapData{ LastHeader: lastHeader, - LastCrossNotarizedHeaders: []bootstrapStorage.BootstrapHeaderInfo{}, + LastCrossNotarizedHeaders: lastCrossNotarizedHeaders, LastSelfNotarizedHeaders: []bootstrapStorage.BootstrapHeaderInfo{lastHeader}, ProcessedMiniBlocks: []bootstrapStorage.MiniBlocksInMeta{}, PendingMiniBlocks: []bootstrapStorage.PendingMiniBlocksInfo{}, @@ -88,3 +96,77 @@ func (ssh *sovereignShardStorageHandler) saveTriggerRegistry(components *Compone return ssh.baseSaveTriggerRegistry(&triggerReg, sovHeader.GetRound()) } + +func (ssh *sovereignShardStorageHandler) saveLastCrossChainNotarizedHeaders( + sovBlock data.MetaHeaderHandler, + headers map[string]data.HeaderHandler, +) ([]bootstrapStorage.BootstrapHeaderInfo, error) { + log.Debug("sovereignShardStorageHandler.saveLastCrossChainNotarizedHeaders") + + lastCrossChainNotarizedData, err := getEpochStartShardData(sovBlock, core.MainChainShardId) + if errors.Is(err, epochStart.ErrEpochStartDataForShardNotFound) { + log.Debug("no cross chain header has been notarized yet") + return []bootstrapStorage.BootstrapHeaderInfo{}, nil + } else if err != nil { + return nil, err + } + + lastCrossChainHeaderHash := lastCrossChainNotarizedData.GetHeaderHash() + log.Debug("sovereignShardStorageHandler.saveLastCrossChainNotarizedHeaders", + "hash", lastCrossChainHeaderHash, + ) + + neededHdr, ok := headers[string(lastCrossChainHeaderHash)] + if !ok { + return nil, fmt.Errorf("%w in sovereignShardStorageHandler.saveLastCrossChainNotarizedHeaders: hash: %s", + epochStart.ErrMissingHeader, + hex.EncodeToString(lastCrossChainHeaderHash)) + } + + extendedShardHeader, ok := neededHdr.(data.ShardHeaderExtendedHandler) + if !ok { + return nil, fmt.Errorf("%w in sovereignShardStorageHandler.saveLastCrossChainNotarizedHeaders for extended shard header", + epochStart.ErrWrongTypeAssertion, + ) + } + + err = ssh.saveExtendedHeaderToStorage(extendedShardHeader, lastCrossChainHeaderHash) + if err != nil { + return nil, err + } + + crossNotarizedHeaders := make([]bootstrapStorage.BootstrapHeaderInfo, 0) + crossNotarizedHeaders = append(crossNotarizedHeaders, bootstrapStorage.BootstrapHeaderInfo{ + ShardId: core.MainChainShardId, + Nonce: lastCrossChainNotarizedData.GetNonce(), + Hash: lastCrossChainHeaderHash, + Epoch: lastCrossChainNotarizedData.GetEpoch(), + }) + + return crossNotarizedHeaders, nil +} + +func (bsh *sovereignShardStorageHandler) saveExtendedHeaderToStorage(extendedShardHeader data.HeaderHandler, headerHash []byte) error { + headerBytes, err := bsh.marshalizer.Marshal(extendedShardHeader) + if err != nil { + return err + } + + extendedHdrStorer, err := bsh.storageService.GetStorer(dataRetriever.ExtendedShardHeadersUnit) + if err != nil { + return err + } + + err = extendedHdrStorer.Put(headerHash, headerBytes) + if err != nil { + return err + } + + nonceToByteSlice := bsh.uint64Converter.ToByteSlice(extendedShardHeader.GetNonce()) + extendedHdrNonceStorage, err := bsh.storageService.GetStorer(dataRetriever.ExtendedShardHeadersNonceHashDataUnit) + if err != nil { + return err + } + + return extendedHdrNonceStorage.Put(nonceToByteSlice, headerHash) +} diff --git a/epochStart/bootstrap/shardSovereignStorageHandler_test.go b/epochStart/bootstrap/shardSovereignStorageHandler_test.go index baf2a8efea7..07335ef0126 100644 --- a/epochStart/bootstrap/shardSovereignStorageHandler_test.go +++ b/epochStart/bootstrap/shardSovereignStorageHandler_test.go @@ -13,6 +13,7 @@ import ( "github.com/multiversx/mx-chain-go/process/block/bootstrapStorage" "github.com/multiversx/mx-chain-go/sharding/nodesCoordinator" "github.com/multiversx/mx-chain-go/storage" + "github.com/multiversx/mx-chain-go/storage/factory" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -83,20 +84,20 @@ func TestSovereignShardStorageHandler_SaveDataToStorage(t *testing.T) { bootStrapData := getStoredBootstrapData(t, sovShardStorage.marshalizer, bootStorer, hdr1.GetRound()) require.Equal(t, &bootstrapStorage.BootstrapData{ LastHeader: bootstrapStorage.BootstrapHeaderInfo{ - Nonce: 1, - Epoch: 1, + Nonce: hdr1.GetNonce(), + Epoch: hdr1.GetEpoch(), Hash: hdrHash, }, LastCrossNotarizedHeaders: []bootstrapStorage.BootstrapHeaderInfo{}, LastSelfNotarizedHeaders: []bootstrapStorage.BootstrapHeaderInfo{{ - Nonce: 1, - Epoch: 1, + Nonce: hdr1.GetNonce(), + Epoch: hdr1.GetEpoch(), Hash: hdrHash, }}, ProcessedMiniBlocks: []bootstrapStorage.MiniBlocksInMeta{}, PendingMiniBlocks: []bootstrapStorage.PendingMiniBlocksInfo{}, NodesCoordinatorConfigKey: nil, - EpochStartTriggerConfigKey: []byte(fmt.Sprint(1)), + EpochStartTriggerConfigKey: []byte(fmt.Sprint(hdr1.GetEpoch())), HighestFinalBlockNonce: hdr1.GetNonce(), LastRound: 0, }, bootStrapData) @@ -125,3 +126,110 @@ func TestSovereignShardStorageHandler_SaveDataToStorage(t *testing.T) { _, err = metaStorer.Get([]byte("epochStartBlock_2")) require.NotNil(t, err) } + +func TestSovereignShardStorageHandler_SaveDataToStorageCheckLastCrossChainNotarizedDataIsSaved(t *testing.T) { + t.Parallel() + + args := createStorageHandlerArgs() + args.AdditionalStorageServiceCreator = factory.NewSovereignAdditionalStorageServiceFactory() + shardStorage, _ := NewShardStorageHandler(args) + sovShardStorage := newSovereignShardStorageHandler(shardStorage) + + hash1 := []byte("hash1") + lastFinalizedCrossChainHeaderHash := []byte("lastFinalizedCrossChainHeaderHash") + lastFinalizedCrossChainHeader := &block.ShardHeaderExtended{ + Header: &block.HeaderV2{ + Header: &block.Header{ + Epoch: 1, + Round: 4, + Nonce: 4, + }, + }, + } + sovHdr := &block.SovereignChainHeader{ + Header: &block.Header{ + Nonce: 2, + Round: 2, + Epoch: 2, + }, + EpochStart: block.EpochStartSovereign{ + LastFinalizedCrossChainHeader: block.EpochStartCrossChainData{ + ShardID: core.MainChainShardId, + Epoch: 1, + Round: 4, + Nonce: 4, + HeaderHash: lastFinalizedCrossChainHeaderHash, + }, + }, + } + headers := map[string]data.HeaderHandler{ + string(hash1): sovHdr, + string(lastFinalizedCrossChainHeaderHash): lastFinalizedCrossChainHeader, + } + + components := &ComponentsNeededForBootstrap{ + EpochStartMetaBlock: sovHdr, + PreviousEpochStart: sovHdr, + ShardHeader: sovHdr, + Headers: headers, + NodesConfig: &nodesCoordinator.NodesCoordinatorRegistry{}, + } + + err := sovShardStorage.SaveDataToStorage(components, components.ShardHeader, false, nil) + require.Nil(t, err) + + bootStorer, err := sovShardStorage.storageService.GetStorer(dataRetriever.BootstrapUnit) + require.Nil(t, err) + + hdrHash, err := core.CalculateHash(sovShardStorage.marshalizer, sovShardStorage.hasher, sovHdr) + require.Nil(t, err) + + bootStrapData := getStoredBootstrapData(t, sovShardStorage.marshalizer, bootStorer, sovHdr.GetRound()) + require.Equal(t, &bootstrapStorage.BootstrapData{ + LastHeader: bootstrapStorage.BootstrapHeaderInfo{ + Nonce: sovHdr.GetNonce(), + Epoch: sovHdr.GetEpoch(), + Hash: hdrHash, + }, + LastCrossNotarizedHeaders: []bootstrapStorage.BootstrapHeaderInfo{ + { + ShardId: core.MainChainShardId, + Epoch: lastFinalizedCrossChainHeader.GetEpoch(), + Nonce: lastFinalizedCrossChainHeader.GetNonce(), + Hash: lastFinalizedCrossChainHeaderHash, + }, + }, + LastSelfNotarizedHeaders: []bootstrapStorage.BootstrapHeaderInfo{ + { + Nonce: sovHdr.GetNonce(), + Epoch: sovHdr.GetEpoch(), + Hash: hdrHash, + }, + }, + ProcessedMiniBlocks: []bootstrapStorage.MiniBlocksInMeta{}, + PendingMiniBlocks: []bootstrapStorage.PendingMiniBlocksInfo{}, + NodesCoordinatorConfigKey: nil, + EpochStartTriggerConfigKey: []byte(fmt.Sprint(sovHdr.GetEpoch())), + HighestFinalBlockNonce: sovHdr.GetNonce(), + LastRound: 0, + }, bootStrapData) + + extendedHdrStorer, err := sovShardStorage.storageService.GetStorer(dataRetriever.ExtendedShardHeadersUnit) + require.Nil(t, err) + + extendedHdrBytes, err := extendedHdrStorer.Get(lastFinalizedCrossChainHeaderHash) + require.Nil(t, err) + + extendedHdrStored := &block.ShardHeaderExtended{} + err = sovShardStorage.marshalizer.Unmarshal(extendedHdrStored, extendedHdrBytes) + require.Nil(t, err) + require.Equal(t, lastFinalizedCrossChainHeader, extendedHdrStored) + + extendedHdrNonceStorer, err := sovShardStorage.storageService.GetStorer(dataRetriever.ExtendedShardHeadersNonceHashDataUnit) + require.Nil(t, err) + + nonceToBytesKey := sovShardStorage.uint64Converter.ToByteSlice(lastFinalizedCrossChainHeader.GetNonce()) + extendedHdrNonceBytesHash, err := extendedHdrNonceStorer.Get(nonceToBytesKey) + require.Nil(t, err) + require.Equal(t, lastFinalizedCrossChainHeaderHash, extendedHdrNonceBytesHash) +} diff --git a/epochStart/bootstrap/storageProcess_test.go b/epochStart/bootstrap/storageProcess_test.go index 49f47214ebc..de553cf2e7e 100644 --- a/epochStart/bootstrap/storageProcess_test.go +++ b/epochStart/bootstrap/storageProcess_test.go @@ -320,15 +320,15 @@ func testRequestAndProcessFromStorageByShardId(t *testing.T, shardId uint32) { func TestStorageEpochStartBootstrap_syncHeadersFromStorage(t *testing.T) { t.Parallel() - coreComp, cryptoComp := createComponentsForEpochStart() - args := createMockStorageEpochStartBootstrapArgs(coreComp, cryptoComp) - - hdrHash1 := []byte("hdrHash1") - hdrHash2 := []byte("hdrHash2") - t.Run("fail to sync missing headers", func(t *testing.T) { t.Parallel() + coreComp, cryptoComp := createComponentsForEpochStart() + args := createMockStorageEpochStartBootstrapArgs(coreComp, cryptoComp) + + hdrHash1 := []byte("hdrHash1") + hdrHash2 := []byte("hdrHash2") + metaBlock := &block.MetaBlock{ Epoch: 2, EpochStart: block.EpochStart{ @@ -362,6 +362,12 @@ func TestStorageEpochStartBootstrap_syncHeadersFromStorage(t *testing.T) { t.Run("fail to get synced headers", func(t *testing.T) { t.Parallel() + coreComp, cryptoComp := createComponentsForEpochStart() + args := createMockStorageEpochStartBootstrapArgs(coreComp, cryptoComp) + + hdrHash1 := []byte("hdrHash1") + hdrHash2 := []byte("hdrHash2") + metaBlock := &block.MetaBlock{ Epoch: 2, EpochStart: block.EpochStart{ @@ -395,6 +401,12 @@ func TestStorageEpochStartBootstrap_syncHeadersFromStorage(t *testing.T) { t.Run("empty prev meta block when first epoch", func(t *testing.T) { t.Parallel() + coreComp, cryptoComp := createComponentsForEpochStart() + args := createMockStorageEpochStartBootstrapArgs(coreComp, cryptoComp) + + hdrHash1 := []byte("hdrHash1") + hdrHash2 := []byte("hdrHash2") + metaBlock := &block.MetaBlock{ Epoch: 1, EpochStart: block.EpochStart{ @@ -445,6 +457,12 @@ func TestStorageEpochStartBootstrap_syncHeadersFromStorage(t *testing.T) { t.Run("should work", func(t *testing.T) { t.Parallel() + coreComp, cryptoComp := createComponentsForEpochStart() + args := createMockStorageEpochStartBootstrapArgs(coreComp, cryptoComp) + + hdrHash1 := []byte("hdrHash1") + hdrHash2 := []byte("hdrHash2") + metaBlock := &block.MetaBlock{ Epoch: 2, EpochStart: block.EpochStart{ diff --git a/epochStart/bootstrap/syncEpochStartSovereign.go b/epochStart/bootstrap/syncEpochStartSovereign.go index f268585d8f5..4986b31a898 100644 --- a/epochStart/bootstrap/syncEpochStartSovereign.go +++ b/epochStart/bootstrap/syncEpochStartSovereign.go @@ -33,7 +33,7 @@ func newEpochStartSovereignSyncer(args ArgsNewEpochStartMetaSyncer) (*epochStart func createShardSingleDataInterceptor(args ArgsNewEpochStartMetaSyncer) (process.Interceptor, error) { argsInterceptedDataFactory := createArgsInterceptedDataFactory(args) - interceptedMetaHdrDataFactory, err := interceptorsFactory.NewInterceptedShardHeaderDataFactory(&argsInterceptedDataFactory) + interceptedMetaHdrDataFactory, err := interceptorsFactory.NewInterceptedSovereignShardHeaderDataFactory(&argsInterceptedDataFactory) if err != nil { return nil, err } diff --git a/epochStart/metachain/sovereignValidators.go b/epochStart/metachain/sovereignValidators.go index 8878a988528..a745e23f6d0 100644 --- a/epochStart/metachain/sovereignValidators.go +++ b/epochStart/metachain/sovereignValidators.go @@ -4,8 +4,11 @@ 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/block" + "github.com/multiversx/mx-chain-go/common" + "github.com/multiversx/mx-chain-go/epochStart" "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/state" ) type sovereignValidatorInfoCreator struct { @@ -74,6 +77,29 @@ func (svic *sovereignValidatorInfoCreator) getMarshalledValidatorInfoTxs(miniBlo return marshalledValidatorInfoTxs } +// CreateValidatorInfoMiniBlocks creates the validatorInfo mini blocks according to the provided validatorInfo map +func (svic *sovereignValidatorInfoCreator) CreateValidatorInfoMiniBlocks(validatorsInfo state.ShardValidatorsInfoMapHandler) (block.MiniBlockSlice, error) { + if validatorsInfo == nil { + return nil, epochStart.ErrNilValidatorInfo + } + + svic.clean() + + miniBlocks := make([]*block.MiniBlock, 0) + validators := validatorsInfo.GetShardValidatorsInfoMap()[core.SovereignChainShardId] + if len(validators) == 0 { + return miniBlocks, nil + } + + miniBlock, err := svic.createMiniBlock(validators, core.SovereignChainShardId) + if err != nil { + return nil, err + } + + miniBlocks = append(miniBlocks, miniBlock) + return miniBlocks, nil +} + // IsInterfaceNil checks if the underlying pointer is nil func (svic *sovereignValidatorInfoCreator) IsInterfaceNil() bool { return svic == nil diff --git a/epochStart/metachain/sovereignValidators_test.go b/epochStart/metachain/sovereignValidators_test.go index 9b58cf71deb..9f0a1511ea8 100644 --- a/epochStart/metachain/sovereignValidators_test.go +++ b/epochStart/metachain/sovereignValidators_test.go @@ -7,14 +7,16 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "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/process" + "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/state" "github.com/multiversx/mx-chain-go/testscommon" dataRetrieverMock "github.com/multiversx/mx-chain-go/testscommon/dataRetriever" vics "github.com/multiversx/mx-chain-go/testscommon/validatorInfoCacher" - "github.com/stretchr/testify/require" ) func TestNewSovereignValidatorInfoCreator(t *testing.T) { @@ -189,3 +191,67 @@ func TestSovereignValidatorInfoCreator_CreateMarshalledDataErrorCases(t *testing require.Empty(t, shardDataCacher.Keys()) }) } + +func TestSovereignValidatorInfoCreator_CreateValidatorInfoMiniBlocks(t *testing.T) { + t.Parallel() + + arguments := createMockEpochValidatorInfoCreatorsArguments() + arguments.ShardCoordinator = sharding.NewSovereignShardCoordinator() + + v1 := &state.ValidatorInfo{ + ShardId: core.SovereignChainShardId, + PublicKey: []byte("pubKey1"), + } + v2 := &state.ValidatorInfo{ + ShardId: core.SovereignChainShardId, + PublicKey: []byte("pubKey2"), + } + + v1ShardData := createShardValidatorInfo(v1) + v2ShardData := createShardValidatorInfo(v2) + + txHash1, _ := core.CalculateHash(arguments.Marshalizer, arguments.Hasher, v1ShardData) + txHash2, _ := core.CalculateHash(arguments.Marshalizer, arguments.Hasher, v2ShardData) + + addDataCt := 0 + valInfoCache := &vics.ValidatorInfoCacherStub{ + AddValidatorInfoCalled: func(validatorInfoHash []byte, validatorInfo *state.ShardValidatorInfo) { + switch addDataCt { + case 0: + require.Equal(t, txHash1, validatorInfoHash) + require.Equal(t, v1ShardData, validatorInfo) + case 1: + require.Equal(t, txHash2, validatorInfoHash) + require.Equal(t, v2ShardData, validatorInfo) + } + + addDataCt++ + }, + } + + arguments.DataPool = &dataRetrieverMock.PoolsHolderStub{ + CurrEpochValidatorInfoCalled: func() dataRetriever.ValidatorInfoCacher { + return valInfoCache + }, + } + + vic, _ := NewValidatorInfoCreator(arguments) + svic, _ := NewSovereignValidatorInfoCreator(vic) + + valMap := state.NewShardValidatorsInfoMap() + _ = valMap.Add(v1) + _ = valMap.Add(v2) + + mb, err := svic.CreateValidatorInfoMiniBlocks(valMap) + require.Nil(t, err) + require.NotNil(t, mb) + require.Equal(t, block.MiniBlockSlice{ + { + TxHashes: [][]byte{txHash1, txHash2}, + ReceiverShardID: core.SovereignChainShardId, + SenderShardID: core.SovereignChainShardId, + Type: block.PeerBlock, + }, + }, mb) + require.Equal(t, 2, addDataCt) +} diff --git a/epochStart/metachain/validators.go b/epochStart/metachain/validators.go index e8eff547a09..06854df4d63 100644 --- a/epochStart/metachain/validators.go +++ b/epochStart/metachain/validators.go @@ -12,6 +12,7 @@ import ( "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/common/compatibility" "github.com/multiversx/mx-chain-go/dataRetriever" @@ -109,7 +110,7 @@ func (vic *validatorInfoCreator) CreateValidatorInfoMiniBlocks(validatorsInfo st continue } - miniBlock, err := vic.createMiniBlock(validators) + miniBlock, err := vic.createMiniBlock(validators, core.AllShardId) if err != nil { return nil, err } @@ -122,7 +123,7 @@ func (vic *validatorInfoCreator) CreateValidatorInfoMiniBlocks(validatorsInfo st return miniBlocks, nil } - miniBlock, err := vic.createMiniBlock(validators) + miniBlock, err := vic.createMiniBlock(validators, core.AllShardId) if err != nil { return nil, err } @@ -132,10 +133,10 @@ func (vic *validatorInfoCreator) CreateValidatorInfoMiniBlocks(validatorsInfo st return miniBlocks, nil } -func (vic *validatorInfoCreator) createMiniBlock(validatorsInfo []state.ValidatorInfoHandler) (*block.MiniBlock, error) { +func (vic *validatorInfoCreator) createMiniBlock(validatorsInfo []state.ValidatorInfoHandler, receiverShardID uint32) (*block.MiniBlock, error) { miniBlock := &block.MiniBlock{} miniBlock.SenderShardID = vic.shardCoordinator.SelfId() - miniBlock.ReceiverShardID = core.AllShardId + miniBlock.ReceiverShardID = receiverShardID miniBlock.TxHashes = make([][]byte, len(validatorsInfo)) miniBlock.Type = block.PeerBlock diff --git a/epochStart/metachain/validators_test.go b/epochStart/metachain/validators_test.go index 662b0192044..f0b2d1a79f0 100644 --- a/epochStart/metachain/validators_test.go +++ b/epochStart/metachain/validators_test.go @@ -1158,7 +1158,7 @@ func testCreateMiniblockBackwardsCompatibility(t *testing.T, deterministFixEnabl arguments.ValidatorInfoStorage = storer vic, _ := NewValidatorInfoCreator(arguments) - mb, err := vic.createMiniBlock(validators.GetAllValidatorsInfo()) + mb, err := vic.createMiniBlock(validators.GetAllValidatorsInfo(), core.AllShardId) require.Nil(t, err) // test all generated miniblock's "txhashes" are the same with the expected ones diff --git a/factory/mock/extendedShardHeaderTrackerStub.go b/factory/mock/extendedShardHeaderTrackerStub.go deleted file mode 100644 index 37d2da7acf9..00000000000 --- a/factory/mock/extendedShardHeaderTrackerStub.go +++ /dev/null @@ -1,17 +0,0 @@ -package mock - -import "github.com/multiversx/mx-chain-core-go/data" - -// ExtendedShardHeaderTrackerStub - -type ExtendedShardHeaderTrackerStub struct { - BlockTrackerStub - ComputeLongestExtendedShardChainFromLastNotarizedCalled func() ([]data.HeaderHandler, [][]byte, error) -} - -// ComputeLongestExtendedShardChainFromLastNotarized - -func (eshts *ExtendedShardHeaderTrackerStub) ComputeLongestExtendedShardChainFromLastNotarized() ([]data.HeaderHandler, [][]byte, error) { - if eshts.ComputeLongestExtendedShardChainFromLastNotarizedCalled != nil { - return eshts.ComputeLongestMetaChainFromLastNotarizedCalled() - } - return nil, nil, nil -} diff --git a/factory/processing/blockProcessorCreator_test.go b/factory/processing/blockProcessorCreator_test.go index c4282d00588..35220ed0934 100644 --- a/factory/processing/blockProcessorCreator_test.go +++ b/factory/processing/blockProcessorCreator_test.go @@ -89,7 +89,7 @@ func Test_newBlockProcessorCreatorForShard(t *testing.T) { &mock.BoostrapStorerStub{}, &testscommon.ValidatorStatisticsProcessorStub{}, &mock.HeaderValidatorStub{}, - &mock.ExtendedShardHeaderTrackerStub{}, + &testscommon.ExtendedShardHeaderTrackerStub{}, &mock.PendingMiniBlocksHandlerStub{}, &sync.RWMutex{}, &testscommon.ScheduledTxsExecutionStub{}, diff --git a/factory/runType/runTypeComponents.go b/factory/runType/runTypeComponents.go index e25cf1007cc..6e6b049e6a7 100644 --- a/factory/runType/runTypeComponents.go +++ b/factory/runType/runTypeComponents.go @@ -153,11 +153,7 @@ func (rcf *runTypeComponentsFactory) Create() (*runTypeComponents, error) { } epochStartBootstrapperFactory := bootstrap.NewEpochStartBootstrapperFactory() - - bootstrapperFromStorageFactory, err := storageBootstrap.NewShardStorageBootstrapperFactory() - if err != nil { - return nil, fmt.Errorf("runTypeComponentsFactory - NewShardStorageBootstrapperFactory failed: %w", err) - } + bootstrapperFromStorageFactory := storageBootstrap.NewShardStorageBootstrapperFactory() shardBootstrapFactory, err := storageBootstrap.NewShardBootstrapFactory() if err != nil { diff --git a/factory/runType/sovereignRunTypeComponents.go b/factory/runType/sovereignRunTypeComponents.go index 9fb73afdd25..e09a94965e3 100644 --- a/factory/runType/sovereignRunTypeComponents.go +++ b/factory/runType/sovereignRunTypeComponents.go @@ -100,10 +100,7 @@ func (rcf *sovereignRunTypeComponentsFactory) Create() (*runTypeComponents, erro return nil, fmt.Errorf("sovereignRunTypeComponentsFactory - NewSovereignEpochStartBootstrapperFactory failed: %w", err) } - bootstrapperFromStorageFactory, err := storageBootstrap.NewSovereignShardStorageBootstrapperFactory(rtc.bootstrapperFromStorageCreator) - if err != nil { - return nil, fmt.Errorf("sovereignRunTypeComponentsFactory - NewSovereignShardStorageBootstrapperFactory failed: %w", err) - } + bootstrapperFromStorageFactory := storageBootstrap.NewSovereignShardStorageBootstrapperFactory() bootstrapperFactory, err := storageBootstrap.NewSovereignShardBootstrapFactory(rtc.bootstrapperCreator) if err != nil { diff --git a/factory/vm/sovereignVmContainerShardCreator_test.go b/factory/vm/sovereignVmContainerShardCreator_test.go index a117d0ca092..0c6b1f40c0d 100644 --- a/factory/vm/sovereignVmContainerShardCreator_test.go +++ b/factory/vm/sovereignVmContainerShardCreator_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/multiversx/mx-chain-go/factory/vm" - factory2 "github.com/multiversx/mx-chain-go/process/factory" + testFactory "github.com/multiversx/mx-chain-go/process/factory" componentsMock "github.com/multiversx/mx-chain-go/testscommon/components" ) @@ -86,12 +86,12 @@ func TestNewSovereignVmContainerShardFactory_CreateVmContainerFactoryShard(t *te require.Equal(t, "*shard.vmContainerFactory", fmt.Sprintf("%T", vmFactory)) require.Equal(t, 2, vmContainer.Len()) - svm, err := vmContainer.Get(factory2.SystemVirtualMachine) + svm, err := vmContainer.Get(testFactory.SystemVirtualMachine) require.Nil(t, err) require.NotNil(t, svm) require.Equal(t, "*process.systemVM", fmt.Sprintf("%T", svm)) - wasmvm, err := vmContainer.Get(factory2.WasmVirtualMachine) + wasmvm, err := vmContainer.Get(testFactory.WasmVirtualMachine) require.Nil(t, err) require.NotNil(t, wasmvm) require.Equal(t, "*hostCore.vmHost", fmt.Sprintf("%T", wasmvm)) diff --git a/integrationTests/vm/staking/metaBlockProcessorCreator.go b/integrationTests/vm/staking/metaBlockProcessorCreator.go index 1ed25347d73..bbab1021742 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/dataRetriever" "github.com/multiversx/mx-chain-go/epochStart" "github.com/multiversx/mx-chain-go/epochStart/metachain" @@ -25,7 +26,7 @@ import ( "github.com/multiversx/mx-chain-go/testscommon" "github.com/multiversx/mx-chain-go/testscommon/components" "github.com/multiversx/mx-chain-go/testscommon/dblookupext" - factory2 "github.com/multiversx/mx-chain-go/testscommon/factory" + testFactory "github.com/multiversx/mx-chain-go/testscommon/factory" "github.com/multiversx/mx-chain-go/testscommon/integrationtests" "github.com/multiversx/mx-chain-go/testscommon/outport" statusHandlerMock "github.com/multiversx/mx-chain-go/testscommon/statusHandler" @@ -78,7 +79,7 @@ func createMetaBlockProcessor( DataComponents: dataComponents, BootstrapComponents: bootstrapComponents, StatusComponents: statusComponents, - StatusCoreComponents: &factory2.StatusCoreComponentsStub{ + StatusCoreComponents: &testFactory.StatusCoreComponentsStub{ AppStatusHandlerField: &statusHandlerMock.AppStatusHandlerStub{}, }, AccountsDB: accountsDb, diff --git a/node/chainSimulator/chainSimulator.go b/node/chainSimulator/chainSimulator.go index f717967ffb7..9082d3d852a 100644 --- a/node/chainSimulator/chainSimulator.go +++ b/node/chainSimulator/chainSimulator.go @@ -66,7 +66,7 @@ type ArgsChainSimulator struct { AlterConfigsFunction func(cfg *config.Configs) VmQueryDelayAfterStartInMs uint64 CreateRunTypeCoreComponents func() (factory.RunTypeCoreComponentsHolder, error) - CreateIncomingHeaderSubscriber func(config *config.NotifierConfig, dataPool dataRetriever.PoolsHolder, mainChainNotarizationStartRound uint64, runTypeComponents factory.RunTypeComponentsHolder) (processing.IncomingHeaderSubscriber, error) + CreateIncomingHeaderSubscriber func(config config.WebSocketConfig, dataPool dataRetriever.PoolsHolder, mainChainNotarizationStartRound uint64, runTypeComponents factory.RunTypeComponentsHolder) (processing.IncomingHeaderSubscriber, error) CreateRunTypeComponents func(args runType.ArgsRunTypeComponents) (factory.RunTypeComponentsHolder, error) NodeFactory node.NodeFactory ChainProcessorFactory ChainHandlerFactory @@ -129,7 +129,7 @@ func setSimulatorRunTypeArguments(args *ArgsChainSimulator) { } } if args.CreateIncomingHeaderSubscriber == nil { - args.CreateIncomingHeaderSubscriber = func(_ *config.NotifierConfig, _ dataRetriever.PoolsHolder, _ uint64, _ factory.RunTypeComponentsHolder) (processing.IncomingHeaderSubscriber, error) { + args.CreateIncomingHeaderSubscriber = func(_ config.WebSocketConfig, _ dataRetriever.PoolsHolder, _ uint64, _ factory.RunTypeComponentsHolder) (processing.IncomingHeaderSubscriber, error) { return &sovereign.IncomingHeaderSubscriberStub{}, nil } } @@ -269,22 +269,22 @@ func (s *simulator) createTestNode( outputConfigs configs.ArgsConfigsSimulator, args ArgsBaseChainSimulator, shardIDStr string, ) (process.NodeHandler, error) { argsTestOnlyProcessorNode := components.ArgsTestOnlyProcessingNode{ - Configs: outputConfigs.Configs, - ChanStopNodeProcess: s.chanStopNodeProcess, - SyncedBroadcastNetwork: s.syncedBroadcastNetwork, - NumShards: s.numOfShards, - GasScheduleFilename: outputConfigs.GasScheduleFilename, - ShardIDStr: shardIDStr, - APIInterface: args.ApiInterface, - BypassTxSignatureCheck: args.BypassTxSignatureCheck, - InitialRound: args.InitialRound, - InitialNonce: args.InitialNonce, - MinNodesPerShard: args.MinNodesPerShard, - ConsensusGroupSize: args.ConsensusGroupSize, - MinNodesMeta: args.MetaChainMinNodes, - MetaChainConsensusGroupSize: args.MetaChainConsensusGroupSize, - RoundDurationInMillis: args.RoundDurationInMillis, - VmQueryDelayAfterStartInMs: args.VmQueryDelayAfterStartInMs, + Configs: outputConfigs.Configs, + ChanStopNodeProcess: s.chanStopNodeProcess, + SyncedBroadcastNetwork: s.syncedBroadcastNetwork, + NumShards: s.numOfShards, + GasScheduleFilename: outputConfigs.GasScheduleFilename, + ShardIDStr: shardIDStr, + APIInterface: args.ApiInterface, + BypassTxSignatureCheck: args.BypassTxSignatureCheck, + InitialRound: args.InitialRound, + InitialNonce: args.InitialNonce, + MinNodesPerShard: args.MinNodesPerShard, + ConsensusGroupSize: args.ConsensusGroupSize, + MinNodesMeta: args.MetaChainMinNodes, + MetaChainConsensusGroupSize: args.MetaChainConsensusGroupSize, + RoundDurationInMillis: args.RoundDurationInMillis, + VmQueryDelayAfterStartInMs: args.VmQueryDelayAfterStartInMs, CreateRunTypeCoreComponents: args.CreateRunTypeCoreComponents, CreateIncomingHeaderSubscriber: args.CreateIncomingHeaderSubscriber, CreateRunTypeComponents: args.CreateRunTypeComponents, diff --git a/node/chainSimulator/components/testOnlyProcessingNode.go b/node/chainSimulator/components/testOnlyProcessingNode.go index 46b0d831713..e306b732795 100644 --- a/node/chainSimulator/components/testOnlyProcessingNode.go +++ b/node/chainSimulator/components/testOnlyProcessingNode.go @@ -38,7 +38,7 @@ type ArgsTestOnlyProcessingNode struct { Configs config.Configs APIInterface APIConfigurator CreateRunTypeCoreComponents func() (factory.RunTypeCoreComponentsHolder, error) - CreateIncomingHeaderSubscriber func(config *config.NotifierConfig, dataPool dataRetriever.PoolsHolder, mainChainNotarizationStartRound uint64, runTypeComponents factory.RunTypeComponentsHolder) (process.IncomingHeaderSubscriber, error) + CreateIncomingHeaderSubscriber func(config config.WebSocketConfig, dataPool dataRetriever.PoolsHolder, mainChainNotarizationStartRound uint64, runTypeComponents factory.RunTypeComponentsHolder) (process.IncomingHeaderSubscriber, error) CreateRunTypeComponents func(args runType.ArgsRunTypeComponents) (factory.RunTypeComponentsHolder, error) NodeFactory node.NodeFactory @@ -221,7 +221,7 @@ func NewTestOnlyProcessingNode(args ArgsTestOnlyProcessingNode) (*testOnlyProces } instance.IncomingHeaderSubscriber, err = args.CreateIncomingHeaderSubscriber( - &args.Configs.GeneralConfig.SovereignConfig.NotifierConfig, + args.Configs.GeneralConfig.SovereignConfig.NotifierConfig.WebSocketConfig, instance.DataComponentsHolder.Datapool(), args.Configs.GeneralConfig.SovereignConfig.MainChainNotarization.MainChainNotarizationStartRound, instance.RunTypeComponents, diff --git a/node/chainSimulator/components/testOnlyProcessingNode_test.go b/node/chainSimulator/components/testOnlyProcessingNode_test.go index effeffc5cd1..ded220066fb 100644 --- a/node/chainSimulator/components/testOnlyProcessingNode_test.go +++ b/node/chainSimulator/components/testOnlyProcessingNode_test.go @@ -61,7 +61,7 @@ func createMockArgsTestOnlyProcessingNode(t *testing.T) ArgsTestOnlyProcessingNo CreateRunTypeCoreComponents: func() (mainFactory.RunTypeCoreComponentsHolder, error) { return createRunTypeCoreComponents() }, - CreateIncomingHeaderSubscriber: func(config *config.NotifierConfig, dataPool dataRetriever.PoolsHolder, mainChainNotarizationStartRound uint64, runTypeComponents mainFactory.RunTypeComponentsHolder) (process.IncomingHeaderSubscriber, error) { + CreateIncomingHeaderSubscriber: func(config config.WebSocketConfig, dataPool dataRetriever.PoolsHolder, mainChainNotarizationStartRound uint64, runTypeComponents mainFactory.RunTypeComponentsHolder) (process.IncomingHeaderSubscriber, error) { return &sovereign.IncomingHeaderSubscriberStub{}, nil }, CreateRunTypeComponents: func(args runType.ArgsRunTypeComponents) (mainFactory.RunTypeComponentsHolder, error) { diff --git a/process/block/displayBlock.go b/process/block/displayBlock.go index f4baef3532e..4ace104253d 100644 --- a/process/block/displayBlock.go +++ b/process/block/displayBlock.go @@ -213,6 +213,7 @@ func (txc *transactionCounter) displaySovereignChainHeader( ) []*display.LineData { lines = txc.displayExtendedShardHeaderHashesIncluded(lines, header.GetExtendedShardHeaderHashes()) lines = txc.displayOutGoingTxData(lines, header.GetOutGoingMiniBlockHeaderHandler()) + lines = txc.displayLastCrossChainNotarizedHeader(lines, header) return lines } @@ -251,6 +252,46 @@ func (txc *transactionCounter) displayOutGoingTxData( return lines } +func (txc *transactionCounter) displayLastCrossChainNotarizedHeader( + lines []*display.LineData, + sovHeader sovereignChainHeader, +) []*display.LineData { + if len(sovHeader.GetEpochStartHandler().GetLastFinalizedHeaderHandlers()) == 0 { + return lines + } + + lastCrossChainData := sovHeader.GetLastFinalizedCrossChainHeaderHandler() + lines = append(lines, display.NewLineData(false, []string{ + "Last cross chain notarized header", + "Hash", + logger.DisplayByteSlice(lastCrossChainData.GetHeaderHash())}), + ) + lines = append(lines, display.NewLineData(false, []string{ + "", + "ShardID", + getShardName(lastCrossChainData.GetShardID())}), + ) + lines = append(lines, display.NewLineData(false, []string{ + "", + "Epoch", + fmt.Sprintf("%d", lastCrossChainData.GetEpoch())}), + ) + lines = append(lines, display.NewLineData(false, []string{ + "", + "Round", + fmt.Sprintf("%d", lastCrossChainData.GetRound())}), + ) + lines = append(lines, display.NewLineData(false, []string{ + "", + "Nonce", + fmt.Sprintf("%d", lastCrossChainData.GetNonce())}), + ) + + lines[len(lines)-1].HorizontalRuleAfter = true + + return lines +} + func (txc *transactionCounter) displayExtendedShardHeaderHashesIncluded( lines []*display.LineData, extendedShardHeaderHashes [][]byte, diff --git a/process/block/displayBlock_test.go b/process/block/displayBlock_test.go index 35830ce4c45..8af3707472c 100644 --- a/process/block/displayBlock_test.go +++ b/process/block/displayBlock_test.go @@ -7,6 +7,7 @@ import ( "testing" "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/block" "github.com/multiversx/mx-chain-core-go/display" @@ -126,7 +127,7 @@ func TestDisplayBlock_DisplaySovereignChainHeader(t *testing.T) { sovChainHeader, ) - require.Equal(t, []*display.LineData{ + expectedLines := []*display.LineData{ { Values: []string{"ExtendedShardHeaderHashes", "ExtendedShardHeaderHash_1", hex.EncodeToString(extendedShardHeaderHashes[0])}, HorizontalRuleAfter: false, @@ -155,7 +156,45 @@ func TestDisplayBlock_DisplaySovereignChainHeader(t *testing.T) { Values: []string{"", "LeaderSignatureOutGoingOperations", hex.EncodeToString(outGoingMbHeader.GetLeaderSignatureOutGoingOperations())}, HorizontalRuleAfter: true, }, - }, lines) + } + require.Equal(t, expectedLines, lines) + + crossChainData := block.EpochStartCrossChainData{ + ShardID: core.MainChainShardId, + Epoch: 5, + Round: 12, + Nonce: 13, + HeaderHash: []byte{0xa, 0xb}, + } + sovChainHeader.EpochStart.LastFinalizedCrossChainHeader = crossChainData + lastFinalizedCrossChainHeaderLines := []*display.LineData{ + { + Values: []string{"Last cross chain notarized header", "Hash", hex.EncodeToString(crossChainData.HeaderHash)}, + HorizontalRuleAfter: false, + }, + { + Values: []string{"", "ShardID", getShardName(core.MainChainShardId)}, + HorizontalRuleAfter: false, + }, + { + Values: []string{"", "Epoch", "5"}, + HorizontalRuleAfter: false, + }, + { + Values: []string{"", "Round", "12"}, + HorizontalRuleAfter: false, + }, + { + Values: []string{"", "Nonce", "13"}, + HorizontalRuleAfter: true, + }, + } + expectedLines = append(expectedLines, lastFinalizedCrossChainHeaderLines...) + lines = txCounter.displaySovereignChainHeader( + shardLines, + sovChainHeader, + ) + require.Equal(t, expectedLines, lines) } func TestDisplayBlock_DisplayExtendedShardHeaderHashesIncluded(t *testing.T) { diff --git a/process/block/interceptedBlocks/common.go b/process/block/interceptedBlocks/common.go index f3d3f1e393f..cdf32f57ad0 100644 --- a/process/block/interceptedBlocks/common.go +++ b/process/block/interceptedBlocks/common.go @@ -122,13 +122,13 @@ func checkShardData(sd data.ShardDataHandler, coordinator sharding.Coordinator) return nil } -func checkMiniBlocksHeaders(mbHeaders []data.MiniBlockHeaderHandler, coordinator sharding.Coordinator) error { +func checkMiniBlocksHeaders(mbHeaders []data.MiniBlockHeaderHandler, coordinator sharding.Coordinator, acceptedCrossShardID uint32) error { for _, mbHeader := range mbHeaders { isWrongSenderShardId := mbHeader.GetSenderShardID() >= coordinator.NumberOfShards() && - mbHeader.GetSenderShardID() != core.MetachainShardId && + mbHeader.GetSenderShardID() != acceptedCrossShardID && mbHeader.GetSenderShardID() != core.AllShardId isWrongDestinationShardId := mbHeader.GetReceiverShardID() >= coordinator.NumberOfShards() && - mbHeader.GetReceiverShardID() != core.MetachainShardId && + mbHeader.GetReceiverShardID() != acceptedCrossShardID && mbHeader.GetReceiverShardID() != core.AllShardId isWrongShardId := isWrongSenderShardId || isWrongDestinationShardId if isWrongShardId { diff --git a/process/block/interceptedBlocks/common_test.go b/process/block/interceptedBlocks/common_test.go index 02be37e9bde..ea6f2da8463 100644 --- a/process/block/interceptedBlocks/common_test.go +++ b/process/block/interceptedBlocks/common_test.go @@ -4,13 +4,15 @@ import ( "errors" "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/assert" + "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/process/mock" "github.com/multiversx/mx-chain-go/testscommon" "github.com/multiversx/mx-chain-go/testscommon/hashingMocks" - "github.com/stretchr/testify/assert" ) func createDefaultBlockHeaderArgument() *ArgInterceptedBlockHeader { @@ -453,8 +455,8 @@ func TestCheckMiniBlocksHeaders_WithNilOrEmptyShouldReturnNil(t *testing.T) { shardCoordinator := mock.NewOneShardCoordinatorMock() - err1 := checkMiniBlocksHeaders(nil, shardCoordinator) - err2 := checkMiniBlocksHeaders(make([]data.MiniBlockHeaderHandler, 0), shardCoordinator) + err1 := checkMiniBlocksHeaders(nil, shardCoordinator, core.MainChainShardId) + err2 := checkMiniBlocksHeaders(make([]data.MiniBlockHeaderHandler, 0), shardCoordinator, core.MainChainShardId) assert.Nil(t, err1) assert.Nil(t, err2) @@ -473,7 +475,7 @@ func TestCheckMiniBlocksHeaders_WrongMiniblockSenderShardIdShouldErr(t *testing. Type: 0, } - err := checkMiniBlocksHeaders([]data.MiniBlockHeaderHandler{&miniblockHeader}, shardCoordinator) + err := checkMiniBlocksHeaders([]data.MiniBlockHeaderHandler{&miniblockHeader}, shardCoordinator, core.MainChainShardId) assert.Equal(t, process.ErrInvalidShardId, err) } @@ -491,7 +493,7 @@ func TestCheckMiniBlocksHeaders_WrongMiniblockReceiverShardIdShouldErr(t *testin Type: 0, } - err := checkMiniBlocksHeaders([]data.MiniBlockHeaderHandler{&miniblockHeader}, shardCoordinator) + err := checkMiniBlocksHeaders([]data.MiniBlockHeaderHandler{&miniblockHeader}, shardCoordinator, core.MainChainShardId) assert.Equal(t, process.ErrInvalidShardId, err) } @@ -509,7 +511,7 @@ func TestCheckMiniBlocksHeaders_ReservedPopulatedShouldErr(t *testing.T) { Reserved: []byte("rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr"), } - err := checkMiniBlocksHeaders([]data.MiniBlockHeaderHandler{&miniblockHeader}, shardCoordinator) + err := checkMiniBlocksHeaders([]data.MiniBlockHeaderHandler{&miniblockHeader}, shardCoordinator, core.MainChainShardId) assert.Equal(t, process.ErrReservedFieldInvalid, err) } @@ -527,7 +529,7 @@ func TestCheckMiniBlocksHeaders_ReservedPopulatedCorrectly(t *testing.T) { Reserved: []byte("r"), } - err := checkMiniBlocksHeaders([]data.MiniBlockHeaderHandler{&miniblockHeader}, shardCoordinator) + err := checkMiniBlocksHeaders([]data.MiniBlockHeaderHandler{&miniblockHeader}, shardCoordinator, core.MainChainShardId) assert.Nil(t, err) } @@ -544,7 +546,7 @@ func TestCheckMiniBlocksHeaders_OkValsShouldWork(t *testing.T) { Type: 0, } - err := checkMiniBlocksHeaders([]data.MiniBlockHeaderHandler{&miniblockHeader}, shardCoordinator) + err := checkMiniBlocksHeaders([]data.MiniBlockHeaderHandler{&miniblockHeader}, shardCoordinator, core.MainChainShardId) assert.Nil(t, err) } diff --git a/process/block/interceptedBlocks/export_test.go b/process/block/interceptedBlocks/export_test.go index fca0028c459..3bef2dd532c 100644 --- a/process/block/interceptedBlocks/export_test.go +++ b/process/block/interceptedBlocks/export_test.go @@ -1,6 +1,16 @@ package interceptedBlocks +import ( + "github.com/multiversx/mx-chain-core-go/data" + "github.com/multiversx/mx-chain-go/sharding" +) + // IsMetaHeaderOutOfRange - func (imh *InterceptedMetaHeader) IsMetaHeaderOutOfRange() bool { return imh.isMetaHeaderEpochOutOfRange() } + +// CheckMiniBlocksHeaders - +func (isbh *interceptedSovereignBlockHeader) CheckMiniBlocksHeaders(mbHeaders []data.MiniBlockHeaderHandler, coordinator sharding.Coordinator) error { + return isbh.checkMiniBlocksHeaders(mbHeaders, coordinator) +} diff --git a/process/block/interceptedBlocks/interceptedBlockHeader.go b/process/block/interceptedBlocks/interceptedBlockHeader.go index 81d78bef5c0..8c6be056984 100644 --- a/process/block/interceptedBlocks/interceptedBlockHeader.go +++ b/process/block/interceptedBlocks/interceptedBlockHeader.go @@ -26,6 +26,8 @@ type InterceptedHeader struct { isForCurrentShard bool validityAttester process.ValidityAttester epochStartTrigger process.EpochStartTriggerHandler + + mbHeadersChecker mbHeadersChecker } // NewInterceptedHeader creates a new instance of InterceptedHeader struct @@ -50,6 +52,7 @@ func NewInterceptedHeader(arg *ArgInterceptedBlockHeader) (*InterceptedHeader, e epochStartTrigger: arg.EpochStartTrigger, } inHdr.processFields(arg.HdrBuff) + inHdr.mbHeadersChecker = inHdr return inHdr, nil } @@ -138,7 +141,7 @@ func (inHdr *InterceptedHeader) integrity() error { return err } - err = checkMiniBlocksHeaders(inHdr.hdr.GetMiniBlockHeaderHandlers(), inHdr.shardCoordinator) + err = inHdr.mbHeadersChecker.checkMiniBlocksHeaders(inHdr.hdr.GetMiniBlockHeaderHandlers(), inHdr.shardCoordinator) if err != nil { return err } @@ -146,6 +149,10 @@ func (inHdr *InterceptedHeader) integrity() error { return nil } +func (inHdr *InterceptedHeader) checkMiniBlocksHeaders(mbHeaders []data.MiniBlockHeaderHandler, coordinator sharding.Coordinator) error { + return checkMiniBlocksHeaders(mbHeaders, coordinator, core.MetachainShardId) +} + // Hash gets the hash of this header func (inHdr *InterceptedHeader) Hash() []byte { return inHdr.hash diff --git a/process/block/interceptedBlocks/interceptedMiniblock.go b/process/block/interceptedBlocks/interceptedMiniblock.go index a6569697b0a..0fce59ba5d4 100644 --- a/process/block/interceptedBlocks/interceptedMiniblock.go +++ b/process/block/interceptedBlocks/interceptedMiniblock.go @@ -82,7 +82,7 @@ func (inMb *InterceptedMiniblock) Miniblock() *block.MiniBlock { // CheckValidity checks if the received tx block body is valid (not nil fields) func (inMb *InterceptedMiniblock) CheckValidity() error { - return inMb.integrity() + return inMb.integrity(core.MetachainShardId) } // IsForCurrentShard returns true if at least one contained miniblock is for current shard @@ -91,17 +91,17 @@ func (inMb *InterceptedMiniblock) IsForCurrentShard() bool { } // integrity checks the integrity of the tx block body -func (inMb *InterceptedMiniblock) integrity() error { +func (inMb *InterceptedMiniblock) integrity(acceptedCrossShardId uint32) error { miniblock := inMb.miniblock receiverNotCurrentShard := miniblock.ReceiverShardID >= inMb.shardCoordinator.NumberOfShards() && - (miniblock.ReceiverShardID != core.MetachainShardId && miniblock.ReceiverShardID != core.AllShardId) + (miniblock.ReceiverShardID != acceptedCrossShardId && miniblock.ReceiverShardID != core.AllShardId) if receiverNotCurrentShard { return process.ErrInvalidShardId } senderNotCurrentShard := miniblock.SenderShardID >= inMb.shardCoordinator.NumberOfShards() && - miniblock.SenderShardID != core.MetachainShardId + miniblock.SenderShardID != acceptedCrossShardId if senderNotCurrentShard { return process.ErrInvalidShardId } diff --git a/process/block/interceptedBlocks/interceptedSovereignBlockHeader.go b/process/block/interceptedBlocks/interceptedSovereignBlockHeader.go new file mode 100644 index 00000000000..e67aeb6ddaa --- /dev/null +++ b/process/block/interceptedBlocks/interceptedSovereignBlockHeader.go @@ -0,0 +1,35 @@ +package interceptedBlocks + +import ( + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/data" + "github.com/multiversx/mx-chain-go/sharding" +) + +type interceptedSovereignBlockHeader struct { + *InterceptedHeader +} + +// NewSovereignInterceptedBlockHeader creates a new intercepted sovereign block header +func NewSovereignInterceptedBlockHeader(arg *ArgInterceptedBlockHeader) (*interceptedSovereignBlockHeader, error) { + interceptedHdr, err := NewInterceptedHeader(arg) + if err != nil { + return nil, err + } + + sovInterceptedBlock := &interceptedSovereignBlockHeader{ + interceptedHdr, + } + + sovInterceptedBlock.mbHeadersChecker = sovInterceptedBlock + return sovInterceptedBlock, nil +} + +func (isbh *interceptedSovereignBlockHeader) checkMiniBlocksHeaders(mbHeaders []data.MiniBlockHeaderHandler, coordinator sharding.Coordinator) error { + return checkMiniBlocksHeaders(mbHeaders, coordinator, core.MainChainShardId) +} + +// IsInterfaceNil returns true if there is no value under the interface +func (isbh *interceptedSovereignBlockHeader) IsInterfaceNil() bool { + return isbh == nil +} diff --git a/process/block/interceptedBlocks/interceptedSovereignBlockHeader_test.go b/process/block/interceptedBlocks/interceptedSovereignBlockHeader_test.go new file mode 100644 index 00000000000..52e120d75cd --- /dev/null +++ b/process/block/interceptedBlocks/interceptedSovereignBlockHeader_test.go @@ -0,0 +1,66 @@ +package interceptedBlocks_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/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/block/interceptedBlocks" + "github.com/stretchr/testify/require" +) + +func TestNewSovereignInterceptedBlockHeader(t *testing.T) { + t.Parallel() + + t.Run("nil input in args", func(t *testing.T) { + args := createDefaultShardArgument() + args.Marshalizer = nil + sovInterceptedBlock, err := interceptedBlocks.NewSovereignInterceptedBlockHeader(args) + require.Nil(t, sovInterceptedBlock) + require.Equal(t, process.ErrNilMarshalizer, err) + }) + t.Run("should work", func(t *testing.T) { + args := createDefaultShardArgument() + sovInterceptedBlock, err := interceptedBlocks.NewSovereignInterceptedBlockHeader(args) + require.Nil(t, err) + require.False(t, sovInterceptedBlock.IsInterfaceNil()) + }) +} + +func TestInterceptedSovereignMiniBlock_checkMiniBlocksHeaders(t *testing.T) { + t.Parallel() + + args := createDefaultShardArgument() + sovInterceptedBlock, _ := interceptedBlocks.NewSovereignInterceptedBlockHeader(args) + + miniBlockHeaders := []data.MiniBlockHeaderHandler{ + &block.MiniBlockHeader{ + Hash: make([]byte, 0), + SenderShardID: core.SovereignChainShardId, + ReceiverShardID: core.SovereignChainShardId, + TxCount: 0, + Type: 0, + Reserved: []byte("rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr"), + }, + } + + err := sovInterceptedBlock.CheckMiniBlocksHeaders(miniBlockHeaders, args.ShardCoordinator) + require.Nil(t, err) + + err = miniBlockHeaders[0].SetReceiverShardID(core.MainChainShardId) + require.Nil(t, err) + err = sovInterceptedBlock.CheckMiniBlocksHeaders(miniBlockHeaders, args.ShardCoordinator) + require.Nil(t, err) + + err = miniBlockHeaders[0].SetSenderShardID(core.MainChainShardId) + require.Nil(t, err) + err = sovInterceptedBlock.CheckMiniBlocksHeaders(miniBlockHeaders, args.ShardCoordinator) + require.Nil(t, err) + + err = miniBlockHeaders[0].SetReceiverShardID(core.MetachainShardId) + require.Nil(t, err) + err = sovInterceptedBlock.CheckMiniBlocksHeaders(miniBlockHeaders, args.ShardCoordinator) + require.Equal(t, process.ErrInvalidShardId, err) +} diff --git a/process/block/interceptedBlocks/interceptedSovereignMiniBlock.go b/process/block/interceptedBlocks/interceptedSovereignMiniBlock.go new file mode 100644 index 00000000000..3ad514dac68 --- /dev/null +++ b/process/block/interceptedBlocks/interceptedSovereignMiniBlock.go @@ -0,0 +1,29 @@ +package interceptedBlocks + +import "github.com/multiversx/mx-chain-core-go/core" + +type interceptedSovereignMiniBlock struct { + *InterceptedMiniblock +} + +// NewInterceptedSovereignMiniBlock creates a new instance of intercepted sovereign mini block +func NewInterceptedSovereignMiniBlock(arg *ArgInterceptedMiniblock) (*interceptedSovereignMiniBlock, error) { + interceptedMbHandler, err := NewInterceptedMiniblock(arg) + if err != nil { + return nil, err + } + + return &interceptedSovereignMiniBlock{ + interceptedMbHandler, + }, nil +} + +// CheckValidity checks if the received tx block body is valid (not nil fields) +func (ismb *interceptedSovereignMiniBlock) CheckValidity() error { + return ismb.integrity(core.MainChainShardId) +} + +// IsInterfaceNil returns true if there is no value under the interface +func (ismb *interceptedSovereignMiniBlock) IsInterfaceNil() bool { + return ismb == nil +} diff --git a/process/block/interceptedBlocks/interceptedSovereignMiniBlock_test.go b/process/block/interceptedBlocks/interceptedSovereignMiniBlock_test.go new file mode 100644 index 00000000000..d27b3012e7c --- /dev/null +++ b/process/block/interceptedBlocks/interceptedSovereignMiniBlock_test.go @@ -0,0 +1,56 @@ +package interceptedBlocks_test + +import ( + "testing" + + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/block/interceptedBlocks" + "github.com/stretchr/testify/require" +) + +func createSovMBInterceptorWithMBInShard(shardID uint32) process.InterceptedData { + mb := createMockMiniblock() + mb.SenderShardID = shardID + buff, _ := testMarshalizer.Marshal(mb) + + args := createDefaultMiniblockArgument() + args.MiniblockBuff = buff + + sovMBInterceptor, _ := interceptedBlocks.NewInterceptedSovereignMiniBlock(args) + return sovMBInterceptor +} + +func TestNewInterceptedSovereignMiniBlock(t *testing.T) { + t.Parallel() + + t.Run("nil input in args", func(t *testing.T) { + args := createDefaultMiniblockArgument() + args.Marshalizer = nil + sovMBInterceptor, err := interceptedBlocks.NewInterceptedSovereignMiniBlock(args) + require.Nil(t, sovMBInterceptor) + require.Equal(t, process.ErrNilMarshalizer, err) + }) + t.Run("should work", func(t *testing.T) { + args := createDefaultMiniblockArgument() + sovMBInterceptor, err := interceptedBlocks.NewInterceptedSovereignMiniBlock(args) + require.Nil(t, err) + require.False(t, sovMBInterceptor.IsInterfaceNil()) + }) +} + +func TestInterceptedSovereignMiniBlock_CheckValidity(t *testing.T) { + t.Parallel() + + sovMBInterceptor := createSovMBInterceptorWithMBInShard(core.MetachainShardId) + err := sovMBInterceptor.CheckValidity() + require.Equal(t, process.ErrInvalidShardId, err) + + sovMBInterceptor = createSovMBInterceptorWithMBInShard(core.SovereignChainShardId) + err = sovMBInterceptor.CheckValidity() + require.Nil(t, err) + + sovMBInterceptor = createSovMBInterceptorWithMBInShard(core.MainChainShardId) + err = sovMBInterceptor.CheckValidity() + require.Nil(t, err) +} diff --git a/process/block/interceptedBlocks/interface.go b/process/block/interceptedBlocks/interface.go new file mode 100644 index 00000000000..5d8896f7f00 --- /dev/null +++ b/process/block/interceptedBlocks/interface.go @@ -0,0 +1,10 @@ +package interceptedBlocks + +import ( + "github.com/multiversx/mx-chain-core-go/data" + "github.com/multiversx/mx-chain-go/sharding" +) + +type mbHeadersChecker interface { + checkMiniBlocksHeaders(mbHeaders []data.MiniBlockHeaderHandler, coordinator sharding.Coordinator) error +} diff --git a/process/block/interface.go b/process/block/interface.go index 2b16b51919d..a653daa4393 100644 --- a/process/block/interface.go +++ b/process/block/interface.go @@ -40,6 +40,8 @@ type validatorStatsRootHashGetter interface { type sovereignChainHeader interface { GetExtendedShardHeaderHashes() [][]byte GetOutGoingMiniBlockHeaderHandler() data.OutGoingMiniBlockHeaderHandler + GetEpochStartHandler() data.EpochStartHandler + GetLastFinalizedCrossChainHeaderHandler() data.EpochStartChainDataHandler } type crossNotarizer interface { diff --git a/cmd/sovereignnode/incomingHeader/errors.go b/process/block/sovereign/incomingHeader/errors.go similarity index 90% rename from cmd/sovereignnode/incomingHeader/errors.go rename to process/block/sovereign/incomingHeader/errors.go index 6695b5d6dfd..0aa6957c585 100644 --- a/cmd/sovereignnode/incomingHeader/errors.go +++ b/process/block/sovereign/incomingHeader/errors.go @@ -12,8 +12,6 @@ var errInvalidEventType = errors.New("incoming event is not of type transaction var errInvalidNumTopicsIncomingEvent = errors.New("received invalid number of topics in incoming event") -var errEmptyLogData = errors.New("empty logs data in incoming event") - var errInvalidIncomingEventIdentifier = errors.New("received invalid/unknown incoming event identifier") var errInvalidIncomingTopicIdentifier = errors.New("received invalid/unknown incoming topic identifier") diff --git a/cmd/sovereignnode/incomingHeader/extendedHeaderProcessor.go b/process/block/sovereign/incomingHeader/extendedHeaderProcessor.go similarity index 66% rename from cmd/sovereignnode/incomingHeader/extendedHeaderProcessor.go rename to process/block/sovereign/incomingHeader/extendedHeaderProcessor.go index 0eecf47b258..7733478339a 100644 --- a/cmd/sovereignnode/incomingHeader/extendedHeaderProcessor.go +++ b/process/block/sovereign/incomingHeader/extendedHeaderProcessor.go @@ -8,10 +8,13 @@ import ( "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" + + "github.com/multiversx/mx-chain-go/process" ) type extendedHeaderProcessor struct { headersPool HeadersPool + txPool TransactionPool marshaller marshal.Marshalizer hasher hashing.Hasher } @@ -68,12 +71,36 @@ func createIncomingMb(scrs []*scrInfo) []*block.MiniBlock { } } -func (ehp *extendedHeaderProcessor) addExtendedHeaderToPool(extendedHeader data.ShardHeaderExtendedHandler) error { +func (ehp *extendedHeaderProcessor) addPreGenesisExtendedHeaderToPool(incomingHeader sovereign.IncomingHeaderHandler) error { + headerV2, castOk := incomingHeader.GetHeaderHandler().(*block.HeaderV2) + if !castOk { + return errInvalidHeaderType + } + + extendedHeader := &block.ShardHeaderExtended{ + Header: headerV2, + IncomingMiniBlocks: []*block.MiniBlock{}, + IncomingEvents: []*transaction.Event{}, + } + + return ehp.addExtendedHeaderAndSCRsToPool(extendedHeader, make([]*scrInfo, 0)) +} + +func (ehp *extendedHeaderProcessor) addExtendedHeaderAndSCRsToPool(extendedHeader data.ShardHeaderExtendedHandler, scrs []*scrInfo) error { extendedHeaderHash, err := core.CalculateHash(ehp.marshaller, ehp.hasher, extendedHeader) if err != nil { return err } + ehp.addSCRsToPool(scrs) ehp.headersPool.AddHeaderInShard(extendedHeaderHash, extendedHeader, core.MainChainShardId) return nil } + +func (ehp *extendedHeaderProcessor) addSCRsToPool(scrs []*scrInfo) { + cacheID := process.ShardCacherIdentifier(core.MainChainShardId, core.SovereignChainShardId) + + for _, scrData := range scrs { + ehp.txPool.AddData(scrData.hash, scrData.scr, scrData.scr.Size(), cacheID) + } +} diff --git a/cmd/sovereignnode/incomingHeader/incomingEventsProcessor.go b/process/block/sovereign/incomingHeader/incomingEventsProcessor.go similarity index 98% rename from cmd/sovereignnode/incomingHeader/incomingEventsProcessor.go rename to process/block/sovereign/incomingHeader/incomingEventsProcessor.go index b7cfe2f296a..ce7e1f4c3dd 100644 --- a/cmd/sovereignnode/incomingHeader/incomingEventsProcessor.go +++ b/process/block/sovereign/incomingHeader/incomingEventsProcessor.go @@ -6,7 +6,6 @@ import ( "math/big" "github.com/multiversx/mx-chain-go/common" - "github.com/multiversx/mx-chain-go/sovereignnode/dataCodec" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data" @@ -58,7 +57,7 @@ type eventsResult struct { type incomingEventsProcessor struct { marshaller marshal.Marshalizer hasher hashing.Hasher - dataCodec dataCodec.SovereignDataCodec + dataCodec SovereignDataCodec topicsChecker TopicsChecker } diff --git a/cmd/sovereignnode/incomingHeader/incomingHeaderHandlerFactory.go b/process/block/sovereign/incomingHeader/incomingHeaderHandlerFactory.go similarity index 75% rename from cmd/sovereignnode/incomingHeader/incomingHeaderHandlerFactory.go rename to process/block/sovereign/incomingHeader/incomingHeaderHandlerFactory.go index 494c09f2915..8b30e5b7fa8 100644 --- a/cmd/sovereignnode/incomingHeader/incomingHeaderHandlerFactory.go +++ b/process/block/sovereign/incomingHeader/incomingHeaderHandlerFactory.go @@ -1,27 +1,30 @@ package incomingHeader import ( + "github.com/multiversx/mx-chain-core-go/core/check" + hasherFactory "github.com/multiversx/mx-chain-core-go/hashing/factory" + marshallerFactory "github.com/multiversx/mx-chain-core-go/marshal/factory" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/dataRetriever" - mainFactory "github.com/multiversx/mx-chain-go/factory" + errorsMx "github.com/multiversx/mx-chain-go/errors" "github.com/multiversx/mx-chain-go/process" - - hasherFactory "github.com/multiversx/mx-chain-core-go/hashing/factory" - marshallerFactory "github.com/multiversx/mx-chain-core-go/marshal/factory" ) // CreateIncomingHeaderProcessor creates the incoming header processor func CreateIncomingHeaderProcessor( - config *config.NotifierConfig, + config config.WebSocketConfig, dataPool dataRetriever.PoolsHolder, mainChainNotarizationStartRound uint64, - runTypeComponents mainFactory.RunTypeComponentsHolder, + runTypeComponents RunTypeComponentsHolder, ) (process.IncomingHeaderSubscriber, error) { - marshaller, err := marshallerFactory.NewMarshalizer(config.WebSocketConfig.MarshallerType) + if check.IfNil(runTypeComponents) { + return nil, errorsMx.ErrNilRunTypeComponents + } + marshaller, err := marshallerFactory.NewMarshalizer(config.MarshallerType) if err != nil { return nil, err } - hasher, err := hasherFactory.NewHasher(config.WebSocketConfig.HasherType) + hasher, err := hasherFactory.NewHasher(config.HasherType) if err != nil { return nil, err } diff --git a/process/block/sovereign/incomingHeader/incomingHeaderHandlerFactory_test.go b/process/block/sovereign/incomingHeader/incomingHeaderHandlerFactory_test.go new file mode 100644 index 00000000000..982dd9fc51e --- /dev/null +++ b/process/block/sovereign/incomingHeader/incomingHeaderHandlerFactory_test.go @@ -0,0 +1,81 @@ +package incomingHeader + +import ( + "testing" + + "github.com/multiversx/mx-chain-go/config" + retriever "github.com/multiversx/mx-chain-go/dataRetriever" + errorsMx "github.com/multiversx/mx-chain-go/errors" + "github.com/multiversx/mx-chain-go/process/mock" + "github.com/multiversx/mx-chain-go/testscommon/dataRetriever" + "github.com/multiversx/mx-chain-go/testscommon/pool" + "github.com/stretchr/testify/require" +) + +func createWSCfg() config.WebSocketConfig { + return config.WebSocketConfig{ + MarshallerType: "json", + HasherType: "keccak", + } +} + +func TestCreateIncomingHeaderProcessor(t *testing.T) { + t.Parallel() + + runTypeComps := mock.NewRunTypeComponentsStub() + headersPool := &dataRetriever.PoolsHolderStub{ + HeadersCalled: func() retriever.HeadersPool { + return &pool.HeadersPoolStub{} + }, + } + + t.Run("nil run type comps, should not work", func(t *testing.T) { + headerProc, err := CreateIncomingHeaderProcessor( + createWSCfg(), + headersPool, + 11, + nil, + ) + require.Equal(t, errorsMx.ErrNilRunTypeComponents, err) + require.Nil(t, headerProc) + }) + + t.Run("invalid marshaller, should not work", func(t *testing.T) { + cfg := createWSCfg() + cfg.MarshallerType = "" + + headerProc, err := CreateIncomingHeaderProcessor( + cfg, + headersPool, + 11, + runTypeComps, + ) + require.NotNil(t, err) + require.Nil(t, headerProc) + }) + + t.Run("invalid hasher, should not work", func(t *testing.T) { + cfg := createWSCfg() + cfg.HasherType = "" + + headerProc, err := CreateIncomingHeaderProcessor( + cfg, + headersPool, + 11, + runTypeComps, + ) + require.NotNil(t, err) + require.Nil(t, headerProc) + }) + + t.Run("should work", func(t *testing.T) { + headerProc, err := CreateIncomingHeaderProcessor( + createWSCfg(), + headersPool, + 11, + runTypeComps, + ) + require.Nil(t, err) + require.False(t, headerProc.IsInterfaceNil()) + }) +} diff --git a/cmd/sovereignnode/incomingHeader/incomingHeaderProcessor.go b/process/block/sovereign/incomingHeader/incomingHeaderProcessor.go similarity index 85% rename from cmd/sovereignnode/incomingHeader/incomingHeaderProcessor.go rename to process/block/sovereign/incomingHeader/incomingHeaderProcessor.go index 7ed14822ed6..597c6b68abd 100644 --- a/cmd/sovereignnode/incomingHeader/incomingHeaderProcessor.go +++ b/process/block/sovereign/incomingHeader/incomingHeaderProcessor.go @@ -3,11 +3,6 @@ package incomingHeader import ( "encoding/hex" - sovereignBlock "github.com/multiversx/mx-chain-go/dataRetriever/dataPool/sovereign" - "github.com/multiversx/mx-chain-go/errors" - "github.com/multiversx/mx-chain-go/process" - "github.com/multiversx/mx-chain-go/sovereignnode/dataCodec" - "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" @@ -15,6 +10,9 @@ import ( "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" logger "github.com/multiversx/mx-chain-logger-go" + + sovereignBlock "github.com/multiversx/mx-chain-go/dataRetriever/dataPool/sovereign" + "github.com/multiversx/mx-chain-go/errors" ) var log = logger.GetOrCreate("headerSubscriber") @@ -27,7 +25,7 @@ type ArgsIncomingHeaderProcessor struct { Marshaller marshal.Marshalizer Hasher hashing.Hasher MainChainNotarizationStartRound uint64 - DataCodec dataCodec.SovereignDataCodec + DataCodec SovereignDataCodec TopicsChecker TopicsChecker } @@ -35,7 +33,6 @@ type incomingHeaderProcessor struct { eventsProc *incomingEventsProcessor extendedHeaderProc *extendedHeaderProcessor - txPool TransactionPool outGoingPool sovereignBlock.OutGoingOperationsPool mainChainNotarizationStartRound uint64 } @@ -75,6 +72,7 @@ func NewIncomingHeaderProcessor(args ArgsIncomingHeaderProcessor) (*incomingHead extendedHearProc := &extendedHeaderProcessor{ headersPool: args.HeadersPool, + txPool: args.TxPool, marshaller: args.Marshaller, hasher: args.Hasher, } @@ -84,7 +82,6 @@ func NewIncomingHeaderProcessor(args ArgsIncomingHeaderProcessor) (*incomingHead return &incomingHeaderProcessor{ eventsProc: eventsProc, extendedHeaderProc: extendedHearProc, - txPool: args.TxPool, outGoingPool: args.OutGoingOperationsPool, mainChainNotarizationStartRound: args.MainChainNotarizationStartRound, }, nil @@ -96,9 +93,21 @@ func (ihp *incomingHeaderProcessor) AddHeader(headerHash []byte, header sovereig return data.ErrNilHeader } - log.Info("received incoming header", "hash", hex.EncodeToString(headerHash), "nonce", header.GetHeaderHandler().GetNonce()) - + log.Info("received incoming header", + "hash", hex.EncodeToString(headerHash), + "nonce", header.GetHeaderHandler().GetNonce(), + "round", header.GetHeaderHandler().GetRound(), + ) round := header.GetHeaderHandler().GetRound() + + // pre-genesis header, needed to track/link genesis header on top of this one. Every node with an enabled notifier + // will validate that the next genesis header with round == mainChainNotarizationStartRound is on top of pre-genesis header. + // just save internal header to tracker, no need to process anything from it + if round == ihp.mainChainNotarizationStartRound-1 { + log.Debug("received pre-genesis header", "round", header.GetHeaderHandler().GetRound()) + return ihp.extendedHeaderProc.addPreGenesisExtendedHeaderToPool(header) + } + if round < ihp.mainChainNotarizationStartRound { log.Debug("do not notarize incoming header, round lower than main chain notarization start round", "round", round, @@ -116,24 +125,15 @@ func (ihp *incomingHeaderProcessor) AddHeader(headerHash []byte, header sovereig return err } - err = ihp.extendedHeaderProc.addExtendedHeaderToPool(extendedHeader) + err = ihp.extendedHeaderProc.addExtendedHeaderAndSCRsToPool(extendedHeader, res.scrs) if err != nil { return err } ihp.addConfirmedBridgeOpsToPool(res.confirmedBridgeOps) - ihp.addSCRsToPool(res.scrs) return nil } -func (ihp *incomingHeaderProcessor) addSCRsToPool(scrs []*scrInfo) { - cacheID := process.ShardCacherIdentifier(core.MainChainShardId, core.SovereignChainShardId) - - for _, scrData := range scrs { - ihp.txPool.AddData(scrData.hash, scrData.scr, scrData.scr.Size(), cacheID) - } -} - func (ihp *incomingHeaderProcessor) addConfirmedBridgeOpsToPool(ops []*confirmedBridgeOp) { for _, op := range ops { // This is not a critical error. This might just happen when a leader tries to re-send unconfirmed confirmation diff --git a/cmd/sovereignnode/incomingHeader/incomingHeaderProcessor_test.go b/process/block/sovereign/incomingHeader/incomingHeaderProcessor_test.go similarity index 96% rename from cmd/sovereignnode/incomingHeader/incomingHeaderProcessor_test.go rename to process/block/sovereign/incomingHeader/incomingHeaderProcessor_test.go index 64f7e99099b..ce7fb72aedb 100644 --- a/cmd/sovereignnode/incomingHeader/incomingHeaderProcessor_test.go +++ b/process/block/sovereign/incomingHeader/incomingHeaderProcessor_test.go @@ -12,7 +12,6 @@ import ( errorsMx "github.com/multiversx/mx-chain-go/errors" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/process/mock" - sovereignTests "github.com/multiversx/mx-chain-go/sovereignnode/testscommon" "github.com/multiversx/mx-chain-go/testscommon" "github.com/multiversx/mx-chain-go/testscommon/hashingMocks" "github.com/multiversx/mx-chain-go/testscommon/marshallerMock" @@ -161,7 +160,7 @@ func TestIncomingHeaderHandler_AddHeaderErrorCases(t *testing.T) { err := handler.AddHeader([]byte("hash"), nil) require.Equal(t, data.ErrNilHeader, err) - incomingHeader := &sovereignTests.IncomingHeaderStub{ + incomingHeader := &sovTests.IncomingHeaderStub{ GetHeaderHandlerCalled: func() data.HeaderHandler { return nil }, @@ -177,25 +176,37 @@ func TestIncomingHeaderHandler_AddHeaderErrorCases(t *testing.T) { args := createArgs() args.MainChainNotarizationStartRound = startRound - wasHeaderAdded := false + wasHeaderAddedCt := 0 args.HeadersPool = &mock.HeadersCacherStub{ AddHeaderInShardCalled: func(headerHash []byte, header data.HeaderHandler, shardID uint32) { - wasHeaderAdded = true - require.Equal(t, header.GetRound(), startRound) + wasHeaderAddedCt++ + switch wasHeaderAddedCt { + case 1: + // pre-genesis header, just track internal header + require.Empty(t, header.(data.ShardHeaderExtendedHandler).GetIncomingEventHandlers()) + require.Equal(t, header.GetRound(), startRound-1) + case 2: + require.NotEmpty(t, header.(data.ShardHeaderExtendedHandler).GetIncomingEventHandlers()) + require.Equal(t, header.GetRound(), startRound) + } }, } handler, _ := NewIncomingHeaderProcessor(args) - headers := createIncomingHeadersWithIncrementalRound(startRound) + headers := createIncomingHeadersWithIncrementalRound(startRound + 1) - for i := 0; i < len(headers)-1; i++ { + for i := 0; i <= int(startRound-2); i++ { err := handler.AddHeader([]byte("hash"), headers[i]) require.Nil(t, err) - require.False(t, wasHeaderAdded) + require.Zero(t, wasHeaderAddedCt) } - err := handler.AddHeader([]byte("hash"), headers[startRound]) + err := handler.AddHeader([]byte("hash"), headers[startRound-1]) + require.Nil(t, err) + require.Equal(t, 1, wasHeaderAddedCt) + + err = handler.AddHeader([]byte("hash"), headers[startRound]) require.Nil(t, err) - require.True(t, wasHeaderAdded) + require.Equal(t, 2, wasHeaderAddedCt) }) t.Run("invalid header type, should return error", func(t *testing.T) { @@ -204,7 +215,7 @@ func TestIncomingHeaderHandler_AddHeaderErrorCases(t *testing.T) { args := createArgs() handler, _ := NewIncomingHeaderProcessor(args) - incomingHeader := &sovereignTests.IncomingHeaderStub{ + incomingHeader := &sovTests.IncomingHeaderStub{ GetHeaderHandlerCalled: func() data.HeaderHandler { return &block.MetaBlock{} }, diff --git a/process/block/sovereign/incomingHeader/interface.go b/process/block/sovereign/incomingHeader/interface.go new file mode 100644 index 00000000000..54857567f09 --- /dev/null +++ b/process/block/sovereign/incomingHeader/interface.go @@ -0,0 +1,44 @@ +package incomingHeader + +import ( + "github.com/multiversx/mx-chain-core-go/data" + "github.com/multiversx/mx-chain-core-go/data/sovereign" + sovereignBlock "github.com/multiversx/mx-chain-go/dataRetriever/dataPool/sovereign" + sovBlock "github.com/multiversx/mx-chain-go/process/block/sovereign" +) + +// HeadersPool should be able to add new headers in pool +type HeadersPool interface { + AddHeaderInShard(headerHash []byte, header data.HeaderHandler, shardID uint32) + IsInterfaceNil() bool +} + +// TransactionPool should be able to add a new transaction in the pool +type TransactionPool interface { + AddData(key []byte, data interface{}, sizeInBytes int, cacheId string) + IsInterfaceNil() bool +} + +// TopicsChecker should be able to check the topics validity +type TopicsChecker interface { + CheckValidity(topics [][]byte) error + IsInterfaceNil() bool +} + +// SovereignDataCodec is the interface for serializing/deserializing data +type SovereignDataCodec interface { + SerializeEventData(eventData sovereign.EventData) ([]byte, error) + DeserializeEventData(data []byte) (*sovereign.EventData, error) + SerializeTokenData(tokenData sovereign.EsdtTokenData) ([]byte, error) + DeserializeTokenData(data []byte) (*sovereign.EsdtTokenData, error) + SerializeOperation(operation sovereign.Operation) ([]byte, error) + IsInterfaceNil() bool +} + +// RunTypeComponentsHolder defines run type components needed to create an incoming header processor +type RunTypeComponentsHolder interface { + OutGoingOperationsPoolHandler() sovereignBlock.OutGoingOperationsPool + DataCodecHandler() sovBlock.DataCodecHandler + TopicsCheckerHandler() sovBlock.TopicsCheckerHandler + IsInterfaceNil() bool +} diff --git a/cmd/sovereignnode/incomingHeader/topicsChecker.go b/process/block/sovereign/incomingHeader/topicsChecker.go similarity index 100% rename from cmd/sovereignnode/incomingHeader/topicsChecker.go rename to process/block/sovereign/incomingHeader/topicsChecker.go diff --git a/cmd/sovereignnode/incomingHeader/topicsChecker_test.go b/process/block/sovereign/incomingHeader/topicsChecker_test.go similarity index 100% rename from cmd/sovereignnode/incomingHeader/topicsChecker_test.go rename to process/block/sovereign/incomingHeader/topicsChecker_test.go diff --git a/process/block/sovereignBlockProcessorFactory.go b/process/block/sovereignBlockProcessorFactory.go index d9ece2c36c7..8e5485936e6 100644 --- a/process/block/sovereignBlockProcessorFactory.go +++ b/process/block/sovereignBlockProcessorFactory.go @@ -63,17 +63,18 @@ func (s *sovereignBlockProcessorFactory) CreateBlockProcessor(argumentsBaseProce } args := ArgsSovereignChainBlockProcessor{ - ShardProcessor: shardProc, - ValidatorStatisticsProcessor: argumentsBaseProcessor.ValidatorStatisticsProcessor, - OutgoingOperationsFormatter: outgoingOpFormatter, - OutGoingOperationsPool: argumentsBaseProcessor.RunTypeComponents.OutGoingOperationsPoolHandler(), - OperationsHasher: operationsHasher, - ValidatorInfoCreator: argsMetaProcessor.EpochValidatorInfoCreator, - EpochRewardsCreator: argsMetaProcessor.EpochRewardsCreator, - EpochStartDataCreator: argsMetaProcessor.EpochStartDataCreator, - EpochSystemSCProcessor: argsMetaProcessor.EpochSystemSCProcessor, - SCToProtocol: argsMetaProcessor.SCToProtocol, - EpochEconomics: argsMetaProcessor.EpochEconomics, + ShardProcessor: shardProc, + ValidatorStatisticsProcessor: argumentsBaseProcessor.ValidatorStatisticsProcessor, + OutgoingOperationsFormatter: outgoingOpFormatter, + OutGoingOperationsPool: argumentsBaseProcessor.RunTypeComponents.OutGoingOperationsPoolHandler(), + OperationsHasher: operationsHasher, + ValidatorInfoCreator: argsMetaProcessor.EpochValidatorInfoCreator, + EpochRewardsCreator: argsMetaProcessor.EpochRewardsCreator, + EpochStartDataCreator: argsMetaProcessor.EpochStartDataCreator, + EpochSystemSCProcessor: argsMetaProcessor.EpochSystemSCProcessor, + SCToProtocol: argsMetaProcessor.SCToProtocol, + EpochEconomics: argsMetaProcessor.EpochEconomics, + MainChainNotarizationStartRound: argumentsBaseProcessor.Config.SovereignConfig.MainChainNotarization.MainChainNotarizationStartRound, } return NewSovereignChainBlockProcessor(args) diff --git a/process/block/sovereignChainBlock.go b/process/block/sovereignChainBlock.go index 7b239d8eb08..642f0c0485c 100644 --- a/process/block/sovereignChainBlock.go +++ b/process/block/sovereignChainBlock.go @@ -32,6 +32,7 @@ var rootHash = "uncomputed root hash" type extendedShardHeaderTrackHandler interface { ComputeLongestExtendedShardChainFromLastNotarized() ([]data.HeaderHandler, [][]byte, error) + IsGenesisLastCrossNotarizedHeader() bool } type extendedShardHeaderRequestHandler interface { @@ -55,21 +56,24 @@ type sovereignChainBlockProcessor struct { validatorInfoCreator process.EpochStartValidatorInfoCreator scToProtocol process.SmartContractToProtocolHandler epochEconomics process.EndOfEpochEconomics + + mainChainNotarizationStartRound uint64 } // ArgsSovereignChainBlockProcessor is a struct placeholder for args needed to create a new sovereign chain block processor type ArgsSovereignChainBlockProcessor struct { - ShardProcessor *shardProcessor - ValidatorStatisticsProcessor process.ValidatorStatisticsProcessor - OutgoingOperationsFormatter sovereign.OutgoingOperationsFormatter - OutGoingOperationsPool sovereignBlock.OutGoingOperationsPool - OperationsHasher hashing.Hasher - EpochStartDataCreator process.EpochStartDataCreator - EpochRewardsCreator process.RewardsCreator - ValidatorInfoCreator process.EpochStartValidatorInfoCreator - EpochSystemSCProcessor process.EpochStartSystemSCProcessor - SCToProtocol process.SmartContractToProtocolHandler - EpochEconomics process.EndOfEpochEconomics + ShardProcessor *shardProcessor + ValidatorStatisticsProcessor process.ValidatorStatisticsProcessor + OutgoingOperationsFormatter sovereign.OutgoingOperationsFormatter + OutGoingOperationsPool sovereignBlock.OutGoingOperationsPool + OperationsHasher hashing.Hasher + EpochStartDataCreator process.EpochStartDataCreator + EpochRewardsCreator process.RewardsCreator + ValidatorInfoCreator process.EpochStartValidatorInfoCreator + EpochSystemSCProcessor process.EpochStartSystemSCProcessor + SCToProtocol process.SmartContractToProtocolHandler + EpochEconomics process.EndOfEpochEconomics + MainChainNotarizationStartRound uint64 } // NewSovereignChainBlockProcessor creates a new sovereign chain block processor @@ -106,16 +110,17 @@ func NewSovereignChainBlockProcessor(args ArgsSovereignChainBlockProcessor) (*so } scbp := &sovereignChainBlockProcessor{ - shardProcessor: args.ShardProcessor, - validatorStatisticsProcessor: args.ValidatorStatisticsProcessor, - outgoingOperationsFormatter: args.OutgoingOperationsFormatter, - outGoingOperationsPool: args.OutGoingOperationsPool, - operationsHasher: args.OperationsHasher, - epochStartDataCreator: args.EpochStartDataCreator, - epochRewardsCreator: args.EpochRewardsCreator, - validatorInfoCreator: args.ValidatorInfoCreator, - scToProtocol: args.SCToProtocol, - epochEconomics: args.EpochEconomics, + shardProcessor: args.ShardProcessor, + validatorStatisticsProcessor: args.ValidatorStatisticsProcessor, + outgoingOperationsFormatter: args.OutgoingOperationsFormatter, + outGoingOperationsPool: args.OutGoingOperationsPool, + operationsHasher: args.OperationsHasher, + epochStartDataCreator: args.EpochStartDataCreator, + epochRewardsCreator: args.EpochRewardsCreator, + validatorInfoCreator: args.ValidatorInfoCreator, + scToProtocol: args.SCToProtocol, + epochEconomics: args.EpochEconomics, + mainChainNotarizationStartRound: args.MainChainNotarizationStartRound, } scbp.baseProcessor.epochSystemSCProcessor = args.EpochSystemSCProcessor @@ -827,14 +832,36 @@ func (scbp *sovereignChainBlockProcessor) checkExtendedShardHeadersValidity() er return err } - log.Trace("checkExtendedShardHeadersValidity", "lastCrossNotarizedHeader nonce", lastCrossNotarizedHeader.GetNonce()) + log.Trace("checkExtendedShardHeadersValidity", + "lastCrossNotarizedHeader nonce", lastCrossNotarizedHeader.GetNonce(), + "lastCrossNotarizedHeader round", lastCrossNotarizedHeader.GetRound(), + ) + extendedShardHdrs := scbp.sortExtendedShardHeadersForCurrentBlockByNonce() if len(extendedShardHdrs) == 0 { return nil } + if scbp.isGenesisHeaderWithNoPreviousTracking(extendedShardHdrs[0]) { + // we are missing pre-genesis header, so we can't link it to previous header + if len(extendedShardHdrs) == 1 { + return nil + } + + lastCrossNotarizedHeader = extendedShardHdrs[0] + extendedShardHdrs = extendedShardHdrs[1:] + + log.Debug("checkExtendedShardHeadersValidity missing pre genesis, updating lastCrossNotarizedHeader", + "lastCrossNotarizedHeader nonce", lastCrossNotarizedHeader.GetNonce(), + "lastCrossNotarizedHeader round", lastCrossNotarizedHeader.GetRound(), + ) + } + for _, extendedShardHdr := range extendedShardHdrs { - log.Trace("checkExtendedShardHeadersValidity", "extendedShardHeader nonce", extendedShardHdr.GetNonce()) + log.Trace("checkExtendedShardHeadersValidity", + "extendedShardHeader nonce", extendedShardHdr.GetNonce(), + "extendedShardHeader round", extendedShardHdr.GetRound(), + ) err = scbp.headerValidator.IsHeaderConstructionValid(extendedShardHdr, lastCrossNotarizedHeader) if err != nil { return fmt.Errorf("%w : checkExtendedShardHeadersValidity -> isHdrConstructionValid", err) @@ -846,6 +873,14 @@ func (scbp *sovereignChainBlockProcessor) checkExtendedShardHeadersValidity() er return nil } +// this will return true if we receive the genesis main chain header in following cases: +// - no notifier is attached => we did not track main chain and don't have pre-genesis header +// - node is in re-sync/start in the exact epoch when we start to notarize main chain => no previous +// main chain tracking(notifier is also disabled) +func (scbp *sovereignChainBlockProcessor) isGenesisHeaderWithNoPreviousTracking(incomingHeader data.HeaderHandler) bool { + return scbp.extendedShardHeaderTracker.IsGenesisLastCrossNotarizedHeader() && incomingHeader.GetRound() == scbp.mainChainNotarizationStartRound +} + func (scbp *sovereignChainBlockProcessor) processEpochStartMetaBlock( header data.HeaderHandler, body *block.Body, @@ -876,6 +911,11 @@ func (scbp *sovereignChainBlockProcessor) processEpochStartMetaBlock( return err } + err = scbp.createEpochStartDataCrossChain(sovHdr) + if err != nil { + return err + } + err = scbp.epochSystemSCProcessor.ProcessSystemSmartContract(allValidatorsInfo, header) if err != nil { return err @@ -921,10 +961,6 @@ func (scbp *sovereignChainBlockProcessor) processEpochStartMetaBlock( return err } - for _, valMB := range validatorMiniBlocks { - valMB.ReceiverShardID = core.SovereignChainShardId - } - finalMiniBlocks := make([]*block.MiniBlock, 0) finalMiniBlocks = append(finalMiniBlocks, rewardMiniBlocks...) finalMiniBlocks = append(finalMiniBlocks, validatorMiniBlocks...) @@ -933,6 +969,34 @@ func (scbp *sovereignChainBlockProcessor) processEpochStartMetaBlock( return scbp.applyBodyToHeaderForEpochChange(header, body) } +func (scbp *sovereignChainBlockProcessor) createEpochStartDataCrossChain(sovHdr *block.SovereignChainHeader) error { + lastCrossNotarizedHeader, lastCrossNotarizedHeaderHash, err := scbp.blockTracker.GetLastCrossNotarizedHeader(core.MainChainShardId) + if err != nil { + return err + } + + if lastCrossNotarizedHeader.GetNonce() == 0 { + log.Debug("sovereignChainBlockProcessor.createEpochStartDataCrossChain: no cross chain header notarized yet") + return nil + } + + log.Debug("sovereignChainBlockProcessor.createEpochStartDataCrossChain", + "lastCrossNotarizedHeaderHash", lastCrossNotarizedHeaderHash, + "lastCrossNotarizedHeaderRound", lastCrossNotarizedHeader.GetRound(), + "lastCrossNotarizedHeaderNonce", lastCrossNotarizedHeader.GetNonce(), + ) + + sovHdr.EpochStart.LastFinalizedCrossChainHeader = block.EpochStartCrossChainData{ + ShardID: core.MainChainShardId, + Epoch: lastCrossNotarizedHeader.GetEpoch(), + Round: lastCrossNotarizedHeader.GetRound(), + Nonce: lastCrossNotarizedHeader.GetNonce(), + HeaderHash: lastCrossNotarizedHeaderHash, + } + + return nil +} + func (scbp *sovereignChainBlockProcessor) applyBodyToHeaderForEpochChange(header data.HeaderHandler, body *block.Body) error { err := header.SetMiniBlockHeaderHandlers(nil) if err != nil { @@ -944,6 +1008,8 @@ func (scbp *sovereignChainBlockProcessor) applyBodyToHeaderForEpochChange(header return err } + scbp.saveMiniBlocksToPool(miniBlockHeaderHandlers, body.MiniBlocks) + err = header.SetMiniBlockHeaderHandlers(miniBlockHeaderHandlers) if err != nil { return err @@ -991,9 +1057,23 @@ func (scbp *sovereignChainBlockProcessor) applyBodyToHeaderForEpochChange(header } scbp.blockSizeThrottler.Add(header.GetRound(), uint32(len(marshaledBody))) + + scbp.createEpochStartData(body) + return nil } +func (scbp *sovereignChainBlockProcessor) saveMiniBlocksToPool( + miniBlockHeaderHandlers []data.MiniBlockHeaderHandler, + miniBlocks []*block.MiniBlock, +) { + for idx, mbHeaderHandler := range miniBlockHeaderHandlers { + mb := miniBlocks[idx] + hash := mbHeaderHandler.GetHash() + scbp.dataPool.MiniBlocks().Put(hash, mb, mb.Size()) + } +} + func (scbp *sovereignChainBlockProcessor) checkAndRequestIfExtendedShardHeadersMissing() { orderedExtendedShardHeaders, _ := scbp.blockTracker.GetTrackedHeaders(core.MainChainShardId) @@ -1540,9 +1620,7 @@ func (scbp *sovereignChainBlockProcessor) indexValidatorsRatingIfNeeded( func (scbp *sovereignChainBlockProcessor) commitEpochStart(header data.HeaderHandler, body *block.Body) error { if header.IsStartOfEpochBlock() { scbp.epochStartTrigger.SetProcessed(header, body) - scbp.createEpochStartData(body) go scbp.validatorInfoCreator.SaveBlockDataToStorage(header, body) - sovMetaHdr, castOk := header.(data.MetaHeaderHandler) if !castOk { log.Error("sovereignChainBlockProcessor.commitEpochStart", "error", process.ErrWrongTypeAssertion) @@ -2026,7 +2104,6 @@ func (scbp *sovereignChainBlockProcessor) cleanupBlockTrackerPoolsForShard(shard actualShardID := shardID if shardID == core.MetachainShardId { actualShardID = core.MainChainShardId - } scbp.baseCleanupBlockTrackerPoolsForShard(actualShardID, noncesToPrevFinal) } diff --git a/process/block/sovereignChainBlock_test.go b/process/block/sovereignChainBlock_test.go index a73ff9fc70b..508388a9892 100644 --- a/process/block/sovereignChainBlock_test.go +++ b/process/block/sovereignChainBlock_test.go @@ -52,17 +52,18 @@ func createSovChainBlockProcessorArgs() blproc.ArgsSovereignChainBlockProcessor baseArgs := createSovChainBaseBlockProcessorArgs() sp, _ := blproc.NewShardProcessor(baseArgs) return blproc.ArgsSovereignChainBlockProcessor{ - ShardProcessor: sp, - ValidatorStatisticsProcessor: &testscommon.ValidatorStatisticsProcessorStub{}, - OutgoingOperationsFormatter: &sovereign.OutgoingOperationsFormatterMock{}, - OutGoingOperationsPool: &sovereign.OutGoingOperationsPoolMock{}, - OperationsHasher: &testscommon.HasherStub{}, - EpochStartDataCreator: &mock.EpochStartDataCreatorStub{}, - EpochRewardsCreator: &testscommon.RewardsCreatorStub{}, - ValidatorInfoCreator: &testscommon.EpochValidatorInfoCreatorStub{}, - EpochSystemSCProcessor: &testscommon.EpochStartSystemSCStub{}, - EpochEconomics: &mock.EpochEconomicsStub{}, - SCToProtocol: &mock.SCToProtocolStub{}, + ShardProcessor: sp, + ValidatorStatisticsProcessor: &testscommon.ValidatorStatisticsProcessorStub{}, + OutgoingOperationsFormatter: &sovereign.OutgoingOperationsFormatterMock{}, + OutGoingOperationsPool: &sovereign.OutGoingOperationsPoolMock{}, + OperationsHasher: &testscommon.HasherStub{}, + EpochStartDataCreator: &mock.EpochStartDataCreatorStub{}, + EpochRewardsCreator: &testscommon.RewardsCreatorStub{}, + ValidatorInfoCreator: &testscommon.EpochValidatorInfoCreatorStub{}, + EpochSystemSCProcessor: &testscommon.EpochStartSystemSCStub{}, + EpochEconomics: &mock.EpochEconomicsStub{}, + SCToProtocol: &mock.SCToProtocolStub{}, + MainChainNotarizationStartRound: 11, } } diff --git a/process/block/sovereignCrossNotarizer.go b/process/block/sovereignCrossNotarizer.go index 8646e531fa3..5c3175582bd 100644 --- a/process/block/sovereignCrossNotarizer.go +++ b/process/block/sovereignCrossNotarizer.go @@ -15,6 +15,8 @@ func (scn *sovereignShardCrossNotarizer) getLastCrossNotarizedHeaders() []bootst return nil } + bootstrapHeaderInfo.ShardId = core.MainChainShardId + lastCrossNotarizedHeaders := make([]bootstrapStorage.BootstrapHeaderInfo, 0, 1) lastCrossNotarizedHeaders = append(lastCrossNotarizedHeaders, *bootstrapHeaderInfo) return trimSliceBootstrapHeaderInfo(lastCrossNotarizedHeaders) diff --git a/process/block/sovereignCrossNotarizer_test.go b/process/block/sovereignCrossNotarizer_test.go index 3db9e337e7c..9f4d1b3d698 100644 --- a/process/block/sovereignCrossNotarizer_test.go +++ b/process/block/sovereignCrossNotarizer_test.go @@ -33,7 +33,7 @@ func TestSovereignShardCrossNotarizer_getLastCrossNotarizedHeaders(t *testing.T) headers := sovereignNotarzier.getLastCrossNotarizedHeaders() expectedHeaders := []bootstrapStorage.BootstrapHeaderInfo{ { - ShardId: header.GetShardID(), + ShardId: core.MainChainShardId, Nonce: header.GetNonce(), Hash: hash, }, diff --git a/process/common.go b/process/common.go index facdba5860b..257b2aae11e 100644 --- a/process/common.go +++ b/process/common.go @@ -285,6 +285,51 @@ func GetMetaHeaderFromStorage( return hdr, nil } +// GetExtendedShardHeaderFromStorage gets the extended shard header, which is associated with the given hash, from storage +func GetExtendedShardHeaderFromStorage( + hash []byte, + marshaller marshal.Marshalizer, + storageService dataRetriever.StorageService, +) (*block.ShardHeaderExtended, error) { + buffHdr, err := GetMarshalizedHeaderFromStorage(dataRetriever.ExtendedShardHeadersUnit, hash, marshaller, storageService) + if err != nil { + return nil, err + } + + hdr := &block.ShardHeaderExtended{} + err = marshaller.Unmarshal(hdr, buffHdr) + if err != nil { + return nil, ErrUnmarshalWithoutSuccess + } + + return hdr, nil +} + +// GetExtendedHeaderFromStorageWithNonce method returns an extended block header from storage with a given nonce +func GetExtendedHeaderFromStorageWithNonce( + nonce uint64, + storageService dataRetriever.StorageService, + uint64Converter typeConverters.Uint64ByteSliceConverter, + marshaller marshal.Marshalizer, +) (*block.ShardHeaderExtended, []byte, error) { + hash, err := GetHeaderHashFromStorageWithNonce( + nonce, + storageService, + uint64Converter, + marshaller, + dataRetriever.ExtendedShardHeadersNonceHashDataUnit) + if err != nil { + return nil, nil, err + } + + hdr, err := GetExtendedShardHeaderFromStorage(hash, marshaller, storageService) + if err != nil { + return nil, nil, err + } + + return hdr, hash, nil +} + // GetMarshalizedHeaderFromStorage gets the marshalized header, which is associated with the given hash, from storage func GetMarshalizedHeaderFromStorage( blockUnit dataRetriever.UnitType, diff --git a/process/common_test.go b/process/common_test.go index a79e2fd5c32..7fad229070e 100644 --- a/process/common_test.go +++ b/process/common_test.go @@ -2362,3 +2362,102 @@ func TestShardedCacheSearchMethod_ToString(t *testing.T) { str := process.ShardedCacheSearchMethod(166).ToString() assert.Equal(t, "unknown method 166", str) } + +func TestGetExtendedSharHeaderFromStorage(t *testing.T) { + t.Parallel() + + hash := []byte("hash") + marshaller := &mock.MarshalizerMock{} + + t.Run("should work", func(t *testing.T) { + expectedHeader := &block.ShardHeaderExtended{ + Header: &block.HeaderV2{ + Header: &block.Header{ + Nonce: 4, + }, + }, + } + + storageService := &storageStubs.ChainStorerStub{ + GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { + return &storageStubs.StorerStub{ + GetCalled: func(key []byte) ([]byte, error) { + if bytes.Equal(key, hash) { + return marshaller.Marshal(expectedHeader) + } + + return nil, errors.New("error") + }, + }, nil + }, + } + + header, err := process.GetExtendedShardHeaderFromStorage(hash, marshaller, storageService) + require.Nil(t, err) + require.Equal(t, expectedHeader, header) + }) + + t.Run("nil storage service, should return error", func(t *testing.T) { + header, err := process.GetExtendedShardHeaderFromStorage(hash, marshaller, nil) + require.Equal(t, process.ErrNilStorage, err) + require.Nil(t, header) + }) + + t.Run("cannot unmarshal, should return error", func(t *testing.T) { + storageService := &storageStubs.ChainStorerStub{ + GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { + return &storageStubs.StorerStub{ + GetCalled: func(key []byte) ([]byte, error) { + return nil, nil + }, + }, nil + }, + } + + header, err := process.GetExtendedShardHeaderFromStorage(hash, marshaller, storageService) + require.Equal(t, process.ErrUnmarshalWithoutSuccess, err) + require.Nil(t, header) + }) +} + +func TestGetExtendedHeaderFromStorageWithNonce(t *testing.T) { + t.Parallel() + + nonce := uint64(1) + hash := []byte("hash") + marshaller := &mock.MarshalizerMock{} + expectedHeader := &block.ShardHeaderExtended{ + Header: &block.HeaderV2{ + Header: &block.Header{ + Nonce: 4, + }, + }, + } + + t.Run("should work", func(t *testing.T) { + storageService, uint64Converter := initDefaultStorageServiceAndConverter(nonce, hash, expectedHeader) + header, headerHash, err := process.GetExtendedHeaderFromStorageWithNonce( + nonce, + storageService, + uint64Converter, + marshaller) + + require.Nil(t, err) + require.Equal(t, hash, headerHash) + require.Equal(t, expectedHeader, header) + }) + + t.Run("cannot unmarshall, should return error", func(t *testing.T) { + marshaller.Fail = true + storageService, uint64Converter := initDefaultStorageServiceAndConverter(nonce, hash, expectedHeader) + header, headerHash, err := process.GetExtendedHeaderFromStorageWithNonce( + nonce, + storageService, + uint64Converter, + marshaller) + + require.Equal(t, process.ErrUnmarshalWithoutSuccess, err) + require.Nil(t, headerHash) + require.Nil(t, header) + }) +} diff --git a/process/factory/interceptorscontainer/baseInterceptorsContainerFactory.go b/process/factory/interceptorscontainer/baseInterceptorsContainerFactory.go index 54b6896c3d9..afc56608066 100644 --- a/process/factory/interceptorscontainer/baseInterceptorsContainerFactory.go +++ b/process/factory/interceptorscontainer/baseInterceptorsContainerFactory.go @@ -394,14 +394,10 @@ func (bicf *baseInterceptorsContainerFactory) createOneRewardTxInterceptor(topic // ------- Hdr interceptor -func (bicf *baseInterceptorsContainerFactory) generateHeaderInterceptors(shardID uint32) error { - shardC := bicf.shardCoordinator - - hdrFactory, err := interceptorFactory.NewInterceptedShardHeaderDataFactory(bicf.argInterceptorFactory) - if err != nil { - return err - } - +func (bicf *baseInterceptorsContainerFactory) generateHeaderInterceptors( + headerDataFactory process.InterceptedDataFactory, + shardID uint32, +) error { argProcessor := &processor.ArgHdrInterceptorProcessor{ Headers: bicf.dataPool.Headers(), BlockBlackList: bicf.blockBlackList, @@ -412,13 +408,13 @@ func (bicf *baseInterceptorsContainerFactory) generateHeaderInterceptors(shardID } // compose header shard topic, for example: shardBlocks_0_META - identifierHdr := factory.ShardBlocksTopic + shardC.CommunicationIdentifier(shardID) + identifierHdr := factory.ShardBlocksTopic + bicf.shardCoordinator.CommunicationIdentifier(shardID) // only one intrashard header topic interceptor, err := interceptors.NewSingleDataInterceptor( interceptors.ArgSingleDataInterceptor{ Topic: identifierHdr, - DataFactory: hdrFactory, + DataFactory: headerDataFactory, Processor: hdrProcessor, Throttler: bicf.globalThrottler, AntifloodHandler: bicf.antifloodHandler, @@ -483,6 +479,18 @@ func (bicf *baseInterceptorsContainerFactory) generateMiniBlocksInterceptors() e } func (bicf *baseInterceptorsContainerFactory) createOneMiniBlocksInterceptor(topic string) (process.Interceptor, error) { + miniBlockFactory, err := interceptorFactory.NewInterceptedMiniblockDataFactory(bicf.argInterceptorFactory) + if err != nil { + return nil, err + } + + return bicf.baseCreateOneMiniBlocksInterceptor(miniBlockFactory, topic) +} + +func (bicf *baseInterceptorsContainerFactory) baseCreateOneMiniBlocksInterceptor( + miniBlockFactory process.InterceptedDataFactory, + topic string, +) (process.Interceptor, error) { internalMarshaller := bicf.argInterceptorFactory.CoreComponents.InternalMarshalizer() hasher := bicf.argInterceptorFactory.CoreComponents.Hasher() argProcessor := &processor.ArgMiniblockInterceptorProcessor{ @@ -497,16 +505,11 @@ func (bicf *baseInterceptorsContainerFactory) createOneMiniBlocksInterceptor(top return nil, err } - miniblockFactory, err := interceptorFactory.NewInterceptedMiniblockDataFactory(bicf.argInterceptorFactory) - if err != nil { - return nil, err - } - interceptor, err := interceptors.NewMultiDataInterceptor( interceptors.ArgMultiDataInterceptor{ Topic: topic, Marshalizer: internalMarshaller, - DataFactory: miniblockFactory, + DataFactory: miniBlockFactory, Processor: miniblockProcessor, Throttler: bicf.globalThrottler, AntifloodHandler: bicf.antifloodHandler, diff --git a/process/factory/interceptorscontainer/shardInterceptorsContainerFactory.go b/process/factory/interceptorscontainer/shardInterceptorsContainerFactory.go index 1d7ee75402d..ec7f8b51e1a 100644 --- a/process/factory/interceptorscontainer/shardInterceptorsContainerFactory.go +++ b/process/factory/interceptorscontainer/shardInterceptorsContainerFactory.go @@ -155,7 +155,7 @@ func (sicf *shardInterceptorsContainerFactory) Create() (process.InterceptorsCon return nil, nil, err } - err = sicf.generateHeaderInterceptors(core.MetachainShardId) + err = sicf.generateShardHeaderInterceptors() if err != nil { return nil, nil, err } @@ -198,6 +198,14 @@ func (sicf *shardInterceptorsContainerFactory) Create() (process.InterceptorsCon return sicf.mainContainer, sicf.fullArchiveContainer, nil } +func (sicf *shardInterceptorsContainerFactory) generateShardHeaderInterceptors() error { + hdrFactory, err := interceptorFactory.NewInterceptedShardHeaderDataFactory(sicf.argInterceptorFactory) + if err != nil { + return err + } + return sicf.generateHeaderInterceptors(hdrFactory, core.MetachainShardId) +} + func (sicf *shardInterceptorsContainerFactory) generateTrieNodesInterceptors() error { shardC := sicf.shardCoordinator diff --git a/process/factory/interceptorscontainer/sovereignShardInterceptorsContainerFactory.go b/process/factory/interceptorscontainer/sovereignShardInterceptorsContainerFactory.go index 2119e74b912..e2c8a170033 100644 --- a/process/factory/interceptorscontainer/sovereignShardInterceptorsContainerFactory.go +++ b/process/factory/interceptorscontainer/sovereignShardInterceptorsContainerFactory.go @@ -53,7 +53,7 @@ func (sicf *sovereignShardInterceptorsContainerFactory) Create() (process.Interc return nil, nil, err } - err = sicf.generateHeaderInterceptors(core.SovereignChainShardId) + err = sicf.generateSovereignHeaderInterceptors() if err != nil { return nil, nil, err } @@ -97,6 +97,14 @@ func (sicf *sovereignShardInterceptorsContainerFactory) Create() (process.Interc return sicf.mainContainer, sicf.fullArchiveContainer, nil } +func (sicf *sovereignShardInterceptorsContainerFactory) generateSovereignHeaderInterceptors() error { + hdrFactory, err := interceptorFactory.NewInterceptedSovereignShardHeaderDataFactory(sicf.argInterceptorFactory) + if err != nil { + return err + } + return sicf.generateHeaderInterceptors(hdrFactory, core.SovereignChainShardId) +} + func (sicf *sovereignShardInterceptorsContainerFactory) generateTxInterceptors() error { keys := make([]string, 0, 1) interceptorSlice := make([]process.Interceptor, 0, 1) @@ -133,7 +141,7 @@ func (sicf *sovereignShardInterceptorsContainerFactory) generateMiniBlocksInterc interceptorsSlice := make([]process.Interceptor, 0, 1) identifierMiniBlocks := factory.MiniBlocksTopic + sicf.shardCoordinator.CommunicationIdentifier(core.SovereignChainShardId) - interceptor, err := sicf.createOneMiniBlocksInterceptor(identifierMiniBlocks) + interceptor, err := sicf.createOneSovereignMiniBlocksInterceptor(identifierMiniBlocks) if err != nil { return err } @@ -144,6 +152,15 @@ func (sicf *sovereignShardInterceptorsContainerFactory) generateMiniBlocksInterc return sicf.addInterceptorsToContainers(keys, interceptorsSlice) } +func (bicf *baseInterceptorsContainerFactory) createOneSovereignMiniBlocksInterceptor(topic string) (process.Interceptor, error) { + miniBlockFactory, err := interceptorFactory.NewInterceptedSovereignMiniBlockDataFactory(bicf.argInterceptorFactory) + if err != nil { + return nil, err + } + + return bicf.baseCreateOneMiniBlocksInterceptor(miniBlockFactory, topic) +} + func (sicf *sovereignShardInterceptorsContainerFactory) generateSovereignExtendedHeaderInterceptors() error { shardC := sicf.shardCoordinator diff --git a/process/interceptors/factory/interceptedMiniBlockSovereignDataFactory.go b/process/interceptors/factory/interceptedMiniBlockSovereignDataFactory.go new file mode 100644 index 00000000000..a9b52380920 --- /dev/null +++ b/process/interceptors/factory/interceptedMiniBlockSovereignDataFactory.go @@ -0,0 +1,33 @@ +package factory + +import ( + "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/block/interceptedBlocks" +) + +type interceptedSovereignMiniBlockDataFactory struct { + *interceptedMiniblockDataFactory +} + +// NewInterceptedSovereignMiniBlockDataFactory creates an instance of intercepted sovereign mini block data factory +func NewInterceptedSovereignMiniBlockDataFactory(argument *ArgInterceptedDataFactory) (*interceptedSovereignMiniBlockDataFactory, error) { + interceptedMbFactory, err := NewInterceptedMiniblockDataFactory(argument) + if err != nil { + return nil, err + } + + return &interceptedSovereignMiniBlockDataFactory{ + interceptedMbFactory, + }, nil +} + +// Create creates instances of InterceptedData by unmarshalling provided buffer +func (imfd *interceptedSovereignMiniBlockDataFactory) Create(buff []byte) (process.InterceptedData, error) { + arg := imfd.createArgsInterceptedMiniBlock(buff) + return interceptedBlocks.NewInterceptedSovereignMiniBlock(arg) +} + +// IsInterfaceNil returns true if there is no value under the interface +func (ismb *interceptedSovereignMiniBlockDataFactory) IsInterfaceNil() bool { + return ismb == nil +} diff --git a/process/interceptors/factory/interceptedMiniBlockSovereignDataFactory_test.go b/process/interceptors/factory/interceptedMiniBlockSovereignDataFactory_test.go new file mode 100644 index 00000000000..3204d4d02af --- /dev/null +++ b/process/interceptors/factory/interceptedMiniBlockSovereignDataFactory_test.go @@ -0,0 +1,47 @@ +package factory + +import ( + "fmt" + "testing" + + "github.com/multiversx/mx-chain-core-go/data/block" + "github.com/multiversx/mx-chain-go/process" + "github.com/stretchr/testify/require" +) + +func TestNewInterceptedSovereignShardHeaderDataFactory(t *testing.T) { + t.Parallel() + + t.Run("nil input in args", func(t *testing.T) { + coreComp, cryptoComp := createMockComponentHolders() + coreComp.IntMarsh = nil + args := createMockArgument(coreComp, cryptoComp) + mbDataFactory, err := NewInterceptedSovereignMiniBlockDataFactory(args) + require.Nil(t, mbDataFactory) + require.Equal(t, process.ErrNilMarshalizer, err) + }) + t.Run("should work", func(t *testing.T) { + coreComp, cryptoComp := createMockComponentHolders() + args := createMockArgument(coreComp, cryptoComp) + mbDataFactory, err := NewInterceptedSovereignMiniBlockDataFactory(args) + require.Nil(t, err) + require.False(t, mbDataFactory.IsInterfaceNil()) + }) +} + +func TestInterceptedSovereignMiniBlockDataFactory_Create(t *testing.T) { + t.Parallel() + + miniBlock := &block.MiniBlock{} + + coreComp, cryptoComp := createMockComponentHolders() + args := createMockArgument(coreComp, cryptoComp) + mbDataFactory, _ := NewInterceptedSovereignMiniBlockDataFactory(args) + + buff, err := coreComp.IntMarsh.Marshal(miniBlock) + require.Nil(t, err) + + interceptedData, err := mbDataFactory.Create(buff) + require.Nil(t, err) + require.Equal(t, "*interceptedBlocks.interceptedSovereignMiniBlock", fmt.Sprintf("%T", interceptedData)) +} diff --git a/process/interceptors/factory/interceptedMiniblockDataFactory.go b/process/interceptors/factory/interceptedMiniblockDataFactory.go index 64315159197..dd1af6a75bc 100644 --- a/process/interceptors/factory/interceptedMiniblockDataFactory.go +++ b/process/interceptors/factory/interceptedMiniblockDataFactory.go @@ -44,14 +44,17 @@ func NewInterceptedMiniblockDataFactory(argument *ArgInterceptedDataFactory) (*i // Create creates instances of InterceptedData by unmarshalling provided buffer func (imfd *interceptedMiniblockDataFactory) Create(buff []byte) (process.InterceptedData, error) { - arg := &interceptedBlocks.ArgInterceptedMiniblock{ + arg := imfd.createArgsInterceptedMiniBlock(buff) + return interceptedBlocks.NewInterceptedMiniblock(arg) +} + +func (imfd *interceptedMiniblockDataFactory) createArgsInterceptedMiniBlock(buff []byte) *interceptedBlocks.ArgInterceptedMiniblock { + return &interceptedBlocks.ArgInterceptedMiniblock{ MiniblockBuff: buff, Marshalizer: imfd.marshalizer, Hasher: imfd.hasher, ShardCoordinator: imfd.shardCoordinator, } - - return interceptedBlocks.NewInterceptedMiniblock(arg) } // IsInterfaceNil returns true if there is no value under the interface diff --git a/process/interceptors/factory/interceptedShardHeaderDataFactory.go b/process/interceptors/factory/interceptedShardHeaderDataFactory.go index fd19194dbd0..9c191b63856 100644 --- a/process/interceptors/factory/interceptedShardHeaderDataFactory.go +++ b/process/interceptors/factory/interceptedShardHeaderDataFactory.go @@ -70,7 +70,12 @@ func NewInterceptedShardHeaderDataFactory(argument *ArgInterceptedDataFactory) ( // Create creates instances of InterceptedData by unmarshalling provided buffer func (ishdf *interceptedShardHeaderDataFactory) Create(buff []byte) (process.InterceptedData, error) { - arg := &interceptedBlocks.ArgInterceptedBlockHeader{ + arg := ishdf.createArgsInterceptedBlockHeader(buff) + return interceptedBlocks.NewInterceptedHeader(arg) +} + +func (ishdf *interceptedShardHeaderDataFactory) createArgsInterceptedBlockHeader(buff []byte) *interceptedBlocks.ArgInterceptedBlockHeader { + return &interceptedBlocks.ArgInterceptedBlockHeader{ HdrBuff: buff, Marshalizer: ishdf.marshalizer, Hasher: ishdf.hasher, @@ -80,8 +85,6 @@ func (ishdf *interceptedShardHeaderDataFactory) Create(buff []byte) (process.Int ValidityAttester: ishdf.validityAttester, EpochStartTrigger: ishdf.epochStartTrigger, } - - return interceptedBlocks.NewInterceptedHeader(arg) } // IsInterfaceNil returns true if there is no value under the interface diff --git a/process/interceptors/factory/interceptedShardSovereignHeaderDataFactory.go b/process/interceptors/factory/interceptedShardSovereignHeaderDataFactory.go new file mode 100644 index 00000000000..925b0e5ddf0 --- /dev/null +++ b/process/interceptors/factory/interceptedShardSovereignHeaderDataFactory.go @@ -0,0 +1,33 @@ +package factory + +import ( + "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/block/interceptedBlocks" +) + +type interceptedSovereignShardHeaderDataFactory struct { + *interceptedShardHeaderDataFactory +} + +// NewInterceptedSovereignShardHeaderDataFactory creates a sovereign shard header data factory +func NewInterceptedSovereignShardHeaderDataFactory(argument *ArgInterceptedDataFactory) (*interceptedSovereignShardHeaderDataFactory, error) { + baseInterceptedShardHeaderFactory, err := NewInterceptedShardHeaderDataFactory(argument) + if err != nil { + return nil, err + } + + return &interceptedSovereignShardHeaderDataFactory{ + baseInterceptedShardHeaderFactory, + }, nil +} + +// Create creates instances of InterceptedData by unmarshalling provided buffer +func (ishdf *interceptedSovereignShardHeaderDataFactory) Create(buff []byte) (process.InterceptedData, error) { + arg := ishdf.createArgsInterceptedBlockHeader(buff) + return interceptedBlocks.NewSovereignInterceptedBlockHeader(arg) +} + +// IsInterfaceNil returns true if there is no value under the interface +func (ishdf *interceptedSovereignShardHeaderDataFactory) IsInterfaceNil() bool { + return ishdf == nil +} diff --git a/process/interceptors/factory/interceptedSovereignShardHeaderDataFactory_test.go b/process/interceptors/factory/interceptedSovereignShardHeaderDataFactory_test.go new file mode 100644 index 00000000000..2e3d169b86b --- /dev/null +++ b/process/interceptors/factory/interceptedSovereignShardHeaderDataFactory_test.go @@ -0,0 +1,49 @@ +package factory + +import ( + "fmt" + "testing" + + "github.com/multiversx/mx-chain-core-go/data/block" + "github.com/multiversx/mx-chain-go/process" + "github.com/stretchr/testify/require" +) + +func TestNewInterceptedSovereignMiniBlockDataFactory(t *testing.T) { + t.Parallel() + + t.Run("nil input in args", func(t *testing.T) { + coreComp, cryptoComp := createMockComponentHolders() + coreComp.IntMarsh = nil + args := createMockArgument(coreComp, cryptoComp) + hdrDataFactory, err := NewInterceptedSovereignShardHeaderDataFactory(args) + require.Nil(t, hdrDataFactory) + require.Equal(t, process.ErrNilMarshalizer, err) + }) + t.Run("should work", func(t *testing.T) { + coreComp, cryptoComp := createMockComponentHolders() + args := createMockArgument(coreComp, cryptoComp) + hdrDataFactory, err := NewInterceptedSovereignShardHeaderDataFactory(args) + require.Nil(t, err) + require.False(t, hdrDataFactory.IsInterfaceNil()) + }) +} + +func TestInterceptedSovereignShardHeaderDataFactory_Create(t *testing.T) { + t.Parallel() + + sovHdr := &block.SovereignChainHeader{ + Header: &block.Header{}, + } + + coreComp, cryptoComp := createMockComponentHolders() + args := createMockArgument(coreComp, cryptoComp) + hdrDataFactory, _ := NewInterceptedSovereignShardHeaderDataFactory(args) + + buff, err := coreComp.IntMarsh.Marshal(sovHdr) + require.Nil(t, err) + + interceptedData, err := hdrDataFactory.Create(buff) + require.Nil(t, err) + require.Equal(t, "*interceptedBlocks.interceptedSovereignBlockHeader", fmt.Sprintf("%T", interceptedData)) +} diff --git a/process/interceptors/processor/miniblockInterceptorProcessor.go b/process/interceptors/processor/miniblockInterceptorProcessor.go index 18950f12d59..0766c765d34 100644 --- a/process/interceptors/processor/miniblockInterceptorProcessor.go +++ b/process/interceptors/processor/miniblockInterceptorProcessor.go @@ -9,7 +9,6 @@ import ( "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/process/block/interceptedBlocks" "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/storage" logger "github.com/multiversx/mx-chain-logger-go" @@ -72,7 +71,7 @@ func (mip *MiniblockInterceptorProcessor) Validate(_ process.InterceptedData, _ // Save will save the received miniblocks inside the miniblock cacher after a new validation round // that will be done on each miniblock func (mip *MiniblockInterceptorProcessor) Save(data process.InterceptedData, _ core.PeerID, topic string) error { - interceptedMiniblock, ok := data.(*interceptedBlocks.InterceptedMiniblock) + interceptedMiniblock, ok := data.(process.InterceptedMiniBlockHandler) if !ok { return process.ErrWrongTypeAssertion } diff --git a/process/interface.go b/process/interface.go index 5170dc316af..b76ecb8f847 100644 --- a/process/interface.go +++ b/process/interface.go @@ -117,6 +117,12 @@ type HdrValidatorHandler interface { HeaderHandler() data.HeaderHandler } +// InterceptedMiniBlockHandler defines the needed functionality for an intercepted mini block +type InterceptedMiniBlockHandler interface { + Hash() []byte + Miniblock() *block.MiniBlock +} + // ExtendedHeaderValidatorHandler defines extended header validator functionality type ExtendedHeaderValidatorHandler interface { Hash() []byte diff --git a/process/sync/baseForkDetector.go b/process/sync/baseForkDetector.go index db5a601524a..3249ca5f2ea 100644 --- a/process/sync/baseForkDetector.go +++ b/process/sync/baseForkDetector.go @@ -7,6 +7,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data" + "github.com/multiversx/mx-chain-go/consensus" "github.com/multiversx/mx-chain-go/process" ) diff --git a/process/sync/storageBootstrap/shardStorageBootstrapFactory.go b/process/sync/storageBootstrap/shardStorageBootstrapFactory.go index 8851d575d5d..32a1c53b2ef 100644 --- a/process/sync/storageBootstrap/shardStorageBootstrapFactory.go +++ b/process/sync/storageBootstrap/shardStorageBootstrapFactory.go @@ -8,8 +8,8 @@ type shardStorageBootstrapperFactory struct { } // NewShardStorageBootstrapperFactory creates a new instance of shardStorageBootstrapperFactory for run type normal -func NewShardStorageBootstrapperFactory() (*shardStorageBootstrapperFactory, error) { - return &shardStorageBootstrapperFactory{}, nil +func NewShardStorageBootstrapperFactory() *shardStorageBootstrapperFactory { + return &shardStorageBootstrapperFactory{} } // CreateBootstrapperFromStorage creates a new instance of shardStorageBootstrapperFactory for run type normal diff --git a/process/sync/storageBootstrap/shardStorageBootstrapFactory_test.go b/process/sync/storageBootstrap/shardStorageBootstrapFactory_test.go index 075293e4069..a07062661c1 100644 --- a/process/sync/storageBootstrap/shardStorageBootstrapFactory_test.go +++ b/process/sync/storageBootstrap/shardStorageBootstrapFactory_test.go @@ -18,16 +18,14 @@ import ( func TestNewShardStorageBootstrapperFactory(t *testing.T) { t.Parallel() - sbf, err := NewShardStorageBootstrapperFactory() - + sbf := NewShardStorageBootstrapperFactory() require.NotNil(t, sbf) - require.Nil(t, err) } func TestShardStorageBootstrapperFactory_CreateShardStorageBootstrapper(t *testing.T) { t.Parallel() - sbf, _ := NewShardStorageBootstrapperFactory() + sbf := NewShardStorageBootstrapperFactory() bootStrapper, err := sbf.CreateBootstrapperFromStorage(getDefaultArgShardBootstrapper()) require.NotNil(t, bootStrapper) @@ -37,7 +35,7 @@ func TestShardStorageBootstrapperFactory_CreateShardStorageBootstrapper(t *testi func TestShardStorageBootstrapperFactory_IsInterfaceNil(t *testing.T) { t.Parallel() - sbf, _ := NewShardStorageBootstrapperFactory() + sbf := NewShardStorageBootstrapperFactory() require.False(t, sbf.IsInterfaceNil()) sbf = nil diff --git a/process/sync/storageBootstrap/shardStorageBootstrapper.go b/process/sync/storageBootstrap/shardStorageBootstrapper.go index d0a5309f1e4..55c58593dd2 100644 --- a/process/sync/storageBootstrap/shardStorageBootstrapper.go +++ b/process/sync/storageBootstrap/shardStorageBootstrapper.go @@ -1,6 +1,8 @@ package storageBootstrap import ( + "fmt" + "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" @@ -127,8 +129,8 @@ func (ssb *shardStorageBootstrapper) cleanupNotarizedStorage(shardHeaderHash []b "nonce", metaBlock.GetNonce(), "hash", metaBlockHash) - ssb.removeMetaFromMetaHeaderNonceToHashUnit(metaBlock, metaBlockHash) - ssb.removeMetaFromMetaBlockUnit(metaBlock, metaBlockHash) + ssb.removeHdrFromHeaderNonceToHashUnit(metaBlock, metaBlockHash, dataRetriever.MetaHdrNonceHashDataUnit) + ssb.removeBlockFromBlockUnit(metaBlock, metaBlockHash, dataRetriever.MetaBlockUnit) } } @@ -137,7 +139,7 @@ func (ssb *shardStorageBootstrapper) cleanupNotarizedStorageForHigherNoncesIfExi ) { var numConsecutiveNoncesNotFound int - lastCrossNotarizedNonce, err := getLastCrossNotarizedHeaderNonce(crossNotarizedHeaders) + lastCrossNotarizedNonce, err := getLastCrossNotarizedHeaderNonce(crossNotarizedHeaders, core.MetachainShardId) if err != nil { log.Warn("cleanupNotarizedStorageForHigherNoncesIfExist", "error", err.Error()) return @@ -177,53 +179,61 @@ func (ssb *shardStorageBootstrapper) cleanupNotarizedStorageForHigherNoncesIfExi "nonce", metaBlock.GetNonce(), "hash", metaBlockHash) - ssb.removeMetaFromMetaHeaderNonceToHashUnit(metaBlock, metaBlockHash) - ssb.removeMetaFromMetaBlockUnit(metaBlock, metaBlockHash) + ssb.removeHdrFromHeaderNonceToHashUnit(metaBlock, metaBlockHash, dataRetriever.MetaHdrNonceHashDataUnit) + ssb.removeBlockFromBlockUnit(metaBlock, metaBlockHash, dataRetriever.MetaBlockUnit) } } -func (ssb *shardStorageBootstrapper) removeMetaFromMetaHeaderNonceToHashUnit(metaBlock *block.MetaBlock, metaBlockHash []byte) { - nonceToByteSlice := ssb.uint64Converter.ToByteSlice(metaBlock.GetNonce()) - metaHdrNonceHashStorer, err := ssb.store.GetStorer(dataRetriever.MetaHdrNonceHashDataUnit) +func (ssb *shardStorageBootstrapper) removeHdrFromHeaderNonceToHashUnit( + block data.HeaderHandler, + hash []byte, + unitType dataRetriever.UnitType, +) { + nonceToByteSlice := ssb.uint64Converter.ToByteSlice(block.GetNonce()) + hdrNonceHashStorer, err := ssb.store.GetStorer(unitType) if err != nil { log.Debug("could not get storage unit", - "unit", dataRetriever.MetaHdrNonceHashDataUnit, + "unit", unitType, "error", err.Error()) return } - err = metaHdrNonceHashStorer.Remove(nonceToByteSlice) + err = hdrNonceHashStorer.Remove(nonceToByteSlice) if err != nil { - log.Debug("meta block was not removed from MetaHdrNonceHashDataUnit storage", - "shardId", metaBlock.GetShardID(), - "nonce", metaBlock.GetNonce(), - "hash", metaBlockHash, + log.Debug(fmt.Sprintf("block was not removed from %s storage", unitType.String()), + "shardId", block.GetShardID(), + "nonce", block.GetNonce(), + "hash", hash, "error", err.Error()) } } -func (ssb *shardStorageBootstrapper) removeMetaFromMetaBlockUnit(metaBlock *block.MetaBlock, metaBlockHash []byte) { - metaBlockStorer, err := ssb.store.GetStorer(dataRetriever.MetaBlockUnit) +func (ssb *shardStorageBootstrapper) removeBlockFromBlockUnit( + block data.HeaderHandler, + hash []byte, + unitType dataRetriever.UnitType, +) { + blockStorer, err := ssb.store.GetStorer(unitType) if err != nil { log.Debug("could not get storage unit", - "unit", dataRetriever.MetaBlockUnit, + "unit", unitType, "error", err.Error()) return } - err = metaBlockStorer.Remove(metaBlockHash) + err = blockStorer.Remove(hash) if err != nil { - log.Debug("meta block was not removed from MetaBlockUnit storage", - "shardId", metaBlock.GetShardID(), - "nonce", metaBlock.GetNonce(), - "hash", metaBlockHash, + log.Debug(fmt.Sprintf("block was not removed from %s storage", unitType.String()), + "shardId", block.GetShardID(), + "nonce", block.GetNonce(), + "hash", hash, "error", err.Error()) } } -func getLastCrossNotarizedHeaderNonce(crossNotarizedHeaders []bootstrapStorage.BootstrapHeaderInfo) (uint64, error) { +func getLastCrossNotarizedHeaderNonce(crossNotarizedHeaders []bootstrapStorage.BootstrapHeaderInfo, shardID uint32) (uint64, error) { for _, crossNotarizedHeader := range crossNotarizedHeaders { - if crossNotarizedHeader.ShardId != core.MetachainShardId { + if crossNotarizedHeader.ShardId != shardID { continue } diff --git a/process/sync/storageBootstrap/shardStorageBootstrapper_test.go b/process/sync/storageBootstrap/shardStorageBootstrapper_test.go index f518b21b788..d25bfdbc3af 100644 --- a/process/sync/storageBootstrap/shardStorageBootstrapper_test.go +++ b/process/sync/storageBootstrap/shardStorageBootstrapper_test.go @@ -152,7 +152,16 @@ func TestShardStorageBootstrapper_LoadFromStorageShouldWork(t *testing.T) { assert.True(t, wasCalledEpochNotifier) } -func TestShardStorageBootstrapper_CleanupNotarizedStorageForHigherNoncesIfExist(t *testing.T) { +func TestShardStorageBootstrapper_cleanupNotarizedStorageForHigherNoncesIfExist(t *testing.T) { + testCleanupNotarizedStorageForHigherNoncesIfExist(t, core.MetachainShardId, &block.MetaBlock{}, NewShardStorageBootstrapperFactory()) +} + +func testCleanupNotarizedStorageForHigherNoncesIfExist( + t *testing.T, + shardID uint32, + header data.HeaderHandler, + factory BootstrapperFromStorageCreator, +) { baseArgs := createMockShardStorageBoostrapperArgs() bForceError := true @@ -163,8 +172,8 @@ func TestShardStorageBootstrapper_CleanupNotarizedStorageForHigherNoncesIfExist( metaHash := []byte("meta_hash") metaNonceToDelete := metaNonce + maxNumOfConsecutiveNoncesNotFoundAccepted + 2 - metaBlock := &block.MetaBlock{Nonce: metaNonceToDelete} - marshalledMetaBlock, _ := baseArgs.Marshalizer.Marshal(metaBlock) + _ = header.SetNonce(metaNonceToDelete) + marshalledMetaBlock, _ := baseArgs.Marshalizer.Marshal(header) baseArgs.Uint64Converter = &mock.Uint64ByteSliceConverterMock{ ToByteSliceCalled: func(u uint64) []byte { @@ -210,46 +219,54 @@ func TestShardStorageBootstrapper_CleanupNotarizedStorageForHigherNoncesIfExist( args := ArgsShardStorageBootstrapper{ ArgsBaseStorageBootstrapper: baseArgs, } - ssb, _ := NewShardStorageBootstrapper(args) + ssb, err := factory.CreateBootstrapperFromStorage(args) + require.Nil(t, err) - crossNotarizedHeaders := make([]bootstrapStorage.BootstrapHeaderInfo, 0) + storageHandler, castOk := ssb.(storageBootstrapperHandler) + require.True(t, castOk) + crossNotarizedHeaders := make([]bootstrapStorage.BootstrapHeaderInfo, 0) crossNotarizedHeaders = append(crossNotarizedHeaders, bootstrapStorage.BootstrapHeaderInfo{ShardId: 0, Nonce: 1}) - ssb.cleanupNotarizedStorageForHigherNoncesIfExist(crossNotarizedHeaders) + storageHandler.cleanupNotarizedStorageForHigherNoncesIfExist(crossNotarizedHeaders) assert.Equal(t, 0, numCalled) - crossNotarizedHeaders = append(crossNotarizedHeaders, bootstrapStorage.BootstrapHeaderInfo{ShardId: core.MetachainShardId, Nonce: metaNonce}) - ssb.cleanupNotarizedStorageForHigherNoncesIfExist(crossNotarizedHeaders) + crossNotarizedHeaders = append(crossNotarizedHeaders, bootstrapStorage.BootstrapHeaderInfo{ShardId: shardID, Nonce: metaNonce}) + storageHandler.cleanupNotarizedStorageForHigherNoncesIfExist(crossNotarizedHeaders) assert.Equal(t, 0, numCalled) assert.Equal(t, maxNumOfConsecutiveNoncesNotFoundAccepted, numKeysNotFound-1) numKeysNotFound = 0 metaNonceToDelete = metaNonce + maxNumOfConsecutiveNoncesNotFoundAccepted + 1 - metaBlock = &block.MetaBlock{Nonce: metaNonceToDelete} - marshalledMetaBlock, _ = baseArgs.Marshalizer.Marshal(metaBlock) + _ = header.SetNonce(metaNonceToDelete) + marshalledMetaBlock, _ = baseArgs.Marshalizer.Marshal(header) - ssb.cleanupNotarizedStorageForHigherNoncesIfExist(crossNotarizedHeaders) + storageHandler.cleanupNotarizedStorageForHigherNoncesIfExist(crossNotarizedHeaders) assert.Equal(t, 0, numCalled) assert.Equal(t, maxNumOfConsecutiveNoncesNotFoundAccepted*2, numKeysNotFound-1) numKeysNotFound = 0 bForceError = false - ssb.cleanupNotarizedStorageForHigherNoncesIfExist(crossNotarizedHeaders) + storageHandler.cleanupNotarizedStorageForHigherNoncesIfExist(crossNotarizedHeaders) assert.Equal(t, 2, numCalled) assert.Equal(t, maxNumOfConsecutiveNoncesNotFoundAccepted*2, numKeysNotFound-1) } -func TestShardStorageBootstrapper_GetCrossNotarizedHeaderNonceShouldWork(t *testing.T) { +func TestGetCrossNotarizedHeaderNonce(t *testing.T) { crossNotarizedHeaders := make([]bootstrapStorage.BootstrapHeaderInfo, 0) crossNotarizedHeaders = append(crossNotarizedHeaders, bootstrapStorage.BootstrapHeaderInfo{ShardId: 0, Nonce: 1}) - nonce, err := getLastCrossNotarizedHeaderNonce(crossNotarizedHeaders) - assert.Equal(t, sync.ErrHeaderNotFound, err) - assert.Equal(t, uint64(0), nonce) + nonce, err := getLastCrossNotarizedHeaderNonce(crossNotarizedHeaders, core.MetachainShardId) + require.Equal(t, sync.ErrHeaderNotFound, err) + require.Equal(t, uint64(0), nonce) crossNotarizedHeaders = append(crossNotarizedHeaders, bootstrapStorage.BootstrapHeaderInfo{ShardId: core.MetachainShardId, Nonce: 2}) - nonce, err = getLastCrossNotarizedHeaderNonce(crossNotarizedHeaders) - assert.Nil(t, err) - assert.Equal(t, uint64(2), nonce) + nonce, err = getLastCrossNotarizedHeaderNonce(crossNotarizedHeaders, core.MetachainShardId) + require.Nil(t, err) + require.Equal(t, uint64(2), nonce) + + crossNotarizedHeaders = append(crossNotarizedHeaders, bootstrapStorage.BootstrapHeaderInfo{ShardId: core.MainChainShardId, Nonce: 4}) + nonce, err = getLastCrossNotarizedHeaderNonce(crossNotarizedHeaders, core.MainChainShardId) + require.Nil(t, err) + require.Equal(t, uint64(4), nonce) } diff --git a/process/sync/storageBootstrap/sovereignChainShardStorageBootstrapper.go b/process/sync/storageBootstrap/sovereignChainShardStorageBootstrapper.go index 830dbc23dec..2d66248f457 100644 --- a/process/sync/storageBootstrap/sovereignChainShardStorageBootstrapper.go +++ b/process/sync/storageBootstrap/sovereignChainShardStorageBootstrapper.go @@ -1,6 +1,12 @@ package storageBootstrap -import "github.com/multiversx/mx-chain-go/process" +import ( + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/data" + "github.com/multiversx/mx-chain-go/dataRetriever" + "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/block/bootstrapStorage" +) type sovereignChainShardStorageBootstrapper struct { *shardStorageBootstrapper @@ -19,5 +25,119 @@ func NewSovereignChainShardStorageBootstrapper(shardStorageBootstrapper *shardSt scssb.getScheduledRootHashMethod = scssb.sovereignChainGetScheduledRootHash scssb.setScheduledInfoMethod = scssb.sovereignChainSetScheduledInfo + scssb.bootstrapper = scssb return scssb, nil } + +func (ssb *sovereignChainShardStorageBootstrapper) applyCrossNotarizedHeaders(crossNotarizedHeaders []bootstrapStorage.BootstrapHeaderInfo) error { + for _, crossNotarizedHeader := range crossNotarizedHeaders { + if crossNotarizedHeader.ShardId != core.MainChainShardId { + continue + } + + extendedHeader, err := process.GetExtendedShardHeaderFromStorage(crossNotarizedHeader.Hash, ssb.marshalizer, ssb.store) + if err != nil { + return err + } + + log.Debug("added cross notarized header in block tracker", + "shard", core.MainChainShardId, + "round", extendedHeader.GetRound(), + "nonce", extendedHeader.GetNonce(), + "hash", crossNotarizedHeader.Hash) + + ssb.blockTracker.AddCrossNotarizedHeader(core.MainChainShardId, extendedHeader, crossNotarizedHeader.Hash) + ssb.blockTracker.AddTrackedHeader(extendedHeader, crossNotarizedHeader.Hash) + } + + return nil +} + +func (ssb *sovereignChainShardStorageBootstrapper) cleanupNotarizedStorage(shardHeaderHash []byte) { + log.Debug("sovereign cleanup notarized storage") + + shardHeader, err := process.GetShardHeaderFromStorage(shardHeaderHash, ssb.marshalizer, ssb.store) + if err != nil { + log.Debug("sovereign shard header is not found in BlockHeaderUnit storage", + "hash", shardHeaderHash) + return + } + + sovereignHeader, castOk := shardHeader.(data.SovereignChainHeaderHandler) + if !castOk { + log.Warn("sovereignChainShardStorageBootstrapper.cleanupNotarizedStorage", + "error", process.ErrWrongTypeAssertion, + "expected shard header of type", "SovereignChainHeaderHandler", + ) + return + } + + var extendedHeader data.HeaderHandler + for _, extendedHeaderHash := range sovereignHeader.GetExtendedShardHeaderHashes() { + extendedHeader, err = process.GetExtendedShardHeaderFromStorage(extendedHeaderHash, ssb.marshalizer, ssb.store) + if err != nil { + log.Debug("extended block is not found in ExtendedShardHeadersUnit storage", + "hash", extendedHeaderHash) + continue + } + + log.Debug("removing extended header from storage", + "shardId", extendedHeader.GetShardID(), + "nonce", extendedHeader.GetNonce(), + "hash", extendedHeaderHash) + + ssb.removeHdrFromHeaderNonceToHashUnit(extendedHeader, extendedHeaderHash, dataRetriever.ExtendedShardHeadersNonceHashDataUnit) + ssb.removeBlockFromBlockUnit(extendedHeader, extendedHeaderHash, dataRetriever.ExtendedShardHeadersUnit) + } +} + +func (ssb *sovereignChainShardStorageBootstrapper) cleanupNotarizedStorageForHigherNoncesIfExist( + crossNotarizedHeaders []bootstrapStorage.BootstrapHeaderInfo, +) { + var numConsecutiveNoncesNotFound int + + lastCrossNotarizedNonce, err := getLastCrossNotarizedHeaderNonce(crossNotarizedHeaders, core.MainChainShardId) + if err != nil { + log.Warn("cleanupNotarizedStorageForHigherNoncesIfExist", "error", err.Error()) + return + } + + log.Debug("cleanup notarized storage has been started", "from nonce", lastCrossNotarizedNonce+1) + nonce := lastCrossNotarizedNonce + + for { + nonce++ + + extendedBlock, extendedBlockHash, err := process.GetExtendedHeaderFromStorageWithNonce( + nonce, + ssb.store, + ssb.uint64Converter, + ssb.marshalizer, + ) + if err != nil { + log.Debug("sovereignChainShardStorageBootstrapper.cleanupNotarizedStorageForHigherNoncesIfExist:"+ + "trying to cleanup an extended header from storage that is not found", + "nonce", nonce, "error", err.Error()) + + numConsecutiveNoncesNotFound++ + if numConsecutiveNoncesNotFound > maxNumOfConsecutiveNoncesNotFoundAccepted { + log.Debug("cleanup notarized storage has been finished", + "from nonce", lastCrossNotarizedNonce+1, + "to nonce", nonce) + break + } + + continue + } + + numConsecutiveNoncesNotFound = 0 + + log.Debug("removing extended block from storage", + "shardId", extendedBlock.GetShardID(), + "nonce", extendedBlock.GetNonce(), + "hash", extendedBlockHash) + + ssb.removeHdrFromHeaderNonceToHashUnit(extendedBlock, extendedBlockHash, dataRetriever.ExtendedShardHeadersNonceHashDataUnit) + ssb.removeBlockFromBlockUnit(extendedBlock, extendedBlockHash, dataRetriever.ExtendedShardHeadersUnit) + } +} diff --git a/process/sync/storageBootstrap/sovereignChainShardStorageBootstrapper_test.go b/process/sync/storageBootstrap/sovereignChainShardStorageBootstrapper_test.go index caf8909958b..270b5c5c3ee 100644 --- a/process/sync/storageBootstrap/sovereignChainShardStorageBootstrapper_test.go +++ b/process/sync/storageBootstrap/sovereignChainShardStorageBootstrapper_test.go @@ -3,8 +3,18 @@ package storageBootstrap 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/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/block/bootstrapStorage" + "github.com/multiversx/mx-chain-go/process/mock" + "github.com/multiversx/mx-chain-go/storage" + "github.com/multiversx/mx-chain-go/testscommon" + storageStubs "github.com/multiversx/mx-chain-go/testscommon/storage" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestNewSovereignChainShardStorageBootstrapper(t *testing.T) { @@ -34,3 +44,155 @@ func TestNewSovereignChainShardStorageBootstrapper(t *testing.T) { assert.Nil(t, err) }) } + +func TestSovereignShardBootstrapFactory_applyCrossNotarizedHeaders(t *testing.T) { + t.Parallel() + + baseArgs := createMockShardStorageBoostrapperArgs() + + extendedHdrhash := []byte("hash") + extendedHdr := &block.ShardHeaderExtended{ + Header: &block.HeaderV2{ + Header: &block.Header{ + Nonce: 4, + }, + }, + } + + wasCrossChainHdrNotarized := false + wasCrossChainHdrTracked := false + baseArgs.BlockTracker = &testscommon.BlockTrackerStub{ + AddCrossNotarizedHeaderCalled: func(shardID uint32, crossNotarizedHeader data.HeaderHandler, crossNotarizedHeaderHash []byte) { + require.Equal(t, core.MainChainShardId, shardID) + require.Equal(t, extendedHdr, crossNotarizedHeader) + require.Equal(t, extendedHdrhash, crossNotarizedHeaderHash) + wasCrossChainHdrNotarized = true + }, + AddTrackedHeaderCalled: func(header data.HeaderHandler, hash []byte) { + require.Equal(t, extendedHdr, header) + require.Equal(t, extendedHdrhash, hash) + wasCrossChainHdrTracked = true + }, + } + + storer := &storageStubs.StorerStub{ + GetCalled: func(key []byte) ([]byte, error) { + return baseArgs.Marshalizer.Marshal(extendedHdr) + }, + } + + baseArgs.Store = &storageStubs.ChainStorerStub{ + GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { + switch unitType { + case dataRetriever.ExtendedShardHeadersUnit: + return storer, nil + + } + return &storageStubs.StorerStub{}, nil + }, + } + args := ArgsShardStorageBootstrapper{ + ArgsBaseStorageBootstrapper: baseArgs, + } + ssb, _ := NewShardStorageBootstrapper(args) + scssb, _ := NewSovereignChainShardStorageBootstrapper(ssb) + + crossNotarizedHeaders := []bootstrapStorage.BootstrapHeaderInfo{ + { + Hash: extendedHdrhash, + Nonce: 4, + ShardId: core.MainChainShardId, + }, + } + err := scssb.applyCrossNotarizedHeaders(crossNotarizedHeaders) + require.Nil(t, err) + require.True(t, wasCrossChainHdrNotarized) + require.True(t, wasCrossChainHdrTracked) +} + +func TestSovereignShardBootstrapFactory_cleanupNotarizedStorageForHigherNoncesIfExist(t *testing.T) { + extendedHeader := &block.ShardHeaderExtended{ + Header: &block.HeaderV2{ + Header: &block.Header{}, + }, + } + testCleanupNotarizedStorageForHigherNoncesIfExist(t, core.MainChainShardId, extendedHeader, NewSovereignShardStorageBootstrapperFactory()) +} + +func TestSovereignShardBootstrapFactory_cleanupNotarizedStorage(t *testing.T) { + t.Parallel() + + baseArgs := createMockShardStorageBoostrapperArgs() + extendedHdr := &block.ShardHeaderExtended{ + Header: &block.HeaderV2{ + Header: &block.Header{ + Nonce: 4, + }, + }, + } + + hdrNonceBytes := []byte("nonce_4") + baseArgs.Uint64Converter = &mock.Uint64ByteSliceConverterMock{ + ToByteSliceCalled: func(u uint64) []byte { + require.Equal(t, u, extendedHdr.GetNonce()) + return hdrNonceBytes + }, + } + + extendedHdrhash := []byte("hash") + sovHdr := &block.SovereignChainHeader{ + Header: &block.Header{ + SoftwareVersion: process.SovereignHeaderVersion, + }, + + ExtendedShardHeaderHashes: [][]byte{extendedHdrhash}, + } + + wasExtendedHeaderRemoved := false + wasExtendedHeaderNonceRemoved := false + extendedHdrStorer := &storageStubs.StorerStub{ + GetCalled: func(key []byte) ([]byte, error) { + return baseArgs.Marshalizer.Marshal(extendedHdr) + }, + RemoveCalled: func(key []byte) error { + require.Equal(t, extendedHdrhash, key) + wasExtendedHeaderRemoved = true + return nil + }, + } + extendedHdrNonceStorer := &storageStubs.StorerStub{ + RemoveCalled: func(key []byte) error { + require.Equal(t, hdrNonceBytes, key) + wasExtendedHeaderNonceRemoved = true + return nil + }, + } + sovHdrStorer := &storageStubs.StorerStub{ + GetCalled: func(key []byte) ([]byte, error) { + return baseArgs.Marshalizer.Marshal(sovHdr) + }, + } + + baseArgs.Store = &storageStubs.ChainStorerStub{ + GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { + switch unitType { + case dataRetriever.ExtendedShardHeadersUnit: + return extendedHdrStorer, nil + case dataRetriever.BlockHeaderUnit: + return sovHdrStorer, nil + case dataRetriever.ExtendedShardHeadersNonceHashDataUnit: + return extendedHdrNonceStorer, nil + } + return &storageStubs.StorerStub{}, nil + }, + } + args := ArgsShardStorageBootstrapper{ + ArgsBaseStorageBootstrapper: baseArgs, + } + ssb, _ := NewShardStorageBootstrapper(args) + scssb, _ := NewSovereignChainShardStorageBootstrapper(ssb) + + scssb.cleanupNotarizedStorage([]byte("hash")) + require.True(t, wasExtendedHeaderRemoved) + require.True(t, wasExtendedHeaderNonceRemoved) +} diff --git a/process/sync/storageBootstrap/sovereignShardStorageBootstrapFactory.go b/process/sync/storageBootstrap/sovereignShardStorageBootstrapFactory.go index 17ff7bc8150..25815602f07 100644 --- a/process/sync/storageBootstrap/sovereignShardStorageBootstrapFactory.go +++ b/process/sync/storageBootstrap/sovereignShardStorageBootstrapFactory.go @@ -1,24 +1,15 @@ package storageBootstrap import ( - "github.com/multiversx/mx-chain-core-go/core/check" - "github.com/multiversx/mx-chain-go/errors" "github.com/multiversx/mx-chain-go/process" ) type sovereignShardStorageBootstrapperFactory struct { - shardStorageBootstrapperFactory BootstrapperFromStorageCreator } // NewSovereignShardStorageBootstrapperFactory creates a new instance of shardStorageBootstrapperFactory for run type sovereign -func NewSovereignShardStorageBootstrapperFactory(ssb BootstrapperFromStorageCreator) (*sovereignShardStorageBootstrapperFactory, error) { - if check.IfNil(ssb) { - return nil, errors.ErrNilShardStorageBootstrapperFactory - } - - return &sovereignShardStorageBootstrapperFactory{ - shardStorageBootstrapperFactory: ssb, - }, nil +func NewSovereignShardStorageBootstrapperFactory() *sovereignShardStorageBootstrapperFactory { + return &sovereignShardStorageBootstrapperFactory{} } // CreateBootstrapperFromStorage creates a new instance of shardStorageBootstrapperFactory for run type sovereign diff --git a/process/sync/storageBootstrap/sovereignShardStorageBootstrapFactory_test.go b/process/sync/storageBootstrap/sovereignShardStorageBootstrapFactory_test.go index a03b48cb211..2c345400524 100644 --- a/process/sync/storageBootstrap/sovereignShardStorageBootstrapFactory_test.go +++ b/process/sync/storageBootstrap/sovereignShardStorageBootstrapFactory_test.go @@ -3,30 +3,20 @@ package storageBootstrap import ( "testing" - "github.com/multiversx/mx-chain-go/errors" "github.com/stretchr/testify/require" ) func TestNewSovereignShardStorageBootstrapperFactory(t *testing.T) { t.Parallel() - ssbf, err := NewSovereignShardStorageBootstrapperFactory(nil) - - require.Nil(t, ssbf) - require.Equal(t, errors.ErrNilShardStorageBootstrapperFactory, err) - - sbf, _ := NewShardStorageBootstrapperFactory() - ssbf, err = NewSovereignShardStorageBootstrapperFactory(sbf) - + ssbf := NewSovereignShardStorageBootstrapperFactory() require.NotNil(t, ssbf) - require.Nil(t, err) } func TestSovereignShardStorageBootstrapperFactory_CreateShardStorageBootstrapper(t *testing.T) { t.Parallel() - sbf, _ := NewShardStorageBootstrapperFactory() - ssbf, _ := NewSovereignShardStorageBootstrapperFactory(sbf) + ssbf := NewSovereignShardStorageBootstrapperFactory() sb, err := ssbf.CreateBootstrapperFromStorage(ArgsShardStorageBootstrapper{}) require.Nil(t, sb) @@ -41,8 +31,7 @@ func TestSovereignShardStorageBootstrapperFactory_CreateShardStorageBootstrapper func TestSovereignShardStorageBootstrapperFactory_IsInterfaceNil(t *testing.T) { t.Parallel() - sbf, _ := NewShardStorageBootstrapperFactory() - ssbf, _ := NewSovereignShardStorageBootstrapperFactory(sbf) + ssbf := NewSovereignShardStorageBootstrapperFactory() require.False(t, ssbf.IsInterfaceNil()) diff --git a/process/track/baseBlockTrack.go b/process/track/baseBlockTrack.go index 77f969e865c..1821bd41a32 100644 --- a/process/track/baseBlockTrack.go +++ b/process/track/baseBlockTrack.go @@ -12,10 +12,11 @@ import ( "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/sharding" - logger "github.com/multiversx/mx-chain-logger-go" ) var _ process.ValidityAttester = (*baseBlockTrack)(nil) @@ -283,12 +284,21 @@ func (bbt *baseBlockTrack) CleanupHeadersBehindNonce( shardID uint32, selfNotarizedNonce uint64, crossNotarizedNonce uint64, +) { + bbt.cleanupHeadersBehindNonce(shardID, shardID, selfNotarizedNonce, crossNotarizedNonce) +} + +func (bbt *baseBlockTrack) cleanupHeadersBehindNonce( + shardID uint32, + crossShardID uint32, + selfNotarizedNonce uint64, + crossNotarizedNonce uint64, ) { bbt.selfNotarizer.CleanupNotarizedHeadersBehindNonce(shardID, selfNotarizedNonce) nonce := selfNotarizedNonce if shardID != bbt.shardCoordinator.SelfId() { - bbt.crossNotarizer.CleanupNotarizedHeadersBehindNonce(shardID, crossNotarizedNonce) + bbt.crossNotarizer.CleanupNotarizedHeadersBehindNonce(crossShardID, crossNotarizedNonce) nonce = crossNotarizedNonce } diff --git a/process/track/blockProcessor.go b/process/track/blockProcessor.go index b602b502ae9..57af130d028 100644 --- a/process/track/blockProcessor.go +++ b/process/track/blockProcessor.go @@ -6,6 +6,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data" + "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/sharding" ) diff --git a/process/track/export_test.go b/process/track/export_test.go index bb8f366365c..6a00eb115bb 100644 --- a/process/track/export_test.go +++ b/process/track/export_test.go @@ -274,6 +274,10 @@ func (scbp *sovereignChainBlockProcessor) ShouldProcessReceivedHeader(headerHand return scbp.shouldProcessReceivedHeaderFunc(headerHandler) } +func (scbp *sovereignChainBlockProcessor) GetBlockFinality() uint64 { + return scbp.blockFinality +} + // miniBlockTrack func (mbt *miniBlockTrack) ReceivedMiniBlock(key []byte, value interface{}) { diff --git a/process/track/sovereignChainBlockProcessor.go b/process/track/sovereignChainBlockProcessor.go index c0796ea4d04..de2cdc229f6 100644 --- a/process/track/sovereignChainBlockProcessor.go +++ b/process/track/sovereignChainBlockProcessor.go @@ -34,6 +34,7 @@ func NewSovereignChainBlockProcessor(blockProcessor *blockProcessor) (*sovereign scbp.doJobOnReceivedCrossNotarizedHeaderFunc = scbp.doJobOnReceivedCrossNotarizedHeader scbp.requestHeaderWithShardAndNonceFunc = scbp.requestHeaderWithShardAndNonce scbp.requestHeadersIfNothingNewIsReceivedFunc = scbp.requestHeadersIfNothingNewIsReceived + scbp.blockFinality = 0 extendedShardHeaderRequester, ok := scbp.requestHandler.(extendedShardHeaderRequestHandler) if !ok { @@ -81,6 +82,10 @@ func (scbp *sovereignChainBlockProcessor) processReceivedHeader(headerHandler da } func (scbp *sovereignChainBlockProcessor) doJobOnReceivedCrossNotarizedHeader(shardID uint32) { + if scbp.crossNotarizedHeadersNotifier.GetNumRegisteredHandlers() == 0 { + return + } + _, _, crossNotarizedHeaders, crossNotarizedHeadersHashes := scbp.computeLongestChainFromLastCrossNotarized(shardID) if len(crossNotarizedHeaders) > 0 { scbp.crossNotarizedHeadersNotifier.CallHandlers(shardID, crossNotarizedHeaders, crossNotarizedHeadersHashes) diff --git a/process/track/sovereignChainBlockProcessor_test.go b/process/track/sovereignChainBlockProcessor_test.go index 93a5d12a2e9..99f1af149d2 100644 --- a/process/track/sovereignChainBlockProcessor_test.go +++ b/process/track/sovereignChainBlockProcessor_test.go @@ -65,8 +65,9 @@ func TestNewSovereignChainBlockProcessor_ShouldWork(t *testing.T) { bp, _ := track.NewBlockProcessor(blockProcessorArguments) scpb, err := track.NewSovereignChainBlockProcessor(bp) - assert.NotNil(t, scpb) - assert.Nil(t, err) + require.NotNil(t, scpb) + require.Zero(t, scpb.GetBlockFinality()) + require.Nil(t, err) } func TestSovereignChainBlockProcessor_ShouldProcessReceivedHeaderShouldReturnFalseWhenGetLastNotarizedHeaderFails(t *testing.T) { @@ -263,18 +264,30 @@ func TestSovereignChainBlockProcessor_DoJobOnReceivedCrossNotarizedHeaderShouldW } wasCalled := false + getNumRegisteredHandlersCalledCt := 0 blockProcessorArguments.CrossNotarizedHeadersNotifier = &mock.BlockNotifierHandlerStub{ CallHandlersCalled: func(shardID uint32, headers []data.HeaderHandler, headersHashes [][]byte) { wasCalled = true }, + GetNumRegisteredHandlersCalled: func() int { + defer func() { + getNumRegisteredHandlersCalledCt++ + }() + + return getNumRegisteredHandlersCalledCt + }, } bp, _ := track.NewBlockProcessor(blockProcessorArguments) scbp, _ := track.NewSovereignChainBlockProcessor(bp) scbp.DoJobOnReceivedCrossNotarizedHeader(core.SovereignChainShardId) + require.False(t, wasCalled) + require.Equal(t, 1, getNumRegisteredHandlersCalledCt) - assert.True(t, wasCalled) + scbp.DoJobOnReceivedCrossNotarizedHeader(core.SovereignChainShardId) + require.True(t, wasCalled) + require.Equal(t, 2, getNumRegisteredHandlersCalledCt) } func TestSovereignChainBlockProcessor_RequestHeadersShouldAddAndRequestForShardHeaders(t *testing.T) { @@ -323,15 +336,15 @@ func TestSovereignChainBlockProcessor_RequestHeadersShouldAddAndRequestForShardH }) mutRequest.Unlock() - require.Equal(t, 2, len(shardIDAddCalled)) - require.Equal(t, 2, len(nonceAddCalled)) - require.Equal(t, 2, len(shardIDRequestCalled)) - require.Equal(t, 2, len(nonceRequestCalled)) + require.Equal(t, 1, len(shardIDAddCalled)) + require.Equal(t, 1, len(nonceAddCalled)) + require.Equal(t, 1, len(shardIDRequestCalled)) + require.Equal(t, 1, len(nonceRequestCalled)) - assert.Equal(t, []uint32{shardID, shardID}, shardIDAddCalled) - assert.Equal(t, []uint64{fromNonce, fromNonce + 1}, nonceAddCalled) - assert.Equal(t, []uint32{shardID, shardID}, shardIDRequestCalled) - assert.Equal(t, []uint64{fromNonce, fromNonce + 1}, nonceRequestCalled) + assert.Equal(t, []uint32{shardID}, shardIDAddCalled) + assert.Equal(t, []uint64{fromNonce}, nonceAddCalled) + assert.Equal(t, []uint32{shardID}, shardIDRequestCalled) + assert.Equal(t, []uint64{fromNonce}, nonceRequestCalled) } func TestSovereignChainBlockProcessor_RequestHeadersShouldAddAndRequestForExtendedShardHeaders(t *testing.T) { @@ -378,13 +391,13 @@ func TestSovereignChainBlockProcessor_RequestHeadersShouldAddAndRequestForExtend }) mutRequest.Unlock() - require.Equal(t, 2, len(shardIDAddCalled)) - require.Equal(t, 2, len(nonceAddCalled)) - require.Equal(t, 2, len(shardIDRequestCalled)) - require.Equal(t, 2, len(nonceRequestCalled)) + require.Equal(t, 1, len(shardIDAddCalled)) + require.Equal(t, 1, len(nonceAddCalled)) + require.Equal(t, 1, len(shardIDRequestCalled)) + require.Equal(t, 1, len(nonceRequestCalled)) - assert.Equal(t, []uint32{shardID, shardID}, shardIDAddCalled) - assert.Equal(t, []uint64{fromNonce, fromNonce + 1}, nonceAddCalled) - assert.Equal(t, []uint32{shardID, shardID}, shardIDRequestCalled) - assert.Equal(t, []uint64{fromNonce, fromNonce + 1}, nonceRequestCalled) + assert.Equal(t, []uint32{shardID}, shardIDAddCalled) + assert.Equal(t, []uint64{fromNonce}, nonceAddCalled) + assert.Equal(t, []uint32{shardID}, shardIDRequestCalled) + assert.Equal(t, []uint64{fromNonce}, nonceRequestCalled) } diff --git a/process/track/sovereignChainShardBlockTrack.go b/process/track/sovereignChainShardBlockTrack.go index d43aaf7331f..07f7792b7fa 100644 --- a/process/track/sovereignChainShardBlockTrack.go +++ b/process/track/sovereignChainShardBlockTrack.go @@ -8,6 +8,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" ) @@ -108,7 +109,7 @@ func (scsbt *sovereignChainShardBlockTrack) receivedExtendedShardHeader( // TODO: This condition will permit to the sovereign chain to follow the main chain headers starting with a header // having a nonce higher than nonce 1 (the first block after genesis) - if scsbt.isGenesisLastCrossNotarizedHeader() { + if scsbt.IsGenesisLastCrossNotarizedHeader() { scsbt.crossNotarizer.AddNotarizedHeader(core.MainChainShardId, extendedShardHeaderHandler, extendedShardHeaderHash) } @@ -126,7 +127,8 @@ func (scsbt *sovereignChainShardBlockTrack) receivedExtendedShardHeader( scsbt.blockProcessor.ProcessReceivedHeader(extendedShardHeaderHandler) } -func (scsbt *sovereignChainShardBlockTrack) isGenesisLastCrossNotarizedHeader() bool { +// IsGenesisLastCrossNotarizedHeader returns true if the last cross chain notarized header is the dummy genesis header +func (scsbt *sovereignChainShardBlockTrack) IsGenesisLastCrossNotarizedHeader() bool { lastNotarizedHeader, _, err := scsbt.crossNotarizer.GetLastNotarizedHeader(core.MainChainShardId) isGenesisLastCrossNotarizedHeader := err != nil && errors.Is(err, process.ErrNotarizedHeadersSliceForShardIsNil) || @@ -212,11 +214,7 @@ func (scsbt *sovereignChainShardBlockTrack) CleanupHeadersBehindNonce( selfNotarizedNonce uint64, crossNotarizedNonce uint64, ) { - scsbt.selfNotarizer.CleanupNotarizedHeadersBehindNonce(shardID, selfNotarizedNonce) - scsbt.cleanupTrackedHeadersBehindNonce(shardID, selfNotarizedNonce) - - scsbt.crossNotarizer.CleanupNotarizedHeadersBehindNonce(core.MainChainShardId, crossNotarizedNonce) - scsbt.cleanupTrackedHeadersBehindNonce(core.MainChainShardId, crossNotarizedNonce) + scsbt.cleanupHeadersBehindNonce(shardID, core.MainChainShardId, selfNotarizedNonce, crossNotarizedNonce) } // DisplayTrackedHeaders displays tracked headers diff --git a/process/track/sovereignChainShardBlockTrack_test.go b/process/track/sovereignChainShardBlockTrack_test.go index a3abae8ef24..3a97bc853c9 100644 --- a/process/track/sovereignChainShardBlockTrack_test.go +++ b/process/track/sovereignChainShardBlockTrack_test.go @@ -9,15 +9,16 @@ 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" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/multiversx/mx-chain-go/process" processBlock "github.com/multiversx/mx-chain-go/process/block" "github.com/multiversx/mx-chain-go/process/mock" "github.com/multiversx/mx-chain-go/process/track" "github.com/multiversx/mx-chain-go/testscommon" "github.com/multiversx/mx-chain-go/testscommon/hashingMocks" - logger "github.com/multiversx/mx-chain-logger-go" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) // CreateSovereignChainShardTrackerMockArguments - @@ -563,9 +564,10 @@ func TestSovereignChainShardBlockTrack_ComputeLongestExtendedShardChainFromLastN headers, _, _ := scsbt.ComputeLongestExtendedShardChainFromLastNotarized() - require.Equal(t, 2, len(headers)) + require.Equal(t, 3, len(headers)) assert.Equal(t, shardHeaderExtended1, headers[0]) assert.Equal(t, shardHeaderExtended2, headers[1]) + assert.Equal(t, shardHeaderExtended3, headers[2]) }) } @@ -611,6 +613,48 @@ func TestSovereignChainShardBlockTrack_CleanupHeadersBehindNonceShouldWork(t *te assert.Zero(t, len(trackedHeadersForCrossShard)) } +func TestSovereignChainShardBlockTrack_CleanupHeadersBehindNonceForMainChainHeaders(t *testing.T) { + t.Parallel() + + shardArguments := CreateSovereignChainShardTrackerMockArguments() + sbt, _ := track.NewShardBlockTrack(shardArguments) + + scsbt, _ := track.NewSovereignChainShardBlockTrack(sbt) + + header := &block.Header{ + ShardID: core.SovereignChainShardId, + Nonce: 1, + } + + headerHash := []byte("hash") + scsbt.AddSelfNotarizedHeader(header.GetShardID(), header, headerHash) + scsbt.AddTrackedHeader(header, headerHash) + + shardHeaderExtended := &block.ShardHeaderExtended{ + Header: &block.HeaderV2{ + Header: &block.Header{ + Nonce: 1, + }, + }, + } + shardHeaderExtendedHash := []byte("extended_hash") + + scsbt.AddCrossNotarizedHeader(core.SovereignChainShardId, shardHeaderExtended, shardHeaderExtendedHash) + scsbt.AddTrackedHeader(shardHeaderExtended, shardHeaderExtendedHash) + + scsbt.CleanupHeadersBehindNonce(core.MainChainShardId, 2, 2) + + lastSelfNotarizedHeader, _, _ := scsbt.GetLastSelfNotarizedHeader(header.GetShardID()) + lastCrossNotarizedHeader, _, _ := scsbt.GetLastCrossNotarizedHeader(core.MainChainShardId) + trackedHeadersForSelfShard, _ := scsbt.GetTrackedHeaders(header.GetShardID()) + trackedHeadersForCrossShard, _ := scsbt.GetTrackedHeaders(core.MainChainShardId) + + require.Equal(t, header, lastSelfNotarizedHeader) + require.Equal(t, shardHeaderExtended, lastCrossNotarizedHeader) + require.Len(t, trackedHeadersForSelfShard, 1) + require.Len(t, trackedHeadersForCrossShard, 0) +} + func TestSovereignChainShardBlockTrack_DisplayTrackedHeadersShouldNotPanic(t *testing.T) { t.Parallel() diff --git a/testscommon/extendedShardHeaderTrackerStub.go b/testscommon/extendedShardHeaderTrackerStub.go index cc2fbce6d96..56345087bec 100644 --- a/testscommon/extendedShardHeaderTrackerStub.go +++ b/testscommon/extendedShardHeaderTrackerStub.go @@ -17,3 +17,8 @@ func (eshts *ExtendedShardHeaderTrackerStub) ComputeLongestExtendedShardChainFro } return nil, nil, nil } + +// IsGenesisLastCrossNotarizedHeader - +func (eshts *ExtendedShardHeaderTrackerStub) IsGenesisLastCrossNotarizedHeader() bool { + return false +} diff --git a/testscommon/generalConfig.go b/testscommon/generalConfig.go index 7c4bdaf4b55..dfe29e98c9f 100644 --- a/testscommon/generalConfig.go +++ b/testscommon/generalConfig.go @@ -437,6 +437,10 @@ func GetGeneralConfig() config.Config { Addresses: []string{"erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"}, }, }, + WebSocketConfig: config.WebSocketConfig{ + HasherType: "keccak", + MarshallerType: "json", + }, }, OutgoingSubscribedEvents: config.OutgoingSubscribedEvents{ SubscribedEvents: []config.SubscribedEvent{ diff --git a/cmd/sovereignnode/testscommon/incomingHeaderStub.go b/testscommon/sovereign/incomingHeaderStub.go similarity index 96% rename from cmd/sovereignnode/testscommon/incomingHeaderStub.go rename to testscommon/sovereign/incomingHeaderStub.go index 6861db521c4..660370d4cff 100644 --- a/cmd/sovereignnode/testscommon/incomingHeaderStub.go +++ b/testscommon/sovereign/incomingHeaderStub.go @@ -1,4 +1,4 @@ -package testscommon +package sovereign import ( "github.com/multiversx/mx-chain-core-go/data" diff --git a/update/sync/errors.go b/update/sync/errors.go new file mode 100644 index 00000000000..620952df43c --- /dev/null +++ b/update/sync/errors.go @@ -0,0 +1,5 @@ +package sync + +import "errors" + +var errNilCrossHeaderRequester = errors.New("nil cross shard/chain header requester provided") diff --git a/update/sync/extendedHeaderRequester.go b/update/sync/extendedHeaderRequester.go new file mode 100644 index 00000000000..7c420362feb --- /dev/null +++ b/update/sync/extendedHeaderRequester.go @@ -0,0 +1,37 @@ +package sync + +import ( + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/multiversx/mx-chain-go/process" +) + +type extendedHeaderRequester struct { + requestHandler ExtendedShardHeaderRequestHandler +} + +// NewExtendedHeaderRequester creates an extended header requester wrapper +func NewExtendedHeaderRequester(requestHandler ExtendedShardHeaderRequestHandler) (*extendedHeaderRequester, error) { + if check.IfNil(requestHandler) { + return nil, process.ErrNilRequestHandler + } + + return &extendedHeaderRequester{ + requestHandler: requestHandler, + }, nil +} + +// ShouldRequestHeader returns true if the shard id is main chain +func (ehr *extendedHeaderRequester) ShouldRequestHeader(shardId uint32) bool { + return shardId == core.MainChainShardId +} + +// RequestHeader requests extended shard header by hash +func (ehr *extendedHeaderRequester) RequestHeader(hash []byte) { + ehr.requestHandler.RequestExtendedShardHeader(hash) +} + +// IsInterfaceNil checks if underlying pointer is nil +func (ehr *extendedHeaderRequester) IsInterfaceNil() bool { + return ehr == nil +} diff --git a/update/sync/extendedHeaderRequester_test.go b/update/sync/extendedHeaderRequester_test.go new file mode 100644 index 00000000000..e423b529771 --- /dev/null +++ b/update/sync/extendedHeaderRequester_test.go @@ -0,0 +1,50 @@ +package sync + +import ( + "testing" + + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/testscommon" + "github.com/stretchr/testify/require" +) + +func TestNewExtendedHeaderRequester(t *testing.T) { + t.Parallel() + + headerRequester, err := NewExtendedHeaderRequester(nil) + require.Equal(t, process.ErrNilRequestHandler, err) + require.Nil(t, headerRequester) + + headerRequester, err = NewExtendedHeaderRequester(&testscommon.ExtendedShardHeaderRequestHandlerStub{}) + require.Nil(t, err) + require.False(t, headerRequester.IsInterfaceNil()) +} + +func TestExtendedHeaderRequester_ShouldRequestHeader(t *testing.T) { + t.Parallel() + + headerRequester, _ := NewExtendedHeaderRequester(&testscommon.ExtendedShardHeaderRequestHandlerStub{}) + require.False(t, headerRequester.IsInterfaceNil()) + + require.False(t, headerRequester.ShouldRequestHeader(0)) + require.False(t, headerRequester.ShouldRequestHeader(1)) + require.False(t, headerRequester.ShouldRequestHeader(core.MetachainShardId)) + require.True(t, headerRequester.ShouldRequestHeader(core.MainChainShardId)) +} + +func TestExtendedHeaderRequester_RequestHeader(t *testing.T) { + t.Parallel() + + headerHash := []byte("hash") + wasHeaderRequested := false + requester := &testscommon.ExtendedShardHeaderRequestHandlerStub{ + RequestExtendedShardHeaderCalled: func(hash []byte) { + require.Equal(t, headerHash, hash) + wasHeaderRequested = true + }, + } + headerRequester, _ := NewExtendedHeaderRequester(requester) + headerRequester.RequestHeader(headerHash) + require.True(t, wasHeaderRequested) +} diff --git a/update/sync/interface.go b/update/sync/interface.go new file mode 100644 index 00000000000..2ec30717a47 --- /dev/null +++ b/update/sync/interface.go @@ -0,0 +1,14 @@ +package sync + +// CrossHeaderRequester defines a cross shard/chain header requester +type CrossHeaderRequester interface { + ShouldRequestHeader(shardId uint32) bool + RequestHeader(hash []byte) + IsInterfaceNil() bool +} + +// ExtendedShardHeaderRequestHandler defines an extended shard header requester +type ExtendedShardHeaderRequestHandler interface { + RequestExtendedShardHeader(hash []byte) + IsInterfaceNil() bool +} diff --git a/update/sync/metaHeaderRequester.go b/update/sync/metaHeaderRequester.go new file mode 100644 index 00000000000..2a4c3da82cd --- /dev/null +++ b/update/sync/metaHeaderRequester.go @@ -0,0 +1,37 @@ +package sync + +import ( + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/multiversx/mx-chain-go/process" +) + +type metaHeaderRequester struct { + requestHandler process.RequestHandler +} + +// NewMetaHeaderRequester creates a new meta header requester wrapper +func NewMetaHeaderRequester(requestHandler process.RequestHandler) (*metaHeaderRequester, error) { + if check.IfNil(requestHandler) { + return nil, process.ErrNilRequestHandler + } + + return &metaHeaderRequester{ + requestHandler: requestHandler, + }, nil +} + +// ShouldRequestHeader returns true if the shard id is metachain +func (mhr *metaHeaderRequester) ShouldRequestHeader(shardId uint32) bool { + return shardId == core.MetachainShardId +} + +// RequestHeader requests meta header by hash +func (mhr *metaHeaderRequester) RequestHeader(hash []byte) { + mhr.requestHandler.RequestMetaHeader(hash) +} + +// IsInterfaceNil checks if underlying pointer is nil +func (mhr *metaHeaderRequester) IsInterfaceNil() bool { + return mhr == nil +} diff --git a/update/sync/metaHeaderRequester_test.go b/update/sync/metaHeaderRequester_test.go new file mode 100644 index 00000000000..a938d4d6ca6 --- /dev/null +++ b/update/sync/metaHeaderRequester_test.go @@ -0,0 +1,50 @@ +package sync + +import ( + "testing" + + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/testscommon" + "github.com/stretchr/testify/require" +) + +func TestNewMetaHeaderRequester(t *testing.T) { + t.Parallel() + + headerRequester, err := NewMetaHeaderRequester(nil) + require.Equal(t, process.ErrNilRequestHandler, err) + require.Nil(t, headerRequester) + + headerRequester, err = NewMetaHeaderRequester(&testscommon.RequestHandlerStub{}) + require.Nil(t, err) + require.False(t, headerRequester.IsInterfaceNil()) +} + +func TestMetaHeaderRequester_ShouldRequestHeader(t *testing.T) { + t.Parallel() + + headerRequester, _ := NewMetaHeaderRequester(&testscommon.RequestHandlerStub{}) + require.False(t, headerRequester.IsInterfaceNil()) + + require.False(t, headerRequester.ShouldRequestHeader(0)) + require.False(t, headerRequester.ShouldRequestHeader(1)) + require.False(t, headerRequester.ShouldRequestHeader(core.MainChainShardId)) + require.True(t, headerRequester.ShouldRequestHeader(core.MetachainShardId)) +} + +func TestMetaHeaderRequester_RequestHeader(t *testing.T) { + t.Parallel() + + headerHash := []byte("hash") + wasHeaderRequested := false + requester := &testscommon.RequestHandlerStub{ + RequestMetaHeaderCalled: func(hash []byte) { + require.Equal(t, headerHash, hash) + wasHeaderRequested = true + }, + } + headerRequester, _ := NewMetaHeaderRequester(requester) + headerRequester.RequestHeader(headerHash) + require.True(t, wasHeaderRequested) +} diff --git a/update/sync/syncHeadersByHash.go b/update/sync/syncHeadersByHash.go index 93a46b8d951..b14625d91d4 100644 --- a/update/sync/syncHeadersByHash.go +++ b/update/sync/syncHeadersByHash.go @@ -28,15 +28,17 @@ type syncHeadersByHash struct { stopSyncing bool syncedAll bool requestHandler process.RequestHandler + crossHeaderRequester CrossHeaderRequester waitTimeBetweenRequests time.Duration } // ArgsNewMissingHeadersByHashSyncer defines the arguments needed for the sycner type ArgsNewMissingHeadersByHashSyncer struct { - Storage storage.Storer - Cache dataRetriever.HeadersPool - Marshalizer marshal.Marshalizer - RequestHandler process.RequestHandler + Storage storage.Storer + Cache dataRetriever.HeadersPool + Marshalizer marshal.Marshalizer + RequestHandler process.RequestHandler + CrossHeaderRequester CrossHeaderRequester } // NewMissingheadersByHashSyncer creates a syncer for all missing headers @@ -53,6 +55,9 @@ func NewMissingheadersByHashSyncer(args ArgsNewMissingHeadersByHashSyncer) (*syn if check.IfNil(args.RequestHandler) { return nil, process.ErrNilRequestHandler } + if check.IfNil(args.CrossHeaderRequester) { + return nil, errNilCrossHeaderRequester + } p := &syncHeadersByHash{ mutMissingHdrs: sync.Mutex{}, @@ -66,6 +71,7 @@ func NewMissingheadersByHashSyncer(args ArgsNewMissingHeadersByHashSyncer) (*syn syncedAll: false, marshalizer: args.Marshalizer, waitTimeBetweenRequests: args.RequestHandler.RequestInterval(), + crossHeaderRequester: args.CrossHeaderRequester, } p.pool.RegisterHandler(p.receivedHeader) @@ -102,8 +108,8 @@ func (m *syncHeadersByHash) SyncMissingHeadersByHash(shardIDs []uint32, headersH } requestedHdrs++ - if shardId == core.MetachainShardId { - m.requestHandler.RequestMetaHeader([]byte(hash)) + if m.crossHeaderRequester.ShouldRequestHeader(shardId) { + m.crossHeaderRequester.RequestHeader([]byte(hash)) continue } diff --git a/update/sync/syncHeadersByHash_test.go b/update/sync/syncHeadersByHash_test.go index 5b7f8da8bdf..cf4bfafe1d9 100644 --- a/update/sync/syncHeadersByHash_test.go +++ b/update/sync/syncHeadersByHash_test.go @@ -41,6 +41,10 @@ func TestNewMissingheadersByHashSyncer_NilParamsShouldErr(t *testing.T) { nilRequestHandlerArgs.RequestHandler = nil testInput[nilRequestHandlerArgs] = update.ErrNilRequestHandler + nilCrossHeaderRequesterArgs := okArgs + nilCrossHeaderRequesterArgs.CrossHeaderRequester = nil + testInput[nilCrossHeaderRequesterArgs] = errNilCrossHeaderRequester + for args, expectedErr := range testInput { mhhs, err := NewMissingheadersByHashSyncer(args) require.True(t, check.IfNil(mhhs)) @@ -173,10 +177,13 @@ func TestSyncHeadersByHash_GetHeadersShouldReceiveAndReturnOkMb(t *testing.T) { } func getMisingHeadersByHashSyncerArgs() ArgsNewMissingHeadersByHashSyncer { + requestHandler := &testscommon.RequestHandlerStub{} + metaHdrRequester, _ := NewMetaHeaderRequester(requestHandler) return ArgsNewMissingHeadersByHashSyncer{ - Storage: genericMocks.NewStorerMock(), - Cache: &testscommon.HeadersCacherStub{}, - Marshalizer: &mock.MarshalizerMock{}, - RequestHandler: &testscommon.RequestHandlerStub{}, + Storage: genericMocks.NewStorerMock(), + Cache: &testscommon.HeadersCacherStub{}, + Marshalizer: &mock.MarshalizerMock{}, + RequestHandler: requestHandler, + CrossHeaderRequester: metaHdrRequester, } }