diff --git a/cmd/node/config/config.toml b/cmd/node/config/config.toml index 7b750bbe5ae..73cc3656348 100644 --- a/cmd/node/config/config.toml +++ b/cmd/node/config/config.toml @@ -176,6 +176,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 100 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [ReceiptsStorage] [ReceiptsStorage.Cache] @@ -189,6 +190,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 100 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [ScheduledSCRsStorage] [ScheduledSCRsStorage.Cache] @@ -202,6 +204,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 100 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [PeerBlockBodyStorage] [PeerBlockBodyStorage.Cache] @@ -215,6 +218,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 100 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [BlockHeaderStorage] [BlockHeaderStorage.Cache] @@ -228,6 +232,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 100 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 ShardIDProviderType = "BinarySplit" NumShards = 4 @@ -243,6 +248,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 100 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [MetaBlockStorage] [MetaBlockStorage.Cache] @@ -256,6 +262,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 100 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 ShardIDProviderType = "BinarySplit" NumShards = 4 @@ -271,6 +278,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 100 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [ExecutionResultsStorage] [ExecutionResultsStorage.Cache] @@ -297,6 +305,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 30000 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 ShardIDProviderType = "BinarySplit" NumShards = 4 @@ -312,6 +321,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 20000 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [RewardTxStorage] [RewardTxStorage.Cache] @@ -325,6 +335,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 20000 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [SmartContractsStorage] [SmartContractsStorage.Cache] @@ -338,6 +349,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 100 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [SmartContractsStorageSimulate] [SmartContractsStorageSimulate.Cache] @@ -351,6 +363,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 100 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [SmartContractsStorageForSCQuery] [SmartContractsStorageForSCQuery.Cache] @@ -364,6 +377,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 100 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [StatusMetricsStorage] [StatusMetricsStorage.Cache] @@ -376,6 +390,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 100 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [TrieEpochRootHashStorage] [TrieEpochRootHashStorage.Cache] @@ -389,6 +404,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 500 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [ShardHdrNonceHashStorage] [ShardHdrNonceHashStorage.Cache] @@ -402,6 +418,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 100 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [MetaHdrNonceHashStorage] [MetaHdrNonceHashStorage.Cache] @@ -415,6 +432,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 100 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [AccountsTrieStorage] [AccountsTrieStorage.Cache] @@ -443,6 +461,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 100 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [PeerAccountsTrieStorage] [PeerAccountsTrieStorage.Cache] @@ -469,6 +488,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 100 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [TrieStorageManagerConfig] PruningBufferLen = 100000 @@ -636,16 +656,18 @@ MaxBatchSize = 45000 MaxOpenFiles = 10 UseTmpAsFilePath = true + BloomFilterBitsPerKey = 10 [Antiflood] Enabled = true + MaxAllowedTrieNodeChunks = 10 + TrieNodeChunksInactivityTimeoutInSec = 10 [[Antiflood.ConfigsByRound]] Round = 0 NumConcurrentResolverJobs = 50 NumConcurrentResolvingTrieNodesJobs = 3 - - [Antiflood.ConfigsByRound.FastReacting] + [Antiflood.ConfigsByRound.FastReacting] IntervalInSeconds = 1 ReservedPercent = 20.0 [Antiflood.ConfigsByRound.FastReacting.PeerMaxInput] @@ -967,6 +989,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 1000 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [Hardfork.ExportKeysStorageConfig] [Hardfork.ExportKeysStorageConfig.Cache] Name = "HardFork.ExportKeysStorageConfig" @@ -978,6 +1001,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 1000 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [Hardfork.ExportTriesStorageConfig] [Hardfork.ExportTriesStorageConfig.Cache] Name = "HardFork.ExportTriesStorageConfig" @@ -989,6 +1013,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 1000 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [Hardfork.ImportStateStorageConfig] [Hardfork.ImportStateStorageConfig.Cache] Name = "HardFork.ImportStateStorageConfig" @@ -1000,6 +1025,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 1000 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [Hardfork.ImportKeysStorageConfig] [Hardfork.ImportKeysStorageConfig.Cache] Name = "HardFork.ImportKeysStorageConfig" @@ -1011,6 +1037,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 1000 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [Debug] [Debug.InterceptorResolver] @@ -1073,6 +1100,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 100 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [DbLookupExtensions] Enabled = false @@ -1087,6 +1115,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 20000 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [DbLookupExtensions.MiniblockHashByTxHashStorageConfig.Cache] Name = "DbLookupExtensions.MiniblockHashByTxHashStorage" Capacity = 20000 @@ -1097,6 +1126,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 20000 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 ShardIDProviderType = "BinarySplit" NumShards = 4 [DbLookupExtensions.EpochByHashStorageConfig.Cache] @@ -1109,6 +1139,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 20000 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 ShardIDProviderType = "BinarySplit" NumShards = 4 [DbLookupExtensions.ResultsHashesByTxHashStorageConfig.Cache] @@ -1121,6 +1152,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 20000 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [DbLookupExtensions.ESDTSuppliesStorageConfig.Cache] Name = "DbLookupExtensions.ESDTSuppliesStorage" Capacity = 20000 @@ -1131,6 +1163,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 20000 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [DbLookupExtensions.RoundHashStorageConfig.Cache] Name = "DbLookupExtensions.RoundHashStorage" Capacity = 20000 @@ -1141,6 +1174,7 @@ BatchDelaySeconds = 2 MaxBatchSize = 20000 MaxOpenFiles = 10 + BloomFilterBitsPerKey = 10 [Logs] LogFileLifeSpanInMB = 1024 # 1GB diff --git a/common/chainparametersnotifier/chainParametersNotifier_test.go b/common/chainparametersnotifier/chainParametersNotifier_test.go index fa1a30959d4..9bca337249c 100644 --- a/common/chainparametersnotifier/chainParametersNotifier_test.go +++ b/common/chainparametersnotifier/chainParametersNotifier_test.go @@ -5,8 +5,9 @@ import ( "testing" "github.com/multiversx/mx-chain-core-go/core/check" - "github.com/multiversx/mx-chain-go/config" "github.com/stretchr/testify/require" + + "github.com/multiversx/mx-chain-go/config" ) func TestNewChainParametersNotifier(t *testing.T) { @@ -112,12 +113,15 @@ func TestChainParametersNotifier_ConcurrentOperations(t *testing.T) { } type dummyNotifee struct { - receivedChainParameters config.ChainParametersByEpochConfig + receivedChainParameters config.ChainParametersByEpochConfig + mutReceivedChainParameters sync.RWMutex } // ChainParametersChanged - func (dn *dummyNotifee) ChainParametersChanged(chainParameters config.ChainParametersByEpochConfig) { + dn.mutReceivedChainParameters.Lock() dn.receivedChainParameters = chainParameters + dn.mutReceivedChainParameters.Unlock() } // IsInterfaceNil - diff --git a/common/common.go b/common/common.go index cf6601328d5..d137b77a188 100644 --- a/common/common.go +++ b/common/common.go @@ -14,10 +14,10 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" - "github.com/multiversx/mx-chain-go/errors" logger "github.com/multiversx/mx-chain-logger-go" "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/errors" ) const ( @@ -153,6 +153,14 @@ func IsConsensusBitmapValid( return ErrWrongSizeBitmap } + paddingBits := consensusSize % 8 + if paddingBits != 0 { + paddingMask := byte(0xFF << paddingBits) + if bitmap[len(bitmap)-1]&paddingMask != 0 { + return ErrPaddingBitsSet + } + } + numOfOnesInBitmap := 0 for index := range bitmap { numOfOnesInBitmap += bits.OnesCount8(bitmap[index]) diff --git a/common/common_test.go b/common/common_test.go index 289d8fb1868..6861b37e583 100644 --- a/common/common_test.go +++ b/common/common_test.go @@ -109,6 +109,29 @@ func TestIsConsensusBitmapValid(t *testing.T) { require.Equal(t, common.ErrNotEnoughSignatures, err) }) + t.Run("padding bits set should return error", func(t *testing.T) { + t.Parallel() + + // consensus size is 10, so bitmap should have 2 bytes + bitmap := make([]byte, len(pubKeys)/8+1) + bitmap[0] = 0xFF + bitmap[1] = 0x07 + + err := common.IsConsensusBitmapValid(log, pubKeys, bitmap, false) + require.Equal(t, common.ErrPaddingBitsSet, err) + }) + + t.Run("padding bits not set should return nil", func(t *testing.T) { + t.Parallel() + + bitmap := make([]byte, len(pubKeys)/8+1) + bitmap[0] = 0xFF + bitmap[1] = 0x03 + + err := common.IsConsensusBitmapValid(log, pubKeys, bitmap, false) + require.Nil(t, err) + }) + t.Run("should work", func(t *testing.T) { t.Parallel() diff --git a/common/errors.go b/common/errors.go index c436b184d86..f389e60172d 100644 --- a/common/errors.go +++ b/common/errors.go @@ -41,6 +41,9 @@ var ErrInvalidHashShardKey = errors.New("invalid hash shard key") // ErrInvalidNonceShardKey signals that the provided nonce-shard key is invalid var ErrInvalidNonceShardKey = errors.New("invalid nonce shard key") +// ErrPaddingBitsSet signals that the provided bitmap has padding bits set to 1 instead of 0 +var ErrPaddingBitsSet = errors.New("padding bits in the bitmap should be zero") + // ErrNilCommonConfigsHandler signals that a nil common configs handler has been provided var ErrNilCommonConfigsHandler = errors.New("nil common configs handler") diff --git a/common/peerAuthentication.go b/common/peerAuthentication.go new file mode 100644 index 00000000000..49132e9b32e --- /dev/null +++ b/common/peerAuthentication.go @@ -0,0 +1,14 @@ +package common + +// MaxPeerAuthenticationPublicKeyIdentifierLen is the maximum number of public key bytes used as +// a peer authentication request/whitelist identifier. +const MaxPeerAuthenticationPublicKeyIdentifierLen = 32 + +// PeerAuthenticationPublicKeyIdentifier returns the public key prefix used as request/whitelist identifier. +func PeerAuthenticationPublicKeyIdentifier(publicKey []byte) []byte { + if len(publicKey) > MaxPeerAuthenticationPublicKeyIdentifierLen { + publicKey = publicKey[:MaxPeerAuthenticationPublicKeyIdentifierLen] + } + + return append([]byte(nil), publicKey...) +} diff --git a/common/peerAuthentication_test.go b/common/peerAuthentication_test.go new file mode 100644 index 00000000000..7b23145c642 --- /dev/null +++ b/common/peerAuthentication_test.go @@ -0,0 +1,34 @@ +package common_test + +import ( + "bytes" + "testing" + + "github.com/multiversx/mx-chain-go/common" + "github.com/stretchr/testify/require" +) + +func TestPeerAuthenticationPublicKeyIdentifier(t *testing.T) { + t.Parallel() + + t.Run("short public key should be copied unchanged", func(t *testing.T) { + t.Parallel() + + publicKey := []byte("public key") + + identifier := common.PeerAuthenticationPublicKeyIdentifier(publicKey) + + require.Equal(t, publicKey, identifier) + require.NotSame(t, &publicKey[0], &identifier[0]) + }) + t.Run("long public key should be trimmed and copied", func(t *testing.T) { + t.Parallel() + + publicKey := bytes.Repeat([]byte("p"), common.MaxPeerAuthenticationPublicKeyIdentifierLen+8) + + identifier := common.PeerAuthenticationPublicKeyIdentifier(publicKey) + + require.Equal(t, publicKey[:common.MaxPeerAuthenticationPublicKeyIdentifierLen], identifier) + require.NotSame(t, &publicKey[0], &identifier[0]) + }) +} diff --git a/config/config.go b/config/config.go index 17aff27f089..e379d3512ac 100644 --- a/config/config.go +++ b/config/config.go @@ -65,6 +65,9 @@ type DBConfig struct { UseTmpAsFilePath bool ShardIDProviderType string NumShards int32 + // BloomFilterBitsPerKey == 0, the Bloom filter is disabled. + // Otherwise, it specifies the number of bits per key used by the Bloom filter. + BloomFilterBitsPerKey int } // StorageConfig will map the storage unit configuration @@ -523,8 +526,10 @@ type TxAccumulatorConfig struct { // AntifloodConfig will hold all p2p antiflood parameters type AntifloodConfig struct { - Enabled bool - ConfigsByRound []AntifloodConfigByRound + Enabled bool + MaxAllowedTrieNodeChunks uint32 + TrieNodeChunksInactivityTimeoutInSec int64 + ConfigsByRound []AntifloodConfigByRound } // AntifloodConfigByRound will hold antiflood parameters by round diff --git a/consensus/broadcast/delayedBroadcast.go b/consensus/broadcast/delayedBroadcast.go index ea382b326de..96f0d9cb468 100644 --- a/consensus/broadcast/delayedBroadcast.go +++ b/consensus/broadcast/delayedBroadcast.go @@ -26,6 +26,7 @@ const prefixHeaderAlarm = "header_" const prefixDelayDataAlarm = "delay_" const sizeHeadersCache = 1000 // 1000 hashes in cache const sizeProcessedMetaHeadersCache = 1000 +const maxPendingMetaHeaders = 50 type shardDataHandler interface { GetHeaderHash() []byte @@ -37,7 +38,6 @@ type shardDataHandler interface { type ArgsDelayedBlockBroadcaster struct { InterceptorsContainer process.InterceptorsContainer HeadersSubscriber consensus.HeadersPoolSubscriber - HeadersPool consensus.HeadersPoolGetter ProofsPool consensus.EquivalentProofsPool EnableEpochsHandler common.EnableEpochsHandler ShardCoordinator sharding.Coordinator @@ -59,12 +59,17 @@ type headerDataForValidator struct { headerHash []byte } +type pendingHeaderInfo struct { + header data.HeaderHandler + hash []byte + nonce uint64 +} + type delayedBlockBroadcaster struct { alarm timersScheduler interceptorsContainer process.InterceptorsContainer shardCoordinator sharding.Coordinator headersSubscriber consensus.HeadersPoolSubscriber - headersPool consensus.HeadersPoolGetter proofsPool consensus.EquivalentProofsPool enableEpochsHandler common.EnableEpochsHandler valHeaderBroadcastData []*shared.ValidatorHeaderBroadcastData @@ -78,8 +83,12 @@ type delayedBlockBroadcaster struct { broadcastHeader func(header data.HeaderHandler, pkBytes []byte) error broadcastConsensusMessage func(message *consensus.Message) error cacheHeaders storage.Cacher - cacheProcessedMetaHeaders storage.Cacher mutHeadersCache sync.RWMutex + // pendingMetaHeaders stores metachain headers waiting for proof arrival before broadcast. + // mutPendingMetaHeaders and mutDataForBroadcast are never held simultaneously. + pendingMetaHeaders map[string]*pendingHeaderInfo + mutPendingMetaHeaders sync.RWMutex + cacheProcessedMetaHeaders storage.Cacher } // NewDelayedBlockBroadcaster create a new instance of a delayed block data broadcaster @@ -93,9 +102,6 @@ func NewDelayedBlockBroadcaster(args *ArgsDelayedBlockBroadcaster) (*delayedBloc if check.IfNil(args.HeadersSubscriber) { return nil, spos.ErrNilHeadersSubscriber } - if check.IfNil(args.HeadersPool) { - return nil, spos.ErrNilHeadersPool - } if check.IfNil(args.ProofsPool) { return nil, spos.ErrNilEquivalentProofPool } @@ -121,7 +127,6 @@ func NewDelayedBlockBroadcaster(args *ArgsDelayedBlockBroadcaster) (*delayedBloc shardCoordinator: args.ShardCoordinator, interceptorsContainer: args.InterceptorsContainer, headersSubscriber: args.HeadersSubscriber, - headersPool: args.HeadersPool, proofsPool: args.ProofsPool, enableEpochsHandler: args.EnableEpochsHandler, valHeaderBroadcastData: make([]*shared.ValidatorHeaderBroadcastData, 0), @@ -131,12 +136,13 @@ func NewDelayedBlockBroadcaster(args *ArgsDelayedBlockBroadcaster) (*delayedBloc maxValidatorDelayCacheSize: args.ValidatorCacheSize, mutDataForBroadcast: sync.RWMutex{}, cacheHeaders: cacheHeaders, - cacheProcessedMetaHeaders: cacheProcessedMetaHeaders, mutHeadersCache: sync.RWMutex{}, + pendingMetaHeaders: make(map[string]*pendingHeaderInfo), + cacheProcessedMetaHeaders: cacheProcessedMetaHeaders, } dbb.headersSubscriber.RegisterHandler(dbb.headerReceived) - dbb.proofsPool.RegisterHandler(dbb.receivedProof) + dbb.proofsPool.RegisterHandler(dbb.proofReceived) err = dbb.registerHeaderInterceptorCallback(dbb.interceptedHeader) if err != nil { return nil, err @@ -297,16 +303,16 @@ func (dbb *delayedBlockBroadcaster) headerReceived(headerHandler data.HeaderHand return } - if common.IsProofsFlagEnabledForHeader(dbb.enableEpochsHandler, headerHandler) { - if !dbb.proofsPool.HasProof(headerHandler.GetShardID(), headerHash) { - return - } + if !common.IsProofsFlagEnabledForHeader(dbb.enableEpochsHandler, headerHandler) { + dbb.processMetachainHeader(headerHandler, headerHash) + return } - dbb.processMetachainHeaderBroadcast(headerHandler, headerHash) + dbb.addPendingMetaHeader(headerHandler, headerHash) + dbb.tryProcessPendingMetaHeader(headerHash) } -func (dbb *delayedBlockBroadcaster) receivedProof(proof data.HeaderProofHandler) { +func (dbb *delayedBlockBroadcaster) proofReceived(proof data.HeaderProofHandler) { if check.IfNil(proof) { return } @@ -315,23 +321,33 @@ func (dbb *delayedBlockBroadcaster) receivedProof(proof data.HeaderProofHandler) } headerHash := proof.GetHeaderHash() - header, err := dbb.headersPool.GetHeaderByHash(headerHash) - if err != nil { - log.Trace("delayedBlockBroadcaster.receivedProof: header not found in pool, will be handled by headerReceived", - "headerHash", headerHash, - ) + dbb.tryProcessPendingMetaHeader(headerHash) + + dbb.mutPendingMetaHeaders.Lock() + dbb.evictPendingMetaHeadersUpToNonce(proof.GetHeaderNonce()) + dbb.mutPendingMetaHeaders.Unlock() +} + +func (dbb *delayedBlockBroadcaster) tryProcessPendingMetaHeader(headerHash []byte) { + dbb.mutPendingMetaHeaders.Lock() + hashStr := string(headerHash) + pending, found := dbb.pendingMetaHeaders[hashStr] + if !found { + dbb.mutPendingMetaHeaders.Unlock() + return + } + if !dbb.proofsPool.HasProof(core.MetachainShardId, headerHash) { + dbb.mutPendingMetaHeaders.Unlock() return } + delete(dbb.pendingMetaHeaders, hashStr) + dbb.mutPendingMetaHeaders.Unlock() - dbb.processMetachainHeaderBroadcast(header, headerHash) + dbb.processMetachainHeader(pending.header, pending.hash) } -func (dbb *delayedBlockBroadcaster) processMetachainHeaderBroadcast(headerHandler data.HeaderHandler, headerHash []byte) { - has, _ := dbb.cacheProcessedMetaHeaders.HasOrAdd(headerHash, struct{}{}, 0) - if has { - log.Trace("delayedBlockBroadcaster.processMetachainHeaderBroadcast: already processed, skipping", - "headerHash", headerHash, - ) +func (dbb *delayedBlockBroadcaster) processMetachainHeader(headerHandler data.HeaderHandler, headerHash []byte) { + if alreadyProcessed, _ := dbb.cacheProcessedMetaHeaders.HasOrAdd(headerHash, struct{}{}, 0); alreadyProcessed { return } @@ -347,27 +363,67 @@ func (dbb *delayedBlockBroadcaster) processMetachainHeaderBroadcast(headerHandle dbb.shardCoordinator.SelfId(), ) if err != nil { - log.Error("delayedBlockBroadcaster.processMetachainHeaderBroadcast", "error", err.Error(), + log.Error("delayedBlockBroadcaster.processMetachainHeader", "error", err.Error(), "headerHash", headerHash, ) return } if len(headerHashes) == 0 { - log.Trace("delayedBlockBroadcaster.processMetachainHeaderBroadcast: no shardData for current shard", + log.Trace("delayedBlockBroadcaster.processMetachainHeader: no shardData for current shard", "headerHash", headerHash, ) return } - log.Trace("delayedBlockBroadcaster.processMetachainHeaderBroadcast", "nbHeaderHashes", len(headerHashes)) + log.Trace("delayedBlockBroadcaster.processMetachainHeader", "nbHeaderHashes", len(headerHashes)) for i := range headerHashes { - log.Trace("delayedBlockBroadcaster.processMetachainHeaderBroadcast", "headerHash", headerHashes[i]) + log.Trace("delayedBlockBroadcaster.processMetachainHeader", "headerHash", headerHashes[i]) } go dbb.scheduleValidatorBroadcast(dataForValidators) go dbb.broadcastDataForHeaders(headerHashes) } +func (dbb *delayedBlockBroadcaster) addPendingMetaHeader(header data.HeaderHandler, headerHash []byte) { + dbb.mutPendingMetaHeaders.Lock() + defer dbb.mutPendingMetaHeaders.Unlock() + + if len(dbb.pendingMetaHeaders) >= maxPendingMetaHeaders { + dbb.evictOldestPendingMetaHeader() + } + + dbb.pendingMetaHeaders[string(headerHash)] = &pendingHeaderInfo{ + header: header, + hash: headerHash, + nonce: header.GetNonce(), + } +} + +func (dbb *delayedBlockBroadcaster) evictOldestPendingMetaHeader() { + var oldestKey string + var oldestNonce uint64 + first := true + for key, pending := range dbb.pendingMetaHeaders { + if first || pending.nonce < oldestNonce { + oldestKey = key + oldestNonce = pending.nonce + first = false + } + } + if !first { + delete(dbb.pendingMetaHeaders, oldestKey) + } +} + +// must be called under mutPendingMetaHeaders lock +func (dbb *delayedBlockBroadcaster) evictPendingMetaHeadersUpToNonce(nonce uint64) { + for key, pending := range dbb.pendingMetaHeaders { + if pending.nonce <= nonce { + delete(dbb.pendingMetaHeaders, key) + } + } +} + func (dbb *delayedBlockBroadcaster) broadcastDataForHeaders(headerHashes [][]byte) { dbb.mutDataForBroadcast.RLock() if len(dbb.delayedBroadcastData) == 0 { diff --git a/consensus/broadcast/delayedBroadcast_test.go b/consensus/broadcast/delayedBroadcast_test.go index 2ee5b40b8ff..3ed91e53236 100644 --- a/consensus/broadcast/delayedBroadcast_test.go +++ b/consensus/broadcast/delayedBroadcast_test.go @@ -139,7 +139,6 @@ func createDefaultDelayedBroadcasterArgs() *broadcast.ArgsDelayedBlockBroadcaste ShardCoordinator: &mock.ShardCoordinatorMock{}, InterceptorsContainer: interceptorsContainer, HeadersSubscriber: headersSubscriber, - HeadersPool: headersSubscriber, ProofsPool: &dataRetrieverMock.ProofsPoolMock{}, EnableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, LeaderCacheSize: 2, @@ -194,9 +193,9 @@ func TestNewDelayedBlockBroadcaster_NilHeadersPoolShouldErr(t *testing.T) { t.Parallel() delayBroadcasterArgs := createDefaultDelayedBroadcasterArgs() - delayBroadcasterArgs.HeadersPool = nil + delayBroadcasterArgs.HeadersSubscriber = nil dbb, err := broadcast.NewDelayedBlockBroadcaster(delayBroadcasterArgs) - require.Equal(t, spos.ErrNilHeadersPool, err) + require.Equal(t, spos.ErrNilHeadersSubscriber, err) require.Nil(t, dbb) } @@ -282,7 +281,7 @@ func TestDelayedBlockBroadcaster_ReceivedProof_HeaderNotInPoolShouldNotBroadcast return flag == common.AndromedaFlag }, } - delayBroadcasterArgs.HeadersPool = &pool.HeadersPoolStub{ + delayBroadcasterArgs.HeadersSubscriber = &pool.HeadersPoolStub{ GetHeaderByHashCalled: func(hash []byte) (data.HeaderHandler, error) { return nil, errors.New("not found") }, @@ -311,7 +310,7 @@ func TestDelayedBlockBroadcaster_ReceivedProof_HeaderNotInPoolShouldNotBroadcast HeaderShardId: core.MetachainShardId, HeaderNonce: 1, } - dbb.ReceivedProof(proof) + dbb.ProofReceived(proof) time.Sleep(common.ExtraDelayForBroadcastBlockInfo + common.ExtraDelayBetweenBroadcastMbsAndTxs + 100*time.Millisecond) assert.False(t, mbBroadcastCalled.IsSet(), "should NOT broadcast when header is not in pool") @@ -340,7 +339,7 @@ func TestDelayedBlockBroadcaster_ReceivedProof_NonMetaShouldBeIgnored(t *testing HeaderHash: []byte("shard hash"), HeaderShardId: 0, // not metachain } - dbb.ReceivedProof(proof) + dbb.ProofReceived(proof) time.Sleep(50 * time.Millisecond) assert.False(t, mbBroadcastCalled.IsSet(), "should NOT broadcast for non-metachain proofs") @@ -354,7 +353,7 @@ func TestDelayedBlockBroadcaster_ReceivedProof_NilProofShouldNotPanic(t *testing require.Nil(t, err) require.NotPanics(t, func() { - dbb.ReceivedProof(nil) + dbb.ProofReceived(nil) }) } @@ -414,7 +413,7 @@ func TestDelayedBlockBroadcaster_HeaderArrivesFirst_ThenProofTriggersBroadcast(t metaBlock.Nonce = 1 metaHash := []byte("meta hash") - delayBroadcasterArgs.HeadersPool = &pool.HeadersPoolStub{ + delayBroadcasterArgs.HeadersSubscriber = &pool.HeadersPoolStub{ GetHeaderByHashCalled: func(hash []byte) (data.HeaderHandler, error) { if bytes.Equal(hash, metaHash) { return metaBlock, nil @@ -455,7 +454,7 @@ func TestDelayedBlockBroadcaster_HeaderArrivesFirst_ThenProofTriggersBroadcast(t HeaderShardId: core.MetachainShardId, HeaderNonce: 1, } - dbb.ReceivedProof(proof) + dbb.ProofReceived(proof) time.Sleep(common.ExtraDelayForBroadcastBlockInfo + common.ExtraDelayBetweenBroadcastMbsAndTxs + 100*time.Millisecond) assert.True(t, mbBroadcastCalled.IsSet(), "should broadcast after proof arrives") } @@ -653,7 +652,7 @@ func TestDelayedBlockBroadcaster_HeaderReceivedWithoutSignaturesForShardShouldNo time.Sleep(sleepTime) logOutputStr := observer.getBufferStr() - expectedLogMsg := "delayedBlockBroadcaster.processMetachainHeaderBroadcast: no shardData for current shard" + expectedLogMsg := "delayedBlockBroadcaster.processMetachainHeader: no shardData for current shard" require.Contains(t, logOutputStr, expectedLogMsg) require.Contains(t, logOutputStr, fmt.Sprintf("headerHash = %s", hex.EncodeToString(headerHash))) @@ -2270,3 +2269,253 @@ func TestDelayedBlockBroadcaster_Close(t *testing.T) { vbd = dbb.GetValidatorBroadcastData() require.Equal(t, 1, len(vbd)) } + +func TestDelayedBlockBroadcaster_HeaderReceivedWithProofsEnabled_DefersUntilProof(t *testing.T) { + t.Parallel() + + mbBroadcastCalled := atomic.Flag{} + txBroadcastCalled := atomic.Flag{} + + delayBroadcasterArgs := createDefaultDelayedBroadcasterArgs() + delayBroadcasterArgs.EnableEpochsHandler = &enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return flag == common.AndromedaFlag + }, + } + + hasProof := false + delayBroadcasterArgs.ProofsPool = &dataRetrieverMock.ProofsPoolMock{ + HasProofCalled: func(shardID uint32, headerHash []byte) bool { + return hasProof + }, + } + + dbb, err := broadcast.NewDelayedBlockBroadcaster(delayBroadcasterArgs) + require.Nil(t, err) + + err = dbb.SetBroadcastHandlers( + func(mbData map[uint32][]byte, pk []byte) error { + mbBroadcastCalled.SetValue(true) + return nil + }, + func(txData map[string][][]byte, pk []byte) error { + txBroadcastCalled.SetValue(true) + return nil + }, + func(header data.HeaderHandler, pk []byte) error { return nil }, + func(message *consensus.Message) error { return nil }, + ) + require.Nil(t, err) + + headerHash, _, miniblocksData, transactionsData := createDelayData("1") + delayedData := broadcast.CreateDelayBroadcastDataForLeader(headerHash, miniblocksData, transactionsData) + err = dbb.SetLeaderData(delayedData) + require.Nil(t, err) + + metaBlock := createMetaBlock() + metaBlock.ShardInfo[0].HeaderHash = headerHash + metaBlock.Epoch = 1 + metaBlock.Nonce = 10 + metaHash := []byte("meta hash") + + dbb.HeaderReceived(metaBlock, metaHash) + + sleepTime := common.ExtraDelayForBroadcastBlockInfo + + common.ExtraDelayBetweenBroadcastMbsAndTxs + + 100*time.Millisecond + time.Sleep(sleepTime) + + assert.False(t, mbBroadcastCalled.IsSet(), "should not broadcast without proof") + assert.False(t, txBroadcastCalled.IsSet(), "should not broadcast without proof") + assert.Equal(t, 1, dbb.GetPendingMetaHeadersCount(), "header should be pending") + + hasProof = true + proof := &block.HeaderProof{ + HeaderHash: metaHash, + HeaderShardId: core.MetachainShardId, + HeaderNonce: 10, + HeaderEpoch: 1, + } + dbb.ProofReceived(proof) + + time.Sleep(sleepTime) + + assert.True(t, mbBroadcastCalled.IsSet(), "should broadcast after proof arrives") + assert.True(t, txBroadcastCalled.IsSet(), "should broadcast after proof arrives") + assert.Equal(t, 0, dbb.GetPendingMetaHeadersCount(), "pending should be cleared") +} + +func TestDelayedBlockBroadcaster_ProofReceivedEvictsOlderNonces(t *testing.T) { + t.Parallel() + + delayBroadcasterArgs := createDefaultDelayedBroadcasterArgs() + delayBroadcasterArgs.EnableEpochsHandler = &enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return flag == common.AndromedaFlag + }, + } + delayBroadcasterArgs.ProofsPool = &dataRetrieverMock.ProofsPoolMock{ + HasProofCalled: func(shardID uint32, headerHash []byte) bool { + return false + }, + } + + dbb, err := broadcast.NewDelayedBlockBroadcaster(delayBroadcasterArgs) + require.Nil(t, err) + + err = dbb.SetBroadcastHandlers( + func(mbData map[uint32][]byte, pk []byte) error { return nil }, + func(txData map[string][][]byte, pk []byte) error { return nil }, + func(header data.HeaderHandler, pk []byte) error { return nil }, + func(message *consensus.Message) error { return nil }, + ) + require.Nil(t, err) + + for i := 0; i < 3; i++ { + headerHash, _, miniblocksData, transactionsData := createDelayData(strconv.Itoa(i)) + delayedData := broadcast.CreateDelayBroadcastDataForLeader(headerHash, miniblocksData, transactionsData) + err = dbb.SetLeaderData(delayedData) + require.Nil(t, err) + + metaBlock := createMetaBlock() + metaBlock.ShardInfo[0].HeaderHash = headerHash + metaBlock.Epoch = 1 + metaBlock.Nonce = uint64(10 + i) + + dbb.HeaderReceived(metaBlock, []byte(fmt.Sprintf("meta hash %d", i))) + } + + assert.Equal(t, 3, dbb.GetPendingMetaHeadersCount()) + + proof := &block.HeaderProof{ + HeaderHash: []byte("unknown hash"), + HeaderShardId: core.MetachainShardId, + HeaderNonce: 11, + HeaderEpoch: 1, + } + dbb.ProofReceived(proof) + + assert.Equal(t, 1, dbb.GetPendingMetaHeadersCount(), "only nonce 12 should remain") +} + +func TestDelayedBlockBroadcaster_HeaderReceivedWithProofsEnabled_ProofAlreadyAvailable(t *testing.T) { + t.Parallel() + + mbBroadcastCalled := atomic.Flag{} + + delayBroadcasterArgs := createDefaultDelayedBroadcasterArgs() + delayBroadcasterArgs.EnableEpochsHandler = &enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return flag == common.AndromedaFlag + }, + } + delayBroadcasterArgs.ProofsPool = &dataRetrieverMock.ProofsPoolMock{ + HasProofCalled: func(shardID uint32, headerHash []byte) bool { + return true + }, + } + + dbb, err := broadcast.NewDelayedBlockBroadcaster(delayBroadcasterArgs) + require.Nil(t, err) + + err = dbb.SetBroadcastHandlers( + func(mbData map[uint32][]byte, pk []byte) error { + mbBroadcastCalled.SetValue(true) + return nil + }, + func(txData map[string][][]byte, pk []byte) error { return nil }, + func(header data.HeaderHandler, pk []byte) error { return nil }, + func(message *consensus.Message) error { return nil }, + ) + require.Nil(t, err) + + headerHash, _, miniblocksData, transactionsData := createDelayData("1") + delayedData := broadcast.CreateDelayBroadcastDataForLeader(headerHash, miniblocksData, transactionsData) + err = dbb.SetLeaderData(delayedData) + require.Nil(t, err) + + metaBlock := createMetaBlock() + metaBlock.ShardInfo[0].HeaderHash = headerHash + metaBlock.Epoch = 1 + metaBlock.Nonce = 10 + + dbb.HeaderReceived(metaBlock, []byte("meta hash")) + + sleepTime := common.ExtraDelayForBroadcastBlockInfo + + common.ExtraDelayBetweenBroadcastMbsAndTxs + + 100*time.Millisecond + time.Sleep(sleepTime) + + assert.True(t, mbBroadcastCalled.IsSet(), "should broadcast immediately when proof is already available") + assert.Equal(t, 0, dbb.GetPendingMetaHeadersCount()) +} + +func TestDelayedBlockBroadcaster_DuplicateProcessingPrevented(t *testing.T) { + t.Parallel() + + broadcastCount := atomic.Counter{} + + delayBroadcasterArgs := createDefaultDelayedBroadcasterArgs() + delayBroadcasterArgs.EnableEpochsHandler = &enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return flag == common.AndromedaFlag + }, + } + delayBroadcasterArgs.ProofsPool = &dataRetrieverMock.ProofsPoolMock{ + HasProofCalled: func(shardID uint32, headerHash []byte) bool { + return true + }, + } + + dbb, err := broadcast.NewDelayedBlockBroadcaster(delayBroadcasterArgs) + require.Nil(t, err) + + err = dbb.SetBroadcastHandlers( + func(mbData map[uint32][]byte, pk []byte) error { + broadcastCount.Increment() + return nil + }, + func(txData map[string][][]byte, pk []byte) error { return nil }, + func(header data.HeaderHandler, pk []byte) error { return nil }, + func(message *consensus.Message) error { return nil }, + ) + require.Nil(t, err) + + headerHash, _, miniblocksData, transactionsData := createDelayData("1") + delayedData := broadcast.CreateDelayBroadcastDataForLeader(headerHash, miniblocksData, transactionsData) + err = dbb.SetLeaderData(delayedData) + require.Nil(t, err) + + metaBlock := createMetaBlock() + metaBlock.ShardInfo[0].HeaderHash = headerHash + metaBlock.Epoch = 1 + metaBlock.Nonce = 10 + metaHash := []byte("meta hash") + + dbb.HeaderReceived(metaBlock, metaHash) + dbb.HeaderReceived(metaBlock, metaHash) + + sleepTime := common.ExtraDelayForBroadcastBlockInfo + + common.ExtraDelayBetweenBroadcastMbsAndTxs + + 100*time.Millisecond + time.Sleep(sleepTime) + + assert.Equal(t, int64(1), broadcastCount.Get(), "should broadcast only once despite two HeaderReceived calls") +} + +func TestDelayedBlockBroadcaster_ProofReceivedNonMetaShouldBeIgnored(t *testing.T) { + t.Parallel() + + delayBroadcasterArgs := createDefaultDelayedBroadcasterArgs() + dbb, err := broadcast.NewDelayedBlockBroadcaster(delayBroadcasterArgs) + require.Nil(t, err) + + proof := &block.HeaderProof{ + HeaderHash: []byte("some hash"), + HeaderShardId: 0, + HeaderNonce: 10, + } + dbb.ProofReceived(proof) + + assert.Equal(t, 0, dbb.GetPendingMetaHeadersCount()) +} diff --git a/consensus/broadcast/export.go b/consensus/broadcast/export.go index 7addb7a4c48..5d45e1584bd 100644 --- a/consensus/broadcast/export.go +++ b/consensus/broadcast/export.go @@ -81,10 +81,16 @@ func (dbb *delayedBlockBroadcaster) HeaderReceived(headerHandler data.HeaderHand dbb.headerReceived(headerHandler, hash) } -// ReceivedProof is the callback registered on the proofs pool -// to be called when a proof is added to the proofs pool -func (dbb *delayedBlockBroadcaster) ReceivedProof(proof data.HeaderProofHandler) { - dbb.receivedProof(proof) +// ProofReceived is the callback for when a proof is received +func (dbb *delayedBlockBroadcaster) ProofReceived(proof data.HeaderProofHandler) { + dbb.proofReceived(proof) +} + +// GetPendingMetaHeadersCount returns the number of pending meta headers +func (dbb *delayedBlockBroadcaster) GetPendingMetaHeadersCount() int { + dbb.mutPendingMetaHeaders.RLock() + defer dbb.mutPendingMetaHeaders.RUnlock() + return len(dbb.pendingMetaHeaders) } // GetValidatorBroadcastData returns the set validator delayed broadcast data diff --git a/consensus/broadcast/shardChainMessenger_test.go b/consensus/broadcast/shardChainMessenger_test.go index 0b0d55d62b1..c349f7fd46a 100644 --- a/consensus/broadcast/shardChainMessenger_test.go +++ b/consensus/broadcast/shardChainMessenger_test.go @@ -574,7 +574,6 @@ func TestShardChainMessenger_BroadcastBlockDataLeaderShouldTriggerWaitingDelayed argsDelayedBroadcaster := broadcast.ArgsDelayedBlockBroadcaster{ InterceptorsContainer: args.InterceptorsContainer, HeadersSubscriber: args.HeadersSubscriber, - HeadersPool: &pool.HeadersPoolStub{}, ProofsPool: &dataRetrieverMock.ProofsPoolMock{}, EnableEpochsHandler: &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, ShardCoordinator: args.ShardCoordinator, diff --git a/consensus/spos/consensusMessageValidator.go b/consensus/spos/consensusMessageValidator.go index 64801fd6fae..bea699d9249 100644 --- a/consensus/spos/consensusMessageValidator.go +++ b/consensus/spos/consensusMessageValidator.go @@ -9,12 +9,13 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/marshal" crypto "github.com/multiversx/mx-chain-crypto-go" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/consensus" "github.com/multiversx/mx-chain-go/p2p" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/sharding" - logger "github.com/multiversx/mx-chain-logger-go" ) type consensusMessageValidator struct { @@ -201,6 +202,7 @@ func (cmv *consensusMessageValidator) checkConsensusMessageValidity(cnsMsg *cons err = cmv.peerSignatureHandler.VerifyPeerSignature(cnsMsg.PubKey, core.PeerID(cnsMsg.OriginatorPid), cnsMsg.Signature) if err != nil { + cmv.removeMessageTypeToPublicKey(cnsMsg.PubKey, cnsMsg.RoundIndex, msgType) return fmt.Errorf("%w : verify signature for received message from consensus topic failed: %s", ErrInvalidSignature, err.Error()) @@ -208,12 +210,11 @@ func (cmv *consensusMessageValidator) checkConsensusMessageValidity(cnsMsg *cons cnsMsgOriginator := core.PeerID(cnsMsg.OriginatorPid) if cnsMsgOriginator != originator { + cmv.removeMessageTypeToPublicKey(cnsMsg.PubKey, cnsMsg.RoundIndex, msgType) return fmt.Errorf("%w : pubsub originator pid: %s, cnsMsg.OriginatorPid: %s", ErrOriginatorMismatch, p2p.PeerIdToShortString(originator), p2p.PeerIdToShortString(cnsMsgOriginator)) } - cmv.addMessageTypeToPublicKey(cnsMsg.PubKey, cnsMsg.RoundIndex, msgType) - return nil } @@ -493,28 +494,34 @@ func (cmv *consensusMessageValidator) checkMessageWithInvalidSingersValidity(cns } func (cmv *consensusMessageValidator) isMessageTypeLimitReached(pk []byte, round int64, msgType consensus.MessageType) bool { - cmv.mutPkConsensusMessages.RLock() - defer cmv.mutPkConsensusMessages.RUnlock() + cmv.mutPkConsensusMessages.Lock() + defer cmv.mutPkConsensusMessages.Unlock() key := fmt.Sprintf("%s_%d", string(pk), round) mapMsgType, ok := cmv.mapPkConsensusMessages[key] if !ok { - return false + return cmv.checkLimitReached(0, pk, round, msgType) } numMsgType, ok := mapMsgType[msgType] if !ok { - return false + return cmv.checkLimitReached(numMsgType, pk, round, msgType) } - return numMsgType >= cmv.consensusService.GetMaxNumOfMessageTypeAccepted(msgType) + return cmv.checkLimitReached(numMsgType, pk, round, msgType) } -func (cmv *consensusMessageValidator) addMessageTypeToPublicKey(pk []byte, round int64, msgType consensus.MessageType) { - cmv.mutPkConsensusMessages.Lock() - defer cmv.mutPkConsensusMessages.Unlock() +func (cmv *consensusMessageValidator) checkLimitReached(numMsgType uint32, pk []byte, round int64, msgType consensus.MessageType) bool { + isLimitReached := numMsgType >= cmv.consensusService.GetMaxNumOfMessageTypeAccepted(msgType) + if !isLimitReached { + cmv.addMessageTypeToPublicKey(pk, round, msgType) + } + return isLimitReached +} + +func (cmv *consensusMessageValidator) addMessageTypeToPublicKey(pk []byte, round int64, msgType consensus.MessageType) { key := fmt.Sprintf("%s_%d", string(pk), round) mapMsgType, ok := cmv.mapPkConsensusMessages[key] @@ -526,6 +533,31 @@ func (cmv *consensusMessageValidator) addMessageTypeToPublicKey(pk []byte, round mapMsgType[msgType]++ } +func (cmv *consensusMessageValidator) removeMessageTypeToPublicKey(pk []byte, round int64, msgType consensus.MessageType) { + cmv.mutPkConsensusMessages.Lock() + defer cmv.mutPkConsensusMessages.Unlock() + + key := fmt.Sprintf("%s_%d", string(pk), round) + + mapMsgType, ok := cmv.mapPkConsensusMessages[key] + if !ok { + return + } + + count, ok := mapMsgType[msgType] + if !ok || count == 0 { + return + } + if count == 1 { + delete(mapMsgType, msgType) + if len(mapMsgType) == 0 { + delete(cmv.mapPkConsensusMessages, key) + } + return + } + mapMsgType[msgType] = count - 1 +} + func (cmv *consensusMessageValidator) resetConsensusMessages() { cmv.mutPkConsensusMessages.Lock() cmv.mapPkConsensusMessages = make(map[string]map[consensus.MessageType]uint32) diff --git a/consensus/spos/consensusMessageValidator_test.go b/consensus/spos/consensusMessageValidator_test.go index 9936694d21f..d247d2cfeff 100644 --- a/consensus/spos/consensusMessageValidator_test.go +++ b/consensus/spos/consensusMessageValidator_test.go @@ -905,7 +905,7 @@ func TestIsMessageTypeLimitReached_ShouldWork(t *testing.T) { cmv.AddMessageTypeToPublicKey([]byte("pk1"), 1, bls.MtBlockHeader) - assert.False(t, cmv.IsMessageTypeLimitReached([]byte("pk1"), 1, bls.MtBlockBody)) + assert.True(t, cmv.IsMessageTypeLimitReached([]byte("pk1"), 1, bls.MtBlockBody)) assert.True(t, cmv.IsMessageTypeLimitReached([]byte("pk1"), 1, bls.MtBlockHeader)) assert.False(t, cmv.IsMessageTypeLimitReached([]byte("pk1"), 2, bls.MtBlockHeader)) } diff --git a/consensus/spos/consensusState.go b/consensus/spos/consensusState.go index 7c136ae8be4..5ef545ac57e 100644 --- a/consensus/spos/consensusState.go +++ b/consensus/spos/consensusState.go @@ -7,9 +7,10 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data" - commonConsensus "github.com/multiversx/mx-chain-go/common/consensus" logger "github.com/multiversx/mx-chain-logger-go" + commonConsensus "github.com/multiversx/mx-chain-go/common/consensus" + "github.com/multiversx/mx-chain-go/consensus" "github.com/multiversx/mx-chain-go/p2p" "github.com/multiversx/mx-chain-go/sharding/nodesCoordinator" @@ -80,7 +81,6 @@ func (cns *ConsensusState) ResetConsensusRoundState() { cns.extendedCalled = false cns.waitingAllSignaturesTimeOut = false cns.mutState.Unlock() - cns.ResetRoundStatus() cns.ResetRoundState() } diff --git a/consensus/spos/errors.go b/consensus/spos/errors.go index 02eb2b36dea..a1b5f69d1d8 100644 --- a/consensus/spos/errors.go +++ b/consensus/spos/errors.go @@ -127,6 +127,9 @@ var ErrInvalidSignature = errors.New("signature is invalid") // ErrInvalidHeader is raised when header is invalid var ErrInvalidHeader = errors.New("header is invalid") +// ErrInvalidBody is raised when body is invalid +var ErrInvalidBody = errors.New("body is invalid") + // ErrMessageFromItself is raised when a message from itself is received var ErrMessageFromItself = errors.New("message is from itself") diff --git a/consensus/spos/export_test.go b/consensus/spos/export_test.go index 6ada6ceccde..3f85da50554 100644 --- a/consensus/spos/export_test.go +++ b/consensus/spos/export_test.go @@ -186,6 +186,11 @@ func (wrk *Worker) SetEnableEpochsHandler(enableEpochsHandler common.EnableEpoch wrk.enableEpochsHandler = enableEpochsHandler } +// AddBlockToPool - +func (wrk *Worker) AddBlockToPool(bodyBytes []byte) { + _ = wrk.addBlockToPool(bodyBytes) +} + // AddFutureHeaderToProcessIfNeeded - func (wrk *Worker) AddFutureHeaderToProcessIfNeeded(header data.HeaderHandler) { wrk.addFutureHeaderToProcessIfNeeded(header) diff --git a/consensus/spos/sposFactory/sposFactory.go b/consensus/spos/sposFactory/sposFactory.go index 6f2900c327b..d8dc5fb93c5 100644 --- a/consensus/spos/sposFactory/sposFactory.go +++ b/consensus/spos/sposFactory/sposFactory.go @@ -34,7 +34,6 @@ func GetBroadcastMessenger( shardCoordinator sharding.Coordinator, peerSignatureHandler crypto.PeerSignatureHandler, headersSubscriber consensus.HeadersPoolSubscriber, - headersPool consensus.HeadersPoolGetter, proofsPool consensus.EquivalentProofsPool, enableEpochsHandler common.EnableEpochsHandler, interceptorsContainer process.InterceptorsContainer, @@ -49,7 +48,6 @@ func GetBroadcastMessenger( dbbArgs := &broadcast.ArgsDelayedBlockBroadcaster{ InterceptorsContainer: interceptorsContainer, HeadersSubscriber: headersSubscriber, - HeadersPool: headersPool, ProofsPool: proofsPool, EnableEpochsHandler: enableEpochsHandler, ShardCoordinator: shardCoordinator, diff --git a/consensus/spos/sposFactory/sposFactory_test.go b/consensus/spos/sposFactory/sposFactory_test.go index fb479babd98..92a08e45107 100644 --- a/consensus/spos/sposFactory/sposFactory_test.go +++ b/consensus/spos/sposFactory/sposFactory_test.go @@ -59,7 +59,6 @@ func TestGetBroadcastMessenger_ShardShouldWork(t *testing.T) { shardCoord, peerSigHandler, headersSubscriber, - headersSubscriber, &dataRetrieverMock.ProofsPoolMock{}, &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, interceptosContainer, @@ -93,7 +92,6 @@ func TestGetBroadcastMessenger_MetachainShouldWork(t *testing.T) { shardCoord, peerSigHandler, headersSubscriber, - headersSubscriber, &dataRetrieverMock.ProofsPoolMock{}, &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, interceptosContainer, @@ -119,7 +117,6 @@ func TestGetBroadcastMessenger_NilShardCoordinatorShouldErr(t *testing.T) { nil, nil, headersSubscriber, - headersSubscriber, &dataRetrieverMock.ProofsPoolMock{}, &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, interceptosContainer, @@ -149,7 +146,6 @@ func TestGetBroadcastMessenger_InvalidShardIdShouldErr(t *testing.T) { shardCoord, nil, headersSubscriber, - headersSubscriber, &dataRetrieverMock.ProofsPoolMock{}, &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, interceptosContainer, diff --git a/consensus/spos/worker.go b/consensus/spos/worker.go index d838838e2db..ce5239087f2 100644 --- a/consensus/spos/worker.go +++ b/consensus/spos/worker.go @@ -84,8 +84,11 @@ type Worker struct { antifloodHandler consensus.P2PAntifloodHandler poolAdder PoolAdder + whiteListHandler process.WhiteListHandler + + cancelFunc func() + mutWorker sync.RWMutex - cancelFunc func() consensusMessageValidator *consensusMessageValidator nodeRedundancyHandler consensus.NodeRedundancyHandler peerBlacklistHandler consensus.PeerBlacklistHandler @@ -117,6 +120,7 @@ type WorkerArgs struct { NetworkShardingCollector consensus.NetworkShardingCollector AntifloodHandler consensus.P2PAntifloodHandler PoolAdder PoolAdder + WhiteListHandler process.WhiteListHandler SignatureSize int PublicKeySize int AppStatusHandler core.AppStatusHandler @@ -178,6 +182,7 @@ func NewWorker(args *WorkerArgs) (*Worker, error) { networkShardingCollector: args.NetworkShardingCollector, antifloodHandler: args.AntifloodHandler, poolAdder: args.PoolAdder, + whiteListHandler: args.WhiteListHandler, nodeRedundancyHandler: args.NodeRedundancyHandler, peerBlacklistHandler: args.PeerBlacklistHandler, closer: closing.NewSafeChanCloser(), @@ -208,7 +213,11 @@ func NewWorker(args *WorkerArgs) (*Worker, error) { // StartWorking actually starts the consensus working mechanism func (wrk *Worker) StartWorking() { var ctx context.Context + + wrk.mutWorker.Lock() ctx, wrk.cancelFunc = context.WithCancel(context.Background()) + wrk.mutWorker.Unlock() + go wrk.checkChannels(ctx) } @@ -276,6 +285,9 @@ func checkNewWorkerParams(args *WorkerArgs) error { if check.IfNil(args.PoolAdder) { return ErrNilPoolAdder } + if check.IfNil(args.WhiteListHandler) { + return process.ErrNilWhiteListHandler + } if check.IfNil(args.AppStatusHandler) { return ErrNilAppStatusHandler } @@ -516,6 +528,11 @@ func (wrk *Worker) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedP return nil, err } + err = wrk.checkValidityAndProcessFinalInfo(cnsMsg, message) + if err != nil { + return nil, err + } + wrk.consensusState.ResetRoundsWithoutReceivedMessages(cnsMsg.GetPubKey(), message.Peer()) if wrk.nodeRedundancyHandler.IsRedundancyNode() { @@ -526,11 +543,6 @@ func (wrk *Worker) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedP ) } - err = wrk.checkValidityAndProcessFinalInfo(cnsMsg, message) - if err != nil { - return nil, err - } - wrk.networkShardingCollector.UpdatePeerIDInfo(message.Peer(), cnsMsg.PubKey, wrk.shardCoordinator.SelfId()) msgType := consensus.MessageType(cnsMsg.MsgType) @@ -540,7 +552,10 @@ func (wrk *Worker) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedP isMessageWithInvalidSigners := wrk.consensusService.IsMessageWithInvalidSigners(msgType) if isMessageWithBlockBody || isMessageWithBlockBodyAndHeader { - wrk.doJobOnMessageWithBlockBody(cnsMsg) + err = wrk.doJobOnMessageWithBlockBody(cnsMsg) + if err != nil { + return nil, err + } } if isMessageWithBlockHeader || isMessageWithBlockBodyAndHeader { @@ -591,8 +606,8 @@ func (wrk *Worker) shouldBlacklistPeer(err error) bool { return true } -func (wrk *Worker) doJobOnMessageWithBlockBody(cnsMsg *consensus.Message) { - wrk.addBlockToPool(cnsMsg.GetBody()) +func (wrk *Worker) doJobOnMessageWithBlockBody(cnsMsg *consensus.Message) error { + return wrk.addBlockToPool(cnsMsg.GetBody()) } func (wrk *Worker) doJobOnMessageWithHeader(cnsMsg *consensus.Message) error { @@ -688,20 +703,35 @@ func (wrk *Worker) doJobOnMessageWithSignature(cnsMsg *consensus.Message, p2pMsg ) } -func (wrk *Worker) addBlockToPool(bodyBytes []byte) { +func (wrk *Worker) addBlockToPool(bodyBytes []byte) error { bodyHandler := wrk.blockProcessor.DecodeBlockBody(bodyBytes) body, ok := bodyHandler.(*block.Body) if !ok { - return + return ErrInvalidBody + } + + for _, miniblock := range body.MiniBlocks { + err := process.CheckMiniBlock(miniblock, wrk.shardCoordinator) + if err != nil { + log.Debug("addBlockToPool: invalid miniblock in received consensus body", "error", err.Error()) + return err + } } for _, miniblock := range body.MiniBlocks { hash, err := core.CalculateHash(wrk.marshalizer, wrk.hasher, miniblock) if err != nil { - return + return err + } + if miniblock.SenderShardID != wrk.shardCoordinator.SelfId() && + !wrk.whiteListHandler.IsWhiteListedAtLeastOne([][]byte{hash}) { + log.Trace("addBlockToPool: skipping non-whitelisted cross-shard mini block", "hash", hash) + continue } wrk.poolAdder.Put(hash, miniblock, miniblock.Size()) } + + return nil } func (wrk *Worker) processReceivedHeaderMetricForConsensusMessage(cnsDta *consensus.Message) { @@ -819,7 +849,11 @@ func (wrk *Worker) checkChannels(ctx context.Context) { msgType := consensus.MessageType(rcvDta.MsgType) - if receivedMessageCallbacks, exist := wrk.receivedMessagesCalls[msgType]; exist { + wrk.mutReceivedMessagesCalls.RLock() + receivedMessageCallbacks, exist := wrk.receivedMessagesCalls[msgType] + wrk.mutReceivedMessagesCalls.RUnlock() + + if exist { for _, callReceivedMessage := range receivedMessageCallbacks { if callReceivedMessage(ctx, rcvDta) { select { @@ -956,9 +990,11 @@ func (wrk *Worker) Close() error { // (just to close some go routines started as edge cases that would otherwise hang) defer wrk.closer.Close() + wrk.mutWorker.RLock() if wrk.cancelFunc != nil { wrk.cancelFunc() } + wrk.mutWorker.RUnlock() wrk.cleanChannels() diff --git a/consensus/spos/worker_internal_test.go b/consensus/spos/worker_internal_test.go new file mode 100644 index 00000000000..b3c56fee041 --- /dev/null +++ b/consensus/spos/worker_internal_test.go @@ -0,0 +1,93 @@ +package spos + +import ( + "bytes" + "testing" + + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/data" + "github.com/multiversx/mx-chain-core-go/data/block" + "github.com/stretchr/testify/require" + + consensusMock "github.com/multiversx/mx-chain-go/consensus/mock" + "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/cache" + "github.com/multiversx/mx-chain-go/testscommon/hashingMocks" +) + +func TestWorker_AddBlockToPoolSkipsNonWhitelistedCrossShardMiniBlocks(t *testing.T) { + t.Parallel() + + miniBlock := &block.MiniBlock{ + SenderShardID: 1, + ReceiverShardID: 0, + Type: block.TxBlock, + TxHashes: [][]byte{[]byte("tx-hash")}, + } + + putCalled := false + worker := &Worker{ + blockProcessor: &testscommon.BlockProcessorStub{ + DecodeBlockBodyCalled: func(_ []byte) data.BodyHandler { + return &block.Body{MiniBlocks: []*block.MiniBlock{miniBlock}} + }, + }, + marshalizer: &consensusMock.MarshalizerMock{}, + hasher: &hashingMocks.HasherMock{}, + shardCoordinator: testscommon.NewMultiShardsCoordinatorMock(2), + whiteListHandler: &testscommon.WhiteListHandlerStub{}, + poolAdder: &cache.CacherStub{ + PutCalled: func(key []byte, value interface{}, sizeInBytes int) (evicted bool) { + putCalled = true + return false + }, + }, + } + + _ = worker.addBlockToPool([]byte("body")) + + require.False(t, putCalled) +} + +func TestWorker_AddBlockToPoolAcceptsWhitelistedCrossShardMiniBlocks(t *testing.T) { + t.Parallel() + + miniBlock := &block.MiniBlock{ + SenderShardID: 1, + ReceiverShardID: 0, + Type: block.TxBlock, + TxHashes: [][]byte{[]byte("tx-hash")}, + } + marshalizer := &consensusMock.MarshalizerMock{} + hasher := &hashingMocks.HasherMock{} + expectedHash, err := core.CalculateHash(marshalizer, hasher, miniBlock) + require.NoError(t, err) + + putCalled := false + worker := &Worker{ + blockProcessor: &testscommon.BlockProcessorStub{ + DecodeBlockBodyCalled: func(_ []byte) data.BodyHandler { + return &block.Body{MiniBlocks: []*block.MiniBlock{miniBlock}} + }, + }, + marshalizer: marshalizer, + hasher: hasher, + shardCoordinator: testscommon.NewMultiShardsCoordinatorMock(2), + whiteListHandler: &testscommon.WhiteListHandlerStub{ + IsWhiteListedAtLeastOneCalled: func(identifiers [][]byte) bool { + return len(identifiers) == 1 && bytes.Equal(identifiers[0], expectedHash) + }, + }, + poolAdder: &cache.CacherStub{ + PutCalled: func(key []byte, value interface{}, sizeInBytes int) (evicted bool) { + putCalled = true + require.True(t, bytes.Equal(expectedHash, key)) + return false + }, + }, + } + + _ = worker.addBlockToPool([]byte("body")) + + require.True(t, putCalled) +} diff --git a/consensus/spos/worker_test.go b/consensus/spos/worker_test.go index 7f6a825ad89..979679941ec 100644 --- a/consensus/spos/worker_test.go +++ b/consensus/spos/worker_test.go @@ -7,6 +7,7 @@ import ( "fmt" "math/big" "strconv" + "sync" "sync/atomic" "testing" "time" @@ -61,7 +62,7 @@ func createDefaultWorkerArgs(appStatusHandler core.AppStatusHandler) *spos.Worke RevertCurrentBlockCalled: func() { }, DecodeBlockBodyCalled: func(dta []byte) data.BodyHandler { - return nil + return &block.Body{} }, } bootstrapperMock := &bootstrapperStubs.BootstrapperStub{} @@ -118,6 +119,7 @@ func createDefaultWorkerArgs(appStatusHandler core.AppStatusHandler) *spos.Worke NetworkShardingCollector: &p2pmocks.NetworkShardingCollectorStub{}, AntifloodHandler: createMockP2PAntifloodHandler(), PoolAdder: poolAdder, + WhiteListHandler: &testscommon.WhiteListHandlerStub{}, SignatureSize: SignatureSize, PublicKeySize: PublicKeySize, AppStatusHandler: appStatusHandler, @@ -414,6 +416,45 @@ func TestWorker_NewWorkerShouldWork(t *testing.T) { assert.False(t, check.IfNil(wrk)) } +func TestWorker_AddBlockToPoolShouldNotAddIfOneInvalidMiniBlock(t *testing.T) { + t.Parallel() + + workerArgs := createDefaultWorkerArgs(&statusHandlerMock.AppStatusHandlerStub{}) + body := &block.Body{ + MiniBlocks: []*block.MiniBlock{ + { + Type: block.TxBlock, + SenderShardID: 0, + ReceiverShardID: 1, + }, + { + Type: block.TxBlock, + SenderShardID: 1, + ReceiverShardID: 0, + }, + // Invalid miniBlock + { + Type: block.TxBlock, + SenderShardID: 1, + ReceiverShardID: 0, + Reserved: bytes.Repeat([]byte{1}, 11), + }, + }, + } + workerArgs.BlockProcessor = &testscommon.BlockProcessorStub{ + DecodeBlockBodyCalled: func(_ []byte) data.BodyHandler { + return body + }, + } + + wrk, err := spos.NewWorker(workerArgs) + require.NoError(t, err) + + wrk.AddBlockToPool(nil) + + require.Equal(t, 0, workerArgs.PoolAdder.(*cache.CacherMock).Len()) +} + func TestWorker_ProcessReceivedMessageShouldErrIfFloodIsDetectedOnTopic(t *testing.T) { t.Parallel() @@ -526,6 +567,7 @@ func TestWorker_RemoveAllReceivedMessageCallsShouldWork(t *testing.T) { func TestWorker_ProcessReceivedMessageTxBlockBodyShouldRetNil(t *testing.T) { t.Parallel() + wrk := *initWorker(&statusHandlerMock.AppStatusHandlerStub{}) blk := &block.Body{} blkStr, _ := mock.MarshalizerMock{}.Marshal(blk) @@ -547,16 +589,79 @@ func TestWorker_ProcessReceivedMessageTxBlockBodyShouldRetNil(t *testing.T) { ) buff, _ := wrk.Marshalizer().Marshal(cnsMsg) time.Sleep(time.Second) + msg := &p2pmocks.P2PMessageMock{ DataField: buff, PeerField: currentPid, SignatureField: []byte("signature"), } + msgID, err := wrk.ProcessReceivedMessage(msg, fromConnectedPeerId, &p2pmocks.MessengerStub{}) assert.Nil(t, err) assert.Len(t, msgID, 0) } +func TestWorker_ProcessReceivedMessage_InvalidBody_ShouldFail(t *testing.T) { + t.Parallel() + + blk := &block.Body{ + MiniBlocks: []*block.MiniBlock{ + &block.MiniBlock{ + SenderShardID: 1, + ReceiverShardID: 2, + Type: block.TxBlock, + }, + &block.MiniBlock{ + SenderShardID: 1, // invalid sender shard id + ReceiverShardID: 0, + Type: block.RewardsBlock, + }, + }, + } + blkStr, _ := mock.MarshalizerMock{}.Marshal(blk) + + blockProcessor := &testscommon.BlockProcessorStub{ + DecodeBlockBodyCalled: func(dta []byte) data.BodyHandler { + return blk + }, + } + + workerArgs := createDefaultWorkerArgs(&statusHandlerMock.AppStatusHandlerStub{}) + workerArgs.BlockProcessor = blockProcessor + wrk, _ := spos.NewWorker(workerArgs) + + wrk.ConsensusState().SetHeader(&block.HeaderV2{}) + + cnsMsg := consensus.NewConsensusMessage( + nil, + nil, + blkStr, + nil, + []byte(wrk.ConsensusState().ConsensusGroup()[0]), + signature, + int(bls.MtBlockBody), + 0, + chainID, + nil, + nil, + nil, + currentPid, + nil, + ) + buff, _ := wrk.Marshalizer().Marshal(cnsMsg) + time.Sleep(time.Second) + + msg := &p2pmocks.P2PMessageMock{ + DataField: buff, + PeerField: currentPid, + SignatureField: []byte("signature"), + } + + msgID, err := wrk.ProcessReceivedMessage(msg, fromConnectedPeerId, &p2pmocks.MessengerStub{}) + assert.ErrorIs(t, err, process.ErrInvalidShardId) + assert.Len(t, msgID, 0) +} + func TestWorker_ProcessReceivedMessageNilMessageShouldErr(t *testing.T) { t.Parallel() wrk := *initWorker(&statusHandlerMock.AppStatusHandlerStub{}) @@ -609,10 +714,30 @@ func TestWorker_ProcessReceivedMessageRedundancyNodeShouldResetInactivityIfNeede }, } wrk.SetNodeRedundancyHandler(nodeRedundancyMock) - buff, _ := wrk.Marshalizer().Marshal(&consensus.Message{}) + hdr := &block.Header{ChainID: chainID} + hdrHash, _ := core.CalculateHash(mock.MarshalizerMock{}, &hashingMocks.HasherMock{}, hdr) + hdrStr, _ := mock.MarshalizerMock{}.Marshal(hdr) + cnsMsg := consensus.NewConsensusMessage( + hdrHash, + nil, + nil, + hdrStr, + []byte(wrk.ConsensusState().ConsensusGroup()[0]), + signature, + int(bls.MtBlockHeader), + 0, + chainID, + nil, + nil, + nil, + currentPid, + nil, + ) + buff, _ := wrk.Marshalizer().Marshal(cnsMsg) _, _ = wrk.ProcessReceivedMessage( &p2pmocks.P2PMessageMock{ DataField: buff, + PeerField: currentPid, SignatureField: []byte("signature"), }, fromConnectedPeerId, @@ -2424,3 +2549,65 @@ func TestWorker_NewWorkerNilConsensusMetrics(t *testing.T) { require.Error(t, err) // should come from NewConsensusMetrics require.Equal(t, spos.ErrNilAppStatusHandler, err) } + +func TestWorker_Concurrency(t *testing.T) { + t.Parallel() + + workerArgs := createDefaultWorkerArgs(&statusHandlerMock.AppStatusHandlerStub{}) + wrk, _ := spos.NewWorker(workerArgs) + + wg := sync.WaitGroup{} + + numOperations := 500 + wg.Add(numOperations) + + for i := 0; i < numOperations; i++ { + go func(idx int) { + switch idx { + case 0: + wrk.AddReceivedHeaderHandler(func(handler data.HeaderHandler) {}) + case 1: + wrk.AddReceivedMessageCall(bls.MtBlockBody, nil) + case 2: + wrk.AddReceivedProofHandler(func(proof consensus.ProofHandler) {}) + case 3: + _ = wrk.Close() + case 4: + wrk.DisplayStatistics() + case 5: + wrk.ExecuteStoredMessages() + case 6: + wrk.Extend(0) + case 7: + _ = wrk.GetConsensusStateChangedChannel() + case 8: + _, _ = wrk.ProcessReceivedMessage(&p2pmocks.P2PMessageMock{}, fromConnectedPeerId, &p2pmocks.MessengerStub{}) + case 9: + wrk.ReceivedHeader(&block.Header{ + ShardID: workerArgs.ShardCoordinator.SelfId(), + Round: uint64(workerArgs.RoundHandler.Index()), + }, nil) + case 10: + wrk.ReceivedProof(&block.HeaderProof{}) + case 11: + wrk.RemoveAllReceivedHeaderHandlers() + case 12: + wrk.RemoveAllReceivedMessagesCalls() + case 13: + wrk.ResetConsensusMessages() + case 14: + wrk.ResetConsensusRoundState() + case 15: + wrk.ResetInvalidSignersCache() + case 16: + wrk.StartWorking() + default: + require.Fail(t, "should have not been called") + } + + wg.Done() + }(i % 17) + } + + wg.Wait() +} diff --git a/dataRetriever/factory/requestersContainer/shardRequestersContainerFactory.go b/dataRetriever/factory/requestersContainer/shardRequestersContainerFactory.go index 014cef057c3..31607ca3a0b 100644 --- a/dataRetriever/factory/requestersContainer/shardRequestersContainerFactory.go +++ b/dataRetriever/factory/requestersContainer/shardRequestersContainerFactory.go @@ -93,6 +93,11 @@ func (srcf *shardRequestersContainerFactory) Create() (dataRetriever.RequestersC return srcf.container, nil } +// AddShardTrieNodeRequesters returns nil +func (srcf *shardRequestersContainerFactory) AddShardTrieNodeRequesters(_ dataRetriever.RequestersContainer) error { + return nil +} + func (srcf *shardRequestersContainerFactory) generateHeaderRequesters() error { shardC := srcf.shardCoordinator diff --git a/dataRetriever/factory/resolverscontainer/disabled/resolversContainerFactory.go b/dataRetriever/factory/resolverscontainer/disabled/resolversContainerFactory.go index e96ebc8c5f0..7d7b7b3abd6 100644 --- a/dataRetriever/factory/resolverscontainer/disabled/resolversContainerFactory.go +++ b/dataRetriever/factory/resolverscontainer/disabled/resolversContainerFactory.go @@ -18,6 +18,11 @@ func (rcf *resolversContainerFactory) Create() (dataRetriever.ResolversContainer return disabled.NewDisabledResolversContainer(), nil } +// AddShardTrieNodeResolvers returns nil as it is disabled +func (rcf *resolversContainerFactory) AddShardTrieNodeResolvers(_ dataRetriever.ResolversContainer) error { + return nil +} + // IsInterfaceNil returns true if there is no value under the interface func (rcf *resolversContainerFactory) IsInterfaceNil() bool { return rcf == nil diff --git a/dataRetriever/factory/resolverscontainer/shardResolversContainerFactory.go b/dataRetriever/factory/resolverscontainer/shardResolversContainerFactory.go index 6fc86dd2426..deadddea5fb 100644 --- a/dataRetriever/factory/resolverscontainer/shardResolversContainerFactory.go +++ b/dataRetriever/factory/resolverscontainer/shardResolversContainerFactory.go @@ -144,6 +144,11 @@ func (srcf *shardResolversContainerFactory) Create() (dataRetriever.ResolversCon return srcf.container, nil } +// AddShardTrieNodeResolvers returns nil +func (srcf *shardResolversContainerFactory) AddShardTrieNodeResolvers(_ dataRetriever.ResolversContainer) error { + return nil +} + // ------- Hdr resolver func (srcf *shardResolversContainerFactory) generateHeaderResolvers() error { diff --git a/dataRetriever/factory/storageRequestersContainer/metaRequestersContainerFactory.go b/dataRetriever/factory/storageRequestersContainer/metaRequestersContainerFactory.go index e430ff170dc..5d3c947d930 100644 --- a/dataRetriever/factory/storageRequestersContainer/metaRequestersContainerFactory.go +++ b/dataRetriever/factory/storageRequestersContainer/metaRequestersContainerFactory.go @@ -83,6 +83,11 @@ func (mrcf *metaRequestersContainerFactory) Create() (dataRetriever.RequestersCo return mrcf.container, nil } +// AddShardTrieNodeRequesters returns nil +func (mrcf *metaRequestersContainerFactory) AddShardTrieNodeRequesters(_ dataRetriever.RequestersContainer) error { + return nil +} + func (mrcf *metaRequestersContainerFactory) generateShardHeaderRequesters() error { shardC := mrcf.shardCoordinator noOfShards := shardC.NumberOfShards() diff --git a/dataRetriever/factory/storageRequestersContainer/shardRequestersContainerFactory.go b/dataRetriever/factory/storageRequestersContainer/shardRequestersContainerFactory.go index 2380a6380cd..bfbd20c9497 100644 --- a/dataRetriever/factory/storageRequestersContainer/shardRequestersContainerFactory.go +++ b/dataRetriever/factory/storageRequestersContainer/shardRequestersContainerFactory.go @@ -83,6 +83,11 @@ func (srcf *shardRequestersContainerFactory) Create() (dataRetriever.RequestersC return srcf.container, nil } +// AddShardTrieNodeRequesters returns nil +func (srcf *shardRequestersContainerFactory) AddShardTrieNodeRequesters(_ dataRetriever.RequestersContainer) error { + return nil +} + func (srcf *shardRequestersContainerFactory) generateHeaderRequesters() error { shardC := srcf.shardCoordinator diff --git a/dataRetriever/interface.go b/dataRetriever/interface.go index 9d0ef822ad5..cae03aef65f 100644 --- a/dataRetriever/interface.go +++ b/dataRetriever/interface.go @@ -87,6 +87,7 @@ type RequestersFinder interface { // ResolversContainerFactory defines the functionality to create a resolvers container type ResolversContainerFactory interface { Create() (ResolversContainer, error) + AddShardTrieNodeResolvers(container ResolversContainer) error IsInterfaceNil() bool } @@ -119,6 +120,7 @@ type RequestersContainer interface { // RequestersContainerFactory defines the functionality to create a requesters container type RequestersContainerFactory interface { Create() (RequestersContainer, error) + AddShardTrieNodeRequesters(container RequestersContainer) error IsInterfaceNil() bool } @@ -176,7 +178,9 @@ type ShardedDataCacherNotifier interface { SearchFirstData(key []byte) (value interface{}, ok bool) RemoveData(key []byte, cacheId string) RemoveSetOfDataFromPool(keys [][]byte, cacheId string) - ImmunizeSetOfDataAgainstEviction(keys [][]byte, cacheId string) + ImmunizeSetOfDataAgainstEviction(keys [][]byte, cacheId string, nonce uint64) + SetOldestImmuneNonce(cacheId string, nonce uint64) + SetOldestImmuneNonceForAllCaches(nonce uint64) RemoveDataFromAllShards(key []byte) MergeShardStores(sourceCacheID, destCacheID string) Clear() diff --git a/dataRetriever/requestHandlers/requestHandler.go b/dataRetriever/requestHandlers/requestHandler.go index 819469d4426..587e46016f2 100644 --- a/dataRetriever/requestHandlers/requestHandler.go +++ b/dataRetriever/requestHandlers/requestHandler.go @@ -860,11 +860,18 @@ func (rrh *resolverRequestHandler) IsInterfaceNil() bool { func (rrh *resolverRequestHandler) getUnrequestedHashes(hashes [][]byte, suffix string) [][]byte { unrequestedHashes := make([][]byte, 0) + seen := make(map[string]struct{}, len(hashes)) rrh.sweepIfNeeded() for _, hash := range hashes { - if !rrh.requestedItemsHandler.Has(string(hash) + suffix) { + key := string(hash) + suffix + if _, alreadySeen := seen[key]; alreadySeen { + continue + } + seen[key] = struct{}{} + + if !rrh.requestedItemsHandler.Has(key) { unrequestedHashes = append(unrequestedHashes, hash) } } @@ -940,6 +947,14 @@ func (rrh *resolverRequestHandler) RequestPeerAuthenticationsByHashesForEpoch(de return } + identifiers := make([][]byte, 0, len(hashes)) + for _, hash := range hashes { + identifier := common.PeerAuthenticationPublicKeyIdentifier(hash) + identifiers = append(identifiers, identifier) + } + + rrh.whiteList.Add(identifiers) + err = peerAuthRequester.RequestDataFromHashArray(hashes, epoch) if err != nil { log.Debug("RequestPeerAuthenticationsByHashesForEpoch.RequestDataFromHashArray", diff --git a/dataRetriever/requestHandlers/requestHandler_test.go b/dataRetriever/requestHandlers/requestHandler_test.go index e3b96000e90..3d838589754 100644 --- a/dataRetriever/requestHandlers/requestHandler_test.go +++ b/dataRetriever/requestHandlers/requestHandler_test.go @@ -1755,10 +1755,13 @@ func TestResolverRequestHandler_RequestPeerAuthenticationsByHashes(t *testing.T) }() wasCalled := false + wasWhitelisted := false + longPublicKey := bytes.Repeat([]byte("p"), common.MaxPeerAuthenticationPublicKeyIdentifierLen+8) + providedHashesForTest := [][]byte{longPublicKey, []byte("h2")} paRequester := &dataRetrieverMocks.HashSliceRequesterStub{ RequestDataFromHashArrayCalled: func(hashes [][]byte, epoch uint32) error { wasCalled = true - assert.Equal(t, providedHashes, hashes) + assert.Equal(t, providedHashesForTest, hashes) return nil }, } @@ -1770,15 +1773,21 @@ func TestResolverRequestHandler_RequestPeerAuthenticationsByHashes(t *testing.T) }, }, &mock.RequestedItemsHandlerStub{}, - &mock.WhiteListHandlerStub{}, + &mock.WhiteListHandlerStub{ + AddCalled: func(keys [][]byte) { + wasWhitelisted = true + assert.Equal(t, [][]byte{longPublicKey[:common.MaxPeerAuthenticationPublicKeyIdentifierLen], []byte("h2")}, keys) + }, + }, 1, 0, time.Second, time.Millisecond, ) - rrh.RequestPeerAuthenticationsByHashes(providedShardId, providedHashes) + rrh.RequestPeerAuthenticationsByHashes(providedShardId, providedHashesForTest) assert.True(t, wasCalled) + assert.True(t, wasWhitelisted) }) } @@ -2155,6 +2164,38 @@ func TestResolverRequestHandler_RequestMiniblocks(t *testing.T) { rrh.RequestMiniBlocks(0, [][]byte{[]byte("mbHash")}) }) + t.Run("should deduplicate hashes within the same batch", func(t *testing.T) { + t.Parallel() + + duplicateHash := []byte("mbHash") + numCalls := uint32(0) + var receivedHashes [][]byte + mbRequester := &dataRetrieverMocks.HashSliceRequesterStub{ + RequestDataFromHashArrayCalled: func(hashes [][]byte, epoch uint32) error { + atomic.AddUint32(&numCalls, 1) + receivedHashes = hashes + return nil + }, + } + rrh, _ := NewResolverRequestHandler( + &dataRetrieverMocks.RequestersFinderStub{ + CrossShardRequesterCalled: func(baseTopic string, crossShard uint32) (dataRetriever.Requester, error) { + return mbRequester, nil + }, + }, + &mock.RequestedItemsHandlerStub{}, + &mock.WhiteListHandlerStub{}, + 100, + 0, + time.Second, + time.Millisecond, + ) + + rrh.RequestMiniBlocks(0, [][]byte{duplicateHash, duplicateHash, duplicateHash}) + assert.Equal(t, uint32(1), atomic.LoadUint32(&numCalls)) + require.Len(t, receivedHashes, 1) + assert.Equal(t, duplicateHash, receivedHashes[0]) + }) } func TestResolverRequestHandler_RequestInterval(t *testing.T) { diff --git a/dataRetriever/resolvers/equivalentProofsResolver.go b/dataRetriever/resolvers/equivalentProofsResolver.go index c36c3e9ac92..b2e51da87b4 100644 --- a/dataRetriever/resolvers/equivalentProofsResolver.go +++ b/dataRetriever/resolvers/equivalentProofsResolver.go @@ -2,6 +2,8 @@ package resolvers import ( "fmt" + "runtime/debug" + "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/batch" @@ -90,8 +92,15 @@ func checkArgEquivalentProofsResolver(args ArgEquivalentProofsResolver) error { // ProcessReceivedMessage represents the callback func from the p2p.Messenger that is called each time a new message is received // (for the topic this validator was registered to, usually a request topic) -func (res *equivalentProofsResolver) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedPeer core.PeerID, source p2p.MessageHandler) ([]byte, error) { - err := res.canProcessMessage(message, fromConnectedPeer) +func (res *equivalentProofsResolver) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedPeer core.PeerID, source p2p.MessageHandler) (msg []byte, err error) { + defer func() { + if r := recover(); r != nil { + logTrieNodes.Error("panic recovered", "peer", fromConnectedPeer, "panic", r, "stack", string(debug.Stack())) + err = fmt.Errorf("panic in equivalentProofsResolver.ProcessReceivedMessage: %v", r) + } + }() + + err = res.canProcessMessage(message, fromConnectedPeer) if err != nil { return nil, err } diff --git a/dataRetriever/resolvers/headerResolver.go b/dataRetriever/resolvers/headerResolver.go index dbd8626bf3a..99eb50dbde0 100644 --- a/dataRetriever/resolvers/headerResolver.go +++ b/dataRetriever/resolvers/headerResolver.go @@ -1,12 +1,14 @@ package resolvers import ( + "fmt" + "runtime/debug" "sync" "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/typeConverters" - "github.com/multiversx/mx-chain-logger-go" + logger "github.com/multiversx/mx-chain-logger-go" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/dataRetriever/resolvers/epochproviders/disabled" @@ -110,8 +112,15 @@ func (hdrRes *HeaderResolver) SetEpochHandler(epochHandler dataRetriever.EpochHa // ProcessReceivedMessage will be the callback func from the p2p.Messenger and will be called each time a new message was received // (for the topic this validator was registered to, usually a request topic) -func (hdrRes *HeaderResolver) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedPeer core.PeerID, source p2p.MessageHandler) ([]byte, error) { - err := hdrRes.canProcessMessage(message, fromConnectedPeer) +func (hdrRes *HeaderResolver) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedPeer core.PeerID, source p2p.MessageHandler) (msg []byte, err error) { + defer func() { + if r := recover(); r != nil { + logTrieNodes.Error("panic recovered", "peer", fromConnectedPeer, "panic", r, "stack", string(debug.Stack())) + err = fmt.Errorf("panic in HeaderResolver.ProcessReceivedMessage: %v", r) + } + }() + + err = hdrRes.canProcessMessage(message, fromConnectedPeer) if err != nil { return nil, err } diff --git a/dataRetriever/resolvers/miniblockResolver.go b/dataRetriever/resolvers/miniblockResolver.go index 3fb74105af5..0909a2efb47 100644 --- a/dataRetriever/resolvers/miniblockResolver.go +++ b/dataRetriever/resolvers/miniblockResolver.go @@ -2,6 +2,7 @@ package resolvers import ( "fmt" + "runtime/debug" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" @@ -78,8 +79,15 @@ func checkArgMiniblockResolver(arg ArgMiniblockResolver) error { // ProcessReceivedMessage will be the callback func from the p2p.Messenger and will be called each time a new message was received // (for the topic this validator was registered to, usually a request topic) -func (mbRes *miniblockResolver) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedPeer core.PeerID, source p2p.MessageHandler) ([]byte, error) { - err := mbRes.canProcessMessage(message, fromConnectedPeer) +func (mbRes *miniblockResolver) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedPeer core.PeerID, source p2p.MessageHandler) (msg []byte, err error) { + defer func() { + if r := recover(); r != nil { + logTrieNodes.Error("panic recovered", "peer", fromConnectedPeer, "panic", r, "stack", string(debug.Stack())) + err = fmt.Errorf("panic in miniblockResolver.ProcessReceivedMessage: %v", r) + } + }() + + err = mbRes.canProcessMessage(message, fromConnectedPeer) if err != nil { return nil, err } diff --git a/dataRetriever/resolvers/peerAuthenticationResolver.go b/dataRetriever/resolvers/peerAuthenticationResolver.go index 49f29ff0246..14091ca7698 100644 --- a/dataRetriever/resolvers/peerAuthenticationResolver.go +++ b/dataRetriever/resolvers/peerAuthenticationResolver.go @@ -2,6 +2,7 @@ package resolvers import ( "fmt" + "runtime/debug" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" @@ -76,8 +77,15 @@ func checkArgPeerAuthenticationResolver(arg ArgPeerAuthenticationResolver) error // ProcessReceivedMessage represents the callback func from the p2p.Messenger that is called each time a new message is received // (for the topic this validator was registered to, usually a request topic) -func (res *peerAuthenticationResolver) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedPeer core.PeerID, source p2p.MessageHandler) ([]byte, error) { - err := res.canProcessMessage(message, fromConnectedPeer) +func (res *peerAuthenticationResolver) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedPeer core.PeerID, source p2p.MessageHandler) (msg []byte, err error) { + defer func() { + if r := recover(); r != nil { + logTrieNodes.Error("panic recovered", "peer", fromConnectedPeer, "panic", r, "stack", string(debug.Stack())) + err = fmt.Errorf("panic in peerAuthenticationResolver.ProcessReceivedMessage: %v", r) + } + }() + + err = res.canProcessMessage(message, fromConnectedPeer) if err != nil { return nil, err } diff --git a/dataRetriever/resolvers/transactionResolver.go b/dataRetriever/resolvers/transactionResolver.go index 8495c970a70..4d79277f260 100644 --- a/dataRetriever/resolvers/transactionResolver.go +++ b/dataRetriever/resolvers/transactionResolver.go @@ -2,6 +2,7 @@ package resolvers import ( "fmt" + "runtime/debug" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" @@ -83,8 +84,15 @@ func checkArgTxResolver(arg ArgTxResolver) error { // ProcessReceivedMessage will be the callback func from the p2p.Messenger and will be called each time a new message was received // (for the topic this validator was registered to, usually a request topic) -func (txRes *TxResolver) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedPeer core.PeerID, source p2p.MessageHandler) ([]byte, error) { - err := txRes.canProcessMessage(message, fromConnectedPeer) +func (txRes *TxResolver) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedPeer core.PeerID, source p2p.MessageHandler) (msg []byte, err error) { + defer func() { + if r := recover(); r != nil { + logTrieNodes.Error("panic recovered", "peer", fromConnectedPeer, "panic", r, "stack", string(debug.Stack())) + err = fmt.Errorf("panic in TxResolver.ProcessReceivedMessage: %v", r) + } + }() + + err = txRes.canProcessMessage(message, fromConnectedPeer) if err != nil { return nil, err } diff --git a/dataRetriever/resolvers/trieNodeResolver.go b/dataRetriever/resolvers/trieNodeResolver.go index 78ed24d0159..721105b64d7 100644 --- a/dataRetriever/resolvers/trieNodeResolver.go +++ b/dataRetriever/resolvers/trieNodeResolver.go @@ -1,6 +1,8 @@ package resolvers import ( + "fmt" + "runtime/debug" "sync" "github.com/multiversx/mx-chain-core-go/core" @@ -63,8 +65,15 @@ func checkArgTrieNodeResolver(arg ArgTrieNodeResolver) error { // ProcessReceivedMessage will be the callback func from the p2p.Messenger and will be called each time a new message was received // (for the topic this validator was registered to, usually a request topic) -func (tnRes *TrieNodeResolver) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedPeer core.PeerID, source p2p.MessageHandler) ([]byte, error) { - err := tnRes.canProcessMessage(message, fromConnectedPeer) +func (tnRes *TrieNodeResolver) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedPeer core.PeerID, source p2p.MessageHandler) (msg []byte, err error) { + defer func() { + if r := recover(); r != nil { + logTrieNodes.Error("panic recovered", "peer", fromConnectedPeer, "panic", r, "stack", string(debug.Stack())) + err = fmt.Errorf("panic in TrieNodeResolver.ProcessReceivedMessage: %v", r) + } + }() + + err = tnRes.canProcessMessage(message, fromConnectedPeer) if err != nil { return nil, err } diff --git a/dataRetriever/resolvers/trieNodeResolver_test.go b/dataRetriever/resolvers/trieNodeResolver_test.go index b988b2f2959..6347f02875a 100644 --- a/dataRetriever/resolvers/trieNodeResolver_test.go +++ b/dataRetriever/resolvers/trieNodeResolver_test.go @@ -587,6 +587,57 @@ func TestTrieNodeResolver_ProcessReceivedMessageLargeTrieNodeShouldSendFirstChun testTrieNodeResolverProcessReceivedMessageLargeTrieNode(t, randBuff, 0, 4, 0, core.MaxBufferSizeToSendTrieNodes) } +func TestTrieNodeResolver_ProcessReceivedMessageLargeTrieNodeMaxChunkIndex(t *testing.T) { + t.Parallel() + + largeBuffer := make([]byte, 393216) // 256k + 128k + chunkIndex := uint32(2) + + nodes := [][]byte{largeBuffer} + hashes := [][]byte{[]byte("hash1")} + + sendWasCalled := false + arg := createMockArgTrieNodeResolver() + arg.SenderResolver = &mock.TopicResolverSenderStub{ + SendCalled: func(buff []byte, peer core.PeerID, source p2p.MessageHandler) error { + sendWasCalled = true + return nil + }, + } + arg.TrieDataGetter = &trieMock.TrieStub{ + GetSerializedNodeCalled: func(hash []byte) ([]byte, error) { + for i := 0; i < len(hashes); i++ { + if bytes.Equal(hash, hashes[i]) { + return nodes[i], nil + } + } + + return nil, fmt.Errorf("not found") + }, + GetSerializedNodesCalled: func(i []byte, u uint64) ([][]byte, uint64, error) { + return make([][]byte, 0), 0, nil + }, + } + tnRes, _ := resolvers.NewTrieNodeResolver(arg) + + data, _ := arg.Marshaller.Marshal( + &dataRetriever.RequestData{ + Type: dataRetriever.HashType, + Value: []byte("hash1"), + ChunkIndex: chunkIndex, + }, + ) + msg := &p2pmocks.P2PMessageMock{DataField: data} + + msgID, err := tnRes.ProcessReceivedMessage(msg, fromConnectedPeer, &p2pmocks.MessengerStub{}) + assert.Nil(t, err) + require.False(t, sendWasCalled) + assert.Len(t, msgID, 0) + + assert.True(t, arg.Throttler.(*mock.ThrottlerStub).StartWasCalled()) + assert.True(t, arg.Throttler.(*mock.ThrottlerStub).EndWasCalled()) +} + func TestTrieNodeResolver_ProcessReceivedMessageLargeTrieNodeShouldSendRequiredChunk(t *testing.T) { t.Parallel() diff --git a/dataRetriever/resolvers/validatorInfoResolver.go b/dataRetriever/resolvers/validatorInfoResolver.go index 65255b8ad8f..f3cb291abef 100644 --- a/dataRetriever/resolvers/validatorInfoResolver.go +++ b/dataRetriever/resolvers/validatorInfoResolver.go @@ -3,6 +3,7 @@ package resolvers import ( "encoding/hex" "fmt" + "runtime/debug" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" @@ -90,8 +91,15 @@ func checkArgs(args ArgValidatorInfoResolver) error { // ProcessReceivedMessage represents the callback func from the p2p.Messenger that is called each time a new message is received // (for the topic this validator was registered to, usually a request topic) -func (res *validatorInfoResolver) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedPeer core.PeerID, source p2p.MessageHandler) ([]byte, error) { - err := res.canProcessMessage(message, fromConnectedPeer) +func (res *validatorInfoResolver) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedPeer core.PeerID, source p2p.MessageHandler) (msg []byte, err error) { + defer func() { + if r := recover(); r != nil { + logTrieNodes.Error("panic recovered", "peer", fromConnectedPeer, "panic", r, "stack", string(debug.Stack())) + err = fmt.Errorf("panic in validatorInfoResolver.ProcessReceivedMessage: %v", r) + } + }() + + err = res.canProcessMessage(message, fromConnectedPeer) if err != nil { return nil, err } diff --git a/dataRetriever/shardedData/interface.go b/dataRetriever/shardedData/interface.go index 75fd0181094..fa9f845d393 100644 --- a/dataRetriever/shardedData/interface.go +++ b/dataRetriever/shardedData/interface.go @@ -6,7 +6,8 @@ import ( type immunityCache interface { storage.Cacher - ImmunizeKeys(keys [][]byte) (numNowTotal, numFutureTotal int) + ImmunizeKeys(keys [][]byte, nonce uint64) (numNowTotal, numFutureTotal int) + SetOldestImmuneNonce(nonce uint64) RemoveWithResult(key []byte) bool NumBytes() int Diagnose(deep bool) diff --git a/dataRetriever/shardedData/shardedData.go b/dataRetriever/shardedData/shardedData.go index b0345a3badd..e08a9f9df5c 100644 --- a/dataRetriever/shardedData/shardedData.go +++ b/dataRetriever/shardedData/shardedData.go @@ -188,11 +188,33 @@ func (sd *shardedData) RemoveSetOfDataFromPool(keys [][]byte, cacheID string) { ) } -// ImmunizeSetOfDataAgainstEviction marks the items as non-evictable -func (sd *shardedData) ImmunizeSetOfDataAgainstEviction(keys [][]byte, cacheID string) { +// ImmunizeSetOfDataAgainstEviction marks the items as non-evictable for the provided confirmation nonce +func (sd *shardedData) ImmunizeSetOfDataAgainstEviction(keys [][]byte, cacheID string, nonce uint64) { store := sd.getOrCreateShardStoreWithLock(cacheID) - numNow, numFuture := store.cache.ImmunizeKeys(keys) - log.Trace("shardedData.ImmunizeSetOfDataAgainstEviction()", "name", sd.name, "cacheID", cacheID, "len(keys)", len(keys), "numNow", numNow, "numFuture", numFuture) + numNow, numFuture := store.cache.ImmunizeKeys(keys, nonce) + log.Trace("shardedData.ImmunizeSetOfDataAgainstEviction()", "name", sd.name, "cacheID", cacheID, "len(keys)", len(keys), "numNow", numNow, "numFuture", numFuture, "nonce", nonce) +} + +// SetOldestImmuneNonce deactivates immunity below the provided nonce +func (sd *shardedData) SetOldestImmuneNonce(cacheID string, nonce uint64) { + store := sd.shardStore(cacheID) + if store == nil { + return + } + + store.cache.SetOldestImmuneNonce(nonce) +} + +// SetOldestImmuneNonceForAllCaches deactivates immunity below the provided nonce +// on every backing shard store. Called from the shard's commit path once the +// cross-notarized metablock has advanced. +func (sd *shardedData) SetOldestImmuneNonceForAllCaches(nonce uint64) { + sd.mutShardedDataStore.RLock() + defer sd.mutShardedDataStore.RUnlock() + + for _, store := range sd.shardedDataStore { + store.cache.SetOldestImmuneNonce(nonce) + } } // RemoveData will remove data hash from the corresponding shard store diff --git a/dataRetriever/shardedData/shardedData_test.go b/dataRetriever/shardedData/shardedData_test.go index 1b8332d8aa2..45ccd29fbbb 100644 --- a/dataRetriever/shardedData/shardedData_test.go +++ b/dataRetriever/shardedData/shardedData_test.go @@ -10,9 +10,11 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data/transaction" - "github.com/multiversx/mx-chain-go/storage/storageunit" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/multiversx/mx-chain-go/storage/storageunit" + cacheStubs "github.com/multiversx/mx-chain-go/testscommon/cache" ) var timeoutWaitForWaitGroups = time.Second * 2 @@ -331,7 +333,38 @@ func TestShardedData_ImmunizeSetOfDataAgainstEviction(t *testing.T) { t.Parallel() sd, _ := NewShardedData("", defaultTestConfig) - sd.ImmunizeSetOfDataAgainstEviction([][]byte{[]byte("aaa")}, "0") + sd.ImmunizeSetOfDataAgainstEviction([][]byte{[]byte("aaa")}, "0", 7) + sd.SetOldestImmuneNonce("0", 7) +} + +func TestShardedData_SetOldestImmuneNonceForAllCaches(t *testing.T) { + t.Parallel() + + sd, _ := NewShardedData("", defaultTestConfig) + + cacheIDs := []string{"0", "1", "2_0"} + received := make(map[string]uint64) + var mu sync.Mutex + for _, id := range cacheIDs { + idCopy := id + spy := &cacheStubs.ImmunityCacheSpy{ + CacherStub: cacheStubs.NewCacherStub(), + SetOldestImmuneNonceCalled: func(nonce uint64) { + mu.Lock() + received[idCopy] = nonce + mu.Unlock() + }, + } + sd.shardedDataStore[id] = &shardStore{cacheID: id, cache: spy} + } + + sd.SetOldestImmuneNonceForAllCaches(42) + + assert.Equal(t, len(cacheIDs), len(sd.shardedDataStore), "no new stores should be created") + assert.Equal(t, len(cacheIDs), len(received), "every store should receive the threshold once") + for _, id := range cacheIDs { + assert.Equal(t, uint64(42), received[id], "store %s did not receive the threshold", id) + } } func TestShardedData_GetCounts(t *testing.T) { diff --git a/dataRetriever/txpool/interface.go b/dataRetriever/txpool/interface.go index 3b214a3dc08..3e70f2da1db 100644 --- a/dataRetriever/txpool/interface.go +++ b/dataRetriever/txpool/interface.go @@ -17,7 +17,8 @@ type txCache interface { AddTx(tx *txcache.WrappedTransaction) (ok bool, added bool) GetByTxHash(txHash []byte) (*txcache.WrappedTransaction, bool) RemoveTxByHash(txHash []byte) bool - ImmunizeTxsAgainstEviction(keys [][]byte) + ImmunizeTxsAgainstEviction(keys [][]byte, nonce uint64) + SetOldestImmuneNonce(nonce uint64) ForEachTransaction(function txcache.ForEachTransaction) NumBytes() int Diagnose(deep bool) diff --git a/dataRetriever/txpool/memorytests/memory_test.go b/dataRetriever/txpool/memorytests/memory_test.go index 727cdbdca72..1a899f742ab 100644 --- a/dataRetriever/txpool/memorytests/memory_test.go +++ b/dataRetriever/txpool/memorytests/memory_test.go @@ -13,11 +13,13 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-core-go/marshal" + "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/dataRetriever/txpool" "github.com/multiversx/mx-chain-go/storage/storageunit" - "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" + "github.com/stretchr/testify/require" ) @@ -114,7 +116,7 @@ func newPool() dataRetriever.ShardedDataCacherNotifier { args := txpool.ArgShardedTxPool{ Config: cacheConfig, - TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + TxGasHandler: mempool.NewTxGasHandlerMock(), Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 2, SelfShardID: 0, diff --git a/dataRetriever/txpool/mempoolHost_test.go b/dataRetriever/txpool/mempoolHost_test.go index a013a88fa19..18f3baf155d 100644 --- a/dataRetriever/txpool/mempoolHost_test.go +++ b/dataRetriever/txpool/mempoolHost_test.go @@ -9,9 +9,11 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-core-go/marshal" + "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/testscommon" - "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" + "github.com/stretchr/testify/require" ) @@ -26,14 +28,14 @@ func TestNewMempoolHost(t *testing.T) { require.ErrorIs(t, err, dataRetriever.ErrNilTxGasHandler) host, err = newMempoolHost(argsMempoolHost{ - txGasHandler: txcachemocks.NewTxGasHandlerMock(), + txGasHandler: mempool.NewTxGasHandlerMock(), marshalizer: nil, }) require.Nil(t, host) require.ErrorIs(t, err, dataRetriever.ErrNilMarshalizer) host, err = newMempoolHost(argsMempoolHost{ - txGasHandler: txcachemocks.NewTxGasHandlerMock(), + txGasHandler: mempool.NewTxGasHandlerMock(), marshalizer: &marshal.GogoProtoMarshalizer{}, }) require.NoError(t, err) @@ -44,7 +46,7 @@ func TestMempoolHost_GetTransferredValue(t *testing.T) { t.Parallel() host, err := newMempoolHost(argsMempoolHost{ - txGasHandler: txcachemocks.NewTxGasHandlerMock(), + txGasHandler: mempool.NewTxGasHandlerMock(), marshalizer: &marshal.GogoProtoMarshalizer{}, }) require.NoError(t, err) @@ -86,7 +88,7 @@ func TestMempoolHost_GetTransferredValue(t *testing.T) { func TestBenchmarkMempoolHost_GetTransferredValue(t *testing.T) { host, err := newMempoolHost(argsMempoolHost{ - txGasHandler: txcachemocks.NewTxGasHandlerMock(), + txGasHandler: mempool.NewTxGasHandlerMock(), marshalizer: &marshal.GogoProtoMarshalizer{}, }) require.NoError(t, err) diff --git a/dataRetriever/txpool/shardedTxPool.go b/dataRetriever/txpool/shardedTxPool.go index f81f4f39fa8..537feb922a6 100644 --- a/dataRetriever/txpool/shardedTxPool.go +++ b/dataRetriever/txpool/shardedTxPool.go @@ -167,10 +167,29 @@ func (txPool *shardedTxPool) createTxCache(cacheID string) txCache { return cache } -// ImmunizeSetOfDataAgainstEviction marks the items as non-evictable -func (txPool *shardedTxPool) ImmunizeSetOfDataAgainstEviction(keys [][]byte, cacheID string) { +// ImmunizeSetOfDataAgainstEviction marks the items as non-evictable for the provided confirmation nonce +func (txPool *shardedTxPool) ImmunizeSetOfDataAgainstEviction(keys [][]byte, cacheID string, nonce uint64) { shard := txPool.getOrCreateShard(cacheID) - shard.Cache.ImmunizeTxsAgainstEviction(keys) + shard.Cache.ImmunizeTxsAgainstEviction(keys, nonce) +} + +// SetOldestImmuneNonce deactivates immunity below the provided nonce +func (txPool *shardedTxPool) SetOldestImmuneNonce(cacheID string, nonce uint64) { + shard := txPool.getOrCreateShard(cacheID) + shard.Cache.SetOldestImmuneNonce(nonce) +} + +// SetOldestImmuneNonceForAllCaches deactivates immunity below the provided nonce +// on every backing cache. Called from the shard's commit path once cross-notarized +// metablock processing has advanced and the items confirmed up to (nonce - 1) +// are guaranteed to have been executed. +func (txPool *shardedTxPool) SetOldestImmuneNonceForAllCaches(nonce uint64) { + txPool.mutexBackingMap.RLock() + defer txPool.mutexBackingMap.RUnlock() + + for _, shard := range txPool.backingMap { + shard.Cache.SetOldestImmuneNonce(nonce) + } } // AddData adds the transaction to the cache diff --git a/dataRetriever/txpool/shardedTxPool_test.go b/dataRetriever/txpool/shardedTxPool_test.go index c2e26288ec6..63a5930a6f6 100644 --- a/dataRetriever/txpool/shardedTxPool_test.go +++ b/dataRetriever/txpool/shardedTxPool_test.go @@ -13,12 +13,16 @@ import ( "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-chain-core-go/marshal" + "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" + + "github.com/stretchr/testify/require" + "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/storage/storageunit" "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" "github.com/multiversx/mx-chain-go/txcache" - "github.com/stretchr/testify/require" ) const maxNumBytesPerSenderUpperBoundTest = 33_554_432 // 32 MB @@ -41,7 +45,7 @@ func Test_NewShardedTxPool_WhenBadConfig(t *testing.T) { SizeInBytesPerSender: 40960, Shards: 16, }, - TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + TxGasHandler: mempool.NewTxGasHandlerMock(), Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 1, TxCacheBoundsConfig: config.TxCacheBoundsConfig{ @@ -125,7 +129,7 @@ func Test_NewShardedTxPool_ComputesCacheConfig(t *testing.T) { cacheConfig := storageunit.CacheConfig{SizeInBytes: 419430400, SizeInBytesPerSender: 614400, Capacity: 600000, SizePerSender: 1000, Shards: 1} args := ArgShardedTxPool{ Config: cacheConfig, - TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + TxGasHandler: mempool.NewTxGasHandlerMock(), Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 2, TxCacheBoundsConfig: config.TxCacheBoundsConfig{ @@ -292,7 +296,7 @@ func TestCleanupSelfShardTxCache_NilMempool(t *testing.T) { txPool := poolAsInterface.(*shardedTxPool) delete(txPool.backingMap, "0") - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() cleanupLoopMaximumDuration := time.Millisecond * 100 require.NotPanics(t, func() { @@ -307,7 +311,7 @@ func Test_Parallel_CleanupSelfShardTxCache(t *testing.T) { t.Parallel() poolAsInterface, _ := newTxPoolToTest() pool := poolAsInterface.(*shardedTxPool) - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() accountsProvider.SetNonce([]byte("alice"), 2) accountsProvider.SetNonce([]byte("bob"), 42) accountsProvider.SetNonce([]byte("carol"), 7) @@ -340,7 +344,7 @@ func Test_CleanupSelfShardTxCache(t *testing.T) { poolAsInterface, _ := newTxPoolToTest() pool := poolAsInterface.(*shardedTxPool) cache := pool.getTxCache("0").(*txcache.TxCache) - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() accountsProvider.SetNonce([]byte("alice"), 2) accountsProvider.SetNonce([]byte("bob"), 42) accountsProvider.SetNonce([]byte("carol"), 7) @@ -470,7 +474,37 @@ func TestShardedTxPool_ImmunizeSetOfDataAgainstEviction(t *testing.T) { poolAsInterface, _ := newTxPoolToTest() pool := poolAsInterface.(*shardedTxPool) - pool.ImmunizeSetOfDataAgainstEviction([][]byte{[]byte("hash")}, "0") + pool.ImmunizeSetOfDataAgainstEviction([][]byte{[]byte("hash")}, "0", 7) + pool.SetOldestImmuneNonce("0", 7) +} + +func TestShardedTxPool_SetOldestImmuneNonceForAllCaches(t *testing.T) { + t.Parallel() + + poolAsInterface, _ := newTxPoolToTest() + pool := poolAsInterface.(*shardedTxPool) + + cacheIDs := []string{"0", "1_0", "2_0"} + received := make(map[string]uint64) + var mu sync.Mutex + for _, id := range cacheIDs { + idCopy := id + mock := txcachemocks.NewTxCacheStub() + mock.SetOldestImmuneNonceCalled = func(nonce uint64) { + mu.Lock() + received[idCopy] = nonce + mu.Unlock() + } + pool.backingMap[id] = &txPoolShard{CacheID: id, Cache: mock} + } + + pool.SetOldestImmuneNonceForAllCaches(42) + + require.Equal(t, len(cacheIDs), len(pool.backingMap), "no new caches should be created") + require.Equal(t, len(cacheIDs), len(received), "every cache should receive the threshold once") + for _, id := range cacheIDs { + require.Equal(t, uint64(42), received[id], "cache %s did not receive the threshold", id) + } } func Test_IsInterfaceNil(t *testing.T) { @@ -495,7 +529,7 @@ func Test_routeToCacheUnions(t *testing.T) { } args := ArgShardedTxPool{ Config: cacheConfig, - TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + TxGasHandler: mempool.NewTxGasHandlerMock(), Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 4, SelfShardID: 42, @@ -528,7 +562,7 @@ func TestShardedTxPool_getSelfShardTxCache(t *testing.T) { } args := ArgShardedTxPool{ Config: cacheConfig, - TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + TxGasHandler: mempool.NewTxGasHandlerMock(), Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 3, SelfShardID: 2, @@ -551,7 +585,7 @@ func TestShardedTxPool_GetNumTrackedBlocks(t *testing.T) { txCache := pool.getSelfShardTxCache() numOfBlocks := 10 - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() for i := 1; i < numOfBlocks+1; i++ { err := txCache.OnProposedBlock( @@ -579,7 +613,7 @@ func TestShardedTxPool_GetNumTrackedAccounts(t *testing.T) { txCache := pool.getSelfShardTxCache() - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() accountsProvider.GetRootHashCalled = func() ([]byte, error) { return []byte("rootHash0"), nil } @@ -661,7 +695,7 @@ func TestShardedTxPool_OnProposedBlock_And_OnExecutedBlock(t *testing.T) { } args := ArgShardedTxPool{ Config: cacheConfig, - TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + TxGasHandler: mempool.NewTxGasHandlerMock(), Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 3, SelfShardID: 0, @@ -684,7 +718,7 @@ func TestShardedTxPool_OnProposedBlock_And_OnExecutedBlock(t *testing.T) { []byte("abba"), &block.Body{}, &block.HeaderV2{}, - txcachemocks.NewAccountNonceAndBalanceProviderMock(), + mempool.NewAccountNonceAndBalanceProviderMock(), nil, ) require.Nil(t, err) @@ -729,7 +763,7 @@ func newTxPoolToTest() (dataRetriever.ShardedDataCacherNotifier, error) { } args := ArgShardedTxPool{ Config: cacheConfig, - TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + TxGasHandler: mempool.NewTxGasHandlerMock(), Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 4, SelfShardID: 0, diff --git a/epochStart/bootstrap/disabled/disabledPeerShardMapper.go b/epochStart/bootstrap/disabled/disabledPeerShardMapper.go index c4695c00c09..a0b68087db3 100644 --- a/epochStart/bootstrap/disabled/disabledPeerShardMapper.go +++ b/epochStart/bootstrap/disabled/disabledPeerShardMapper.go @@ -17,7 +17,7 @@ func (p *peerShardMapper) GetLastKnownPeerID(_ []byte) (core.PeerID, bool) { } // UpdatePeerIDPublicKeyPair does nothing -func (p *peerShardMapper) UpdatePeerIDPublicKeyPair(_ core.PeerID, _ []byte) { +func (p *peerShardMapper) UpdatePeerIDPublicKeyPair(_ core.PeerID, _ []byte, _ int64) { } // PutPeerIdShardId does nothing diff --git a/epochStart/bootstrap/export_test.go b/epochStart/bootstrap/export_test.go index da76995a804..020c838d8cc 100644 --- a/epochStart/bootstrap/export_test.go +++ b/epochStart/bootstrap/export_test.go @@ -2,6 +2,9 @@ package bootstrap import ( "github.com/multiversx/mx-chain-core-go/data" + + "github.com/multiversx/mx-chain-go/dataRetriever" + "github.com/multiversx/mx-chain-go/process" ) func (e *epochStartMetaSyncer) SetEpochStartMetaBlockInterceptorProcessor(proc EpochStartMetaBlockInterceptorProcessor) { @@ -14,3 +17,23 @@ func (e *epochStartMetaBlockProcessor) GetMapMetaBlock() map[string]data.MetaHea return e.mapReceivedMetaBlocks } + +func (e *epochStartBootstrap) RebuildNetworkComponentsForShard() error { + return e.rebuildNetworkComponentsForShard() +} + +func (e *epochStartBootstrap) ResolversContainer() dataRetriever.ResolversContainer { + return e.resolversContainer +} + +func (e *epochStartBootstrap) MainInterceptorContainer() process.InterceptorsContainer { + return e.mainInterceptorContainer +} + +func (e *epochStartBootstrap) FullArchiveInterceptorContainer() process.InterceptorsContainer { + return e.fullArchiveInterceptorContainer +} + +func (e *epochStartBootstrap) RequestHandler() process.RequestHandler { + return e.requestHandler +} diff --git a/epochStart/bootstrap/factory/epochStartInterceptorsContainerFactory.go b/epochStart/bootstrap/factory/epochStartInterceptorsContainerFactory.go index 9397c5775e7..12f870332cd 100644 --- a/epochStart/bootstrap/factory/epochStartInterceptorsContainerFactory.go +++ b/epochStart/bootstrap/factory/epochStartInterceptorsContainerFactory.go @@ -80,41 +80,49 @@ func NewEpochStartInterceptorsContainer(args ArgsEpochStartInterceptorContainer) hardforkTrigger := disabledFactory.HardforkTrigger() containerFactoryArgs := interceptorscontainer.CommonInterceptorsContainerFactoryArgs{ - CoreComponents: args.CoreComponents, - CryptoComponents: cryptoComponents, - Accounts: accountsAdapter, - ShardCoordinator: args.ShardCoordinator, - NodesCoordinator: nodesCoordinator, - MainMessenger: args.MainMessenger, - FullArchiveMessenger: args.FullArchiveMessenger, - Store: storer, - DataPool: args.DataPool, - MaxTxNonceDeltaAllowed: common.MaxTxNonceDeltaAllowed, - TxFeeHandler: feeHandler, - BlockBlackList: blackListHandler, - HeaderSigVerifier: headerSigVerifier, - HeaderIntegrityVerifier: args.HeaderIntegrityVerifier, - ValidityAttester: validityAttester, - EpochStartTrigger: epochStartTrigger, - WhiteListHandler: args.WhiteListHandler, - WhiteListerVerifiedTxs: args.WhiteListerVerifiedTxs, - AntifloodHandler: antiFloodHandler, - ArgumentsParser: args.ArgumentsParser, - PreferredPeersHolder: disabled.NewPreferredPeersHolder(), - SizeCheckDelta: uint32(sizeCheckDelta), - RequestHandler: args.RequestHandler, - PeerSignatureHandler: cryptoComponents.PeerSignatureHandler(), - SignaturesHandler: args.SignaturesHandler, - HeartbeatExpiryTimespanInSec: args.Config.HeartbeatV2.HeartbeatExpiryTimespanInSec, - MainPeerShardMapper: peerShardMapper, - FullArchivePeerShardMapper: fullArchivePeerShardMapper, - HardforkTrigger: hardforkTrigger, - NodeOperationMode: args.NodeOperationMode, - InterceptedDataVerifierFactory: args.InterceptedDataVerifierFactory, - Config: args.Config, + CoreComponents: args.CoreComponents, + CryptoComponents: cryptoComponents, + Accounts: accountsAdapter, + ShardCoordinator: args.ShardCoordinator, + NodesCoordinator: nodesCoordinator, + MainMessenger: args.MainMessenger, + FullArchiveMessenger: args.FullArchiveMessenger, + Store: storer, + DataPool: args.DataPool, + MaxTxNonceDeltaAllowed: common.MaxTxNonceDeltaAllowed, + TxFeeHandler: feeHandler, + BlockBlackList: blackListHandler, + HeaderSigVerifier: headerSigVerifier, + HeaderIntegrityVerifier: args.HeaderIntegrityVerifier, + ValidityAttester: validityAttester, + EpochStartTrigger: epochStartTrigger, + WhiteListHandler: args.WhiteListHandler, + WhiteListerVerifiedTxs: args.WhiteListerVerifiedTxs, + AntifloodHandler: antiFloodHandler, + ArgumentsParser: args.ArgumentsParser, + PreferredPeersHolder: disabled.NewPreferredPeersHolder(), + SizeCheckDelta: uint32(sizeCheckDelta), + RequestHandler: args.RequestHandler, + PeerSignatureHandler: cryptoComponents.PeerSignatureHandler(), + SignaturesHandler: args.SignaturesHandler, + HeartbeatExpiryTimespanInSec: args.Config.HeartbeatV2.HeartbeatExpiryTimespanInSec, + PeerAuthenticationTimeBetweenSendsInSec: args.Config.HeartbeatV2.PeerAuthenticationTimeBetweenSendsInSec, + MaxAllowedTrieNodeChunks: args.Config.Antiflood.MaxAllowedTrieNodeChunks, + TrieNodeChunksInactivityTimeout: time.Duration(args.Config.Antiflood.TrieNodeChunksInactivityTimeoutInSec) * time.Second, + MainPeerShardMapper: peerShardMapper, + FullArchivePeerShardMapper: fullArchivePeerShardMapper, + HardforkTrigger: hardforkTrigger, + NodeOperationMode: args.NodeOperationMode, + InterceptedDataVerifierFactory: args.InterceptedDataVerifierFactory, + Config: args.Config, } - interceptorsContainerFactory, err := interceptorscontainer.NewMetaInterceptorsContainerFactory(containerFactoryArgs) + var interceptorsContainerFactory process.InterceptorsContainerFactory + if args.ShardCoordinator.SelfId() == core.MetachainShardId { + interceptorsContainerFactory, err = interceptorscontainer.NewMetaInterceptorsContainerFactory(containerFactoryArgs) + } else { + interceptorsContainerFactory, err = interceptorscontainer.NewShardInterceptorsContainerFactory(containerFactoryArgs) + } if err != nil { return nil, nil, err } diff --git a/epochStart/bootstrap/fromLocalStorage.go b/epochStart/bootstrap/fromLocalStorage.go index c6ae3f5bccf..4692ae1eea5 100644 --- a/epochStart/bootstrap/fromLocalStorage.go +++ b/epochStart/bootstrap/fromLocalStorage.go @@ -128,6 +128,11 @@ func (e *epochStartBootstrap) prepareEpochFromStorage() (Parameters, error) { log.Debug("prepareEpochFromStorage for shuffled out", "initial shard id", e.baseData.shardId, "new shard id", newShardId) e.baseData.shardId = newShardId + e.shardCoordinator, err = sharding.NewMultiShardCoordinator(e.baseData.numberOfShards, e.baseData.shardId) + if err != nil { + return Parameters{}, err + } + err = e.createRequestHandler() if err != nil { return Parameters{}, err @@ -162,11 +167,6 @@ func (e *epochStartBootstrap) prepareEpochFromStorage() (Parameters, error) { } e.prevEpochStartMeta = prevEpochStartMeta - e.shardCoordinator, err = sharding.NewMultiShardCoordinator(e.baseData.numberOfShards, e.baseData.shardId) - if err != nil { - return Parameters{}, err - } - consensusTopic := common.ConsensusTopic + e.shardCoordinator.CommunicationIdentifier(e.shardCoordinator.SelfId()) err = e.mainMessenger.CreateTopic(consensusTopic, true) if err != nil { diff --git a/epochStart/bootstrap/process.go b/epochStart/bootstrap/process.go index 17d8021cb16..6e675c8ffa3 100644 --- a/epochStart/bootstrap/process.go +++ b/epochStart/bootstrap/process.go @@ -137,6 +137,7 @@ type epochStartBootstrap struct { requestHandler process.RequestHandler mainInterceptorContainer process.InterceptorsContainer fullArchiveInterceptorContainer process.InterceptorsContainer + resolversContainer dataRetriever.ResolversContainer dataPool dataRetriever.PoolsHolder miniBlocksSyncer epochStart.PendingMiniBlocksSyncHandler headersSyncer epochStart.HeadersByHashSyncer @@ -366,8 +367,14 @@ func (e *epochStartBootstrap) Bootstrap() (Parameters, error) { defer e.cleanupOnBootstrapFinish() - var err error - e.shardCoordinator, err = sharding.NewMultiShardCoordinator(e.genesisShardCoordinator.NumberOfShards(), core.MetachainShardId) + newShardId, _, err := e.getShardIDForLatestEpoch() + if err != nil { + // fallback to meta if nothing was loaded from the last epoch + newShardId = e.applyShardIDAsObserverIfNeeded(core.MetachainShardId) + } + log.Debug("epochStartBootstrap.Bootstrap", "newShardId", newShardId, "from last epoch", err == nil) + + e.shardCoordinator, err = sharding.NewMultiShardCoordinator(e.genesisShardCoordinator.NumberOfShards(), newShardId) if err != nil { return Parameters{}, err } @@ -408,14 +415,18 @@ func (e *epochStartBootstrap) Bootstrap() (Parameters, error) { } defer func() { - errClose := e.mainInterceptorContainer.Close() - if errClose != nil { - log.Warn("prepareEpochFromStorage mainInterceptorContainer.Close()", "error", errClose) + if !check.IfNil(e.mainInterceptorContainer) { + errClose := e.mainInterceptorContainer.Close() + if errClose != nil { + log.Warn("prepareEpochFromStorage mainInterceptorContainer.Close()", "error", errClose) + } } - errClose = e.fullArchiveInterceptorContainer.Close() - if errClose != nil { - log.Warn("prepareEpochFromStorage fullArchiveInterceptorContainer.Close()", "error", errClose) + if !check.IfNil(e.fullArchiveInterceptorContainer) { + errClose := e.fullArchiveInterceptorContainer.Close() + if errClose != nil { + log.Warn("prepareEpochFromStorage fullArchiveInterceptorContainer.Close()", "error", errClose) + } } }() @@ -594,20 +605,22 @@ func (e *epochStartBootstrap) prepareComponentsToSyncFromNetwork() error { } argsEpochStartSyncer := ArgsNewEpochStartMetaSyncer{ - CoreComponentsHolder: e.coreComponentsHolder, - CryptoComponentsHolder: e.cryptoComponentsHolder, - RequestHandler: e.requestHandler, - Messenger: e.mainMessenger, - ShardCoordinator: e.shardCoordinator, - EconomicsData: e.economicsData, - WhitelistHandler: e.whiteListHandler, - StartInEpochConfig: epochStartConfig, - HeaderIntegrityVerifier: e.headerIntegrityVerifier, - MetaBlockProcessor: metaBlockProcessor, - InterceptedDataVerifierFactory: e.interceptedDataVerifierFactory, - ProofsPool: e.dataPool.Proofs(), - HeadersPool: e.dataPool.Headers(), - ProofsInterceptorProcessor: processor.NewEquivalentProofsInterceptorProcessor(), + CoreComponentsHolder: e.coreComponentsHolder, + CryptoComponentsHolder: e.cryptoComponentsHolder, + RequestHandler: e.requestHandler, + Messenger: e.mainMessenger, + ShardCoordinator: e.shardCoordinator, + EconomicsData: e.economicsData, + WhitelistHandler: e.whiteListHandler, + StartInEpochConfig: epochStartConfig, + HeaderIntegrityVerifier: e.headerIntegrityVerifier, + MetaBlockProcessor: metaBlockProcessor, + InterceptedDataVerifierFactory: e.interceptedDataVerifierFactory, + ProofsPool: e.dataPool.Proofs(), + HeadersPool: e.dataPool.Headers(), + ProofsInterceptorProcessor: processor.NewEquivalentProofsInterceptorProcessor(), + PeerAuthCacher: e.dataPool.PeerAuthentications(), + PeerAuthenticationTimeBetweenSendsInSec: e.generalConfig.HeartbeatV2.PeerAuthenticationTimeBetweenSendsInSec, } e.epochStartMetaBlockSyncer, err = NewEpochStartMetaSyncer(argsEpochStartSyncer) if err != nil { @@ -698,7 +711,12 @@ func (e *epochStartBootstrap) syncHeadersV3From(meta data.MetaHeaderHandler) (ma hashesToRequest := make([][]byte, 0) shardIds := make([]uint32, 0) + isCurrentShardMeta := e.shardCoordinator.SelfId() == core.MetachainShardId for _, epochStartData := range meta.GetEpochStartHandler().GetLastFinalizedHeaderHandlers() { + if !isCurrentShardMeta && epochStartData.GetShardID() != e.shardCoordinator.SelfId() { + continue + } + err := e.syncEpochStartDataInfo(meta, epochStartData, syncedHeaders) if err != nil { return nil, err @@ -927,6 +945,67 @@ func (e *epochStartBootstrap) syncEpochStartMetaHeaders( return syncedHeaders, nil } +// rebuildNetworkComponentsForShard must be called after e.shardCoordinator is reassigned to the +// node's discovered destination shard ID. +func (e *epochStartBootstrap) rebuildNetworkComponentsForShard() error { + // Nothing to rebuild when the bootstrap network stack was never set up (unit fixtures that + // invoke requestAndProcessing in isolation). In production both fields are non-nil here. + if check.IfNil(e.mainInterceptorContainer) && check.IfNil(e.resolversContainer) { + return nil + } + + log.Debug("rebuilding bootstrap network components for resolved shard", "shard", e.shardCoordinator.SelfId()) + + e.tearDownStaleNetworkComponents() + + err := e.createResolversContainer() + if err != nil { + return err + } + + err = e.createRequestHandler() + if err != nil { + return err + } + + if !check.IfNil(e.epochStartMeta) { + e.requestHandler.SetEpoch(e.epochStartMeta.GetEpoch()) + } + + return e.createSyncers() +} + +func (e *epochStartBootstrap) tearDownStaleNetworkComponents() { + log.LogIfError(e.mainMessenger.UnregisterAllMessageProcessors()) + log.LogIfError(e.mainMessenger.UnJoinAllTopics()) + log.LogIfError(e.fullArchiveMessenger.UnregisterAllMessageProcessors()) + log.LogIfError(e.fullArchiveMessenger.UnJoinAllTopics()) + + if !check.IfNil(e.mainInterceptorContainer) { + errClose := e.mainInterceptorContainer.Close() + if errClose != nil { + log.Warn("rebuildNetworkComponentsForShard mainInterceptorContainer.Close()", "error", errClose) + } + e.mainInterceptorContainer = nil + } + + if !check.IfNil(e.fullArchiveInterceptorContainer) { + errClose := e.fullArchiveInterceptorContainer.Close() + if errClose != nil { + log.Warn("rebuildNetworkComponentsForShard fullArchiveInterceptorContainer.Close()", "error", errClose) + } + e.fullArchiveInterceptorContainer = nil + } + + if !check.IfNil(e.resolversContainer) { + errClose := e.resolversContainer.Close() + if errClose != nil { + log.Warn("rebuildNetworkComponentsForShard resolversContainer.Close()", "error", errClose) + } + e.resolversContainer = nil + } +} + func (e *epochStartBootstrap) syncHeadersFrom(meta data.MetaHeaderHandler) (map[string]data.HeaderHandler, error) { if meta.IsHeaderV3() { return e.syncHeadersV3From(meta) @@ -938,7 +1017,13 @@ func (e *epochStartBootstrap) syncHeadersFrom(meta data.MetaHeaderHandler) (map[ if err != nil { return nil, err } + + isCurrentShardMeta := e.shardCoordinator.SelfId() == core.MetachainShardId for _, epochStartData := range meta.GetEpochStartHandler().GetLastFinalizedHeaderHandlers() { + if !isCurrentShardMeta && epochStartData.GetShardID() != e.shardCoordinator.SelfId() { + continue + } + hashesToRequest = append(hashesToRequest, epochStartData.GetHeaderHash()) shardIds = append(shardIds, epochStartData.GetShardID()) @@ -1169,12 +1254,21 @@ func (e *epochStartBootstrap) requestAndProcessing() (Parameters, error) { log.Debug("start in epoch bootstrap: processNodesConfig") e.saveSelfShardId() + oldShardID := e.shardCoordinator.SelfId() + oldNumberOfShards := e.shardCoordinator.NumberOfShards() e.shardCoordinator, err = sharding.NewMultiShardCoordinator(e.baseData.numberOfShards, e.baseData.shardId) if err != nil { return Parameters{}, fmt.Errorf("%w numberOfShards=%v shardId=%v", err, e.baseData.numberOfShards, e.baseData.shardId) } log.Debug("start in epoch bootstrap: shardCoordinator", "numOfShards", e.baseData.numberOfShards, "shardId", e.baseData.shardId) + if oldShardID != e.shardCoordinator.SelfId() || oldNumberOfShards != e.shardCoordinator.NumberOfShards() { + err = e.rebuildNetworkComponentsForShard() + if err != nil { + return Parameters{}, err + } + } + consensusTopic := common.ConsensusTopic + e.shardCoordinator.CommunicationIdentifier(e.shardCoordinator.SelfId()) err = e.mainMessenger.CreateTopic(consensusTopic, true) if err != nil { @@ -1825,14 +1919,12 @@ func (e *epochStartBootstrap) createResolversContainer() error { storageService := disabled.NewChainStorer() - payloadValidator, err := validator.NewPeerAuthenticationPayloadValidator(e.generalConfig.HeartbeatV2.HeartbeatExpiryTimespanInSec) + payloadValidator, err := validator.NewPeerAuthenticationPayloadValidator(e.generalConfig.HeartbeatV2.PeerAuthenticationTimeBetweenSendsInSec) if err != nil { return err } - // TODO - create a dedicated request handler to be used when fetching required data with the correct shard coordinator - // this one should only be used before determining the correct shard where the node should reside - log.Debug("epochStartBootstrap.createRequestHandler", "shard", e.shardCoordinator.SelfId()) + log.Debug("epochStartBootstrap.createResolversContainer", "shard", e.shardCoordinator.SelfId()) resolversContainerArgs := resolverscontainer.FactoryArgs{ ShardCoordinator: e.shardCoordinator, MainMessenger: e.mainMessenger, @@ -1851,7 +1943,12 @@ func (e *epochStartBootstrap) createResolversContainer() error { PayloadValidator: payloadValidator, AntifloodConfigsHandler: e.coreComponentsHolder.AntifloodConfigsHandler(), } - resolverFactory, err := resolverscontainer.NewMetaResolversContainerFactory(resolversContainerArgs) + var resolverFactory dataRetriever.ResolversContainerFactory + if e.shardCoordinator.SelfId() == core.MetachainShardId { + resolverFactory, err = resolverscontainer.NewMetaResolversContainerFactory(resolversContainerArgs) + } else { + resolverFactory, err = resolverscontainer.NewShardResolversContainerFactory(resolversContainerArgs) + } if err != nil { return err } @@ -1861,7 +1958,14 @@ func (e *epochStartBootstrap) createResolversContainer() error { return err } - return resolverFactory.AddShardTrieNodeResolvers(container) + err = resolverFactory.AddShardTrieNodeResolvers(container) + if err != nil { + _ = container.Close() + return err + } + + e.resolversContainer = container + return nil } func (e *epochStartBootstrap) createRequestHandler() error { @@ -1880,7 +1984,14 @@ func (e *epochStartBootstrap) createRequestHandler() error { SizeCheckDelta: 0, EnableEpochsHandler: e.enableEpochsHandler, } - requestersFactory, err := requesterscontainer.NewMetaRequestersContainerFactory(requestersContainerArgs) + + var requestersFactory dataRetriever.RequestersContainerFactory + var err error + if e.shardCoordinator.SelfId() == core.MetachainShardId { + requestersFactory, err = requesterscontainer.NewMetaRequestersContainerFactory(requestersContainerArgs) + } else { + requestersFactory, err = requesterscontainer.NewShardRequestersContainerFactory(requestersContainerArgs) + } if err != nil { return err } @@ -1906,7 +2017,7 @@ func (e *epochStartBootstrap) createRequestHandler() error { requestedItemsHandler, e.whiteListHandler, maxToRequest, - core.MetachainShardId, + e.shardCoordinator.SelfId(), timeBetweenRequests, time.Duration(e.generalConfig.Requesters.RequestProofByNonceDelayMs)*time.Millisecond, ) diff --git a/epochStart/bootstrap/process_test.go b/epochStart/bootstrap/process_test.go index d1eb850dac8..e6d6f0942d3 100644 --- a/epochStart/bootstrap/process_test.go +++ b/epochStart/bootstrap/process_test.go @@ -19,6 +19,9 @@ import ( dataBatch "github.com/multiversx/mx-chain-core-go/data/batch" "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/common/graceperiod" "github.com/multiversx/mx-chain-go/common/statistics" @@ -29,6 +32,7 @@ import ( "github.com/multiversx/mx-chain-go/epochStart/bootstrap/disabled" "github.com/multiversx/mx-chain-go/epochStart/bootstrap/types" "github.com/multiversx/mx-chain-go/epochStart/mock" + "github.com/multiversx/mx-chain-go/p2p" "github.com/multiversx/mx-chain-go/process" processMock "github.com/multiversx/mx-chain-go/process/mock" "github.com/multiversx/mx-chain-go/sharding" @@ -55,11 +59,10 @@ import ( statusHandlerMock "github.com/multiversx/mx-chain-go/testscommon/statusHandler" storageMocks "github.com/multiversx/mx-chain-go/testscommon/storage" "github.com/multiversx/mx-chain-go/testscommon/syncer" + trieMock "github.com/multiversx/mx-chain-go/testscommon/trie" validatorInfoCacherStub "github.com/multiversx/mx-chain-go/testscommon/validatorInfoCacher" "github.com/multiversx/mx-chain-go/trie/factory" updateMock "github.com/multiversx/mx-chain-go/update/mock" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) var errExpected = errors.New("expected error") @@ -1048,6 +1051,224 @@ func TestCreateSyncers(t *testing.T) { assert.Nil(t, err) } +func TestEpochStartBootstrap_RebuildNetworkComponentsForShard_NoopWhenNotInitialized(t *testing.T) { + t.Parallel() + + coreComp, cryptoComp := createComponentsForEpochStart() + args := createMockEpochStartBootstrapArgs(coreComp, cryptoComp) + + epochStartProvider, _ := NewEpochStartBootstrap(args) + epochStartProvider.shardCoordinator = mock.NewMultipleShardsCoordinatorMock() + + err := epochStartProvider.RebuildNetworkComponentsForShard() + assert.Nil(t, err) + assert.True(t, check.IfNil(epochStartProvider.MainInterceptorContainer())) + assert.True(t, check.IfNil(epochStartProvider.FullArchiveInterceptorContainer())) + assert.True(t, check.IfNil(epochStartProvider.ResolversContainer())) +} + +func TestEpochStartBootstrap_RebuildNetworkComponentsForShard_RewiresStaleCoordinator(t *testing.T) { + t.Parallel() + + coreComp, cryptoComp := createComponentsForEpochStart() + args := createMockEpochStartBootstrapArgs(coreComp, cryptoComp) + + registeredInterceptors := make(map[string]struct{}) + registeredResolvers := make(map[string]struct{}) + expectedEpoch := uint32(37) + requestedEpoch := uint32(0) + + // Mimic the libp2p messenger: reject duplicate (topic, identifier) registrations so that a + // missed unregister during the rebuild is caught as test failure. + args.MainMessenger = &p2pmocks.MessengerStub{ + RegisterMessageProcessorCalled: func(topic string, identifier string, _ p2p.MessageProcessor) error { + switch identifier { + case common.DefaultInterceptorsIdentifier: + if _, dup := registeredInterceptors[topic]; dup { + return fmt.Errorf("topic %q already has an interceptor processor", topic) + } + registeredInterceptors[topic] = struct{}{} + case common.DefaultResolversIdentifier: + if _, dup := registeredResolvers[topic]; dup { + return fmt.Errorf("topic %q already has a resolver processor", topic) + } + registeredResolvers[topic] = struct{}{} + } + return nil + }, + UnregisterMessageProcessorCalled: func(topic string, identifier string) error { + require.Fail(t, "should have not been called") + return nil + }, + UnregisterAllMessageProcessorsCalled: func() error { + registeredInterceptors = make(map[string]struct{}) + registeredResolvers = make(map[string]struct{}) + + return nil + }, + ConnectedPeersCalled: func() []core.PeerID { + return []core.PeerID{"peer0", "peer1", "peer2"} + }, + ConnectedPeersOnTopicCalled: func(_ string) []core.PeerID { + return []core.PeerID{"peer0"} + }, + SendToConnectedPeerCalled: func(_ string, buff []byte, _ core.PeerID) error { + requestData := &dataRetriever.RequestData{} + err := coreComp.InternalMarshalizer().Unmarshal(requestData, buff) + assert.Nil(t, err) + requestedEpoch = requestData.Epoch + + return nil + }, + } + args.FullArchiveMessenger = &p2pmocks.MessengerStub{} + + epochStartProvider, _ := NewEpochStartBootstrap(args) + epochStartProvider.epochStartMeta = &block.MetaBlock{Epoch: expectedEpoch} + + // Shard-to-shard rather than Meta-to-shard: the rebuild's mechanics are identical, but a Meta + // initial coordinator would require populated trie roots that aren't relevant to this test. + staleCoordinator, errCoord := sharding.NewMultiShardCoordinator(2, 0) + require.Nil(t, errCoord) + epochStartProvider.shardCoordinator = staleCoordinator + epochStartProvider.dataPool = buildRebuildTestDataPool() + epochStartProvider.whiteListHandler = &testscommon.WhiteListHandlerStub{} + epochStartProvider.whiteListerVerifiedTxs = &testscommon.WhiteListHandlerStub{} + epochStartProvider.storageService = &storageMocks.ChainStorerStub{} + epochStartProvider.interceptedDataVerifierFactory = &processMock.InterceptedDataVerifierFactoryMock{} + epochStartProvider.trieContainer.Put([]byte(dataRetriever.UserAccountsUnit.String()), &trieMock.TrieStub{}) + + require.Nil(t, epochStartProvider.createResolversContainer()) + require.Nil(t, epochStartProvider.createRequestHandler()) + require.Nil(t, epochStartProvider.createSyncers()) + + require.False(t, check.IfNil(epochStartProvider.MainInterceptorContainer())) + require.False(t, check.IfNil(epochStartProvider.ResolversContainer())) + + oldInterceptorTopics := collectInterceptorTopics(epochStartProvider.MainInterceptorContainer()) + oldResolverTopics := collectResolverTopics(epochStartProvider.ResolversContainer()) + require.NotEmpty(t, oldInterceptorTopics) + require.NotEmpty(t, oldResolverTopics) + + oldMainInterceptor := epochStartProvider.MainInterceptorContainer() + oldResolvers := epochStartProvider.ResolversContainer() + oldRequestHandler := epochStartProvider.RequestHandler() + + newCoordinator, errCoord := sharding.NewMultiShardCoordinator(2, 1) + require.Nil(t, errCoord) + epochStartProvider.shardCoordinator = newCoordinator + + err := epochStartProvider.RebuildNetworkComponentsForShard() + require.Nil(t, err) + + assert.NotSame(t, oldMainInterceptor, epochStartProvider.MainInterceptorContainer()) + assert.NotSame(t, oldResolvers, epochStartProvider.ResolversContainer()) + assert.NotSame(t, oldRequestHandler, epochStartProvider.RequestHandler()) + + assert.NotEmpty(t, registeredInterceptors) + assert.NotEmpty(t, registeredResolvers) + + epochStartProvider.RequestHandler().RequestMiniBlock(0, []byte("hash")) + assert.Equal(t, expectedEpoch, requestedEpoch) +} + +func TestEpochStartBootstrap_RebuildNetworkComponentsForShard_ErrorPropagatesAndLeavesNoHalfState(t *testing.T) { + t.Parallel() + + coreComp, cryptoComp := createComponentsForEpochStart() + args := createMockEpochStartBootstrapArgs(coreComp, cryptoComp) + + epochStartProvider, _ := NewEpochStartBootstrap(args) + epochStartProvider.epochStartMeta = &block.MetaBlock{Epoch: 37} + + staleCoordinator, errCoord := sharding.NewMultiShardCoordinator(2, 0) + require.Nil(t, errCoord) + epochStartProvider.shardCoordinator = staleCoordinator + epochStartProvider.dataPool = buildRebuildTestDataPool() + epochStartProvider.whiteListHandler = &testscommon.WhiteListHandlerStub{} + epochStartProvider.whiteListerVerifiedTxs = &testscommon.WhiteListHandlerStub{} + epochStartProvider.storageService = &storageMocks.ChainStorerStub{} + epochStartProvider.interceptedDataVerifierFactory = &processMock.InterceptedDataVerifierFactoryMock{} + epochStartProvider.trieContainer.Put([]byte(dataRetriever.UserAccountsUnit.String()), &trieMock.TrieStub{}) + + require.Nil(t, epochStartProvider.createResolversContainer()) + require.Nil(t, epochStartProvider.createRequestHandler()) + require.Nil(t, epochStartProvider.createSyncers()) + + // Inject a failure into the createSyncers step of the rebuild + epochStartProvider.interceptedDataVerifierFactory = nil + + newCoordinator, errCoord := sharding.NewMultiShardCoordinator(2, 1) + require.Nil(t, errCoord) + epochStartProvider.shardCoordinator = newCoordinator + + err := epochStartProvider.RebuildNetworkComponentsForShard() + require.NotNil(t, err) + + // Tear-down ran before the failure point, so deferred Bootstrap cleanup will not double-close + assert.True(t, check.IfNil(epochStartProvider.MainInterceptorContainer())) + assert.True(t, check.IfNil(epochStartProvider.FullArchiveInterceptorContainer())) +} + +func buildRebuildTestDataPool() dataRetriever.PoolsHolder { + return &dataRetrieverMock.PoolsHolderStub{ + HeadersCalled: func() dataRetriever.HeadersPool { + return &mock.HeadersCacherStub{} + }, + TransactionsCalled: func() dataRetriever.ShardedDataCacherNotifier { + return testscommon.NewShardedDataStub() + }, + UnsignedTransactionsCalled: func() dataRetriever.ShardedDataCacherNotifier { + return testscommon.NewShardedDataStub() + }, + RewardTransactionsCalled: func() dataRetriever.ShardedDataCacherNotifier { + return testscommon.NewShardedDataStub() + }, + MiniBlocksCalled: func() storage.Cacher { + return cache.NewCacherStub() + }, + TrieNodesCalled: func() storage.Cacher { + return cache.NewCacherStub() + }, + PeerAuthenticationsCalled: func() storage.Cacher { + return cache.NewCacherStub() + }, + HeartbeatsCalled: func() storage.Cacher { + return cache.NewCacherStub() + }, + ProofsCalled: func() dataRetriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{} + }, + DirectSentTransactionsCalled: func() storage.Cacher { + return cache.NewCacherStub() + }, + } +} + +func collectInterceptorTopics(container process.InterceptorsContainer) map[string]struct{} { + topics := make(map[string]struct{}) + if check.IfNil(container) { + return topics + } + container.Iterate(func(key string, _ process.Interceptor) bool { + topics[key] = struct{}{} + return true + }) + return topics +} + +func collectResolverTopics(container dataRetriever.ResolversContainer) map[string]struct{} { + topics := make(map[string]struct{}) + if check.IfNil(container) { + return topics + } + container.Iterate(func(key string, _ dataRetriever.Resolver) bool { + topics[key] = struct{}{} + return true + }) + return topics +} + func TestSyncHeadersFrom_MockHeadersSyncerShouldSyncHeaders(t *testing.T) { hdrHash1 := []byte("hdrHash1") hdrHash2 := []byte("hdrHash2") @@ -3330,6 +3551,89 @@ func TestEpochStartBoostrap_SyncHeadersV3FromMeta(t *testing.T) { require.Nil(t, headers) }) + t.Run("shard node should not request other shards epoch start data", func(t *testing.T) { + t.Parallel() + + hdrHash1 := []byte("hdrHash1") + hdrHash2 := []byte("hdrHash2") + otherShardHdrHash := []byte("otherShardHdrHash") + lastExecMetaHash := []byte("lastExecMetaHash") + + header1 := &block.Header{ + Nonce: 11, + PrevHash: hdrHash2, + } + + lastExecMeta := &block.MetaBlockV3{ + Nonce: 20, + LastExecutionResult: &block.MetaExecutionResultInfo{ + ExecutionResult: &block.BaseMetaExecutionResult{ + BaseExecutionResult: &block.BaseExecutionResult{}, + }, + }, + } + + coreComp, cryptoComp := createComponentsForEpochStart() + args := createMockEpochStartBootstrapArgs(coreComp, cryptoComp) + + epochStartProvider, _ := NewEpochStartBootstrap(args) + require.Equal(t, uint32(0), epochStartProvider.shardCoordinator.SelfId()) + + epochStartProvider.headersSyncer = &epochStartMocks.HeadersByHashSyncerStub{ + SyncMissingHeadersByHashCalled: func(shardIDs []uint32, headersHashes [][]byte, ctx context.Context) error { + for _, hash := range headersHashes { + require.NotEqual(t, otherShardHdrHash, hash) + } + for _, shardID := range shardIDs { + require.True(t, shardID == 0 || shardID == core.MetachainShardId) + } + return nil + }, + GetHeadersCalled: func() (m map[string]data.HeaderHandler, err error) { + return map[string]data.HeaderHandler{ + string(hdrHash1): header1, + string(lastExecMetaHash): lastExecMeta, + }, nil + }, + } + + metaBlock := &block.MetaBlockV3{ + Epoch: 2, + Nonce: 21, + PrevHash: lastExecMetaHash, + EpochStart: block.EpochStart{ + LastFinalizedHeaders: []block.EpochStartShardData{ + { + HeaderHash: hdrHash1, + ShardID: 0, + LastFinishedMetaBlock: lastExecMetaHash, + }, + { + HeaderHash: otherShardHdrHash, + ShardID: 1, + LastFinishedMetaBlock: lastExecMetaHash, + }, + }, + Economics: block.Economics{ + PrevEpochStartHash: hdrHash2, + }, + }, + LastExecutionResult: &block.MetaExecutionResultInfo{ + ExecutionResult: &block.BaseMetaExecutionResult{ + BaseExecutionResult: &block.BaseExecutionResult{ + HeaderNonce: 20, + HeaderHash: lastExecMetaHash, + }, + }, + }, + } + + headers, err := epochStartProvider.syncHeadersFrom(metaBlock) + require.Nil(t, err) + require.Equal(t, 2, len(headers)) + require.NotContains(t, headers, string(otherShardHdrHash)) + }) + t.Run("should work with meta v3 and shard v2", func(t *testing.T) { t.Parallel() diff --git a/epochStart/bootstrap/storageProcess.go b/epochStart/bootstrap/storageProcess.go index 09851eef041..0ce9289a64a 100644 --- a/epochStart/bootstrap/storageProcess.go +++ b/epochStart/bootstrap/storageProcess.go @@ -12,8 +12,6 @@ import ( "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/data/endProcess" - "github.com/multiversx/mx-chain-go/process/interceptors/processor" - "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/dataRetriever" @@ -24,6 +22,7 @@ import ( "github.com/multiversx/mx-chain-go/epochStart" "github.com/multiversx/mx-chain-go/epochStart/bootstrap/disabled" "github.com/multiversx/mx-chain-go/epochStart/notifier" + "github.com/multiversx/mx-chain-go/process/interceptors/processor" "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/storage/cache" storageFactory "github.com/multiversx/mx-chain-go/storage/factory" @@ -84,18 +83,9 @@ func (sesb *storageEpochStartBootstrap) Bootstrap() (Parameters, error) { defer func() { sesb.cleanupOnBootstrapFinish() - if !check.IfNil(sesb.container) { - err := sesb.container.Close() - if err != nil { - log.Debug("non critical error closing requesters", "error", err) - } - } - - if !check.IfNil(sesb.store) { - err := sesb.store.CloseAll() - if err != nil { - log.Debug("non critical error closing storage service", "error", err) - } + err := sesb.closeStorageRequesters() + if err != nil { + log.Debug("non critical error closing storage requesters", "error", err) } }() @@ -180,20 +170,22 @@ func (sesb *storageEpochStartBootstrap) prepareComponentsToSync() error { } argsEpochStartSyncer := ArgsNewEpochStartMetaSyncer{ - CoreComponentsHolder: sesb.coreComponentsHolder, - CryptoComponentsHolder: sesb.cryptoComponentsHolder, - RequestHandler: sesb.requestHandler, - Messenger: sesb.mainMessenger, - ShardCoordinator: sesb.shardCoordinator, - EconomicsData: sesb.economicsData, - WhitelistHandler: sesb.whiteListHandler, - StartInEpochConfig: sesb.generalConfig.EpochStartConfig, - HeaderIntegrityVerifier: sesb.headerIntegrityVerifier, - MetaBlockProcessor: metablockProcessor, - InterceptedDataVerifierFactory: sesb.interceptedDataVerifierFactory, - ProofsPool: sesb.dataPool.Proofs(), - HeadersPool: sesb.dataPool.Headers(), - ProofsInterceptorProcessor: processor.NewEquivalentProofsInterceptorProcessor(), + CoreComponentsHolder: sesb.coreComponentsHolder, + CryptoComponentsHolder: sesb.cryptoComponentsHolder, + RequestHandler: sesb.requestHandler, + Messenger: sesb.mainMessenger, + ShardCoordinator: sesb.shardCoordinator, + EconomicsData: sesb.economicsData, + WhitelistHandler: sesb.whiteListHandler, + StartInEpochConfig: sesb.generalConfig.EpochStartConfig, + HeaderIntegrityVerifier: sesb.headerIntegrityVerifier, + MetaBlockProcessor: metablockProcessor, + InterceptedDataVerifierFactory: sesb.interceptedDataVerifierFactory, + ProofsPool: sesb.dataPool.Proofs(), + HeadersPool: sesb.dataPool.Headers(), + ProofsInterceptorProcessor: processor.NewEquivalentProofsInterceptorProcessor(), + PeerAuthCacher: sesb.dataPool.PeerAuthentications(), + PeerAuthenticationTimeBetweenSendsInSec: sesb.generalConfig.HeartbeatV2.PeerAuthenticationTimeBetweenSendsInSec, } sesb.epochStartMetaBlockSyncer, err = NewEpochStartMetaSyncer(argsEpochStartSyncer) @@ -221,7 +213,7 @@ func (sesb *storageEpochStartBootstrap) createStorageRequestHandler() error { requestedItemsHandler, sesb.whiteListHandler, maxToRequest, - core.MetachainShardId, + sesb.shardCoordinator.SelfId(), timeBetweenRequests, time.Duration(sesb.generalConfig.Requesters.RequestProofByNonceDelayMs)*time.Millisecond, ) @@ -234,7 +226,7 @@ func (sesb *storageEpochStartBootstrap) createStorageRequesters() error { return err } - shardCoordinator, err := sharding.NewMultiShardCoordinator(sesb.genesisShardCoordinator.NumberOfShards(), sesb.genesisShardCoordinator.SelfId()) + shardCoordinator, err := sharding.NewMultiShardCoordinator(sesb.shardCoordinator.NumberOfShards(), sesb.shardCoordinator.SelfId()) if err != nil { return err } @@ -301,6 +293,59 @@ func (sesb *storageEpochStartBootstrap) createStoreForStorageResolvers(shardCoor ) } +func (sesb *storageEpochStartBootstrap) rebuildStorageComponentsForShard() error { + // Nothing to rebuild when the bootstrap stack was never set up by the storage bootstrap flow. + if check.IfNil(sesb.mainInterceptorContainer) && check.IfNil(sesb.container) { + return nil + } + + log.Debug("rebuilding storage bootstrap components for resolved shard", "shard", sesb.shardCoordinator.SelfId()) + + sesb.tearDownStaleNetworkComponents() + err := sesb.closeStorageRequesters() + if err != nil { + return err + } + + err = sesb.createStorageRequestHandler() + if err != nil { + return err + } + if !check.IfNil(sesb.epochStartMeta) { + sesb.requestHandler.SetEpoch(sesb.epochStartMeta.GetEpoch()) + } + return sesb.createSyncers() +} + +func (sesb *storageEpochStartBootstrap) closeStorageRequesters() error { + var containerErr error + if !check.IfNil(sesb.container) { + err := sesb.container.Close() + if err != nil { + containerErr = fmt.Errorf("close storage requesters container: %w", err) + } + sesb.container = nil + } + + var storeErr error + if !check.IfNil(sesb.store) { + err := sesb.store.CloseAll() + if err != nil { + storeErr = fmt.Errorf("close storage service: %w", err) + } + sesb.store = nil + } + + switch { + case containerErr != nil && storeErr != nil: + return fmt.Errorf("%v; %w", containerErr, storeErr) + case storeErr != nil: + return storeErr + default: + return containerErr + } +} + func (sesb *storageEpochStartBootstrap) requestAndProcessFromStorage() (Parameters, error) { var err error sesb.baseData.numberOfShards = uint32(len(sesb.epochStartMeta.GetEpochStartHandler().GetLastFinalizedHeaderHandlers())) @@ -331,12 +376,21 @@ func (sesb *storageEpochStartBootstrap) requestAndProcessFromStorage() (Paramete log.Debug("start in epoch bootstrap: processNodesConfig") sesb.saveSelfShardId() + oldShardID := sesb.shardCoordinator.SelfId() + oldNumberOfShards := sesb.shardCoordinator.NumberOfShards() sesb.shardCoordinator, err = sharding.NewMultiShardCoordinator(sesb.baseData.numberOfShards, sesb.baseData.shardId) if err != nil { return Parameters{}, fmt.Errorf("%w numberOfShards=%v shardId=%v", err, sesb.baseData.numberOfShards, sesb.baseData.shardId) } log.Debug("start in epoch bootstrap: shardCoordinator", "numOfShards", sesb.baseData.numberOfShards, "shardId", sesb.baseData.shardId) + if oldShardID != sesb.shardCoordinator.SelfId() || oldNumberOfShards != sesb.shardCoordinator.NumberOfShards() { + err = sesb.rebuildStorageComponentsForShard() + if err != nil { + return Parameters{}, err + } + } + consensusTopic := common.ConsensusTopic + sesb.shardCoordinator.CommunicationIdentifier(sesb.shardCoordinator.SelfId()) err = sesb.mainMessenger.CreateTopic(consensusTopic, true) if err != nil { diff --git a/epochStart/bootstrap/storageProcess_test.go b/epochStart/bootstrap/storageProcess_test.go index 7276886985d..8885488c15d 100644 --- a/epochStart/bootstrap/storageProcess_test.go +++ b/epochStart/bootstrap/storageProcess_test.go @@ -16,7 +16,9 @@ import ( "github.com/multiversx/mx-chain-go/epochStart" "github.com/multiversx/mx-chain-go/epochStart/mock" "github.com/multiversx/mx-chain-go/process" + processFactory "github.com/multiversx/mx-chain-go/process/factory" processMock "github.com/multiversx/mx-chain-go/process/mock" + "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/sharding/nodesCoordinator" "github.com/multiversx/mx-chain-go/storage" "github.com/multiversx/mx-chain-go/testscommon" @@ -68,6 +70,38 @@ func TestNewStorageEpochStartBootstrap_ShouldWork(t *testing.T) { assert.Nil(t, err) } +func TestStorageEpochStartBootstrap_CreateStorageRequestHandlerUsesCurrentShard(t *testing.T) { + t.Parallel() + + coreComp, cryptoComp := createComponentsForEpochStart() + args := createMockStorageEpochStartBootstrapArgs(coreComp, cryptoComp) + args.GeneralConfig = testscommon.GetGeneralConfig() + args.ImportDbConfig = config.ImportDbConfig{ + ImportDBWorkingDir: t.TempDir(), + ImportDBTargetShardID: 1, + } + + sesb, err := NewStorageEpochStartBootstrap(args) + assert.Nil(t, err) + sesb.shardCoordinator, err = sharding.NewMultiShardCoordinator(2, 1) + assert.Nil(t, err) + + err = sesb.createStorageRequestHandler() + assert.Nil(t, err) + defer func() { + _ = sesb.closeStorageRequesters() + }() + + expectedTopic := processFactory.MiniBlocksTopic + core.CommunicationIdentifierBetweenShards(0, 1) + oldTopic := processFactory.MiniBlocksTopic + core.CommunicationIdentifierBetweenShards(0, core.MetachainShardId) + + _, err = sesb.container.Get(expectedTopic) + assert.Nil(t, err) + + _, err = sesb.container.Get(oldTopic) + assert.NotNil(t, err) +} + func TestStorageEpochStartBootstrap_BootstrapStartInEpochNotEnabled(t *testing.T) { coreComp, cryptoComp := createComponentsForEpochStart() args := createMockStorageEpochStartBootstrapArgs(coreComp, cryptoComp) diff --git a/epochStart/bootstrap/syncEpochStartMeta.go b/epochStart/bootstrap/syncEpochStartMeta.go index a0437a5f33b..db2221f3e00 100644 --- a/epochStart/bootstrap/syncEpochStartMeta.go +++ b/epochStart/bootstrap/syncEpochStartMeta.go @@ -9,6 +9,7 @@ import ( "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/hashing" "github.com/multiversx/mx-chain-core-go/marshal" + "github.com/multiversx/mx-chain-go/storage" "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/config" @@ -37,21 +38,23 @@ type epochStartMetaSyncer struct { // ArgsNewEpochStartMetaSyncer - type ArgsNewEpochStartMetaSyncer struct { - CoreComponentsHolder process.CoreComponentsHolder - CryptoComponentsHolder process.CryptoComponentsHolder - RequestHandler RequestHandler - Messenger Messenger - ShardCoordinator sharding.Coordinator - EconomicsData process.EconomicsDataHandler - WhitelistHandler process.WhiteListHandler - StartInEpochConfig config.EpochStartConfig - ArgsParser process.ArgumentsParser - HeaderIntegrityVerifier process.HeaderIntegrityVerifier - MetaBlockProcessor EpochStartMetaBlockInterceptorProcessor - InterceptedDataVerifierFactory process.InterceptedDataVerifierFactory - ProofsPool dataRetriever.ProofsPool - HeadersPool dataRetriever.HeadersPool - ProofsInterceptorProcessor process.InterceptorProcessor + CoreComponentsHolder process.CoreComponentsHolder + CryptoComponentsHolder process.CryptoComponentsHolder + RequestHandler RequestHandler + Messenger Messenger + ShardCoordinator sharding.Coordinator + EconomicsData process.EconomicsDataHandler + WhitelistHandler process.WhiteListHandler + StartInEpochConfig config.EpochStartConfig + ArgsParser process.ArgumentsParser + HeaderIntegrityVerifier process.HeaderIntegrityVerifier + MetaBlockProcessor EpochStartMetaBlockInterceptorProcessor + InterceptedDataVerifierFactory process.InterceptedDataVerifierFactory + ProofsPool dataRetriever.ProofsPool + HeadersPool dataRetriever.HeadersPool + ProofsInterceptorProcessor process.InterceptorProcessor + PeerAuthCacher storage.Cacher + PeerAuthenticationTimeBetweenSendsInSec int64 } // NewEpochStartMetaSyncer will return a new instance of epochStartMetaSyncer @@ -88,16 +91,18 @@ func NewEpochStartMetaSyncer(args ArgsNewEpochStartMetaSyncer) (*epochStartMetaS } argsInterceptedDataFactory := interceptorsFactory.ArgInterceptedDataFactory{ - CoreComponents: args.CoreComponentsHolder, - CryptoComponents: args.CryptoComponentsHolder, - ShardCoordinator: args.ShardCoordinator, - NodesCoordinator: disabled.NewNodesCoordinator(), - FeeHandler: args.EconomicsData, - HeaderSigVerifier: disabled.NewHeaderSigVerifier(), - HeaderIntegrityVerifier: args.HeaderIntegrityVerifier, - ValidityAttester: disabled.NewValidityAttester(), - EpochStartTrigger: disabled.NewEpochStartTrigger(), - ArgsParser: args.ArgsParser, + CoreComponents: args.CoreComponentsHolder, + CryptoComponents: args.CryptoComponentsHolder, + ShardCoordinator: args.ShardCoordinator, + NodesCoordinator: disabled.NewNodesCoordinator(), + FeeHandler: args.EconomicsData, + HeaderSigVerifier: disabled.NewHeaderSigVerifier(), + HeaderIntegrityVerifier: args.HeaderIntegrityVerifier, + ValidityAttester: disabled.NewValidityAttester(), + EpochStartTrigger: disabled.NewEpochStartTrigger(), + ArgsParser: args.ArgsParser, + PeerAuthCacher: args.PeerAuthCacher, + PeerAuthenticationTimeBetweenSendsInSec: args.PeerAuthenticationTimeBetweenSendsInSec, } argsInterceptedMetaHeaderFactory := interceptorsFactory.ArgInterceptedMetaHeaderFactory{ ArgInterceptedDataFactory: argsInterceptedDataFactory, diff --git a/epochStart/bootstrap/syncEpochStartMeta_test.go b/epochStart/bootstrap/syncEpochStartMeta_test.go index f1740d797d5..f51642a2e9c 100644 --- a/epochStart/bootstrap/syncEpochStartMeta_test.go +++ b/epochStart/bootstrap/syncEpochStartMeta_test.go @@ -9,6 +9,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" + "github.com/multiversx/mx-chain-go/testscommon/cache" "github.com/multiversx/mx-chain-go/testscommon/pool" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -174,11 +175,13 @@ func getEpochStartSyncerArgs() ArgsNewEpochStartMetaSyncer { MinNumConnectedPeersToStart: 2, MinNumOfPeersToConsiderBlockValid: 2, }, - HeaderIntegrityVerifier: &mock.HeaderIntegrityVerifierStub{}, - MetaBlockProcessor: &mock.EpochStartMetaBlockProcessorStub{}, - InterceptedDataVerifierFactory: &processMock.InterceptedDataVerifierFactoryMock{}, - ProofsPool: &dataRetriever.ProofsPoolMock{}, - HeadersPool: &pool.HeadersPoolStub{}, - ProofsInterceptorProcessor: &processMock.InterceptorProcessorStub{}, + HeaderIntegrityVerifier: &mock.HeaderIntegrityVerifierStub{}, + MetaBlockProcessor: &mock.EpochStartMetaBlockProcessorStub{}, + InterceptedDataVerifierFactory: &processMock.InterceptedDataVerifierFactoryMock{}, + ProofsPool: &dataRetriever.ProofsPoolMock{}, + HeadersPool: &pool.HeadersPoolStub{}, + ProofsInterceptorProcessor: &processMock.InterceptorProcessorStub{}, + PeerAuthCacher: cache.NewCacherStub(), + PeerAuthenticationTimeBetweenSendsInSec: 60, } } diff --git a/epochStart/metachain/trigger.go b/epochStart/metachain/trigger.go index 0f81203adf1..84e5bd0b33e 100644 --- a/epochStart/metachain/trigger.go +++ b/epochStart/metachain/trigger.go @@ -33,7 +33,7 @@ var _ process.EpochStartTriggerHandler = (*trigger)(nil) var _ process.EpochBootstrapper = (*trigger)(nil) var _ closing.Closer = (*trigger)(nil) -const minimumNonceToStartEpoch = 4 +const minimumBlocksPerEpoch = 4 const disabledRoundForForceEpochStart = math.MaxUint64 // ArgsNewMetaEpochStartTrigger defines struct needed to create a new start of epoch trigger @@ -251,10 +251,11 @@ func (t *trigger) GetEpochChangeProposed() bool { } func (t *trigger) shouldTriggerEpochStart(currentRound uint64, currentNonce uint64) bool { - isZeroEpochEdgeCase := currentNonce < minimumNonceToStartEpoch + epochStartNonce := t.epochStartMeta.GetNonce() + hasMinBlocksInEpoch := currentNonce >= epochStartNonce+minimumBlocksPerEpoch isNormalEpochStart := currentRound > t.currEpochStartRound+t.getRoundsPerEpoch(t.epoch)-t.getOffsetPerEpoch(t.epoch) isWithEarlyEndOfEpoch := currentRound >= t.nextEpochStartRound - shouldTriggerEpochStart := (isNormalEpochStart || isWithEarlyEndOfEpoch) && !isZeroEpochEdgeCase + shouldTriggerEpochStart := (isNormalEpochStart || isWithEarlyEndOfEpoch) && hasMinBlocksInEpoch return shouldTriggerEpochStart } diff --git a/epochStart/metachain/trigger_test.go b/epochStart/metachain/trigger_test.go index 1ef55403afe..7483fae6a99 100644 --- a/epochStart/metachain/trigger_test.go +++ b/epochStart/metachain/trigger_test.go @@ -304,12 +304,91 @@ func TestTrigger_ForceEpochStartShouldOk(t *testing.T) { assert.Equal(t, expectedRound, epochStartTrigger.nextEpochStartRound) - epochStartTrigger.Update(expectedRound, minimumNonceToStartEpoch) + epochStartTrigger.Update(expectedRound, minimumBlocksPerEpoch) isEpochStart := epochStartTrigger.IsEpochStart() assert.True(t, isEpochStart) } +func TestTrigger_ForceEpochStartShouldWaitMinimumNonceEvenWhenForced(t *testing.T) { + t.Parallel() + + arguments := createMockEpochStartTriggerArguments() + arguments.ChainParametersHandler = &chainParameters.ChainParametersHandlerStub{ + ChainParametersForEpochCalled: func(epoch uint32) (config.ChainParametersByEpochConfig, error) { + return config.ChainParametersByEpochConfig{ + MinRoundsBetweenEpochs: 20, + RoundsPerEpoch: 200, + }, nil + }, + } + + epochStartTrigger, err := NewEpochStartTrigger(arguments) + require.Nil(t, err) + + forcedRound := uint64(60) + epochStartTrigger.ForceEpochStart(forcedRound) + + epochStartTrigger.Update(forcedRound, minimumBlocksPerEpoch-1) + assert.False(t, epochStartTrigger.IsEpochStart()) + + epochStartTrigger.Update(forcedRound, minimumBlocksPerEpoch) + assert.True(t, epochStartTrigger.IsEpochStart()) +} + +func TestTrigger_UpdateShouldWaitMinimumNonceFromPreviousEpochStart(t *testing.T) { + t.Parallel() + + arguments := createMockEpochStartTriggerArguments() + epochStartTrigger, err := NewEpochStartTrigger(arguments) + require.Nil(t, err) + + epochStartNonce := uint64(100) + epochStartTrigger.epochStartMeta = &block.MetaBlock{Nonce: epochStartNonce} + + round := uint64(3) + epochStartTrigger.Update(round, epochStartNonce+minimumBlocksPerEpoch-1) + assert.False(t, epochStartTrigger.IsEpochStart()) + + epochStartTrigger.Update(round, epochStartNonce+minimumBlocksPerEpoch) + assert.True(t, epochStartTrigger.IsEpochStart()) +} + +func TestTrigger_UpdateShouldEnforceMinBlocksAfterEpochTransition(t *testing.T) { + t.Parallel() + + arguments := createMockEpochStartTriggerArguments() + epochStartTrigger, err := NewEpochStartTrigger(arguments) + require.Nil(t, err) + + // default mock: RoundsPerEpoch=2, currEpochStartRound=0 + // round 3 > 0+2 satisfies isNormalEpochStart; nonce 4 satisfies hasMinBlocksInEpoch (4 >= 0+4) + epochStartTrigger.Update(3, minimumBlocksPerEpoch) + assert.True(t, epochStartTrigger.IsEpochStart()) + + // SetProcessed moves epochStartMeta to the epoch-1-start block at nonce 500 + // this resets the baseline: next epoch needs currentNonce >= 500+4 + epochOneStartNonce := uint64(500) + epochStartTrigger.SetProcessed(&block.MetaBlock{ + Round: 3, + Nonce: epochOneStartNonce, + Epoch: 1, + EpochStart: block.EpochStart{ + LastFinalizedHeaders: []block.EpochStartShardData{{RootHash: []byte("root")}}, + }, + }, nil) + assert.False(t, epochStartTrigger.IsEpochStart()) + + // round 6 > 3+2 satisfies isNormalEpochStart for epoch 2 + // but only 3 blocks since epoch 1 start - hasMinBlocksInEpoch must block it + epochStartTrigger.Update(6, epochOneStartNonce+minimumBlocksPerEpoch-1) + assert.False(t, epochStartTrigger.IsEpochStart()) + + // 4th block since epoch 1 start - guard satisfied + epochStartTrigger.Update(6, epochOneStartNonce+minimumBlocksPerEpoch) + assert.True(t, epochStartTrigger.IsEpochStart()) +} + func TestTrigger_ForceEpochStartShouldWorkForSupernovaEpoch(t *testing.T) { t.Parallel() diff --git a/epochStart/shardchain/trigger.go b/epochStart/shardchain/trigger.go index 9a42f515fb9..a2211daa2b1 100644 --- a/epochStart/shardchain/trigger.go +++ b/epochStart/shardchain/trigger.go @@ -1317,16 +1317,26 @@ func (t *trigger) watchdogRequestEpochStartMetaBlock(ctx context.Context) { timer := time.NewTimer(watchdogTimeout) defer timer.Stop() + resetTimer := func(d time.Duration) { + if !timer.Stop() { + select { + case <-timer.C: + default: + } + } + timer.Reset(d) + } + for { select { case <-ctx.Done(): log.Debug("watchdogRequestEpochStartMetaBlock: trigger's go routine is stopping...") return case <-t.chanMetaBlockReceived: - timer.Reset(t.resetWatchdogTimeout(watchdogTimeout)) + resetTimer(t.resetWatchdogTimeout(watchdogTimeout)) case <-timer.C: t.handleWatchdogTimeout() - timer.Reset(t.resetWatchdogTimeout(watchdogTimeout)) + resetTimer(t.resetWatchdogTimeout(watchdogTimeout)) } } } @@ -1349,10 +1359,6 @@ func (t *trigger) handleWatchdogTimeout() { return } - if !t.enableEpochsHandler.IsFlagEnabledInEpoch(common.AndromedaFlag, epoch) { - return - } - log.Debug("watchdog: no metablock received for too long, requesting epoch start metablock", "current epoch", epoch, "requesting epoch", epoch+1, diff --git a/epochStart/shardchain/triggerRegistry_test.go b/epochStart/shardchain/triggerRegistry_test.go index 316fc62ac60..dd3e4730c68 100644 --- a/epochStart/shardchain/triggerRegistry_test.go +++ b/epochStart/shardchain/triggerRegistry_test.go @@ -18,6 +18,8 @@ import ( func cloneTrigger(t *trigger) *trigger { rt := &trigger{} + t.mutTrigger.RLock() + defer t.mutTrigger.RUnlock() rt.epoch = t.epoch rt.metaEpoch = t.metaEpoch @@ -68,15 +70,16 @@ func createDummyEpochStartTriggers(arguments *ArgsShardEpochStartTrigger, key [] // create a copy epochStartTrigger2 := cloneTrigger(epochStartTrigger1) + epochStartTrigger1.mutTrigger.Lock() + defer epochStartTrigger1.mutTrigger.Unlock() + epochStartTrigger1.triggerStateKey = key epochStartTrigger1.epoch = 10 epochStartTrigger1.metaEpoch = 11 epochStartTrigger1.currentRoundIndex = 800 epochStartTrigger1.epochStartRound = 650 epochStartTrigger1.epochMetaBlockHash = []byte("meta block hash") - epochStartTrigger1.mutTrigger.Lock() epochStartTrigger1.isEpochStart = false - epochStartTrigger1.mutTrigger.Unlock() epochStartTrigger1.epochFinalityAttestingRound = 680 epochStartTrigger1.cancelFunc = nil epochStartTrigger1.epochStartShardHeader = &block.Header{} @@ -98,13 +101,19 @@ func TestTrigger_LoadHeaderV1StateAfterSave(t *testing.T) { } key := []byte("key") epochStartTrigger1, epochStartTrigger2 := createDummyEpochStartTriggers(arguments, key) + + epochStartTrigger1.mutTrigger.RLock() err := epochStartTrigger1.saveState(key) + epochStartTrigger1.mutTrigger.RUnlock() + assert.Nil(t, err) - assert.NotEqual(t, epochStartTrigger1, epochStartTrigger2) + trigger1Clone := cloneTrigger(epochStartTrigger1) + assert.NotEqual(t, trigger1Clone, epochStartTrigger2) err = epochStartTrigger2.LoadState(key) assert.Nil(t, err) - assert.Equal(t, epochStartTrigger1, epochStartTrigger2) + trigger2Clone := cloneTrigger(epochStartTrigger2) + assert.Equal(t, trigger1Clone, trigger2Clone) } func TestTrigger_LoadHeaderV2StateAfterSave(t *testing.T) { @@ -126,13 +135,18 @@ func TestTrigger_LoadHeaderV2StateAfterSave(t *testing.T) { epochStartTrigger1.epochStartShardHeader = &block.HeaderV2{ Header: &block.Header{}, ScheduledRootHash: []byte("scheduled root hash")} + + epochStartTrigger1.mutTrigger.RLock() err := epochStartTrigger1.saveState(key) + epochStartTrigger1.mutTrigger.RUnlock() assert.Nil(t, err) - assert.NotEqual(t, epochStartTrigger1, epochStartTrigger2) + trigger1Clone := cloneTrigger(epochStartTrigger1) + assert.NotEqual(t, trigger1Clone, epochStartTrigger2) err = epochStartTrigger2.LoadState(key) assert.Nil(t, err) - assert.Equal(t, epochStartTrigger1, epochStartTrigger2) + trigger2Clone := cloneTrigger(epochStartTrigger2) + assert.Equal(t, trigger1Clone, trigger2Clone) } func TestTrigger_LoadStateBackwardsCompatibility(t *testing.T) { @@ -154,7 +168,11 @@ func TestTrigger_LoadStateBackwardsCompatibility(t *testing.T) { epochStartTrigger1, epochStartTrigger2 := createDummyEpochStartTriggers(arguments, key) - trig := createLegacyTriggerRegistryFromTrigger(epochStartTrigger1) + epochStartTrigger1.mutTrigger.RLock() + trigger1Clone := cloneTrigger(epochStartTrigger1) + epochStartTrigger1.mutTrigger.RUnlock() + + trig := createLegacyTriggerRegistryFromTrigger(trigger1Clone) d, _ := json.Marshal(trig) trigInternalKey := append([]byte(common.TriggerRegistryKeyPrefix), key...) @@ -190,13 +208,16 @@ func TestTrigger_LoadStateBackwardsCompatibility(t *testing.T) { require.Nil(t, err) epochStartTrigger2 := cloneTrigger(epochStartTrigger1) + epochStartTrigger1.mutTrigger.Lock() epochStartTrigger1.epoch = epoch epochStartTrigger1.triggerStateKey = key epochStartTrigger1.cancelFunc = nil + epochStartTrigger1.mutTrigger.Unlock() err = epochStartTrigger2.LoadState(key) require.Nil(t, err) - require.Equal(t, epochStartTrigger1, epochStartTrigger2) + triggerClone := cloneTrigger(epochStartTrigger1) + require.Equal(t, triggerClone, epochStartTrigger2) }) t.Run("header v2", func(t *testing.T) { @@ -225,16 +246,19 @@ func TestTrigger_LoadStateBackwardsCompatibility(t *testing.T) { require.Nil(t, err) epochStartTrigger2 := cloneTrigger(epochStartTrigger1) + epochStartTrigger1.mutTrigger.Lock() epochStartTrigger1.epoch = epoch epochStartTrigger1.triggerStateKey = key epochStartTrigger1.epochStartShardHeader = &block.HeaderV2{ Header: &block.Header{}, } epochStartTrigger1.cancelFunc = nil + epochStartTrigger1.mutTrigger.Unlock() err = epochStartTrigger2.LoadState(key) require.Nil(t, err) - require.Equal(t, epochStartTrigger1, epochStartTrigger2) + triggerClone := cloneTrigger(epochStartTrigger1) + require.Equal(t, triggerClone, epochStartTrigger2) }) t.Run("header v3", func(t *testing.T) { @@ -259,14 +283,17 @@ func TestTrigger_LoadStateBackwardsCompatibility(t *testing.T) { require.Nil(t, err) epochStartTrigger2 := cloneTrigger(epochStartTrigger1) + epochStartTrigger1.mutTrigger.Lock() epochStartTrigger1.epoch = epoch epochStartTrigger1.triggerStateKey = key epochStartTrigger1.epochStartShardHeader = &block.HeaderV3{} epochStartTrigger1.cancelFunc = nil + epochStartTrigger1.mutTrigger.Unlock() err = epochStartTrigger2.LoadState(key) require.Nil(t, err) - require.Equal(t, epochStartTrigger1, epochStartTrigger2) + triggerClone := cloneTrigger(epochStartTrigger1) + require.Equal(t, triggerClone, epochStartTrigger2) }) } diff --git a/epochStart/shardchain/trigger_test.go b/epochStart/shardchain/trigger_test.go index 0b47eca15d5..4fa9ccb54f1 100644 --- a/epochStart/shardchain/trigger_test.go +++ b/epochStart/shardchain/trigger_test.go @@ -771,6 +771,75 @@ func TestTrigger_ReceivedHeaderChangeEpochWithoutPrevHeader(t *testing.T) { require.True(t, epochStartTrigger.IsEpochStart()) } +func TestTrigger_ReceivedMetaBlock_WithoutProof(t *testing.T) { + t.Parallel() + + t.Run("receivedMetaBlock should request proof when missing", func(t *testing.T) { + t.Parallel() + + var proofRequested atomic.Int32 + var requestedHashMut sync.Mutex + var requestedHash []byte + + args := createMockShardEpochStartTriggerArguments() + args.Epoch = 5 + args.EnableEpochsHandler = &enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return flag == common.AndromedaFlag + }, + } + args.RequestHandler = &testscommon.RequestHandlerStub{ + RequestEquivalentProofByHashCalled: func(headerShard uint32, headerHash []byte) { + requestedHashMut.Lock() + requestedHash = headerHash + requestedHashMut.Unlock() + proofRequested.Add(1) + }, + } + + args.DataPool = &dataRetrieverMock.PoolsHolderStub{ + HeadersCalled: func() dataRetriever.HeadersPool { + return &mock.HeadersCacherStub{} + }, + MiniBlocksCalled: func() storage.Cacher { + return cache.NewCacherStub() + }, + CurrEpochValidatorInfoCalled: func() dataRetriever.ValidatorInfoCacher { + return &vic.ValidatorInfoCacherStub{} + }, + ProofsCalled: func() dataRetriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{ + GetProofCalled: func(_ uint32, _ []byte) (data.HeaderProofHandler, error) { + return nil, errors.New("proof not found") + }, + } + }, + } + + et, err := NewEpochStartTrigger(args) + require.Nil(t, err) + defer func() { + _ = et.Close() + }() + + metaBlockHash := []byte("metablock-hash") + et.receivedMetaBlock(&block.MetaBlock{ + Nonce: 10, + Round: 42, + Epoch: 6, + EpochStart: block.EpochStart{LastFinalizedHeaders: []block.EpochStartShardData{{}}}, + }, metaBlockHash) + + time.Sleep(10 * time.Millisecond) + + require.Equal(t, int32(1), proofRequested.Load()) + + requestedHashMut.Lock() + require.Equal(t, metaBlockHash, requestedHash) + requestedHashMut.Unlock() + }) +} + func TestTrigger_ClearMissingValidatorsInfoMapShouldWork(t *testing.T) { t.Parallel() @@ -1183,9 +1252,10 @@ func TestTrigger_WatchdogRequestEpochStartMetaBlock(t *testing.T) { require.Equal(t, int32(0), called.Load()) }) - t.Run("skips when Andromeda disabled", func(t *testing.T) { + t.Run("fires even when Andromeda disabled", func(t *testing.T) { t.Parallel() + var requestedEpoch atomic.Uint32 var called atomic.Int32 args := createMockShardEpochStartTriggerArguments() args.RoundHandler = &mock.RoundHandlerStub{ @@ -1196,8 +1266,10 @@ func TestTrigger_WatchdogRequestEpochStartMetaBlock(t *testing.T) { return 100 }, } + args.Epoch = 5 args.RequestHandler = &testscommon.RequestHandlerStub{ RequestStartOfEpochMetaBlockCalled: func(epoch uint32) { + requestedEpoch.Store(epoch) called.Add(1) }, } @@ -1215,7 +1287,8 @@ func TestTrigger_WatchdogRequestEpochStartMetaBlock(t *testing.T) { time.Sleep(200 * time.Millisecond) - require.Equal(t, int32(0), called.Load()) + require.Greater(t, called.Load(), int32(0)) + require.Equal(t, uint32(6), requestedEpoch.Load()) }) t.Run("stops on context cancellation", func(t *testing.T) { diff --git a/factory/consensus/consensusComponents.go b/factory/consensus/consensusComponents.go index 8862fe21de6..ceac0769327 100644 --- a/factory/consensus/consensusComponents.go +++ b/factory/consensus/consensusComponents.go @@ -161,7 +161,6 @@ func (ccf *consensusComponentsFactory) Create() (*consensusComponents, error) { ccf.processComponents.ShardCoordinator(), ccf.cryptoComponents.PeerSignatureHandler(), ccf.dataComponents.Datapool().Headers(), - ccf.dataComponents.Datapool().Headers(), ccf.dataComponents.Datapool().Proofs(), ccf.coreComponents.EnableEpochsHandler(), ccf.processComponents.InterceptorsContainer(), @@ -219,6 +218,7 @@ func (ccf *consensusComponentsFactory) Create() (*consensusComponents, error) { NetworkShardingCollector: ccf.processComponents.PeerShardMapper(), AntifloodHandler: ccf.networkComponents.InputAntiFloodHandler(), PoolAdder: ccf.dataComponents.Datapool().MiniBlocks(), + WhiteListHandler: ccf.processComponents.WhiteListHandler(), SignatureSize: ccf.config.ValidatorPubkeyConverter.SignatureLength, PublicKeySize: ccf.config.ValidatorPubkeyConverter.Length, AppStatusHandler: ccf.statusCoreComponents.AppStatusHandler(), @@ -674,6 +674,7 @@ func (ccf *consensusComponentsFactory) createMetaChainBootstrapper() (process.Bo EpochBootstrapper: ccf.processComponents.EpochStartTrigger(), ValidatorAccountsDB: ccf.stateComponents.PeerAccounts(), ValidatorStatisticsDBSyncer: validatorAccountsDBSyncer, + Watchdog: ccf.coreComponents.Watchdog(), } return sync.NewMetaBootstrap(argsMetaBootstrapper) diff --git a/factory/consensus/consensusComponents_test.go b/factory/consensus/consensusComponents_test.go index d89121da7f1..100ad2d1b8b 100644 --- a/factory/consensus/consensusComponents_test.go +++ b/factory/consensus/consensusComponents_test.go @@ -150,6 +150,7 @@ func createMockConsensusComponentsFactoryArgs() consensusComp.ConsensusComponent HeaderSigVerif: &consensusMocks.HeaderSigVerifierMock{}, HeaderIntegrVerif: &mock.HeaderIntegrityVerifierStub{}, FallbackHdrValidator: &testscommon.FallBackHeaderValidatorStub{}, + WhiteListHandlerInternal: &testscommon.WhiteListHandlerStub{}, SentSignaturesTrackerInternal: &testscommon.SentSignatureTrackerStub{}, BlockchainHookField: &testscommon.BlockChainHookStub{}, ExecManager: &processMocks.ExecutionManagerMock{}, diff --git a/factory/heartbeat/heartbeatV2Components_test.go b/factory/heartbeat/heartbeatV2Components_test.go index f605bc67b9c..b6e3ca53975 100644 --- a/factory/heartbeat/heartbeatV2Components_test.go +++ b/factory/heartbeat/heartbeatV2Components_test.go @@ -93,7 +93,7 @@ func createMockHeartbeatV2ComponentsFactoryArgs() heartbeatComp.ArgHeartbeatV2Co func createMockConfig() config.Config { return config.Config{ HeartbeatV2: config.HeartbeatV2Config{ - PeerAuthenticationTimeBetweenSendsInSec: 1, + PeerAuthenticationTimeBetweenSendsInSec: 10, PeerAuthenticationTimeBetweenSendsWhenErrorInSec: 1, PeerAuthenticationTimeThresholdBetweenSends: 0.1, HeartbeatTimeBetweenSendsInSec: 1, diff --git a/factory/processing/blockProcessorCreator.go b/factory/processing/blockProcessorCreator.go index 5d418679096..3e6b4232c82 100644 --- a/factory/processing/blockProcessorCreator.go +++ b/factory/processing/blockProcessorCreator.go @@ -11,17 +11,13 @@ import ( vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/multiversx/mx-chain-vm-common-go/parsers" - "github.com/multiversx/mx-chain-go/epochStart/metachain/disabled" - - "github.com/multiversx/mx-chain-go/process/estimator" - "github.com/multiversx/mx-chain-go/process/missingData" - "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/dataRetriever" debugFactory "github.com/multiversx/mx-chain-go/debug/factory" "github.com/multiversx/mx-chain-go/epochStart" metachainEpochStart "github.com/multiversx/mx-chain-go/epochStart/metachain" + "github.com/multiversx/mx-chain-go/epochStart/metachain/disabled" "github.com/multiversx/mx-chain-go/epochStart/notifier" mainFactory "github.com/multiversx/mx-chain-go/factory" factoryDisabled "github.com/multiversx/mx-chain-go/factory/disabled" @@ -37,9 +33,11 @@ import ( "github.com/multiversx/mx-chain-go/process/block/postprocess" "github.com/multiversx/mx-chain-go/process/block/preprocess" "github.com/multiversx/mx-chain-go/process/coordinator" + "github.com/multiversx/mx-chain-go/process/estimator" "github.com/multiversx/mx-chain-go/process/factory" "github.com/multiversx/mx-chain-go/process/factory/metachain" "github.com/multiversx/mx-chain-go/process/factory/shard" + "github.com/multiversx/mx-chain-go/process/missingData" "github.com/multiversx/mx-chain-go/process/rewardTransaction" "github.com/multiversx/mx-chain-go/process/scToProtocol" "github.com/multiversx/mx-chain-go/process/smartContract" @@ -638,6 +636,7 @@ func (pcf *processComponentsFactory) newShardBlockProcessor( AccountsProposal: pcf.state.AccountsAdapterProposal(), ForkDetector: forkDetector, NodesCoordinator: pcf.nodesCoordinator, + MiniBlockTracker: pcf.miniBlockTracker, FeeHandler: txFeeHandler, RequestHandler: requestHandler, BlockChainHook: vmFactory.BlockChainHookImpl(), @@ -1228,6 +1227,7 @@ func (pcf *processComponentsFactory) newMetaBlockProcessor( HeaderValidator: headerValidator, BootStorer: bootStorer, BlockTracker: blockTracker, + MiniBlockTracker: pcf.miniBlockTracker, FeeHandler: txFeeHandler, BlockSizeThrottler: blockSizeThrottler, HistoryRepository: pcf.historyRepo, diff --git a/factory/processing/processComponents.go b/factory/processing/processComponents.go index 24a0b4898bd..40959d170c9 100644 --- a/factory/processing/processComponents.go +++ b/factory/processing/processComponents.go @@ -119,6 +119,7 @@ type processComponents struct { fullArchivePeerShardMapper process.NetworkShardingCollector apiTransactionEvaluator factory.TransactionEvaluator miniBlocksPoolCleaner process.PoolsCleaner + miniBlockTracker process.MiniBlockTracker txsPoolCleaner process.PoolsCleaner fallbackHeaderValidator process.FallbackHeaderValidator whiteListHandler process.WhiteListHandler @@ -181,6 +182,7 @@ type ProcessComponentsFactoryArgs struct { } type processComponentsFactory struct { + miniBlockTracker process.MiniBlockTracker config config.Config roundConfig config.RoundConfig epochConfig config.EpochConfig @@ -529,14 +531,16 @@ func (pcf *processComponentsFactory) Create() (*processComponents, error) { txsPoolsCleaner.StartCleaning() - _, err = track.NewMiniBlockTrack( + miniBlockTracker, err := track.NewMiniBlockTrack( pcf.data.Datapool(), + blockTracker, pcf.bootstrapComponents.ShardCoordinator(), pcf.whiteListHandler, ) if err != nil { return nil, err } + pcf.miniBlockTracker = miniBlockTracker hardforkTrigger, err := pcf.createHardforkTrigger(epochStartTrigger) if err != nil { @@ -811,6 +815,7 @@ func (pcf *processComponentsFactory) Create() (*processComponents, error) { fullArchivePeerShardMapper: fullArchivePeerShardMapper, apiTransactionEvaluator: apiTransactionEvaluator, miniBlocksPoolCleaner: mbsPoolsCleaner, + miniBlockTracker: miniBlockTracker, txsPoolCleaner: txsPoolsCleaner, fallbackHeaderValidator: fallbackHeaderValidator, whiteListHandler: pcf.whiteListHandler, @@ -1458,7 +1463,7 @@ func (pcf *processComponentsFactory) newResolverContainerFactory() (dataRetrieve return disabledResolversContainer.NewDisabledResolversContainerFactory(), nil } - payloadValidator, err := validator.NewPeerAuthenticationPayloadValidator(pcf.config.HeartbeatV2.HeartbeatExpiryTimespanInSec) + payloadValidator, err := validator.NewPeerAuthenticationPayloadValidator(pcf.config.HeartbeatV2.PeerAuthenticationTimeBetweenSendsInSec) if err != nil { return nil, err } @@ -1759,38 +1764,41 @@ func (pcf *processComponentsFactory) newShardInterceptorContainerFactory( ) (process.InterceptorsContainerFactory, process.TimeCacher, error) { headerBlackList := cache.NewTimeCache(timeSpanForBadHeaders) shardInterceptorsContainerFactoryArgs := interceptorscontainer.CommonInterceptorsContainerFactoryArgs{ - CoreComponents: pcf.coreData, - CryptoComponents: pcf.crypto, - Accounts: pcf.state.AccountsAdapterAPI(), - ShardCoordinator: pcf.bootstrapComponents.ShardCoordinator(), - NodesCoordinator: pcf.nodesCoordinator, - MainMessenger: pcf.network.NetworkMessenger(), - FullArchiveMessenger: pcf.network.FullArchiveNetworkMessenger(), - Store: pcf.data.StorageService(), - DataPool: pcf.data.Datapool(), - MaxTxNonceDeltaAllowed: common.MaxTxNonceDeltaAllowed, - TxFeeHandler: pcf.coreData.EconomicsData(), - BlockBlackList: headerBlackList, - HeaderSigVerifier: headerSigVerifier, - HeaderIntegrityVerifier: headerIntegrityVerifier, - ValidityAttester: validityAttester, - EpochStartTrigger: epochStartTrigger, - WhiteListHandler: pcf.whiteListHandler, - WhiteListerVerifiedTxs: pcf.whiteListerVerifiedTxs, - AntifloodHandler: pcf.network.InputAntiFloodHandler(), - ArgumentsParser: smartContract.NewArgumentParser(), - PreferredPeersHolder: pcf.network.PreferredPeersHolderHandler(), - SizeCheckDelta: pcf.config.Marshalizer.SizeCheckDelta, - RequestHandler: requestHandler, - PeerSignatureHandler: pcf.crypto.PeerSignatureHandler(), - SignaturesHandler: pcf.network.NetworkMessenger(), - HeartbeatExpiryTimespanInSec: pcf.config.HeartbeatV2.HeartbeatExpiryTimespanInSec, - MainPeerShardMapper: mainPeerShardMapper, - FullArchivePeerShardMapper: fullArchivePeerShardMapper, - HardforkTrigger: hardforkTrigger, - NodeOperationMode: nodeOperationMode, - InterceptedDataVerifierFactory: pcf.interceptedDataVerifierFactory, - Config: pcf.config, + CoreComponents: pcf.coreData, + CryptoComponents: pcf.crypto, + Accounts: pcf.state.AccountsAdapterAPI(), + ShardCoordinator: pcf.bootstrapComponents.ShardCoordinator(), + NodesCoordinator: pcf.nodesCoordinator, + MainMessenger: pcf.network.NetworkMessenger(), + FullArchiveMessenger: pcf.network.FullArchiveNetworkMessenger(), + Store: pcf.data.StorageService(), + DataPool: pcf.data.Datapool(), + MaxTxNonceDeltaAllowed: common.MaxTxNonceDeltaAllowed, + TxFeeHandler: pcf.coreData.EconomicsData(), + BlockBlackList: headerBlackList, + HeaderSigVerifier: headerSigVerifier, + HeaderIntegrityVerifier: headerIntegrityVerifier, + ValidityAttester: validityAttester, + EpochStartTrigger: epochStartTrigger, + WhiteListHandler: pcf.whiteListHandler, + WhiteListerVerifiedTxs: pcf.whiteListerVerifiedTxs, + AntifloodHandler: pcf.network.InputAntiFloodHandler(), + ArgumentsParser: smartContract.NewArgumentParser(), + PreferredPeersHolder: pcf.network.PreferredPeersHolderHandler(), + SizeCheckDelta: pcf.config.Marshalizer.SizeCheckDelta, + RequestHandler: requestHandler, + PeerSignatureHandler: pcf.crypto.PeerSignatureHandler(), + SignaturesHandler: pcf.network.NetworkMessenger(), + HeartbeatExpiryTimespanInSec: pcf.config.HeartbeatV2.HeartbeatExpiryTimespanInSec, + PeerAuthenticationTimeBetweenSendsInSec: pcf.config.HeartbeatV2.PeerAuthenticationTimeBetweenSendsInSec, + MaxAllowedTrieNodeChunks: pcf.config.Antiflood.MaxAllowedTrieNodeChunks, + TrieNodeChunksInactivityTimeout: time.Duration(pcf.config.Antiflood.TrieNodeChunksInactivityTimeoutInSec) * time.Second, + MainPeerShardMapper: mainPeerShardMapper, + FullArchivePeerShardMapper: fullArchivePeerShardMapper, + HardforkTrigger: hardforkTrigger, + NodeOperationMode: nodeOperationMode, + InterceptedDataVerifierFactory: pcf.interceptedDataVerifierFactory, + Config: pcf.config, } interceptorContainerFactory, err := interceptorscontainer.NewShardInterceptorsContainerFactory(shardInterceptorsContainerFactoryArgs) @@ -1814,38 +1822,41 @@ func (pcf *processComponentsFactory) newMetaInterceptorContainerFactory( ) (process.InterceptorsContainerFactory, process.TimeCacher, error) { headerBlackList := cache.NewTimeCache(timeSpanForBadHeaders) metaInterceptorsContainerFactoryArgs := interceptorscontainer.CommonInterceptorsContainerFactoryArgs{ - CoreComponents: pcf.coreData, - CryptoComponents: pcf.crypto, - ShardCoordinator: pcf.bootstrapComponents.ShardCoordinator(), - NodesCoordinator: pcf.nodesCoordinator, - MainMessenger: pcf.network.NetworkMessenger(), - FullArchiveMessenger: pcf.network.FullArchiveNetworkMessenger(), - Store: pcf.data.StorageService(), - DataPool: pcf.data.Datapool(), - Accounts: pcf.state.AccountsAdapterAPI(), - MaxTxNonceDeltaAllowed: common.MaxTxNonceDeltaAllowed, - TxFeeHandler: pcf.coreData.EconomicsData(), - BlockBlackList: headerBlackList, - HeaderSigVerifier: headerSigVerifier, - HeaderIntegrityVerifier: headerIntegrityVerifier, - ValidityAttester: validityAttester, - EpochStartTrigger: epochStartTrigger, - WhiteListHandler: pcf.whiteListHandler, - WhiteListerVerifiedTxs: pcf.whiteListerVerifiedTxs, - AntifloodHandler: pcf.network.InputAntiFloodHandler(), - ArgumentsParser: smartContract.NewArgumentParser(), - SizeCheckDelta: pcf.config.Marshalizer.SizeCheckDelta, - PreferredPeersHolder: pcf.network.PreferredPeersHolderHandler(), - RequestHandler: requestHandler, - PeerSignatureHandler: pcf.crypto.PeerSignatureHandler(), - SignaturesHandler: pcf.network.NetworkMessenger(), - HeartbeatExpiryTimespanInSec: pcf.config.HeartbeatV2.HeartbeatExpiryTimespanInSec, - MainPeerShardMapper: mainPeerShardMapper, - FullArchivePeerShardMapper: fullArchivePeerShardMapper, - HardforkTrigger: hardforkTrigger, - NodeOperationMode: nodeOperationMode, - InterceptedDataVerifierFactory: pcf.interceptedDataVerifierFactory, - Config: pcf.config, + CoreComponents: pcf.coreData, + CryptoComponents: pcf.crypto, + ShardCoordinator: pcf.bootstrapComponents.ShardCoordinator(), + NodesCoordinator: pcf.nodesCoordinator, + MainMessenger: pcf.network.NetworkMessenger(), + FullArchiveMessenger: pcf.network.FullArchiveNetworkMessenger(), + Store: pcf.data.StorageService(), + DataPool: pcf.data.Datapool(), + Accounts: pcf.state.AccountsAdapterAPI(), + MaxTxNonceDeltaAllowed: common.MaxTxNonceDeltaAllowed, + TxFeeHandler: pcf.coreData.EconomicsData(), + BlockBlackList: headerBlackList, + HeaderSigVerifier: headerSigVerifier, + HeaderIntegrityVerifier: headerIntegrityVerifier, + ValidityAttester: validityAttester, + EpochStartTrigger: epochStartTrigger, + WhiteListHandler: pcf.whiteListHandler, + WhiteListerVerifiedTxs: pcf.whiteListerVerifiedTxs, + AntifloodHandler: pcf.network.InputAntiFloodHandler(), + ArgumentsParser: smartContract.NewArgumentParser(), + SizeCheckDelta: pcf.config.Marshalizer.SizeCheckDelta, + PreferredPeersHolder: pcf.network.PreferredPeersHolderHandler(), + RequestHandler: requestHandler, + PeerSignatureHandler: pcf.crypto.PeerSignatureHandler(), + SignaturesHandler: pcf.network.NetworkMessenger(), + HeartbeatExpiryTimespanInSec: pcf.config.HeartbeatV2.HeartbeatExpiryTimespanInSec, + PeerAuthenticationTimeBetweenSendsInSec: pcf.config.HeartbeatV2.PeerAuthenticationTimeBetweenSendsInSec, + MaxAllowedTrieNodeChunks: pcf.config.Antiflood.MaxAllowedTrieNodeChunks, + TrieNodeChunksInactivityTimeout: time.Duration(pcf.config.Antiflood.TrieNodeChunksInactivityTimeoutInSec) * time.Second, + MainPeerShardMapper: mainPeerShardMapper, + FullArchivePeerShardMapper: fullArchivePeerShardMapper, + HardforkTrigger: hardforkTrigger, + NodeOperationMode: nodeOperationMode, + InterceptedDataVerifierFactory: pcf.interceptedDataVerifierFactory, + Config: pcf.config, } interceptorContainerFactory, err := interceptorscontainer.NewMetaInterceptorsContainerFactory(metaInterceptorsContainerFactoryArgs) @@ -1937,38 +1948,40 @@ func (pcf *processComponentsFactory) createExportFactoryHandler( nodeOperationMode = common.FullArchiveMode } argsExporter := updateFactory.ArgsExporter{ - CoreComponents: pcf.coreData, - CryptoComponents: pcf.crypto, - StatusCoreComponents: pcf.statusCoreComponents, - NetworkComponents: pcf.network, - HeaderValidator: headerValidator, - DataPool: pcf.data.Datapool(), - StorageService: pcf.data.StorageService(), - RequestHandler: requestHandler, - ShardCoordinator: pcf.bootstrapComponents.ShardCoordinator(), - ActiveAccountsDBs: accountsDBs, - ExistingResolvers: resolversContainer, - ExistingRequesters: requestersContainer, - ExportFolder: exportFolder, - ExportTriesStorageConfig: hardforkConfig.ExportTriesStorageConfig, - ExportStateStorageConfig: hardforkConfig.ExportStateStorageConfig, - ExportStateKeysConfig: hardforkConfig.ExportKeysStorageConfig, - MaxTrieLevelInMemory: pcf.config.StateTriesConfig.MaxStateTrieLevelInMemory, - WhiteListHandler: pcf.whiteListHandler, - WhiteListerVerifiedTxs: pcf.whiteListerVerifiedTxs, - MainInterceptorsContainer: mainInterceptorsContainer, - FullArchiveInterceptorsContainer: fullArchiveInterceptorsContainer, - NodesCoordinator: pcf.nodesCoordinator, - HeaderSigVerifier: headerSigVerifier, - HeaderIntegrityVerifier: pcf.bootstrapComponents.HeaderIntegrityVerifier(), - ValidityAttester: blockTracker, - RoundHandler: pcf.coreData.RoundHandler(), - MaxHardCapForMissingNodes: pcf.config.TrieSync.MaxHardCapForMissingNodes, - NumConcurrentTrieSyncers: pcf.config.TrieSync.NumConcurrentTrieSyncers, - TrieSyncerVersion: pcf.config.TrieSync.TrieSyncerVersion, - NodeOperationMode: nodeOperationMode, - InterceptedDataVerifierFactory: pcf.interceptedDataVerifierFactory, - Config: pcf.config, + CoreComponents: pcf.coreData, + CryptoComponents: pcf.crypto, + StatusCoreComponents: pcf.statusCoreComponents, + NetworkComponents: pcf.network, + HeaderValidator: headerValidator, + DataPool: pcf.data.Datapool(), + StorageService: pcf.data.StorageService(), + RequestHandler: requestHandler, + ShardCoordinator: pcf.bootstrapComponents.ShardCoordinator(), + ActiveAccountsDBs: accountsDBs, + ExistingResolvers: resolversContainer, + ExistingRequesters: requestersContainer, + ExportFolder: exportFolder, + ExportTriesStorageConfig: hardforkConfig.ExportTriesStorageConfig, + ExportStateStorageConfig: hardforkConfig.ExportStateStorageConfig, + ExportStateKeysConfig: hardforkConfig.ExportKeysStorageConfig, + MaxTrieLevelInMemory: pcf.config.StateTriesConfig.MaxStateTrieLevelInMemory, + WhiteListHandler: pcf.whiteListHandler, + WhiteListerVerifiedTxs: pcf.whiteListerVerifiedTxs, + MainInterceptorsContainer: mainInterceptorsContainer, + FullArchiveInterceptorsContainer: fullArchiveInterceptorsContainer, + NodesCoordinator: pcf.nodesCoordinator, + HeaderSigVerifier: headerSigVerifier, + HeaderIntegrityVerifier: pcf.bootstrapComponents.HeaderIntegrityVerifier(), + ValidityAttester: blockTracker, + RoundHandler: pcf.coreData.RoundHandler(), + InterceptorDebugConfig: pcf.config.Debug.InterceptorResolver, + MaxHardCapForMissingNodes: pcf.config.TrieSync.MaxHardCapForMissingNodes, + NumConcurrentTrieSyncers: pcf.config.TrieSync.NumConcurrentTrieSyncers, + TrieSyncerVersion: pcf.config.TrieSync.TrieSyncerVersion, + NodeOperationMode: nodeOperationMode, + InterceptedDataVerifierFactory: pcf.interceptedDataVerifierFactory, + PeerAuthenticationTimeBetweenSendsInSec: pcf.config.HeartbeatV2.PeerAuthenticationTimeBetweenSendsInSec, + Config: pcf.config, } return updateFactory.NewExportHandlerFactory(argsExporter) } diff --git a/go.mod b/go.mod index 6586dc95a90..8ca0e6c5c89 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module github.com/multiversx/mx-chain-go go 1.23.0 +toolchain go1.23.9 + require ( github.com/beevik/ntp v1.3.0 github.com/davecgh/go-spew v1.1.1 @@ -16,14 +18,14 @@ require ( github.com/libp2p/go-libp2p v0.38.2 github.com/libp2p/go-libp2p-pubsub v0.13.0 github.com/mitchellh/mapstructure v1.5.0 - github.com/multiversx/mx-chain-communication-go v1.3.1 - github.com/multiversx/mx-chain-core-go v1.5.0 + github.com/multiversx/mx-chain-communication-go v1.3.3-0.20260608072730-982186a1ad78 + github.com/multiversx/mx-chain-core-go v1.5.1-0.20260608073155-f1f550c8a612 github.com/multiversx/mx-chain-crypto-go v1.3.1 - github.com/multiversx/mx-chain-es-indexer-go v1.10.2 + github.com/multiversx/mx-chain-es-indexer-go v1.10.3-0.20260608081825-40e586306036 github.com/multiversx/mx-chain-logger-go v1.1.0 github.com/multiversx/mx-chain-scenario-go v1.6.0 - github.com/multiversx/mx-chain-storage-go v1.1.0 - github.com/multiversx/mx-chain-vm-common-go v1.6.6 + github.com/multiversx/mx-chain-storage-go v1.1.2-0.20260608080818-1fde35395146 + github.com/multiversx/mx-chain-vm-common-go v1.6.7 github.com/multiversx/mx-chain-vm-go v1.5.45 github.com/multiversx/mx-chain-vm-v1_2-go v1.2.69 github.com/multiversx/mx-chain-vm-v1_3-go v1.3.70 diff --git a/go.sum b/go.sum index db597d35dba..28a0b77d4a0 100644 --- a/go.sum +++ b/go.sum @@ -399,22 +399,22 @@ github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/n github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= 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.3.1 h1:rJj4FOTqacD+yaAfz61FoEtwpAYmOQFyLEHdy1YZya4= -github.com/multiversx/mx-chain-communication-go v1.3.1/go.mod h1:gDVWn6zUW6aCN1YOm/FbbT5MUmhgn/L1Rmpl8EoH3Yg= -github.com/multiversx/mx-chain-core-go v1.5.0 h1:YBxTsxBGd4hy9A3plcILu+jDy4BcQaD8oyVRDC1tz8A= -github.com/multiversx/mx-chain-core-go v1.5.0/go.mod h1:IO+vspNan+gT0WOHnJ95uvWygiziHZvfXpff6KnxV7g= +github.com/multiversx/mx-chain-communication-go v1.3.3-0.20260608072730-982186a1ad78 h1:vdYSj8Jj83H5wMkQOcDKvBXf2yVLXgVmA1IthHuv3aY= +github.com/multiversx/mx-chain-communication-go v1.3.3-0.20260608072730-982186a1ad78/go.mod h1:gDVWn6zUW6aCN1YOm/FbbT5MUmhgn/L1Rmpl8EoH3Yg= +github.com/multiversx/mx-chain-core-go v1.5.1-0.20260608073155-f1f550c8a612 h1:Hol8/gBD3d84kIuVsss+1Zx+sUIOpshU/wHC1d2DFVI= +github.com/multiversx/mx-chain-core-go v1.5.1-0.20260608073155-f1f550c8a612/go.mod h1:IO+vspNan+gT0WOHnJ95uvWygiziHZvfXpff6KnxV7g= github.com/multiversx/mx-chain-crypto-go v1.3.1 h1:tCoGkfiv0wz97kuW6AZPW4RVL0Yp7PBo8NKQj9f2oh4= github.com/multiversx/mx-chain-crypto-go v1.3.1/go.mod h1:nPIkxxzyTP8IquWKds+22Q2OJ9W7LtusC7cAosz7ojM= -github.com/multiversx/mx-chain-es-indexer-go v1.10.2 h1:mLFRUpZ2bWeYplU1e0kb318kk1x7AV9owq5B4XRdOqE= -github.com/multiversx/mx-chain-es-indexer-go v1.10.2/go.mod h1:HtHJx2XGnFTZE2GBcWxDiBr/DIuDsmb5R38+P3Jp87c= +github.com/multiversx/mx-chain-es-indexer-go v1.10.3-0.20260608081825-40e586306036 h1:a0euQ0LrFvP3y7Uf4CJ39tXBo0tsTEzPLrvJriuO2oA= +github.com/multiversx/mx-chain-es-indexer-go v1.10.3-0.20260608081825-40e586306036/go.mod h1:LbUYOxarVj0sHG9vPMNaiGuCRmKvEDAUxY4pnBAtzX8= github.com/multiversx/mx-chain-logger-go v1.1.0 h1:97x84A6L4RfCa6YOx1HpAFxZp1cf/WI0Qh112whgZNM= github.com/multiversx/mx-chain-logger-go v1.1.0/go.mod h1:K9XgiohLwOsNACETMNL0LItJMREuEvTH6NsoXWXWg7g= github.com/multiversx/mx-chain-scenario-go v1.6.0 h1:cwDFuS1pSc4YXnfiKKDTEb+QDY4fulPQaiRgIebnKxI= github.com/multiversx/mx-chain-scenario-go v1.6.0/go.mod h1:GrSYu1SnMvsIm9djUz1X13224HcvdY6Nb5KHNT3xZPA= -github.com/multiversx/mx-chain-storage-go v1.1.0 h1:M1Y9DqMrJ62s7Zw31+cyuqsnPIvlG4jLBJl5WzeZLe8= -github.com/multiversx/mx-chain-storage-go v1.1.0/go.mod h1:o6Jm7cjfPmcc6XpyihYWrd6sx3sgqwurrunw3ZrfyxI= -github.com/multiversx/mx-chain-vm-common-go v1.6.6 h1:BJSQndP8KSqcSIi47wQwQy3uBIn5rbT3213eJroVaog= -github.com/multiversx/mx-chain-vm-common-go v1.6.6/go.mod h1:Lc7r4VDPYRDS0CVIaWAoLtf3YQn6PZEYHv4QtaOE2Z0= +github.com/multiversx/mx-chain-storage-go v1.1.2-0.20260608080818-1fde35395146 h1:ECkaR/1fkcJhw1YMi1gqeBDEmLeyM7/veNAqyLQ+LWo= +github.com/multiversx/mx-chain-storage-go v1.1.2-0.20260608080818-1fde35395146/go.mod h1:o6Jm7cjfPmcc6XpyihYWrd6sx3sgqwurrunw3ZrfyxI= +github.com/multiversx/mx-chain-vm-common-go v1.6.7 h1:oX2/RMXdhqUkJSebK+cosknBjNBX0DFAEDR6ZqNTN80= +github.com/multiversx/mx-chain-vm-common-go v1.6.7/go.mod h1:Lc7r4VDPYRDS0CVIaWAoLtf3YQn6PZEYHv4QtaOE2Z0= github.com/multiversx/mx-chain-vm-go v1.5.45 h1:0JBB/imgI8wa6muXtdGMDrW685sdsRwH/+gMPuX96OU= github.com/multiversx/mx-chain-vm-go v1.5.45/go.mod h1:Qc2Sckw+EfQwnapkzghFfhuUAOGv29oSZgvj8LJ+xWQ= github.com/multiversx/mx-chain-vm-v1_2-go v1.2.69 h1:5gSR3IMw1mcp/v5oO+vZ5YOyWO8w7O2qKhCKNPwsWNE= diff --git a/integrationTests/benchmarks/loadFromTrie_test.go b/integrationTests/benchmarks/loadFromTrie_test.go index 8b2d2736b1a..c9c051a7f84 100644 --- a/integrationTests/benchmarks/loadFromTrie_test.go +++ b/integrationTests/benchmarks/loadFromTrie_test.go @@ -156,7 +156,7 @@ func getNewTrieStorage() storage.Storer { maxBatchSize := 40000 maxNumOpenedFiles := 10 - db, _ := database.NewSerialDB("AccountsTrie", batchDelaySeconds, maxBatchSize, maxNumOpenedFiles) + db, _ := database.NewSerialDB("AccountsTrie", batchDelaySeconds, maxBatchSize, maxNumOpenedFiles, 0) cacher, _ := storageunit.NewCache(storageunit.CacheConfig{ Type: storageunit.SizeLRUCache, Capacity: 1, diff --git a/integrationTests/chainSimulator/mempool/mempool_test.go b/integrationTests/chainSimulator/mempool/mempool_test.go index f6d22147c5f..eee80ec0775 100644 --- a/integrationTests/chainSimulator/mempool/mempool_test.go +++ b/integrationTests/chainSimulator/mempool/mempool_test.go @@ -17,7 +17,7 @@ import ( "github.com/multiversx/mx-chain-go/common/holders" stateMock "github.com/multiversx/mx-chain-go/testscommon/state" - "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" "github.com/multiversx/mx-chain-go/txcache" "github.com/multiversx/mx-chain-go/config" @@ -504,7 +504,7 @@ func TestMempoolWithChainSimulator_Eviction(t *testing.T) { func Test_Selection_ShouldNotSelectSameTransactionsWithSameSender(t *testing.T) { t.Parallel() - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -522,12 +522,12 @@ func Test_Selection_ShouldNotSelectSameTransactionsWithSameSender(t *testing.T) }, } - selectionSession := txcachemocks.NewSelectionSessionMockWithAccounts(accounts) + selectionSession := mempool.NewSelectionSessionMockWithAccounts(accounts) selectionSession.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) accountsProvider.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } @@ -609,7 +609,7 @@ func Test_Selection_ShouldNotSelectSameTransactionsWithSameSender(t *testing.T) func Test_Selection_ShouldNotSelectSameTransactionsWithDifferentSenders(t *testing.T) { t.Parallel() - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -631,12 +631,12 @@ func Test_Selection_ShouldNotSelectSameTransactionsWithDifferentSenders(t *testi }, } - selectionSession := txcachemocks.NewSelectionSessionMockWithAccounts(accounts) + selectionSession := mempool.NewSelectionSessionMockWithAccounts(accounts) selectionSession.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) accountsProvider.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } @@ -753,7 +753,7 @@ func Test_Selection_ShouldNotSelectSameTransactionsWithDifferentSenders(t *testi func Test_Selection_ShouldNotSelectSameTransactionsWithManyTransactions(t *testing.T) { t.Parallel() - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -778,12 +778,12 @@ func Test_Selection_ShouldNotSelectSameTransactionsWithManyTransactions(t *testi }, } - selectionSession := txcachemocks.NewSelectionSessionMockWithAccounts(accounts) + selectionSession := mempool.NewSelectionSessionMockWithAccounts(accounts) selectionSession.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) accountsProvider.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } @@ -851,7 +851,7 @@ func Test_Selection_ShouldNotSelectSameTransactionsWithManyTransactions(t *testi func Test_Selection_ProposeEmptyBlocks(t *testing.T) { t.Parallel() - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -876,12 +876,12 @@ func Test_Selection_ProposeEmptyBlocks(t *testing.T) { }, } - selectionSession := txcachemocks.NewSelectionSessionMockWithAccounts(accounts) + selectionSession := mempool.NewSelectionSessionMockWithAccounts(accounts) selectionSession.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) accountsProvider.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } @@ -973,7 +973,7 @@ func Test_Selection_ProposeBlocksWithSameNonceToTriggerForkScenarios(t *testing. t.Parallel() t.Run("should work with only one proposed block being replaced", func(t *testing.T) { - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -998,12 +998,12 @@ func Test_Selection_ProposeBlocksWithSameNonceToTriggerForkScenarios(t *testing. }, } - selectionSession := txcachemocks.NewSelectionSessionMockWithAccounts(accounts) + selectionSession := mempool.NewSelectionSessionMockWithAccounts(accounts) selectionSession.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) accountsProvider.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } @@ -1099,7 +1099,7 @@ func Test_Selection_ProposeBlocksWithSameNonceToTriggerForkScenarios(t *testing. }) t.Run("should work with many proposed blocks being replaced", func(t *testing.T) { - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -1128,12 +1128,12 @@ func Test_Selection_ProposeBlocksWithSameNonceToTriggerForkScenarios(t *testing. }, } - selectionSession := txcachemocks.NewSelectionSessionMockWithAccounts(accounts) + selectionSession := mempool.NewSelectionSessionMockWithAccounts(accounts) selectionSession.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) accountsProvider.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } @@ -1275,7 +1275,7 @@ func Test_Selection_ProposeBlocksWithSameNonceToTriggerForkScenarios(t *testing. func Test_Selection_ShouldNotSelectSameTransactionsWithManyTransactionsAndExecutedBlockNotification(t *testing.T) { t.Parallel() - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -1298,12 +1298,12 @@ func Test_Selection_ShouldNotSelectSameTransactionsWithManyTransactionsAndExecut }, } - selectionSession := txcachemocks.NewSelectionSessionMockWithAccounts(accounts) + selectionSession := mempool.NewSelectionSessionMockWithAccounts(accounts) selectionSession.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) accountsProvider.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } @@ -1397,7 +1397,7 @@ func Test_Selection_ShouldNotSelectSameTransactionsWithManyTransactionsAndExecut func Test_Selection_ProposeEmptyBlocksAndExecutedBlockNotification(t *testing.T) { t.Parallel() - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -1417,12 +1417,12 @@ func Test_Selection_ProposeEmptyBlocksAndExecutedBlockNotification(t *testing.T) }, } - selectionSession := txcachemocks.NewSelectionSessionMockWithAccounts(accounts) + selectionSession := mempool.NewSelectionSessionMockWithAccounts(accounts) selectionSession.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) accountsProvider.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } @@ -1554,7 +1554,7 @@ func Test_Selection_ProposeEmptyBlocksAndExecutedBlockNotification(t *testing.T) func Test_Selection_WithRemovingProposedBlocks(t *testing.T) { t.Parallel() - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -1583,12 +1583,12 @@ func Test_Selection_WithRemovingProposedBlocks(t *testing.T) { }, } - selectionSession := txcachemocks.NewSelectionSessionMockWithAccounts(accounts) + selectionSession := mempool.NewSelectionSessionMockWithAccounts(accounts) selectionSession.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) accountsProvider.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } @@ -1688,7 +1688,7 @@ func Test_Selection_WithRemovingProposedBlocks(t *testing.T) { func Test_SimulateSelection_ShouldNotRemoveProposedBlocks(t *testing.T) { t.Parallel() - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -1717,12 +1717,12 @@ func Test_SimulateSelection_ShouldNotRemoveProposedBlocks(t *testing.T) { }, } - selectionSession := txcachemocks.NewSelectionSessionMockWithAccounts(accounts) + selectionSession := mempool.NewSelectionSessionMockWithAccounts(accounts) selectionSession.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) accountsProvider.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } @@ -1805,7 +1805,7 @@ func Test_SimulateSelection_ShouldNotRemoveProposedBlocks(t *testing.T) { func Test_Selection_MaxTrackedBlocksReached(t *testing.T) { t.Parallel() - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(txcache.ConfigSourceMe{ Name: "test", NumChunks: 16, @@ -1843,12 +1843,12 @@ func Test_Selection_MaxTrackedBlocksReached(t *testing.T) { }, } - selectionSession := txcachemocks.NewSelectionSessionMockWithAccounts(accounts) + selectionSession := mempool.NewSelectionSessionMockWithAccounts(accounts) selectionSession.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) accountsProvider.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } @@ -1973,7 +1973,7 @@ func Test_Selection_MaxTrackedBlocksReached(t *testing.T) { func Test_SelectionWhenFeeExceedsBalanceWithMax3TxsSelected(t *testing.T) { t.Parallel() - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -2002,12 +2002,12 @@ func Test_SelectionWhenFeeExceedsBalanceWithMax3TxsSelected(t *testing.T) { }, } - selectionSession := txcachemocks.NewSelectionSessionMockWithAccounts(accounts) + selectionSession := mempool.NewSelectionSessionMockWithAccounts(accounts) selectionSession.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) accountsProvider.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } @@ -2131,7 +2131,7 @@ func Test_SelectionWhenFeeExceedsBalanceWithMax3TxsSelected(t *testing.T) { func Test_SelectionWhenFeeExceedsBalanceWithMax2TxsSelected(t *testing.T) { t.Parallel() - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -2160,12 +2160,12 @@ func Test_SelectionWhenFeeExceedsBalanceWithMax2TxsSelected(t *testing.T) { }, } - selectionSession := txcachemocks.NewSelectionSessionMockWithAccounts(accounts) + selectionSession := mempool.NewSelectionSessionMockWithAccounts(accounts) selectionSession.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) accountsProvider.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } @@ -2289,7 +2289,7 @@ func Test_SelectionWhenFeeExceedsBalanceWithMax2TxsSelected(t *testing.T) { func Test_SelectionWithRootHashMismatch(t *testing.T) { t.Parallel() - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(txcache.ConfigSourceMe{ Name: "test", NumChunks: 16, @@ -2327,7 +2327,7 @@ func Test_SelectionWithRootHashMismatch(t *testing.T) { }, } - selectionSession := txcachemocks.NewSelectionSessionMockWithAccounts(accounts) + selectionSession := mempool.NewSelectionSessionMockWithAccounts(accounts) // keep the same root hash with the one used on the OnExecutedBlock to avoid root hash mismatch on selection selectionSession.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil @@ -2374,7 +2374,7 @@ func Test_SelectionWithRootHashMismatch(t *testing.T) { func Test_SelectionWithAliceRelayerAndSenderOnSameTxs(t *testing.T) { t.Parallel() - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -2390,13 +2390,13 @@ func Test_SelectionWithAliceRelayerAndSenderOnSameTxs(t *testing.T) { }, } - selectionSession := txcachemocks.NewSelectionSessionMockWithAccounts(accounts) + selectionSession := mempool.NewSelectionSessionMockWithAccounts(accounts) // keep the same root hash with the one used on the OnExecutedBlock to avoid root hash mismatch on selection selectionSession.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) accountsProvider.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } @@ -2484,7 +2484,7 @@ func Test_SelectionWithAliceRelayerAndSenderOnSameTxs(t *testing.T) { func Test_SelectionWithAliceSenderAndThenRelayerOnDifferentTxs(t *testing.T) { t.Parallel() - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -2504,13 +2504,13 @@ func Test_SelectionWithAliceSenderAndThenRelayerOnDifferentTxs(t *testing.T) { }, } - selectionSession := txcachemocks.NewSelectionSessionMockWithAccounts(accounts) + selectionSession := mempool.NewSelectionSessionMockWithAccounts(accounts) // keep the same root hash with the one used on the OnExecutedBlock to avoid root hash mismatch on selection selectionSession.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) accountsProvider.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } @@ -2747,7 +2747,7 @@ func TestMempoolWithChainSimulator_Selection_InstantChangeGuardian(t *testing.T) func TestMempoolWithChainSimulator_Selection_InstantChangeGuardian_ReplaceHeader(t *testing.T) { t.Parallel() - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -2770,7 +2770,7 @@ func TestMempoolWithChainSimulator_Selection_InstantChangeGuardian_ReplaceHeader }, } - selectionSession := txcachemocks.NewSelectionSessionMockWithAccounts(accounts) + selectionSession := mempool.NewSelectionSessionMockWithAccounts(accounts) // all transactions are correctly guarded, except the last one selectionSession.IsGuardedCalled = func(tx data.TransactionHandler) bool { return true @@ -2783,7 +2783,7 @@ func TestMempoolWithChainSimulator_Selection_InstantChangeGuardian_ReplaceHeader return []byte(testRootHash), nil } - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMockWithAccounts(accounts) accountsProvider.GetRootHashCalled = func() ([]byte, error) { return []byte(testRootHash), nil } diff --git a/integrationTests/chainSimulator/mempool/testutils_test.go b/integrationTests/chainSimulator/mempool/testutils_test.go index 7d0a5ddb3b5..3b03552fff0 100644 --- a/integrationTests/chainSimulator/mempool/testutils_test.go +++ b/integrationTests/chainSimulator/mempool/testutils_test.go @@ -23,7 +23,7 @@ import ( "github.com/multiversx/mx-chain-go/node/chainSimulator/dtos" "github.com/multiversx/mx-chain-go/process/block/preprocess" "github.com/multiversx/mx-chain-go/testscommon" - "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" "github.com/multiversx/mx-chain-go/txcache" ) @@ -326,7 +326,7 @@ func testOnProposed(t *testing.T, sw *core.StopWatch, numTxs int, numAddresses i // create some fake address for each account accounts := createFakeAddresses(numAddresses) - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -339,7 +339,7 @@ func testOnProposed(t *testing.T, sw *core.StopWatch, numTxs int, numAddresses i _ = initialAmount.Mul(numTxsAsBigInt, core.SafeMul(uint64(gasLimit), uint64(gasPrice))) _ = initialAmount.Add(initialAmount, core.SafeMul(uint64(numTxs), uint64(transferredValue))) - selectionSession := &txcachemocks.SelectionSessionMock{ + selectionSession := &mempool.SelectionSessionMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, initialAmount, true, nil }, @@ -348,7 +348,7 @@ func testOnProposed(t *testing.T, sw *core.StopWatch, numTxs int, numAddresses i }, } - accountsAdapter := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsAdapter := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, initialAmount, true, nil }, @@ -396,7 +396,7 @@ func testFirstSelection(t *testing.T, sw *core.StopWatch, numTxs int, numTxsToBe // create some fake address for each account accounts := createFakeAddresses(numAddresses) - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -409,7 +409,7 @@ func testFirstSelection(t *testing.T, sw *core.StopWatch, numTxs int, numTxsToBe _ = initialAmount.Mul(numTxsAsBigInt, core.SafeMul(uint64(gasLimit), uint64(gasPrice))) _ = initialAmount.Add(initialAmount, big.NewInt(int64(numTxs))) - selectionSession := &txcachemocks.SelectionSessionMock{ + selectionSession := &mempool.SelectionSessionMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, initialAmount, true, nil }, @@ -445,7 +445,7 @@ func testSecondSelection(t *testing.T, sw *core.StopWatch, numTxs int, numTxsToB // create some fake address for each account accounts := createFakeAddresses(numAddresses) - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -458,7 +458,7 @@ func testSecondSelection(t *testing.T, sw *core.StopWatch, numTxs int, numTxsToB _ = initialAmount.Mul(numTxsAsBigInt, core.SafeMul(uint64(gasLimit), uint64(gasPrice))) _ = initialAmount.Add(initialAmount, core.SafeMul(uint64(numTxs), uint64(transferredValue))) - selectionSession := &txcachemocks.SelectionSessionMock{ + selectionSession := &mempool.SelectionSessionMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, initialAmount, true, nil }, @@ -467,7 +467,7 @@ func testSecondSelection(t *testing.T, sw *core.StopWatch, numTxs int, numTxsToB }, } - accountsAdapter := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsAdapter := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, initialAmount, true, nil }, @@ -536,7 +536,7 @@ func testSecondSelection(t *testing.T, sw *core.StopWatch, numTxs int, numTxsToB func testSecondSelectionWithManyTxsInPool(t *testing.T, sw *core.StopWatch, numTxs int, numTxsToBeSelected int, numAddresses int) { accounts := createFakeAddresses(numAddresses) - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() txpool, err := txcache.NewTxCache(configSourceMe, host, 0) require.Nil(t, err) @@ -549,7 +549,7 @@ func testSecondSelectionWithManyTxsInPool(t *testing.T, sw *core.StopWatch, numT _ = initialAmount.Mul(numTxsAsBigInt, core.SafeMul(uint64(gasLimit), uint64(gasPrice))) _ = initialAmount.Add(initialAmount, core.SafeMul(uint64(numTxs), uint64(transferredValue))) - selectionSession := &txcachemocks.SelectionSessionMock{ + selectionSession := &mempool.SelectionSessionMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, initialAmount, true, nil }, @@ -558,7 +558,7 @@ func testSecondSelectionWithManyTxsInPool(t *testing.T, sw *core.StopWatch, numT }, } - accountsAdapter := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsAdapter := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, initialAmount, true, nil }, diff --git a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go index 42a59c4c4c3..b6dc665d510 100644 --- a/integrationTests/chainSimulator/relayedTx/relayedTx_test.go +++ b/integrationTests/chainSimulator/relayedTx/relayedTx_test.go @@ -41,7 +41,7 @@ const ( mockTxSignature = "ssig" mockRelayerTxSignature = "rsig" maxNumOfBlocksToGenerateWhenExecutingTx = 10 - roundsPerEpoch = 40 + roundsPerEpoch = 30 guardAccountCost = 250_000 extraGasLimitForGuarded = minGasLimit extraGasESDTTransfer = 250000 diff --git a/integrationTests/consensus/consensus_test.go b/integrationTests/consensus/consensus_test.go index 85ad98c97bf..e6677b0e682 100644 --- a/integrationTests/consensus/consensus_test.go +++ b/integrationTests/consensus/consensus_test.go @@ -112,7 +112,7 @@ func TestConsensusBLSWithFullProcessing_WithEquivalentProofs(t *testing.T) { } enableEpochsConfig := integrationTests.CreateEnableEpochsConfig() - enableEpochsConfig.AndromedaEnableEpoch = uint32(0) + enableEpochsConfig.AndromedaEnableEpoch = uint32(1) enableEpochsConfig.SupernovaEnableEpoch = integrationTests.UnreachableEpoch numKeysOnEachNode := 1 targetEpoch := uint32(2) diff --git a/integrationTests/interface.go b/integrationTests/interface.go index 08a63c6fa4d..3261044d67f 100644 --- a/integrationTests/interface.go +++ b/integrationTests/interface.go @@ -48,7 +48,7 @@ type NodesCoordinatorFactory interface { // NetworkShardingUpdater defines the updating methods used by the network sharding component type NetworkShardingUpdater interface { GetPeerInfo(pid core.PeerID) core.P2PPeerInfo - UpdatePeerIDPublicKeyPair(pid core.PeerID, pk []byte) + UpdatePeerIDPublicKeyPair(pid core.PeerID, pk []byte, timestamp int64) PutPeerIdShardId(pid core.PeerID, shardID uint32) UpdatePeerIDInfo(pid core.PeerID, pk []byte, shardID uint32) PutPeerIdSubType(pid core.PeerID, peerSubType core.P2PPeerSubType) diff --git a/integrationTests/longTests/storage/storagePutRemove_test.go b/integrationTests/longTests/storage/storagePutRemove_test.go index a10d0085ffc..f89459b96db 100644 --- a/integrationTests/longTests/storage/storagePutRemove_test.go +++ b/integrationTests/longTests/storage/storagePutRemove_test.go @@ -20,7 +20,7 @@ func TestPutRemove(t *testing.T) { cache, _ := storageunit.NewCache(storageunit.CacheConfig{Type: storageunit.LRUCache, Capacity: 5000, Shards: 16, SizeInBytes: 0}) dir := t.TempDir() log.Info("opened in", "directory", dir) - lvdb1, err := database.NewLevelDB(dir, 2, 1000, 10) + lvdb1, err := database.NewLevelDB(dir, 2, 1000, 10, 10) assert.NoError(t, err) defer func() { diff --git a/integrationTests/mock/networkShardingCollectorMock.go b/integrationTests/mock/networkShardingCollectorMock.go index cfd163e88ea..d3c8a17901a 100644 --- a/integrationTests/mock/networkShardingCollectorMock.go +++ b/integrationTests/mock/networkShardingCollectorMock.go @@ -33,7 +33,7 @@ func NewNetworkShardingCollectorMock() *networkShardingCollectorMock { } // UpdatePeerIDPublicKeyPair - -func (nscm *networkShardingCollectorMock) UpdatePeerIDPublicKeyPair(pid core.PeerID, pk []byte) { +func (nscm *networkShardingCollectorMock) UpdatePeerIDPublicKeyPair(pid core.PeerID, pk []byte, _ int64) { nscm.mutMaps.Lock() nscm.peerIdPkMap[pid] = pk nscm.pkPeerIdMap[string(pk)] = pid diff --git a/integrationTests/mock/peerShardMapperStub.go b/integrationTests/mock/peerShardMapperStub.go index b32a1045c7b..4f49be5a4f9 100644 --- a/integrationTests/mock/peerShardMapperStub.go +++ b/integrationTests/mock/peerShardMapperStub.go @@ -5,7 +5,7 @@ import "github.com/multiversx/mx-chain-core-go/core" // PeerShardMapperStub - type PeerShardMapperStub struct { GetLastKnownPeerIDCalled func(pk []byte) (core.PeerID, bool) - UpdatePeerIDPublicKeyPairCalled func(pid core.PeerID, pk []byte) + UpdatePeerIDPublicKeyPairCalled func(pid core.PeerID, pk []byte, timestamp int64) PutPeerIdShardIdCalled func(pid core.PeerID, shardID uint32) PutPeerIdSubTypeCalled func(pid core.PeerID, peerSubType core.P2PPeerSubType) UpdatePeerIDInfoCalled func(pid core.PeerID, pk []byte, shardID uint32) @@ -19,9 +19,9 @@ func (psms *PeerShardMapperStub) UpdatePeerIDInfo(pid core.PeerID, pk []byte, sh } // UpdatePeerIDPublicKeyPair - -func (psms *PeerShardMapperStub) UpdatePeerIDPublicKeyPair(pid core.PeerID, pk []byte) { +func (psms *PeerShardMapperStub) UpdatePeerIDPublicKeyPair(pid core.PeerID, pk []byte, timestamp int64) { if psms.UpdatePeerIDPublicKeyPairCalled != nil { - psms.UpdatePeerIDPublicKeyPairCalled(pid, pk) + psms.UpdatePeerIDPublicKeyPairCalled(pid, pk, timestamp) } } diff --git a/integrationTests/multiShard/hardFork/hardFork_test.go b/integrationTests/multiShard/hardFork/hardFork_test.go index 234dd81f063..730bde3beb9 100644 --- a/integrationTests/multiShard/hardFork/hardFork_test.go +++ b/integrationTests/multiShard/hardFork/hardFork_test.go @@ -691,6 +691,7 @@ func createHardForkExporter( CacheSpanInSec: 1, }, }, + PeerAuthenticationTimeBetweenSendsInSec: 60, } exportHandler, err := factory.NewExportHandlerFactory(argsExportHandler) diff --git a/integrationTests/multiShard/softfork/scDeploy_test.go b/integrationTests/multiShard/softfork/scDeploy_test.go index d0cdcff69a6..1374e1bb003 100644 --- a/integrationTests/multiShard/softfork/scDeploy_test.go +++ b/integrationTests/multiShard/softfork/scDeploy_test.go @@ -41,6 +41,7 @@ func TestScDeploy(t *testing.T) { enableEpochs.StakingV4Step2EnableEpoch = integrationTests.StakingV4Step2EnableEpoch enableEpochs.StakingV4Step3EnableEpoch = integrationTests.StakingV4Step3EnableEpoch enableEpochs.SupernovaEnableEpoch = integrationTests.UnreachableEpoch + enableEpochs.AndromedaEnableEpoch = integrationTests.UnreachableEpoch shardNode := integrationTests.NewTestProcessorNode(integrationTests.ArgTestProcessorNode{ MaxShards: 1, diff --git a/integrationTests/resolvers/miniblocks/miniblocks_test.go b/integrationTests/resolvers/miniblocks/miniblocks_test.go index 989dd239ec6..3b94806dc96 100644 --- a/integrationTests/resolvers/miniblocks/miniblocks_test.go +++ b/integrationTests/resolvers/miniblocks/miniblocks_test.go @@ -5,6 +5,8 @@ import ( "testing" "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/data/block" + "github.com/multiversx/mx-chain-go/integrationTests/resolvers" "github.com/multiversx/mx-chain-go/process/factory" ) @@ -21,12 +23,12 @@ func TestRequestResolveMiniblockByHashRequestingShardResolvingSameShard(t *testi nRequester.Close() nResolver.Close() }() - miniblock, hash := resolvers.CreateMiniblock(shardId, shardId) + miniblock, hash := resolvers.CreateMiniblock(shardId, shardId, block.TxBlock) - //add miniblock in pool + // add miniblock in pool _, _ = nResolver.DataPool.MiniBlocks().HasOrAdd(hash, miniblock, miniblock.Size()) - //setup header received event + // setup header received event nRequester.DataPool.MiniBlocks().RegisterHandler(func(key []byte, value interface{}) { if bytes.Equal(key, hash) { resolvers.Log.Info("received miniblock", "hash", key) @@ -34,7 +36,7 @@ func TestRequestResolveMiniblockByHashRequestingShardResolvingSameShard(t *testi } }, core.UniqueIdentifier()) - //request by hash should work + // request by hash should work requester, err := nRequester.RequestersFinder.IntraShardRequester(factory.MiniBlocksTopic) resolvers.Log.LogIfError(err) nRequester.WhiteListHandler.Add([][]byte{hash}) @@ -57,12 +59,12 @@ func TestRequestResolveMiniblockByHashRequestingShardResolvingOtherShard(t *test nRequester.Close() nResolver.Close() }() - miniblock, hash := resolvers.CreateMiniblock(shardIdResolver, shardIdRequester) + miniblock, hash := resolvers.CreateMiniblock(shardIdResolver, shardIdRequester, block.TxBlock) - //add miniblock in pool + // add miniblock in pool _, _ = nResolver.DataPool.MiniBlocks().HasOrAdd(hash, miniblock, miniblock.Size()) - //setup header received event + // setup header received event nRequester.DataPool.MiniBlocks().RegisterHandler(func(key []byte, value interface{}) { if bytes.Equal(key, hash) { resolvers.Log.Info("received miniblock", "hash", key) @@ -70,7 +72,7 @@ func TestRequestResolveMiniblockByHashRequestingShardResolvingOtherShard(t *test } }, core.UniqueIdentifier()) - //request by hash should work + // request by hash should work requester, err := nRequester.RequestersFinder.CrossShardRequester(factory.MiniBlocksTopic, shardIdResolver) resolvers.Log.LogIfError(err) nRequester.WhiteListHandler.Add([][]byte{hash}) @@ -92,12 +94,12 @@ func TestRequestResolveMiniblockByHashRequestingShardResolvingMeta(t *testing.T) nRequester.Close() nResolver.Close() }() - miniblock, hash := resolvers.CreateMiniblock(shardId, shardId) + miniblock, hash := resolvers.CreateMiniblock(shardId, shardId, block.TxBlock) - //add miniblock in pool + // add miniblock in pool _, _ = nResolver.DataPool.MiniBlocks().HasOrAdd(hash, miniblock, miniblock.Size()) - //setup header received event + // setup header received event nRequester.DataPool.MiniBlocks().RegisterHandler(func(key []byte, value interface{}) { if bytes.Equal(key, hash) { resolvers.Log.Info("received miniblock", "hash", key) @@ -105,7 +107,7 @@ func TestRequestResolveMiniblockByHashRequestingShardResolvingMeta(t *testing.T) } }, core.UniqueIdentifier()) - //request by hash should work + // request by hash should work requester, err := nRequester.RequestersFinder.CrossShardRequester(factory.MiniBlocksTopic, core.MetachainShardId) resolvers.Log.LogIfError(err) nRequester.WhiteListHandler.Add([][]byte{hash}) @@ -127,12 +129,12 @@ func TestRequestResolveMiniblockByHashRequestingMetaResolvingShard(t *testing.T) nRequester.Close() nResolver.Close() }() - miniblock, hash := resolvers.CreateMiniblock(shardId, core.MetachainShardId) + miniblock, hash := resolvers.CreateMiniblock(shardId, core.MetachainShardId, block.TxBlock) - //add miniblock in pool + // add miniblock in pool _, _ = nResolver.DataPool.MiniBlocks().HasOrAdd(hash, miniblock, miniblock.Size()) - //setup header received event + // setup header received event nRequester.DataPool.MiniBlocks().RegisterHandler(func(key []byte, value interface{}) { if bytes.Equal(key, hash) { resolvers.Log.Info("received miniblock", "hash", key) @@ -140,7 +142,7 @@ func TestRequestResolveMiniblockByHashRequestingMetaResolvingShard(t *testing.T) } }, core.UniqueIdentifier()) - //request by hash should work + // request by hash should work requester, err := nRequester.RequestersFinder.CrossShardRequester(factory.MiniBlocksTopic, shardId) resolvers.Log.LogIfError(err) nRequester.WhiteListHandler.Add([][]byte{hash}) @@ -162,12 +164,12 @@ func TestRequestResolvePeerMiniblockByHashRequestingShardResolvingSameShard(t *t nRequester.Close() nResolver.Close() }() - miniblock, hash := resolvers.CreateMiniblock(core.MetachainShardId, core.AllShardId) + miniblock, hash := resolvers.CreateMiniblock(core.MetachainShardId, core.AllShardId, block.PeerBlock) - //add miniblock in pool + // add miniblock in pool _, _ = nResolver.DataPool.MiniBlocks().HasOrAdd(hash, miniblock, miniblock.Size()) - //setup header received event + // setup header received event nRequester.DataPool.MiniBlocks().RegisterHandler(func(key []byte, value interface{}) { if bytes.Equal(key, hash) { resolvers.Log.Info("received miniblock", "hash", key) @@ -175,7 +177,7 @@ func TestRequestResolvePeerMiniblockByHashRequestingShardResolvingSameShard(t *t } }, core.UniqueIdentifier()) - //request by hash should work + // request by hash should work requester, err := nRequester.RequestersFinder.CrossShardRequester(factory.MiniBlocksTopic, core.AllShardId) resolvers.Log.LogIfError(err) nRequester.WhiteListHandler.Add([][]byte{hash}) @@ -198,12 +200,12 @@ func TestRequestResolvePeerMiniblockByHashRequestingShardResolvingOtherShard(t * nRequester.Close() nResolver.Close() }() - miniblock, hash := resolvers.CreateMiniblock(shardIdResolver, core.AllShardId) + miniblock, hash := resolvers.CreateMiniblock(core.MetachainShardId, core.AllShardId, block.PeerBlock) - //add miniblock in pool + // add miniblock in pool _, _ = nResolver.DataPool.MiniBlocks().HasOrAdd(hash, miniblock, miniblock.Size()) - //setup header received event + // setup header received event nRequester.DataPool.MiniBlocks().RegisterHandler(func(key []byte, value interface{}) { if bytes.Equal(key, hash) { resolvers.Log.Info("received miniblock", "hash", key) @@ -211,7 +213,7 @@ func TestRequestResolvePeerMiniblockByHashRequestingShardResolvingOtherShard(t * } }, core.UniqueIdentifier()) - //request by hash should work + // request by hash should work requester, err := nRequester.RequestersFinder.CrossShardRequester(factory.MiniBlocksTopic, core.AllShardId) resolvers.Log.LogIfError(err) nRequester.WhiteListHandler.Add([][]byte{hash}) @@ -233,12 +235,12 @@ func TestRequestResolvePeerMiniblockByHashRequestingShardResolvingMeta(t *testin nRequester.Close() nResolver.Close() }() - miniblock, hash := resolvers.CreateMiniblock(shardId, core.AllShardId) + miniblock, hash := resolvers.CreateMiniblock(core.MetachainShardId, core.AllShardId, block.PeerBlock) - //add miniblock in pool + // add miniblock in pool _, _ = nResolver.DataPool.MiniBlocks().HasOrAdd(hash, miniblock, miniblock.Size()) - //setup header received event + // setup header received event nRequester.DataPool.MiniBlocks().RegisterHandler(func(key []byte, value interface{}) { if bytes.Equal(key, hash) { resolvers.Log.Info("received miniblock", "hash", key) @@ -246,7 +248,7 @@ func TestRequestResolvePeerMiniblockByHashRequestingShardResolvingMeta(t *testin } }, core.UniqueIdentifier()) - //request by hash should work + // request by hash should work requester, err := nRequester.RequestersFinder.CrossShardRequester(factory.MiniBlocksTopic, core.AllShardId) resolvers.Log.LogIfError(err) nRequester.WhiteListHandler.Add([][]byte{hash}) @@ -268,12 +270,12 @@ func TestRequestResolvePeerMiniblockByHashRequestingMetaResolvingShard(t *testin nRequester.Close() nResolver.Close() }() - miniblock, hash := resolvers.CreateMiniblock(shardId, core.AllShardId) + miniblock, hash := resolvers.CreateMiniblock(core.MetachainShardId, core.AllShardId, block.PeerBlock) - //add miniblock in pool + // add miniblock in pool _, _ = nResolver.DataPool.MiniBlocks().HasOrAdd(hash, miniblock, miniblock.Size()) - //setup header received event + // setup header received event nRequester.DataPool.MiniBlocks().RegisterHandler(func(key []byte, value interface{}) { if bytes.Equal(key, hash) { resolvers.Log.Info("received miniblock", "hash", key) @@ -281,7 +283,7 @@ func TestRequestResolvePeerMiniblockByHashRequestingMetaResolvingShard(t *testin } }, core.UniqueIdentifier()) - //request by hash should work + // request by hash should work requester, err := nRequester.RequestersFinder.CrossShardRequester(factory.MiniBlocksTopic, core.AllShardId) resolvers.Log.LogIfError(err) nRequester.WhiteListHandler.Add([][]byte{hash}) diff --git a/integrationTests/resolvers/testInitializer.go b/integrationTests/resolvers/testInitializer.go index 2910c7590f7..6db05e61a3f 100644 --- a/integrationTests/resolvers/testInitializer.go +++ b/integrationTests/resolvers/testInitializer.go @@ -10,8 +10,9 @@ import ( "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-core-go/data/rewardTx" "github.com/multiversx/mx-chain-core-go/data/smartContractResult" - "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-logger-go" + + "github.com/multiversx/mx-chain-go/integrationTests" ) // Log - @@ -115,13 +116,13 @@ func CreateMetaHeader(nonce uint64, chainID []byte) (data.HeaderHandler, []byte) } // CreateMiniblock - -func CreateMiniblock(senderShardId uint32, receiverSharId uint32) (*block.MiniBlock, []byte) { +func CreateMiniblock(senderShardId uint32, receiverSharId uint32, mbType block.Type) (*block.MiniBlock, []byte) { dummyTxHash := make([]byte, integrationTests.TestHasher.Size()) miniblock := &block.MiniBlock{ TxHashes: [][]byte{dummyTxHash}, ReceiverShardID: receiverSharId, SenderShardID: senderShardId, - Type: 0, + Type: mbType, } hash, err := core.CalculateHash(integrationTests.TestMarshalizer, integrationTests.TestHasher, miniblock) diff --git a/integrationTests/singleShard/block/consensusNotAchieved/consensusNotAchieved_test.go b/integrationTests/singleShard/block/consensusNotAchieved/consensusNotAchieved_test.go index 560e8f0ae74..0ea5993550c 100644 --- a/integrationTests/singleShard/block/consensusNotAchieved/consensusNotAchieved_test.go +++ b/integrationTests/singleShard/block/consensusNotAchieved/consensusNotAchieved_test.go @@ -10,11 +10,12 @@ import ( "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" "github.com/multiversx/mx-chain-crypto-go" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/stretchr/testify/assert" + "github.com/multiversx/mx-chain-go/integrationTests" "github.com/multiversx/mx-chain-go/integrationTests/mock" testBlock "github.com/multiversx/mx-chain-go/integrationTests/singleShard/block" - logger "github.com/multiversx/mx-chain-logger-go" - "github.com/stretchr/testify/assert" ) var log = logger.GetOrCreate("consensusNotAchieved") @@ -108,7 +109,7 @@ func TestConsensus_BlockWithoutTwoThirdsPlusOneSignaturesOrWrongBitmapShouldNotB for _, nodes := range nodesMap { integrationTests.UpdateRound(nodes, round) } - bitMapEnough := []byte{11} // 11 = 0b0000 1011 so 3 signatures + bitMapEnough := []byte{0x3} // 0b0000 0011 so 2 signatures body, hdr, _ = proposeBlock(nodesMap[0][0], round, nonce, bitMapEnough) assert.NotNil(t, body) assert.NotNil(t, hdr) diff --git a/integrationTests/testConsensusNode.go b/integrationTests/testConsensusNode.go index ae39e806ebe..e79cd0d6737 100644 --- a/integrationTests/testConsensusNode.go +++ b/integrationTests/testConsensusNode.go @@ -521,37 +521,40 @@ func (tcn *TestConsensusNode) initInterceptors( whiteListerVerifiedTxs, _ := interceptors.NewWhiteListDataVerifier(cacheVerified) interceptorContainerFactoryArgs := interceptorscontainer.CommonInterceptorsContainerFactoryArgs{ - CoreComponents: coreComponents, - CryptoComponents: cryptoComponents, - Accounts: accountsAdapter, - ShardCoordinator: tcn.ShardCoordinator, - NodesCoordinator: tcn.NodesCoordinator, - MainMessenger: tcn.MainMessenger, - FullArchiveMessenger: tcn.FullArchiveMessenger, - Store: storage, - DataPool: tcn.DataPool, - MaxTxNonceDeltaAllowed: common.MaxTxNonceDeltaAllowed, - TxFeeHandler: &economicsmocks.EconomicsHandlerMock{}, - BlockBlackList: blockBlackListHandler, - HeaderSigVerifier: &consensusMocks.HeaderSigVerifierMock{}, - HeaderIntegrityVerifier: CreateHeaderIntegrityVerifier(), - ValidityAttester: blockTracker, - EpochStartTrigger: epochStartTrigger, - WhiteListHandler: whiteLstHandler, - WhiteListerVerifiedTxs: whiteListerVerifiedTxs, - AntifloodHandler: &mock.NilAntifloodHandler{}, - ArgumentsParser: smartContract.NewArgumentParser(), - PreferredPeersHolder: &p2pmocks.PeersHolderStub{}, - SizeCheckDelta: sizeCheckDelta, - RequestHandler: &testscommon.RequestHandlerStub{}, - PeerSignatureHandler: &processMock.PeerSignatureHandlerStub{}, - SignaturesHandler: &processMock.SignaturesHandlerStub{}, - HeartbeatExpiryTimespanInSec: 30, - MainPeerShardMapper: mock.NewNetworkShardingCollectorMock(), - FullArchivePeerShardMapper: mock.NewNetworkShardingCollectorMock(), - HardforkTrigger: &testscommon.HardforkTriggerStub{}, - NodeOperationMode: common.NormalOperation, - InterceptedDataVerifierFactory: interceptorsFactory.NewInterceptedDataVerifierFactory(interceptorDataVerifierArgs), + CoreComponents: coreComponents, + CryptoComponents: cryptoComponents, + Accounts: accountsAdapter, + ShardCoordinator: tcn.ShardCoordinator, + NodesCoordinator: tcn.NodesCoordinator, + MainMessenger: tcn.MainMessenger, + FullArchiveMessenger: tcn.FullArchiveMessenger, + Store: storage, + DataPool: tcn.DataPool, + MaxTxNonceDeltaAllowed: common.MaxTxNonceDeltaAllowed, + TxFeeHandler: &economicsmocks.EconomicsHandlerMock{}, + BlockBlackList: blockBlackListHandler, + HeaderSigVerifier: &consensusMocks.HeaderSigVerifierMock{}, + HeaderIntegrityVerifier: CreateHeaderIntegrityVerifier(), + ValidityAttester: blockTracker, + EpochStartTrigger: epochStartTrigger, + WhiteListHandler: whiteLstHandler, + WhiteListerVerifiedTxs: whiteListerVerifiedTxs, + AntifloodHandler: &mock.NilAntifloodHandler{}, + ArgumentsParser: smartContract.NewArgumentParser(), + PreferredPeersHolder: &p2pmocks.PeersHolderStub{}, + SizeCheckDelta: sizeCheckDelta, + RequestHandler: &testscommon.RequestHandlerStub{}, + PeerSignatureHandler: &processMock.PeerSignatureHandlerStub{}, + SignaturesHandler: &processMock.SignaturesHandlerStub{}, + HeartbeatExpiryTimespanInSec: 30, + PeerAuthenticationTimeBetweenSendsInSec: 60, + MaxAllowedTrieNodeChunks: 10, + TrieNodeChunksInactivityTimeout: 10 * time.Second, + MainPeerShardMapper: mock.NewNetworkShardingCollectorMock(), + FullArchivePeerShardMapper: mock.NewNetworkShardingCollectorMock(), + HardforkTrigger: &testscommon.HardforkTriggerStub{}, + NodeOperationMode: common.NormalOperation, + InterceptedDataVerifierFactory: interceptorsFactory.NewInterceptedDataVerifierFactory(interceptorDataVerifierArgs), Config: config.Config{ InterceptedDataVerifier: config.InterceptedDataVerifierConfig{ CacheSpanInSec: 1, diff --git a/integrationTests/testFullNode.go b/integrationTests/testFullNode.go index fdce2e36300..ae6f2f9d362 100644 --- a/integrationTests/testFullNode.go +++ b/integrationTests/testFullNode.go @@ -394,7 +394,6 @@ func (tpn *TestFullNode) initTestNodeWithArgs(args ArgTestProcessorNode, fullArg tpn.ShardCoordinator, tpn.OwnAccount.PeerSigHandler, tpn.DataPool.Headers(), - tpn.DataPool.Headers(), tpn.DataPool.Proofs(), &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, tpn.MainInterceptorsContainer, @@ -802,37 +801,40 @@ func (tfn *TestFullNode) initInterceptors( whiteListerVerifiedTxs, _ := interceptors.NewWhiteListDataVerifier(cacheVerified) interceptorContainerFactoryArgs := interceptorscontainer.CommonInterceptorsContainerFactoryArgs{ - CoreComponents: coreComponents, - CryptoComponents: cryptoComponents, - Accounts: accountsAdapter, - ShardCoordinator: tfn.ShardCoordinator, - NodesCoordinator: tfn.NodesCoordinator, - MainMessenger: tfn.MainMessenger, - FullArchiveMessenger: tfn.FullArchiveMessenger, - Store: storage, - DataPool: tfn.DataPool, - MaxTxNonceDeltaAllowed: common.MaxTxNonceDeltaAllowed, - TxFeeHandler: &economicsmocks.EconomicsHandlerMock{}, - BlockBlackList: blockBlackListHandler, - HeaderSigVerifier: &consensusMocks.HeaderSigVerifierMock{}, - HeaderIntegrityVerifier: CreateHeaderIntegrityVerifier(), - ValidityAttester: blockTracker, - EpochStartTrigger: epochStartTrigger, - WhiteListHandler: whiteLstHandler, - WhiteListerVerifiedTxs: whiteListerVerifiedTxs, - AntifloodHandler: &mock.NilAntifloodHandler{}, - ArgumentsParser: smartContract.NewArgumentParser(), - PreferredPeersHolder: &p2pmocks.PeersHolderStub{}, - SizeCheckDelta: sizeCheckDelta, - RequestHandler: &testscommon.RequestHandlerStub{}, - PeerSignatureHandler: &processMock.PeerSignatureHandlerStub{}, - SignaturesHandler: &processMock.SignaturesHandlerStub{}, - HeartbeatExpiryTimespanInSec: 30, - MainPeerShardMapper: mock.NewNetworkShardingCollectorMock(), - FullArchivePeerShardMapper: mock.NewNetworkShardingCollectorMock(), - HardforkTrigger: &testscommon.HardforkTriggerStub{}, - NodeOperationMode: common.NormalOperation, - InterceptedDataVerifierFactory: interceptorsFactory.NewInterceptedDataVerifierFactory(interceptorDataVerifierArgs), + CoreComponents: coreComponents, + CryptoComponents: cryptoComponents, + Accounts: accountsAdapter, + ShardCoordinator: tfn.ShardCoordinator, + NodesCoordinator: tfn.NodesCoordinator, + MainMessenger: tfn.MainMessenger, + FullArchiveMessenger: tfn.FullArchiveMessenger, + Store: storage, + DataPool: tfn.DataPool, + MaxTxNonceDeltaAllowed: common.MaxTxNonceDeltaAllowed, + TxFeeHandler: &economicsmocks.EconomicsHandlerMock{}, + BlockBlackList: blockBlackListHandler, + HeaderSigVerifier: &consensusMocks.HeaderSigVerifierMock{}, + HeaderIntegrityVerifier: CreateHeaderIntegrityVerifier(), + ValidityAttester: blockTracker, + EpochStartTrigger: epochStartTrigger, + WhiteListHandler: whiteLstHandler, + WhiteListerVerifiedTxs: whiteListerVerifiedTxs, + AntifloodHandler: &mock.NilAntifloodHandler{}, + ArgumentsParser: smartContract.NewArgumentParser(), + PreferredPeersHolder: &p2pmocks.PeersHolderStub{}, + SizeCheckDelta: sizeCheckDelta, + RequestHandler: &testscommon.RequestHandlerStub{}, + PeerSignatureHandler: &processMock.PeerSignatureHandlerStub{}, + SignaturesHandler: &processMock.SignaturesHandlerStub{}, + HeartbeatExpiryTimespanInSec: 30, + PeerAuthenticationTimeBetweenSendsInSec: 60, + MaxAllowedTrieNodeChunks: 10, + TrieNodeChunksInactivityTimeout: 10 * time.Second, + MainPeerShardMapper: mock.NewNetworkShardingCollectorMock(), + FullArchivePeerShardMapper: mock.NewNetworkShardingCollectorMock(), + HardforkTrigger: &testscommon.HardforkTriggerStub{}, + NodeOperationMode: common.NormalOperation, + InterceptedDataVerifierFactory: interceptorsFactory.NewInterceptedDataVerifierFactory(interceptorDataVerifierArgs), Config: config.Config{ InterceptedDataVerifier: config.InterceptedDataVerifierConfig{ CacheSpanInSec: 1, @@ -844,6 +846,7 @@ func (tfn *TestFullNode) initInterceptors( interceptorContainerFactory, err := interceptorscontainer.NewMetaInterceptorsContainerFactory(interceptorContainerFactoryArgs) if err != nil { fmt.Println(err.Error()) + return } tfn.MainInterceptorsContainer, _, err = interceptorContainerFactory.Create() @@ -880,6 +883,7 @@ func (tfn *TestFullNode) initInterceptors( interceptorContainerFactory, err := interceptorscontainer.NewShardInterceptorsContainerFactory(interceptorContainerFactoryArgs) if err != nil { fmt.Println(err.Error()) + return } tfn.MainInterceptorsContainer, _, err = interceptorContainerFactory.Create() @@ -1045,6 +1049,7 @@ func (tpn *TestFullNode) initBlockProcessor( }, }, BlockTracker: tpn.BlockTracker, + MiniBlockTracker: &testscommon.MiniBlockTrackerStub{}, BlockSizeThrottler: TestBlockSizeThrottler, HistoryRepository: tpn.HistoryRepository, GasHandler: tpn.GasHandler, @@ -1426,6 +1431,7 @@ func (tpn *TestFullNode) initBlockProcessorWithSync( }, }, BlockTracker: tpn.BlockTracker, + MiniBlockTracker: &testscommon.MiniBlockTrackerStub{}, BlockSizeThrottler: TestBlockSizeThrottler, HistoryRepository: tpn.HistoryRepository, GasHandler: tpn.GasHandler, diff --git a/integrationTests/testHeartbeatNode.go b/integrationTests/testHeartbeatNode.go index d2288b13353..25749203833 100644 --- a/integrationTests/testHeartbeatNode.go +++ b/integrationTests/testHeartbeatNode.go @@ -646,12 +646,16 @@ func (thn *TestHeartbeatNode) initInterceptors() { IntMarsh: TestMarshaller, HardforkTriggerPubKeyField: []byte(providedHardforkPubKey), }, - ShardCoordinator: thn.ShardCoordinator, - NodesCoordinator: thn.NodesCoordinator, - PeerSignatureHandler: thn.PeerSigHandler, - SignaturesHandler: &processMock.SignaturesHandlerStub{}, - HeartbeatExpiryTimespanInSec: thn.heartbeatExpiryTimespanInSec, - PeerID: thn.MainMessenger.ID(), + ShardCoordinator: thn.ShardCoordinator, + NodesCoordinator: thn.NodesCoordinator, + PeerSignatureHandler: thn.PeerSigHandler, + SignaturesHandler: &processMock.SignaturesHandlerStub{}, + HeartbeatExpiryTimespanInSec: thn.heartbeatExpiryTimespanInSec, + PeerID: thn.MainMessenger.ID(), + PeerShardMapper: thn.MainPeerShardMapper, + PeerAuthCacher: thn.DataPool.PeerAuthentications(), + PeerAuthenticationTimeBetweenSendsInSec: thn.heartbeatExpiryTimespanInSec, + CryptoComponents: GetDefaultCryptoComponents(), } thn.createPeerAuthInterceptor(argsFactory) diff --git a/integrationTests/testProcessorNode.go b/integrationTests/testProcessorNode.go index daf7510f6a7..24dd8c40045 100644 --- a/integrationTests/testProcessorNode.go +++ b/integrationTests/testProcessorNode.go @@ -957,7 +957,6 @@ func (tpn *TestProcessorNode) initTestNodeWithArgs(args ArgTestProcessorNode) { tpn.ShardCoordinator, tpn.OwnAccount.PeerSigHandler, tpn.DataPool.Headers(), - tpn.DataPool.Headers(), tpn.DataPool.Proofs(), &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, tpn.MainInterceptorsContainer, @@ -1190,7 +1189,6 @@ func (tpn *TestProcessorNode) InitializeProcessors(gasMap map[string]map[string] tpn.ShardCoordinator, tpn.OwnAccount.PeerSigHandler, tpn.DataPool.Headers(), - tpn.DataPool.Headers(), tpn.DataPool.Proofs(), &enableEpochsHandlerMock.EnableEpochsHandlerStub{}, tpn.MainInterceptorsContainer, @@ -1459,37 +1457,40 @@ func (tpn *TestProcessorNode) initInterceptors(heartbeatPk string) { coreComponents.HardforkTriggerPubKeyField = providedHardforkPk metaInterceptorContainerFactoryArgs := interceptorscontainer.CommonInterceptorsContainerFactoryArgs{ - CoreComponents: coreComponents, - CryptoComponents: cryptoComponents, - Accounts: tpn.AccntState, - ShardCoordinator: tpn.ShardCoordinator, - NodesCoordinator: tpn.NodesCoordinator, - MainMessenger: tpn.MainMessenger, - FullArchiveMessenger: tpn.FullArchiveMessenger, - Store: tpn.Storage, - DataPool: tpn.DataPool, - MaxTxNonceDeltaAllowed: common.MaxTxNonceDeltaAllowed, - TxFeeHandler: tpn.EconomicsData, - BlockBlackList: tpn.BlockBlackListHandler, - HeaderSigVerifier: tpn.HeaderSigVerifier, - HeaderIntegrityVerifier: tpn.HeaderIntegrityVerifier, - ValidityAttester: tpn.BlockTracker, - EpochStartTrigger: tpn.EpochStartTrigger, - WhiteListHandler: tpn.WhiteListHandler, - WhiteListerVerifiedTxs: tpn.WhiteListerVerifiedTxs, - AntifloodHandler: &mock.NilAntifloodHandler{}, - ArgumentsParser: smartContract.NewArgumentParser(), - PreferredPeersHolder: &p2pmocks.PeersHolderStub{}, - SizeCheckDelta: sizeCheckDelta, - RequestHandler: tpn.RequestHandler, - PeerSignatureHandler: &processMock.PeerSignatureHandlerStub{}, - SignaturesHandler: &processMock.SignaturesHandlerStub{}, - HeartbeatExpiryTimespanInSec: 30, - MainPeerShardMapper: tpn.MainPeerShardMapper, - FullArchivePeerShardMapper: tpn.FullArchivePeerShardMapper, - HardforkTrigger: tpn.HardforkTrigger, - NodeOperationMode: tpn.NodeOperationMode, - InterceptedDataVerifierFactory: interceptorsFactory.NewInterceptedDataVerifierFactory(interceptorDataVerifierArgs), + CoreComponents: coreComponents, + CryptoComponents: cryptoComponents, + Accounts: tpn.AccntState, + ShardCoordinator: tpn.ShardCoordinator, + NodesCoordinator: tpn.NodesCoordinator, + MainMessenger: tpn.MainMessenger, + FullArchiveMessenger: tpn.FullArchiveMessenger, + Store: tpn.Storage, + DataPool: tpn.DataPool, + MaxTxNonceDeltaAllowed: common.MaxTxNonceDeltaAllowed, + TxFeeHandler: tpn.EconomicsData, + BlockBlackList: tpn.BlockBlackListHandler, + HeaderSigVerifier: tpn.HeaderSigVerifier, + HeaderIntegrityVerifier: tpn.HeaderIntegrityVerifier, + ValidityAttester: tpn.BlockTracker, + EpochStartTrigger: tpn.EpochStartTrigger, + WhiteListHandler: tpn.WhiteListHandler, + WhiteListerVerifiedTxs: tpn.WhiteListerVerifiedTxs, + AntifloodHandler: &mock.NilAntifloodHandler{}, + ArgumentsParser: smartContract.NewArgumentParser(), + PreferredPeersHolder: &p2pmocks.PeersHolderStub{}, + SizeCheckDelta: sizeCheckDelta, + RequestHandler: tpn.RequestHandler, + PeerSignatureHandler: &processMock.PeerSignatureHandlerStub{}, + SignaturesHandler: &processMock.SignaturesHandlerStub{}, + HeartbeatExpiryTimespanInSec: 30, + PeerAuthenticationTimeBetweenSendsInSec: 60, + MaxAllowedTrieNodeChunks: 10, + TrieNodeChunksInactivityTimeout: 10 * time.Second, + MainPeerShardMapper: tpn.MainPeerShardMapper, + FullArchivePeerShardMapper: tpn.FullArchivePeerShardMapper, + HardforkTrigger: tpn.HardforkTrigger, + NodeOperationMode: tpn.NodeOperationMode, + InterceptedDataVerifierFactory: interceptorsFactory.NewInterceptedDataVerifierFactory(interceptorDataVerifierArgs), Config: config.Config{ InterceptedDataVerifier: config.InterceptedDataVerifierConfig{ CacheSpanInSec: 1, @@ -1535,37 +1536,40 @@ func (tpn *TestProcessorNode) initInterceptors(heartbeatPk string) { coreComponents.HardforkTriggerPubKeyField = providedHardforkPk shardIntereptorContainerFactoryArgs := interceptorscontainer.CommonInterceptorsContainerFactoryArgs{ - CoreComponents: coreComponents, - CryptoComponents: cryptoComponents, - Accounts: tpn.AccntState, - ShardCoordinator: tpn.ShardCoordinator, - NodesCoordinator: tpn.NodesCoordinator, - MainMessenger: tpn.MainMessenger, - FullArchiveMessenger: tpn.FullArchiveMessenger, - Store: tpn.Storage, - DataPool: tpn.DataPool, - MaxTxNonceDeltaAllowed: common.MaxTxNonceDeltaAllowed, - TxFeeHandler: tpn.EconomicsData, - BlockBlackList: tpn.BlockBlackListHandler, - HeaderSigVerifier: tpn.HeaderSigVerifier, - HeaderIntegrityVerifier: tpn.HeaderIntegrityVerifier, - ValidityAttester: tpn.BlockTracker, - EpochStartTrigger: tpn.EpochStartTrigger, - WhiteListHandler: tpn.WhiteListHandler, - WhiteListerVerifiedTxs: tpn.WhiteListerVerifiedTxs, - AntifloodHandler: &mock.NilAntifloodHandler{}, - ArgumentsParser: smartContract.NewArgumentParser(), - PreferredPeersHolder: &p2pmocks.PeersHolderStub{}, - SizeCheckDelta: sizeCheckDelta, - RequestHandler: tpn.RequestHandler, - PeerSignatureHandler: &processMock.PeerSignatureHandlerStub{}, - SignaturesHandler: &processMock.SignaturesHandlerStub{}, - HeartbeatExpiryTimespanInSec: 30, - MainPeerShardMapper: tpn.MainPeerShardMapper, - FullArchivePeerShardMapper: tpn.FullArchivePeerShardMapper, - HardforkTrigger: tpn.HardforkTrigger, - NodeOperationMode: tpn.NodeOperationMode, - InterceptedDataVerifierFactory: interceptorsFactory.NewInterceptedDataVerifierFactory(interceptorDataVerifierArgs), + CoreComponents: coreComponents, + CryptoComponents: cryptoComponents, + Accounts: tpn.AccntState, + ShardCoordinator: tpn.ShardCoordinator, + NodesCoordinator: tpn.NodesCoordinator, + MainMessenger: tpn.MainMessenger, + FullArchiveMessenger: tpn.FullArchiveMessenger, + Store: tpn.Storage, + DataPool: tpn.DataPool, + MaxTxNonceDeltaAllowed: common.MaxTxNonceDeltaAllowed, + TxFeeHandler: tpn.EconomicsData, + BlockBlackList: tpn.BlockBlackListHandler, + HeaderSigVerifier: tpn.HeaderSigVerifier, + HeaderIntegrityVerifier: tpn.HeaderIntegrityVerifier, + ValidityAttester: tpn.BlockTracker, + EpochStartTrigger: tpn.EpochStartTrigger, + WhiteListHandler: tpn.WhiteListHandler, + WhiteListerVerifiedTxs: tpn.WhiteListerVerifiedTxs, + AntifloodHandler: &mock.NilAntifloodHandler{}, + ArgumentsParser: smartContract.NewArgumentParser(), + PreferredPeersHolder: &p2pmocks.PeersHolderStub{}, + SizeCheckDelta: sizeCheckDelta, + RequestHandler: tpn.RequestHandler, + PeerSignatureHandler: &processMock.PeerSignatureHandlerStub{}, + SignaturesHandler: &processMock.SignaturesHandlerStub{}, + HeartbeatExpiryTimespanInSec: 30, + PeerAuthenticationTimeBetweenSendsInSec: 60, + MaxAllowedTrieNodeChunks: 10, + TrieNodeChunksInactivityTimeout: 10 * time.Second, + MainPeerShardMapper: tpn.MainPeerShardMapper, + FullArchivePeerShardMapper: tpn.FullArchivePeerShardMapper, + HardforkTrigger: tpn.HardforkTrigger, + NodeOperationMode: tpn.NodeOperationMode, + InterceptedDataVerifierFactory: interceptorsFactory.NewInterceptedDataVerifierFactory(interceptorDataVerifierArgs), Config: config.Config{ InterceptedDataVerifier: config.InterceptedDataVerifierConfig{ CacheSpanInSec: 1, @@ -2640,6 +2644,7 @@ func (tpn *TestProcessorNode) initBlockProcessor() { }, }, BlockTracker: tpn.BlockTracker, + MiniBlockTracker: &testscommon.MiniBlockTrackerStub{}, BlockSizeThrottler: TestBlockSizeThrottler, HistoryRepository: tpn.HistoryRepository, GasHandler: tpn.GasHandler, @@ -3670,7 +3675,7 @@ func (tpn *TestProcessorNode) createHeartbeatWithHardforkTrigger() { // ============== HeartbeatV2 ============= // hbv2Config := config.HeartbeatV2Config{ - PeerAuthenticationTimeBetweenSendsInSec: 5, + PeerAuthenticationTimeBetweenSendsInSec: 10, PeerAuthenticationTimeBetweenSendsWhenErrorInSec: 1, PeerAuthenticationTimeThresholdBetweenSends: 0.1, HeartbeatTimeBetweenSendsInSec: 2, @@ -3889,6 +3894,7 @@ func GetDefaultProcessComponents() *mock.ProcessComponentsStub { CurrentEpochProviderInternal: &testscommon.CurrentEpochProviderStub{}, HistoryRepositoryInternal: &dblookupextMock.HistoryRepositoryStub{}, HardforkTriggerField: &testscommon.HardforkTriggerStub{}, + WhiteListHandlerInternal: &testscommon.WhiteListHandlerStub{}, } } diff --git a/integrationTests/testStorage.go b/integrationTests/testStorage.go index 567b3b9a349..b477927c566 100644 --- a/integrationTests/testStorage.go +++ b/integrationTests/testStorage.go @@ -77,7 +77,7 @@ func (ts *TestStorage) CreateStoredData(nonce uint64) ([]byte, []byte) { // CreateStorageLevelDB creates a storage levelDB func (ts *TestStorage) CreateStorageLevelDB() storage.Storer { - db, _ := database.NewLevelDB("Transactions", batchDelaySeconds, maxBatchSize, maxOpenFiles) + db, _ := database.NewLevelDB("Transactions", batchDelaySeconds, maxBatchSize, maxOpenFiles, 0) cacher, _ := cache.NewLRUCache(50000) store, _ := storageunit.NewStorageUnit( cacher, @@ -89,7 +89,7 @@ func (ts *TestStorage) CreateStorageLevelDB() storage.Storer { // CreateStorageLevelDBSerial creates a storage levelDB serial func (ts *TestStorage) CreateStorageLevelDBSerial() storage.Storer { - db, _ := database.NewSerialDB("Transactions", batchDelaySeconds, maxBatchSize, maxOpenFiles) + db, _ := database.NewSerialDB("Transactions", batchDelaySeconds, maxBatchSize, maxOpenFiles, 0) cacher, _ := cache.NewLRUCache(50000) store, _ := storageunit.NewStorageUnit( cacher, diff --git a/integrationTests/testSyncNode.go b/integrationTests/testSyncNode.go index 45e040e2821..f53374775c5 100644 --- a/integrationTests/testSyncNode.go +++ b/integrationTests/testSyncNode.go @@ -213,6 +213,7 @@ func (tpn *TestProcessorNode) initBlockProcessorWithSync() { }, }, BlockTracker: tpn.BlockTracker, + MiniBlockTracker: &testscommon.MiniBlockTrackerStub{}, BlockSizeThrottler: TestBlockSizeThrottler, HistoryRepository: tpn.HistoryRepository, GasHandler: tpn.GasHandler, @@ -413,6 +414,7 @@ func (tpn *TestProcessorNode) createMetaChainBootstrapper() (TestBootstrapper, e EpochBootstrapper: tpn.EpochStartTrigger, ValidatorAccountsDB: tpn.PeerState, ValidatorStatisticsDBSyncer: &mock.AccountsDBSyncerStub{}, + Watchdog: &testscommon.WatchdogMock{}, } bootstrap, err := sync.NewMetaBootstrap(argsMetaBootstrapper) diff --git a/integrationTests/vm/staking/metaBlockProcessorCreator.go b/integrationTests/vm/staking/metaBlockProcessorCreator.go index a276d1e4c4d..eb59ce9b6e5 100644 --- a/integrationTests/vm/staking/metaBlockProcessorCreator.go +++ b/integrationTests/vm/staking/metaBlockProcessorCreator.go @@ -179,6 +179,7 @@ func createMetaBlockProcessor( HeaderValidator: headerValidator, BootStorer: bootStorer, BlockTracker: blockTracker, + MiniBlockTracker: &testscommon.MiniBlockTrackerStub{}, BlockSizeThrottler: &mock.BlockSizeThrottlerStub{}, HistoryRepository: &dblookupext.HistoryRepositoryStub{}, VMContainersFactory: metaVMFactory, diff --git a/node/chainSimulator/components/testOnlyProcessingNode.go b/node/chainSimulator/components/testOnlyProcessingNode.go index 3360e56a6bc..b0753f468cf 100644 --- a/node/chainSimulator/components/testOnlyProcessingNode.go +++ b/node/chainSimulator/components/testOnlyProcessingNode.go @@ -374,7 +374,6 @@ func (node *testOnlyProcessingNode) createBroadcastMessenger() error { node.ProcessComponentsHolder.ShardCoordinator(), node.CryptoComponentsHolder.PeerSignatureHandler(), node.DataComponentsHolder.Datapool().Headers(), - node.DataComponentsHolder.Datapool().Headers(), node.DataComponentsHolder.Datapool().Proofs(), node.CoreComponentsHolder.EnableEpochsHandler(), node.ProcessComponentsHolder.InterceptorsContainer(), diff --git a/node/external/transactionAPI/apiTransactionProcessor.go b/node/external/transactionAPI/apiTransactionProcessor.go index 61e97dc0f6b..6b3f31815e6 100644 --- a/node/external/transactionAPI/apiTransactionProcessor.go +++ b/node/external/transactionAPI/apiTransactionProcessor.go @@ -290,7 +290,7 @@ func (atp *apiTransactionProcessor) GetTransactionsPoolForSender(sender, fields requestedFieldsHandler := newFieldsHandler(fields) transactions := &common.TransactionsPoolForSenderApiResponse{} for _, wrappedTx := range wrappedTxs { - tx := atp.extractRequestedTxInfo(wrappedTx, requestedFieldsHandler) + tx := atp.extractRequestedTxInfo(wrappedTx, requestedFieldsHandler, transaction.TxTypeNormal) // use TxTypeNormal for all, only used for sender encoding transactions.Transactions = append(transactions.Transactions, tx) } @@ -376,7 +376,7 @@ func (atp *apiTransactionProcessor) extractRequestedTxInfoFromObj(txObj interfac TxHash: txHash, } - requestedTxInfo := atp.extractRequestedTxInfo(wrappedTx, requestedFieldsHandler) + requestedTxInfo := atp.extractRequestedTxInfo(wrappedTx, requestedFieldsHandler, txType) return requestedTxInfo } @@ -432,8 +432,12 @@ func (atp *apiTransactionProcessor) getUnsignedTransactionsFromPool(requestedFie return unsignedTxs } -func (atp *apiTransactionProcessor) extractRequestedTxInfo(wrappedTx *txcache.WrappedTransaction, requestedFieldsHandler fieldsHandler) common.Transaction { - fieldGetters := atp.getFieldGettersForTx(wrappedTx) +func (atp *apiTransactionProcessor) extractRequestedTxInfo( + wrappedTx *txcache.WrappedTransaction, + requestedFieldsHandler fieldsHandler, + txType transaction.TxType, +) common.Transaction { + fieldGetters := atp.getFieldGettersForTx(wrappedTx, txType) tx := common.Transaction{ TxFields: make(map[string]interface{}), } @@ -447,16 +451,19 @@ func (atp *apiTransactionProcessor) extractRequestedTxInfo(wrappedTx *txcache.Wr return tx } -func (atp *apiTransactionProcessor) getFieldGettersForTx(wrappedTx *txcache.WrappedTransaction) map[string]interface{} { - senderAddr := "" - if len(wrappedTx.Tx.GetSndAddr()) != 0 { - senderAddr = atp.addressPubKeyConverter.SilentEncode(wrappedTx.Tx.GetSndAddr(), log) +func (atp *apiTransactionProcessor) getFieldGettersForTx( + wrappedTx *txcache.WrappedTransaction, + txType transaction.TxType, +) map[string]interface{} { + senderStr := "metachain" + if txType != transaction.TxTypeReward { + senderStr = atp.addressPubKeyConverter.SilentEncode(wrappedTx.Tx.GetSndAddr(), log) } var fieldGetters = map[string]interface{}{ hashField: hex.EncodeToString(wrappedTx.TxHash), nonceField: wrappedTx.Tx.GetNonce(), - senderField: senderAddr, + senderField: senderStr, receiverField: atp.addressPubKeyConverter.SilentEncode(wrappedTx.Tx.GetRcvAddr(), log), gasLimitField: wrappedTx.Tx.GetGasLimit(), gasPriceField: wrappedTx.Tx.GetGasPrice(), @@ -590,16 +597,16 @@ func (atp *apiTransactionProcessor) selectTransactions(accountsAdapter state.Acc return nil, err } - return atp.extractTransactions(selectedTxs, selectionOptions), nil + // selection done from outgoing txPool + return atp.extractTransactions(selectedTxs, selectionOptions, transaction.TxTypeNormal), nil } -func (atp *apiTransactionProcessor) extractTransactions(txs []*txcache.WrappedTransaction, selectionOptions common.TxSelectionOptionsAPI) []common.Transaction { +func (atp *apiTransactionProcessor) extractTransactions(txs []*txcache.WrappedTransaction, selectionOptions common.TxSelectionOptionsAPI, txType transaction.TxType) []common.Transaction { requestedFieldsHandler := newFieldsHandler(selectionOptions.GetRequestedFields()) transactions := make([]common.Transaction, len(txs)) for i, tx := range txs { - transactions[i] = atp.extractRequestedTxInfo(tx, requestedFieldsHandler) - + transactions[i] = atp.extractRequestedTxInfo(tx, requestedFieldsHandler, txType) } return transactions diff --git a/node/external/transactionAPI/apiTransactionProcessor_test.go b/node/external/transactionAPI/apiTransactionProcessor_test.go index 5abf818c1a8..c76fd90c648 100644 --- a/node/external/transactionAPI/apiTransactionProcessor_test.go +++ b/node/external/transactionAPI/apiTransactionProcessor_test.go @@ -43,7 +43,7 @@ import ( "github.com/multiversx/mx-chain-go/testscommon/marshallerMock" stateMock "github.com/multiversx/mx-chain-go/testscommon/state" storageStubs "github.com/multiversx/mx-chain-go/testscommon/storage" - "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" "github.com/multiversx/mx-chain-go/txcache" ) @@ -1128,7 +1128,7 @@ func TestApiTransactionProcessor_GetTransactionsPoolForSender(t *testing.T) { MaxNumBytesPerSenderUpperBound: 33_554_432, MaxTrackedBlocks: maxTrackedBlocks, }, - }, txcachemocks.NewMempoolHostMock(), 0) + }, mempool.NewMempoolHostMock(), 0) require.NoError(t, err) @@ -1149,7 +1149,7 @@ func TestApiTransactionProcessor_GetTransactionsPoolForSender(t *testing.T) { MaxNumBytesPerSenderUpperBound: 33_554_432, MaxTrackedBlocks: maxTrackedBlocks, }, - }, txcachemocks.NewMempoolHostMock(), 0) + }, mempool.NewMempoolHostMock(), 0) require.NoError(t, err) txCacheWithMeta.AddTx(createTx(txHash3, sender, 4)) @@ -1241,7 +1241,7 @@ func TestApiTransactionProcessor_GetLastPoolNonceForSender(t *testing.T) { MaxNumBytesPerSenderUpperBound: 33_554_432, MaxTrackedBlocks: maxTrackedBlocks, }, - }, txcachemocks.NewMempoolHostMock(), 0) + }, mempool.NewMempoolHostMock(), 0) txCacheIntraShard.AddTx(createTx(txHash2, sender, 3)) txCacheIntraShard.AddTx(createTx(txHash0, sender, 1)) @@ -1298,7 +1298,7 @@ func TestApiTransactionProcessor_GetTransactionsPoolNonceGapsForSender(t *testin MaxNumBytesPerSenderUpperBound: 33_554_432, MaxTrackedBlocks: maxTrackedBlocks, }, - }, txcachemocks.NewMempoolHostMock(), 0) + }, mempool.NewMempoolHostMock(), 0) require.NoError(t, err) @@ -1314,7 +1314,7 @@ func TestApiTransactionProcessor_GetTransactionsPoolNonceGapsForSender(t *testin MaxNumBytesPerSenderUpperBound: 33_554_432, MaxTrackedBlocks: maxTrackedBlocks, }, - }, txcachemocks.NewMempoolHostMock(), 0) + }, mempool.NewMempoolHostMock(), 0) require.NoError(t, err) @@ -1406,7 +1406,7 @@ func TestApiTransactionProcessor_GetSelectedTransactions(t *testing.T) { MaxNumBytesPerSenderUpperBound: 33_554_432, MaxTrackedBlocks: maxTrackedBlocks, }, - }, txcachemocks.NewMempoolHostMock(), 0) + }, mempool.NewMempoolHostMock(), 0) require.NoError(t, err) @@ -1746,7 +1746,7 @@ func TestApiTransactionProcessor_GetVirtualNonce(t *testing.T) { MaxNumBytesPerSenderUpperBound: 33_554_432, MaxTrackedBlocks: maxTrackedBlocks, }, - }, txcachemocks.NewMempoolHostMock(), 0) + }, mempool.NewMempoolHostMock(), 0) require.NoError(t, err) diff --git a/outport/process/transactionsfee/transactionChecker.go b/outport/process/transactionsfee/transactionChecker.go index c1b2c5586a5..830820c60f4 100644 --- a/outport/process/transactionsfee/transactionChecker.go +++ b/outport/process/transactionsfee/transactionChecker.go @@ -13,8 +13,8 @@ import ( vmcommon "github.com/multiversx/mx-chain-vm-common-go" ) -func (tep *transactionsFeeProcessor) isESDTOperationWithSCCall(tx data.TransactionHandler) bool { - res := tep.dataFieldParser.Parse(tx.GetData(), tx.GetSndAddr(), tx.GetRcvAddr(), tep.shardCoordinator.NumberOfShards(), tep.enableEpochsHandler.GetCurrentEpoch()) +func (tep *transactionsFeeProcessor) isESDTOperationWithSCCall(tx data.TransactionHandler, epoch uint32) bool { + res := tep.dataFieldParser.Parse(tx.GetData(), tx.GetSndAddr(), tx.GetRcvAddr(), tep.shardCoordinator.NumberOfShards(), epoch) isESDTTransferOperation := res.Operation == core.BuiltInFunctionESDTTransfer || res.Operation == core.BuiltInFunctionESDTNFTTransfer || res.Operation == core.BuiltInFunctionMultiESDTNFTTransfer diff --git a/outport/process/transactionsfee/transactionsFeeProcessor.go b/outport/process/transactionsfee/transactionsFeeProcessor.go index da7cecaea1c..5cd000ca8e5 100644 --- a/outport/process/transactionsfee/transactionsFeeProcessor.go +++ b/outport/process/transactionsfee/transactionsFeeProcessor.go @@ -132,7 +132,7 @@ func (tep *transactionsFeeProcessor) prepareNormalTxs(transactionsAndScrs *trans isRelayed := tep.isRelayedTxV1V2(txWithResult, epoch) isFeeFixActive := tep.enableEpochsHandler.IsFlagEnabledInEpoch(common.FixRelayedBaseCostFlag, epoch) isRelayedBeforeFix := isRelayed && !isFeeFixActive - if isRelayedBeforeFix || tep.isESDTOperationWithSCCall(txHandler) { + if isRelayedBeforeFix || tep.isESDTOperationWithSCCall(txHandler, epoch) { feeInfo.SetGasUsed(txWithResult.GetTxHandler().GetGasLimit()) feeInfo.SetFee(initialPaidFee) } diff --git a/process/block/argProcessor.go b/process/block/argProcessor.go index 6c1d09a90b9..27f839da7ef 100644 --- a/process/block/argProcessor.go +++ b/process/block/argProcessor.go @@ -102,6 +102,7 @@ type ArgBaseProcessor struct { ManagedPeersHolder common.ManagedPeersHolder SentSignaturesTracker process.SentSignaturesTracker StateAccessesCollector state.StateAccessesCollector + MiniBlockTracker process.MiniBlockTracker HeadersForBlock HeadersForBlock ExecutionResultsInclusionEstimator process.InclusionEstimator MiniBlocksSelectionSession MiniBlocksSelectionSession diff --git a/process/block/baseProcess.go b/process/block/baseProcess.go index e729455b4b0..e2b53abe93c 100644 --- a/process/block/baseProcess.go +++ b/process/block/baseProcess.go @@ -102,6 +102,7 @@ type baseProcessor struct { requestBlockBodyHandler process.RequestBlockBodyHandler requestHandler process.RequestHandler blockTracker process.BlockTracker + miniBlockTracker process.MiniBlockTracker dataPool dataRetriever.PoolsHolder feeHandler process.TransactionFeeHandler blockChain data.ChainHandler @@ -262,6 +263,7 @@ func NewBaseProcessor(arguments ArgBaseProcessor) (*baseProcessor, error) { maxProposalNonceGap: maxProposalNonceGap, ewlResetThreshold: ewlResetThreshold, closingNodeStarted: arguments.CoreComponents.ClosingNodeStarted(), + miniBlockTracker: arguments.MiniBlockTracker, } err = base.OnExecutedBlock(genesisHdr, genesisHdr.GetRootHash()) @@ -376,8 +378,8 @@ func (bp *baseProcessor) checkTimestamp(headerHandler data.HeaderHandler) error return nil } -// checkScheduledRootHash checks if the scheduled root hash from the given header is the same with the current user accounts state root hash -func (bp *baseProcessor) checkScheduledRootHash(headerHandler data.HeaderHandler) error { +// checkScheduledData checks if the scheduled data from the given header matches the locally computed scheduled data +func (bp *baseProcessor) checkScheduledData(headerHandler data.HeaderHandler) error { if !bp.enableEpochsHandler.IsFlagEnabled(common.ScheduledMiniBlocksFlag) { return nil } @@ -398,6 +400,26 @@ func (bp *baseProcessor) checkScheduledRootHash(headerHandler data.HeaderHandler return process.ErrScheduledRootHashDoesNotMatch } + scheduledGasAndFees := bp.scheduledTxsExecutionHandler.GetScheduledGasAndFees() + if additionalData.GetScheduledAccumulatedFees().Cmp(scheduledGasAndFees.AccumulatedFees) != 0 || + additionalData.GetScheduledDeveloperFees().Cmp(scheduledGasAndFees.DeveloperFees) != 0 || + additionalData.GetScheduledGasProvided() != scheduledGasAndFees.GasProvided || + additionalData.GetScheduledGasPenalized() != scheduledGasAndFees.GasPenalized || + additionalData.GetScheduledGasRefunded() != scheduledGasAndFees.GasRefunded { + log.Debug("scheduled gas and fees do not match", + "header accumulated fees", additionalData.GetScheduledAccumulatedFees(), + "computed accumulated fees", scheduledGasAndFees.AccumulatedFees, + "header developer fees", additionalData.GetScheduledDeveloperFees(), + "computed developer fees", scheduledGasAndFees.DeveloperFees, + "header gas provided", additionalData.GetScheduledGasProvided(), + "computed gas provided", scheduledGasAndFees.GasProvided, + "header gas penalized", additionalData.GetScheduledGasPenalized(), + "computed gas penalized", scheduledGasAndFees.GasPenalized, + "header gas refunded", additionalData.GetScheduledGasRefunded(), + "computed gas refunded", scheduledGasAndFees.GasRefunded) + return process.ErrScheduledGasAndFeesDoesNotMatch + } + return nil } @@ -748,6 +770,9 @@ func checkProcessorParameters(arguments ArgBaseProcessor) error { if check.IfNil(arguments.BlockTracker) { return process.ErrNilBlockTracker } + if check.IfNil(arguments.MiniBlockTracker) { + return process.ErrNilMiniBlockTracker + } if check.IfNil(arguments.FeeHandler) { return process.ErrNilEconomicsFeeHandler } @@ -1148,73 +1173,19 @@ func isPartiallyExecuted( return processedMiniBlockInfo != nil && !processedMiniBlockInfo.FullyProcessed } -// check if header has the same mini blocks as presented in body -func (bp *baseProcessor) checkHeaderBodyCorrelationProposal(miniBlockHeaders []data.MiniBlockHeaderHandler, body *block.Body) error { - mbHashesFromHdr := make(map[string]struct{}, len(miniBlockHeaders)) - for i := 0; i < len(miniBlockHeaders); i++ { - if miniBlockHeaders[i] == nil { - return process.ErrNilMiniBlockHeader - } - - mbHashesFromHdr[string(miniBlockHeaders[i].GetHash())] = struct{}{} +func (bp *baseProcessor) checkConstructionStateProcessingTypeAndIndexesCorrectnessProposal(miniBlockHeader data.MiniBlockHeaderHandler) error { + // for Supernova all miniBlocks not part of an execution result need to have construction state Proposed + if miniBlockHeader.GetConstructionState() != int32(block.Proposed) { + return process.ErrWrongMiniBlockConstructionState } - - if len(miniBlockHeaders) != len(body.MiniBlocks) { - return process.ErrHeaderBodyMismatch + if miniBlockHeader.GetProcessingType() != int32(block.Normal) { + return process.ErrWrongMiniBlockProcessingType } - if len(mbHashesFromHdr) != len(miniBlockHeaders) { - return process.ErrDuplicatedHashInBlock - } - - var mbHdr data.MiniBlockHeaderHandler - var miniBlock *block.MiniBlock - for i := 0; i < len(body.MiniBlocks); i++ { - miniBlock = body.MiniBlocks[i] - mbHdr = miniBlockHeaders[i] - if miniBlock == nil { - return process.ErrNilMiniBlock - } - if mbHdr == nil { - return process.ErrNilMiniBlockHeader - } - - mbHash, err := core.CalculateHash(bp.marshalizer, bp.hasher, miniBlock) - if err != nil { - return err - } - - mbHashStr := string(mbHash) - _, ok := mbHashesFromHdr[mbHashStr] - if !ok { - return process.ErrHeaderBodyMismatch - } - - err = checkMiniBlockWithMiniBlockHeader(mbHash, mbHdr, miniBlock) - if err != nil { - return err - } - - delete(mbHashesFromHdr, mbHashStr) - } - - return bp.checkMiniBlocksConstructionProposal(miniBlockHeaders) -} - -func (bp *baseProcessor) checkMiniBlocksConstructionProposal(miniBlockHeaders []data.MiniBlockHeaderHandler) error { - for i := 0; i < len(miniBlockHeaders); i++ { - // for Supernova all miniBlocks not part of an execution result need to have construction state Proposed - if miniBlockHeaders[i].GetConstructionState() != int32(block.Proposed) { - return process.ErrWrongMiniBlockConstructionState - } - if miniBlockHeaders[i].GetProcessingType() != int32(block.Normal) { - return process.ErrWrongMiniBlockProcessingType - } - } return nil } -func checkMiniBlockWithMiniBlockHeader(mbHash []byte, mbHdr data.MiniBlockHeaderHandler, miniBlock *block.MiniBlock) error { +func (bp *baseProcessor) checkMiniBlockWithMiniBlockHeaderWithoutConstructionAndProcessing(mbHash []byte, mbHdr data.MiniBlockHeaderHandler, miniBlock *block.MiniBlock) error { if !bytes.Equal(mbHash, mbHdr.GetHash()) { return process.ErrHeaderBodyMismatch } @@ -1230,18 +1201,49 @@ func checkMiniBlockWithMiniBlockHeader(mbHash []byte, mbHdr data.MiniBlockHeader if mbHdr.GetSenderShardID() != miniBlock.SenderShardID { return fmt.Errorf("%w: different mb sender shard ID", process.ErrHeaderBodyMismatch) } + + if mbHdr.GetTypeInt32() != int32(miniBlock.Type) { + return process.ErrHeaderBodyMismatch + } + + err := process.CheckIfIndexesAreOutOfBound(mbHdr.GetIndexOfFirstTxProcessed(), mbHdr.GetIndexOfLastTxProcessed(), miniBlock) + if err != nil { + return err + } + + err = bp.checkIndexOfFirstTxProcessedAgainstTracker(mbHdr, mbHash) + if err != nil { + return err + } + return nil } +func (bp *baseProcessor) checkMiniBlockWithMiniBlockHeaderProposal(mbHash []byte, mbHdr data.MiniBlockHeaderHandler, miniBlock *block.MiniBlock, _ uint32) error { + err := bp.checkMiniBlockWithMiniBlockHeaderWithoutConstructionAndProcessing(mbHash, mbHdr, miniBlock) + if err != nil { + return err + } + return bp.checkConstructionStateProcessingTypeAndIndexesCorrectnessProposal(mbHdr) +} + +func (bp *baseProcessor) checkMiniBlockWithMiniBlockHeader(mbHash []byte, mbHdr data.MiniBlockHeaderHandler, miniBlock *block.MiniBlock, blockShardID uint32) error { + err := bp.checkMiniBlockWithMiniBlockHeaderWithoutConstructionAndProcessing(mbHash, mbHdr, miniBlock) + if err != nil { + return err + } + return checkConstructionStateProcessingTypeAndIndexesCorrectness(mbHdr, miniBlock, blockShardID) +} + // check if header has the same mini blocks as presented in body -func (bp *baseProcessor) checkHeaderBodyCorrelation(miniBlockHeaders []data.MiniBlockHeaderHandler, body *block.Body) error { - mbHashesFromHdr := make(map[string]struct{}, len(miniBlockHeaders)) +func (bp *baseProcessor) checkHeaderBodyCorrelation(miniBlockHeaders []data.MiniBlockHeaderHandler, body *block.Body, blockShardID uint32, proposal bool) error { + mbHashesFromHdr := make(map[string]data.MiniBlockHeaderHandler, len(miniBlockHeaders)) for i := 0; i < len(miniBlockHeaders); i++ { if miniBlockHeaders[i] == nil { return process.ErrNilMiniBlockHeader } - mbHashesFromHdr[string(miniBlockHeaders[i].GetHash())] = struct{}{} + mbHashesFromHdr[string(miniBlockHeaders[i].GetHash())] = miniBlockHeaders[i] } if len(miniBlockHeaders) != len(body.MiniBlocks) { @@ -1262,6 +1264,9 @@ func (bp *baseProcessor) checkHeaderBodyCorrelation(miniBlockHeaders []data.Mini if miniBlock == nil { return process.ErrNilMiniBlock } + if mbHdr == nil { + return process.ErrNilMiniBlockHeader + } mbHash, err = core.CalculateHash(bp.marshalizer, bp.hasher, miniBlock) if err != nil { @@ -1274,37 +1279,143 @@ func (bp *baseProcessor) checkHeaderBodyCorrelation(miniBlockHeaders []data.Mini return process.ErrHeaderBodyMismatch } - err = checkMiniBlockWithMiniBlockHeader(mbHash, mbHdr, miniBlock) + if !proposal { + err = bp.checkMiniBlockWithMiniBlockHeader(mbHash, mbHdr, miniBlock, blockShardID) + } else { + err = bp.checkMiniBlockWithMiniBlockHeaderProposal(mbHash, mbHdr, miniBlock, blockShardID) + } if err != nil { return err } - if mbHdr.GetTypeInt32() != int32(miniBlock.Type) { - return process.ErrHeaderBodyMismatch + delete(mbHashesFromHdr, mbHashStr) + } + + return nil +} + +// checkConstructionStateProcessingTypeAndIndexesCorrectness validates the (miniBlock, +// miniBlockHeader) pair belonging to a block of shard blockShardID against the legal +// (hdrPT, sender == blockShardID?, allowed state) rows. PartialExecuted is allowed alongside +// the primary state of each processing type, validated by the index check: +// +// Normal, yes -> Final +// Normal, no -> Final | PartialExecuted +// Scheduled, yes -> Proposed | PartialExecuted +// Scheduled, no -> Final | PartialExecuted +// Processed, yes -> Final +// Processed, no -> impossible +// +// It also checks body PT validity, type-vs-scheduling, and IndexOfLastTxProcessed vs +// ConstructionState. Body-vs-header PT consistency is enforced only when sender is +// blockShardID; for incoming MBs the body PT belongs to the source shard. +func checkConstructionStateProcessingTypeAndIndexesCorrectness( + mbh data.MiniBlockHeaderHandler, + miniBlock *block.MiniBlock, + blockShardID uint32, +) error { + bodyPT := miniBlock.GetProcessingType() + hdrPT := mbh.GetProcessingType() + mbType := miniBlock.Type + senderIsBlockShard := mbh.GetSenderShardID() == blockShardID + + // Processed is a header-only re-inclusion tag and must never appear on the body. + if bodyPT != int32(block.Normal) && bodyPT != int32(block.Scheduled) { + return fmt.Errorf("%w: body has invalid processing type %d", + process.ErrInvalidMiniBlockProcessingType, bodyPT) + } + + if mbType != block.TxBlock { + if bodyPT != int32(block.Normal) || hdrPT != int32(block.Normal) { + return fmt.Errorf("%w: miniblock type %s cannot be scheduled (body=%d, header=%d)", + process.ErrInvalidMiniBlockProcessingTypeForType, mbType, bodyPT, hdrPT) } + } - err = process.CheckIfIndexesAreOutOfBound(mbHdr.GetIndexOfFirstTxProcessed(), mbHdr.GetIndexOfLastTxProcessed(), miniBlock) - if err != nil { - return err + constructionState := mbh.GetConstructionState() + switch hdrPT { + case int32(block.Normal): + if senderIsBlockShard { + if bodyPT != int32(block.Normal) { + return fmt.Errorf("%w: Normal header at sender shard requires Normal body, got body=%d", + process.ErrProcessingTypeBodyHeaderMismatch, bodyPT) + } + if constructionState != int32(block.Final) { + return fmt.Errorf("%w: Normal header at sender shard requires Final, got %d", + process.ErrInvalidConstructionState, constructionState) + } + } else { + // an incoming normal miniblock may be partially executed at the destination + if constructionState != int32(block.Final) && constructionState != int32(block.PartialExecuted) { + return fmt.Errorf("%w: incoming Normal header requires Final or PartialExecuted, got %d", + process.ErrInvalidConstructionState, constructionState) + } } - err = checkConstructionStateAndIndexesCorrectness(mbHdr) - if err != nil { - return err + case int32(block.Scheduled): + if senderIsBlockShard { + if bodyPT != int32(block.Scheduled) { + return fmt.Errorf("%w: header=Scheduled requires body=Scheduled, got body=%d", + process.ErrProcessingTypeBodyHeaderMismatch, bodyPT) + } + if constructionState != int32(block.Proposed) && constructionState != int32(block.PartialExecuted) { + return fmt.Errorf("%w: Scheduled header at sender shard requires Proposed or PartialExecuted, got %d", + process.ErrInvalidConstructionState, constructionState) + } + } else { + // incoming body PT belongs to the source shard, so it is not constrained here + if constructionState != int32(block.Final) && constructionState != int32(block.PartialExecuted) { + return fmt.Errorf("%w: cross-shard incoming Scheduled requires Final or PartialExecuted, got %d", + process.ErrInvalidConstructionState, constructionState) + } + } + case int32(block.Processed): + if bodyPT != int32(block.Scheduled) { + return fmt.Errorf("%w: header=Processed requires body=Scheduled, got body=%d", + process.ErrProcessingTypeBodyHeaderMismatch, bodyPT) + } + if !senderIsBlockShard { + return fmt.Errorf("%w: Processed header requires sender == blockShard", + process.ErrInvalidMiniBlockShardRole) + } + if constructionState != int32(block.Final) { + return fmt.Errorf("%w: Processed header requires Final, got %d", + process.ErrInvalidConstructionState, constructionState) } + default: + return fmt.Errorf("%w: unknown header processing type %d", + process.ErrInvalidMiniBlockProcessingType, hdrPT) + } - delete(mbHashesFromHdr, mbHashStr) + lastIdx := mbh.GetIndexOfLastTxProcessed() + finalIdx := int32(mbh.GetTxCount()) - 1 + if constructionState == int32(block.PartialExecuted) && lastIdx == finalIdx { + return process.ErrIndexDoesNotMatchWithPartialExecutedMiniBlock + } + if constructionState != int32(block.PartialExecuted) && lastIdx != finalIdx { + return process.ErrIndexDoesNotMatchWithFullyExecutedMiniBlock } return nil } -func checkConstructionStateAndIndexesCorrectness(mbh data.MiniBlockHeaderHandler) error { - if mbh.GetConstructionState() == int32(block.PartialExecuted) && mbh.GetIndexOfLastTxProcessed() == int32(mbh.GetTxCount())-1 { - return process.ErrIndexDoesNotMatchWithPartialExecutedMiniBlock - +func (bp *baseProcessor) checkIndexOfFirstTxProcessedAgainstTracker(mbHdr data.MiniBlockHeaderHandler, miniBlockHash []byte) error { + selfShardID := bp.shardCoordinator.SelfId() + isIncomingCross := mbHdr.GetReceiverShardID() == selfShardID && mbHdr.GetSenderShardID() != selfShardID + if !isIncomingCross { + return nil } - if mbh.GetConstructionState() != int32(block.PartialExecuted) && mbh.GetIndexOfLastTxProcessed() != int32(mbh.GetTxCount())-1 { - return process.ErrIndexDoesNotMatchWithFullyExecutedMiniBlock + + processedMiniBlockInfo, _ := bp.processedMiniBlocksTracker.GetProcessedMiniBlockInfo(miniBlockHash) + expectedIndexOfFirstTxProcessed := processedMiniBlockInfo.IndexOfLastTxProcessed + 1 + if mbHdr.GetIndexOfFirstTxProcessed() != expectedIndexOfFirstTxProcessed { + log.Debug("checkIndexOfFirstTxProcessedAgainstTracker: mismatch", + "mb hash", miniBlockHash, + "sender shard", mbHdr.GetSenderShardID(), + "receiver shard", mbHdr.GetReceiverShardID(), + "header index of first tx processed", mbHdr.GetIndexOfFirstTxProcessed(), + "expected index of first tx processed", expectedIndexOfFirstTxProcessed, + ) + return process.ErrIndexOfFirstTxProcessedMismatch } return nil @@ -2790,23 +2901,37 @@ func (bp *baseProcessor) getIndexOfFirstMiniBlockToBeExecuted(header data.Header return 0, nil } - for index, miniBlockHeaderHandler := range header.GetMiniBlockHeaderHandlers() { - if miniBlockHeaderHandler.GetProcessingType() == int32(block.Processed) { - if !bp.scheduledTxsExecutionHandler.IsMiniBlockExecuted(miniBlockHeaderHandler.GetHash()) { - return 0, fmt.Errorf("%w: mini block %s not executed", - process.ErrMiniBlockNotExecuted, + miniBlockHeaderHandlers := header.GetMiniBlockHeaderHandlers() + indexOfFirstMiniBlockToBeExecuted := len(miniBlockHeaderHandlers) + foundFirstNonProcessed := false + for index, miniBlockHeaderHandler := range miniBlockHeaderHandlers { + isProcessed := miniBlockHeaderHandler.GetProcessingType() == int32(block.Processed) + + // processed mini blocks are the ones executed as scheduled in the previous block and + // must form the contiguous leading prefix of the body; any later one is unverified + if foundFirstNonProcessed { + if isProcessed { + return 0, fmt.Errorf("%w: %s", + process.ErrProcessedMiniBlockNotInLeadingPrefix, hex.EncodeToString(miniBlockHeaderHandler.GetHash())) } - log.Debug("baseProcessor.getIndexOfFirstMiniBlockToBeExecuted: mini block is already executed", - "mb hash", miniBlockHeaderHandler.GetHash(), - "mb index", index) continue } - return index, nil + if !isProcessed { + indexOfFirstMiniBlockToBeExecuted = index + foundFirstNonProcessed = true + continue + } + + if !bp.scheduledTxsExecutionHandler.IsMiniBlockExecuted(miniBlockHeaderHandler.GetHash()) { + return 0, fmt.Errorf("%w: mini block %s not executed", + process.ErrMiniBlockNotExecuted, + hex.EncodeToString(miniBlockHeaderHandler.GetHash())) + } } - return len(header.GetMiniBlockHeaderHandlers()), nil + return indexOfFirstMiniBlockToBeExecuted, nil } func displayCleanupErrorMessage(message string, shardID uint32, noncesToPrevFinal uint64, err error) { @@ -4011,7 +4136,7 @@ func (bp *baseProcessor) cacheUnexecutableTxHashes(headerHash []byte) { } func (bp *baseProcessor) getBlockBodyFromPool( - header data.HeaderHandler, + _ data.HeaderHandler, miniBlockHeaderHandlers []data.MiniBlockHeaderHandler, ) (data.BodyHandler, error) { miniBlocksPool := bp.dataPool.MiniBlocks() diff --git a/process/block/baseProcess_test.go b/process/block/baseProcess_test.go index 88953d46185..3032f7f202d 100644 --- a/process/block/baseProcess_test.go +++ b/process/block/baseProcess_test.go @@ -34,12 +34,12 @@ import ( "github.com/multiversx/mx-chain-go/process/aotSelection" headersCache "github.com/multiversx/mx-chain-go/process/asyncExecution/cache" "github.com/multiversx/mx-chain-go/process/asyncExecution/executionManager" + "github.com/multiversx/mx-chain-go/testscommon/pool" "github.com/multiversx/mx-chain-go/process/asyncExecution/executionTrack" "github.com/multiversx/mx-chain-go/process/estimator" "github.com/multiversx/mx-chain-go/process/missingData" "github.com/multiversx/mx-chain-go/testscommon/mbSelection" - "github.com/multiversx/mx-chain-go/testscommon/pool" "github.com/multiversx/mx-chain-go/testscommon/processMocks" "github.com/multiversx/mx-chain-go/common" @@ -227,6 +227,7 @@ func createArgBaseProcessor( }, }, BlockTracker: blockTracker, + MiniBlockTracker: &testscommon.MiniBlockTrackerStub{}, BlockSizeThrottler: &mock.BlockSizeThrottlerStub{}, Version: "softwareVersion", HistoryRepository: &dblookupext.HistoryRepositoryStub{}, @@ -1244,6 +1245,120 @@ func TestBaseProcessor_SetIndexOfFirstTxProcessed(t *testing.T) { assert.Equal(t, int32(9), miniBlockHeader.GetIndexOfFirstTxProcessed()) } +func TestBaseProcessor_CheckHeaderBodyCorrelationIndexOfFirstTxProcessed(t *testing.T) { + t.Parallel() + + hasher := &mock.HasherStub{} + marshaller := &mock.MarshalizerMock{} + + t.Run("fresh incoming mb with non-zero IndexOfFirstTxProcessed should error", func(t *testing.T) { + t.Parallel() + + hdr, body := createOneHeaderOneBody() + hdr.MiniBlockHeaders[0].TxCount = 3 + body.MiniBlocks[0].TxHashes = [][]byte{[]byte("tx1"), []byte("tx2"), []byte("tx3")} + mbBytes, _ := marshaller.Marshal(body.MiniBlocks[0]) + hdr.MiniBlockHeaders[0].Hash = hasher.Compute(string(mbBytes)) + _ = hdr.MiniBlockHeaders[0].SetIndexOfFirstTxProcessed(2) + _ = hdr.MiniBlockHeaders[0].SetIndexOfLastTxProcessed(2) + + arguments := CreateMockArguments(createComponentHolderMocks()) + arguments.ProcessedMiniBlocksTracker = processedMb.NewProcessedMiniBlocksTracker() + sp, _ := blproc.NewShardProcessor(arguments) + + err := sp.CheckHeaderBodyCorrelation(hdr, body) + assert.Equal(t, process.ErrIndexOfFirstTxProcessedMismatch, err) + }) + + t.Run("fresh incoming mb with IndexOfFirstTxProcessed=0 should pass", func(t *testing.T) { + t.Parallel() + + hdr, body := createOneHeaderOneBody() + + arguments := CreateMockArguments(createComponentHolderMocks()) + arguments.ProcessedMiniBlocksTracker = processedMb.NewProcessedMiniBlocksTracker() + sp, _ := blproc.NewShardProcessor(arguments) + + err := sp.CheckHeaderBodyCorrelation(hdr, body) + assert.Nil(t, err) + }) + + t.Run("partially processed mb with matching continuation should pass", func(t *testing.T) { + t.Parallel() + + hdr, body := createOneHeaderOneBody() + hdr.MiniBlockHeaders[0].TxCount = 5 + body.MiniBlocks[0].TxHashes = [][]byte{[]byte("tx1"), []byte("tx2"), []byte("tx3"), []byte("tx4"), []byte("tx5")} + mbBytes, _ := marshaller.Marshal(body.MiniBlocks[0]) + mbHash := hasher.Compute(string(mbBytes)) + hdr.MiniBlockHeaders[0].Hash = mbHash + // tracker says we already processed indices 0, 1, 2 so next first must be 3 + _ = hdr.MiniBlockHeaders[0].SetIndexOfFirstTxProcessed(3) + _ = hdr.MiniBlockHeaders[0].SetIndexOfLastTxProcessed(4) + + arguments := CreateMockArguments(createComponentHolderMocks()) + tracker := processedMb.NewProcessedMiniBlocksTracker() + tracker.SetProcessedMiniBlockInfo([]byte("meta_hash"), mbHash, &processedMb.ProcessedMiniBlockInfo{ + FullyProcessed: false, + IndexOfLastTxProcessed: 2, + }) + arguments.ProcessedMiniBlocksTracker = tracker + sp, _ := blproc.NewShardProcessor(arguments) + + err := sp.CheckHeaderBodyCorrelation(hdr, body) + assert.Nil(t, err) + }) + + t.Run("partially processed mb with mismatched continuation should error", func(t *testing.T) { + t.Parallel() + + hdr, body := createOneHeaderOneBody() + hdr.MiniBlockHeaders[0].TxCount = 5 + body.MiniBlocks[0].TxHashes = [][]byte{[]byte("tx1"), []byte("tx2"), []byte("tx3"), []byte("tx4"), []byte("tx5")} + mbBytes, _ := marshaller.Marshal(body.MiniBlocks[0]) + mbHash := hasher.Compute(string(mbBytes)) + hdr.MiniBlockHeaders[0].Hash = mbHash + // tracker says next first must be 3, but proposer forged 0 + _ = hdr.MiniBlockHeaders[0].SetIndexOfFirstTxProcessed(0) + _ = hdr.MiniBlockHeaders[0].SetIndexOfLastTxProcessed(4) + + arguments := CreateMockArguments(createComponentHolderMocks()) + tracker := processedMb.NewProcessedMiniBlocksTracker() + tracker.SetProcessedMiniBlockInfo([]byte("meta_hash"), mbHash, &processedMb.ProcessedMiniBlockInfo{ + FullyProcessed: false, + IndexOfLastTxProcessed: 2, + }) + arguments.ProcessedMiniBlocksTracker = tracker + sp, _ := blproc.NewShardProcessor(arguments) + + err := sp.CheckHeaderBodyCorrelation(hdr, body) + assert.Equal(t, process.ErrIndexOfFirstTxProcessedMismatch, err) + }) + + t.Run("intra shard mb should skip the tracker check", func(t *testing.T) { + t.Parallel() + + hdr, body := createOneHeaderOneBody() + hdr.MiniBlockHeaders[0].TxCount = 3 + body.MiniBlocks[0].TxHashes = [][]byte{[]byte("tx1"), []byte("tx2"), []byte("tx3")} + body.MiniBlocks[0].SenderShardID = 0 + body.MiniBlocks[0].ReceiverShardID = 0 + hdr.MiniBlockHeaders[0].SenderShardID = 0 + hdr.MiniBlockHeaders[0].ReceiverShardID = 0 + mbBytes, _ := marshaller.Marshal(body.MiniBlocks[0]) + hdr.MiniBlockHeaders[0].Hash = hasher.Compute(string(mbBytes)) + _ = hdr.MiniBlockHeaders[0].SetIndexOfFirstTxProcessed(1) + _ = hdr.MiniBlockHeaders[0].SetIndexOfLastTxProcessed(2) + + arguments := CreateMockArguments(createComponentHolderMocks()) + arguments.ProcessedMiniBlocksTracker = processedMb.NewProcessedMiniBlocksTracker() + sp, _ := blproc.NewShardProcessor(arguments) + + err := sp.CheckHeaderBodyCorrelation(hdr, body) + assert.Nil(t, err) + }) +} + func TestBaseProcessor_SetIndexOfLastTxProcessed(t *testing.T) { t.Parallel() @@ -2821,6 +2936,113 @@ func TestBaseProcessor_ProcessScheduledBlockShouldWork(t *testing.T) { assert.Equal(t, []string{busyIdentifier, idleIdentifier}, busyIdleCalled) // the order is important } +func TestBaseProcessor_CheckScheduledData(t *testing.T) { + t.Parallel() + + scheduledGasAndFees := scheduled.GasAndFees{ + AccumulatedFees: big.NewInt(11), + DeveloperFees: big.NewInt(12), + GasProvided: 13, + GasPenalized: 14, + GasRefunded: 15, + } + + createProcessorAndHeader := func(t *testing.T) (interface { + CheckScheduledData(data.HeaderHandler) error + }, *block.HeaderV2) { + t.Helper() + coreComponents, dataComponents, bootstrapComponents, statusComponents := createComponentHolderMocks() + coreComponents.EnableEpochsHandlerField = enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.ScheduledMiniBlocksFlag) + arguments := CreateMockArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.ArgBaseProcessor.AccountsDB[state.UserAccountsState] = &stateMock.AccountsStub{ + RootHashCalled: func() ([]byte, error) { + return []byte("scheduled-root"), nil + }, + } + arguments.ArgBaseProcessor.ScheduledTxsExecutionHandler = &testscommon.ScheduledTxsExecutionStub{ + GetScheduledGasAndFeesCalled: func() scheduled.GasAndFees { + return scheduledGasAndFees + }, + } + processor, err := blproc.NewShardProcessor(arguments) + require.NoError(t, err) + header := &block.HeaderV2{ + Header: &block.Header{}, + ScheduledRootHash: []byte("scheduled-root"), + ScheduledAccumulatedFees: big.NewInt(11), + ScheduledDeveloperFees: big.NewInt(12), + ScheduledGasProvided: 13, + ScheduledGasPenalized: 14, + ScheduledGasRefunded: 15, + } + return processor, header + } + + t.Run("should work when scheduled data matches", func(t *testing.T) { + t.Parallel() + + processor, header := createProcessorAndHeader(t) + err := processor.CheckScheduledData(header) + + require.NoError(t, err) + }) + + t.Run("should fail when scheduled accumulated fees mismatch", func(t *testing.T) { + t.Parallel() + + processor, header := createProcessorAndHeader(t) + header.ScheduledAccumulatedFees = big.NewInt(111) + + err := processor.CheckScheduledData(header) + + require.ErrorIs(t, err, process.ErrScheduledGasAndFeesDoesNotMatch) + }) + + t.Run("should fail when scheduled developer fees mismatch", func(t *testing.T) { + t.Parallel() + + processor, header := createProcessorAndHeader(t) + header.ScheduledDeveloperFees = big.NewInt(112) + + err := processor.CheckScheduledData(header) + + require.ErrorIs(t, err, process.ErrScheduledGasAndFeesDoesNotMatch) + }) + + t.Run("should fail when scheduled gas provided mismatch", func(t *testing.T) { + t.Parallel() + + processor, header := createProcessorAndHeader(t) + header.ScheduledGasProvided++ + + err := processor.CheckScheduledData(header) + + require.ErrorIs(t, err, process.ErrScheduledGasAndFeesDoesNotMatch) + }) + + t.Run("should fail when scheduled gas penalized mismatch", func(t *testing.T) { + t.Parallel() + + processor, header := createProcessorAndHeader(t) + header.ScheduledGasPenalized++ + + err := processor.CheckScheduledData(header) + + require.ErrorIs(t, err, process.ErrScheduledGasAndFeesDoesNotMatch) + }) + + t.Run("should fail when scheduled gas refunded mismatch", func(t *testing.T) { + t.Parallel() + + processor, header := createProcessorAndHeader(t) + header.ScheduledGasRefunded++ + + err := processor.CheckScheduledData(header) + + require.ErrorIs(t, err, process.ErrScheduledGasAndFeesDoesNotMatch) + }) +} + // get initial fees on first getGasAndFees call and final fees on second call func createFeeHandlerMockForProcessScheduledBlock(initial, final scheduled.GasAndFees) process.TransactionFeeHandler { runCount := 0 @@ -3055,6 +3277,63 @@ func TestBaseProcessor_getIndexOfFirstMiniBlockToBeExecuted(t *testing.T) { assert.Nil(t, err) assert.Equal(t, 1, index) }) + + t.Run("leading processed miniBlock not executed locally is rejected", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createComponentHolderMocks() + coreComponents.EnableEpochsHandlerField = enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.ScheduledMiniBlocksFlag) + arguments := CreateMockArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.ScheduledTxsExecutionHandler = &testscommon.ScheduledTxsExecutionStub{ + IsMiniBlockExecutedCalled: func(_ []byte) bool { + return false + }, + } + bp, _ := blproc.NewShardProcessor(arguments) + + mbh := block.MiniBlockHeader{} + mbhReserved := block.MiniBlockHeaderReserved{ExecutionType: block.Processed} + mbh.Reserved, _ = mbhReserved.Marshal() + + metaBlock := &block.MetaBlock{MiniBlockHeaders: []block.MiniBlockHeader{mbh}} + + index, err := bp.GetIndexOfFirstMiniBlockToBeExecuted(metaBlock) + assert.Zero(t, index) + assert.ErrorIs(t, err, process.ErrMiniBlockNotExecuted) + }) + + t.Run("processed miniBlock after a non-processed one is rejected", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createComponentHolderMocks() + coreComponents.EnableEpochsHandlerField = enableEpochsHandlerMock.NewEnableEpochsHandlerStub(common.ScheduledMiniBlocksFlag) + arguments := CreateMockArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.ScheduledTxsExecutionHandler = &testscommon.ScheduledTxsExecutionStub{ + IsMiniBlockExecutedCalled: func(_ []byte) bool { + return true + }, + } + bp, _ := blproc.NewShardProcessor(arguments) + + mbhNormal := block.MiniBlockHeader{} + mbhNormalReserved := block.MiniBlockHeaderReserved{ExecutionType: block.Normal} + mbhNormal.Reserved, _ = mbhNormalReserved.Marshal() + + mbhProcessed := block.MiniBlockHeader{} + mbhProcessedReserved := block.MiniBlockHeaderReserved{ExecutionType: block.Processed} + mbhProcessed.Reserved, _ = mbhProcessedReserved.Marshal() + + metaBlock := &block.MetaBlock{ + MiniBlockHeaders: []block.MiniBlockHeader{ + mbhNormal, + mbhProcessed, + }, + } + + index, err := bp.GetIndexOfFirstMiniBlockToBeExecuted(metaBlock) + assert.Zero(t, index) + assert.ErrorIs(t, err, process.ErrProcessedMiniBlockNotInLeadingPrefix) + }) } func TestBaseProcessor_getFinalMiniBlocks(t *testing.T) { @@ -3546,43 +3825,166 @@ func TestBaseProcessor_getPruningHandlerSetsDefaulPruningDelay(t *testing.T) { assert.False(t, ph.IsPruningEnabled()) } -func TestBaseProcessor_checkConstructionStateAndIndexesCorrectness(t *testing.T) { +func TestCheckConstructionStateProcessingTypeAndIndexesCorrectness(t *testing.T) { t.Parallel() - arguments := CreateMockArguments(createComponentHolderMocks()) - bp, _ := blproc.NewShardProcessor(arguments) + const blockShard = uint32(1) + const otherShard = uint32(2) - mbh := &block.MiniBlockHeader{ - TxCount: 5, + makeMb := func(sender, receiver uint32, mbType block.Type, bodyScheduled bool, txCount int) *block.MiniBlock { + mb := &block.MiniBlock{ + SenderShardID: sender, + ReceiverShardID: receiver, + Type: mbType, + TxHashes: make([][]byte, txCount), + } + for i := range mb.TxHashes { + mb.TxHashes[i] = []byte{byte(i)} + } + if bodyScheduled { + reserved, _ := (&block.MiniBlockReserved{ExecutionType: block.Scheduled}).Marshal() + mb.Reserved = reserved + } + return mb } - _ = mbh.SetConstructionState(int32(block.PartialExecuted)) + makeMbh := func(mb *block.MiniBlock, hdrPT block.ProcessingType, state block.MiniBlockState, lastIdx int32) *block.MiniBlockHeader { + mbh := &block.MiniBlockHeader{ + SenderShardID: mb.SenderShardID, + ReceiverShardID: mb.ReceiverShardID, + Type: mb.Type, + TxCount: uint32(len(mb.TxHashes)), + } + _ = mbh.SetProcessingType(int32(hdrPT)) + _ = mbh.SetConstructionState(int32(state)) + _ = mbh.SetIndexOfLastTxProcessed(lastIdx) + return mbh + } - _ = mbh.SetIndexOfLastTxProcessed(int32(mbh.TxCount)) - err := bp.CheckConstructionStateAndIndexesCorrectness(mbh) - assert.Nil(t, err) + t.Run("legal cells pass", func(t *testing.T) { + t.Parallel() - _ = mbh.SetIndexOfLastTxProcessed(int32(mbh.TxCount) - 2) - err = bp.CheckConstructionStateAndIndexesCorrectness(mbh) - assert.Nil(t, err) + cases := []struct { + name string + sender uint32 + receiver uint32 + body bool + hdrPT block.ProcessingType + state block.MiniBlockState + txCount int + lastIdx int32 + }{ + {"normal intra", blockShard, blockShard, false, block.Normal, block.Final, 3, 2}, + {"normal outgoing", blockShard, otherShard, false, block.Normal, block.Final, 3, 2}, + {"normal incoming", otherShard, blockShard, false, block.Normal, block.Final, 3, 2}, + {"normal incoming with scheduled body", otherShard, blockShard, true, block.Normal, block.Final, 3, 2}, + {"scheduled intra", blockShard, blockShard, true, block.Scheduled, block.Proposed, 3, 2}, + {"scheduled outgoing", blockShard, otherShard, true, block.Scheduled, block.Proposed, 3, 2}, + {"scheduled incoming with scheduled body", otherShard, blockShard, true, block.Scheduled, block.Final, 3, 2}, + {"scheduled incoming with normal body", otherShard, blockShard, false, block.Scheduled, block.Final, 3, 2}, + {"processed intra", blockShard, blockShard, true, block.Processed, block.Final, 3, 2}, + {"processed outgoing", blockShard, otherShard, true, block.Processed, block.Final, 3, 2}, + {"broadcast peer mb", blockShard, core.AllShardId, false, block.Normal, block.Final, 1, 0}, + } + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + mb := makeMb(tc.sender, tc.receiver, block.TxBlock, tc.body, tc.txCount) + mbh := makeMbh(mb, tc.hdrPT, tc.state, tc.lastIdx) + err := blproc.CheckConstructionStateProcessingTypeAndIndexesCorrectness(mbh, mb, blockShard) + assert.NoError(t, err) + }) + } + }) + + t.Run("scheduled plus partial executed allowed at sender", func(t *testing.T) { + t.Parallel() + mb := makeMb(blockShard, blockShard, block.TxBlock, true, 3) + mbh := makeMbh(mb, block.Scheduled, block.PartialExecuted, 1) + err := blproc.CheckConstructionStateProcessingTypeAndIndexesCorrectness(mbh, mb, blockShard) + assert.NoError(t, err) + }) - _ = mbh.SetIndexOfLastTxProcessed(int32(mbh.TxCount) - 1) - err = bp.CheckConstructionStateAndIndexesCorrectness(mbh) - assert.Equal(t, process.ErrIndexDoesNotMatchWithPartialExecutedMiniBlock, err) + t.Run("scheduled plus partial executed allowed at incoming", func(t *testing.T) { + t.Parallel() + mb := makeMb(otherShard, blockShard, block.TxBlock, true, 3) + mbh := makeMbh(mb, block.Scheduled, block.PartialExecuted, 1) + err := blproc.CheckConstructionStateProcessingTypeAndIndexesCorrectness(mbh, mb, blockShard) + assert.NoError(t, err) + }) - _ = mbh.SetConstructionState(int32(block.Final)) + t.Run("scheduled body required when header is scheduled", func(t *testing.T) { + t.Parallel() + mb := makeMb(blockShard, blockShard, block.TxBlock, false, 3) + mbh := makeMbh(mb, block.Scheduled, block.Proposed, 2) + err := blproc.CheckConstructionStateProcessingTypeAndIndexesCorrectness(mbh, mb, blockShard) + assert.ErrorIs(t, err, process.ErrProcessingTypeBodyHeaderMismatch) + }) - _ = mbh.SetIndexOfLastTxProcessed(int32(mbh.TxCount)) - err = bp.CheckConstructionStateAndIndexesCorrectness(mbh) - assert.Equal(t, process.ErrIndexDoesNotMatchWithFullyExecutedMiniBlock, err) + t.Run("sender shard normal header with scheduled body rejected", func(t *testing.T) { + t.Parallel() + mb := makeMb(blockShard, otherShard, block.TxBlock, true, 3) + mbh := makeMbh(mb, block.Normal, block.Final, 2) + err := blproc.CheckConstructionStateProcessingTypeAndIndexesCorrectness(mbh, mb, blockShard) + assert.ErrorIs(t, err, process.ErrProcessingTypeBodyHeaderMismatch) + }) - _ = mbh.SetIndexOfLastTxProcessed(int32(mbh.TxCount) - 2) - err = bp.CheckConstructionStateAndIndexesCorrectness(mbh) - assert.Equal(t, process.ErrIndexDoesNotMatchWithFullyExecutedMiniBlock, err) + t.Run("processed must have sender equal block shard", func(t *testing.T) { + t.Parallel() + mb := makeMb(otherShard, blockShard, block.TxBlock, true, 3) + mbh := makeMbh(mb, block.Processed, block.Final, 2) + err := blproc.CheckConstructionStateProcessingTypeAndIndexesCorrectness(mbh, mb, blockShard) + assert.ErrorIs(t, err, process.ErrInvalidMiniBlockShardRole) + }) - _ = mbh.SetIndexOfLastTxProcessed(int32(mbh.TxCount) - 1) - err = bp.CheckConstructionStateAndIndexesCorrectness(mbh) - assert.Nil(t, err) + t.Run("processed requires scheduled body", func(t *testing.T) { + t.Parallel() + mb := makeMb(blockShard, blockShard, block.TxBlock, false, 3) + mbh := makeMbh(mb, block.Processed, block.Final, 2) + err := blproc.CheckConstructionStateProcessingTypeAndIndexesCorrectness(mbh, mb, blockShard) + assert.ErrorIs(t, err, process.ErrProcessingTypeBodyHeaderMismatch) + }) + + t.Run("incoming normal partial executed allowed", func(t *testing.T) { + t.Parallel() + mb := makeMb(otherShard, blockShard, block.TxBlock, false, 3) + mbh := makeMbh(mb, block.Normal, block.PartialExecuted, 1) + err := blproc.CheckConstructionStateProcessingTypeAndIndexesCorrectness(mbh, mb, blockShard) + assert.NoError(t, err) + }) + + t.Run("sender shard normal partial executed rejected", func(t *testing.T) { + t.Parallel() + mb := makeMb(blockShard, otherShard, block.TxBlock, false, 3) + mbh := makeMbh(mb, block.Normal, block.PartialExecuted, 1) + err := blproc.CheckConstructionStateProcessingTypeAndIndexesCorrectness(mbh, mb, blockShard) + assert.ErrorIs(t, err, process.ErrInvalidConstructionState) + }) + + t.Run("outgoing normal proposed with final index rejected", func(t *testing.T) { + t.Parallel() + mb := makeMb(blockShard, otherShard, block.TxBlock, false, 3) + mbh := makeMbh(mb, block.Normal, block.Proposed, 2) + err := blproc.CheckConstructionStateProcessingTypeAndIndexesCorrectness(mbh, mb, blockShard) + assert.ErrorIs(t, err, process.ErrInvalidConstructionState) + }) + + t.Run("non TxBlock cannot be scheduled", func(t *testing.T) { + t.Parallel() + mb := makeMb(blockShard, blockShard, block.SmartContractResultBlock, true, 2) + mbh := makeMbh(mb, block.Scheduled, block.Proposed, 1) + err := blproc.CheckConstructionStateProcessingTypeAndIndexesCorrectness(mbh, mb, blockShard) + assert.ErrorIs(t, err, process.ErrInvalidMiniBlockProcessingTypeForType) + }) + + t.Run("index inconsistency with partial executed", func(t *testing.T) { + t.Parallel() + mb := makeMb(blockShard, blockShard, block.TxBlock, true, 3) + mbh := makeMbh(mb, block.Processed, block.PartialExecuted, 2) + err := blproc.CheckConstructionStateProcessingTypeAndIndexesCorrectness(mbh, mb, blockShard) + assert.ErrorIs(t, err, process.ErrInvalidConstructionState) + }) } func TestBaseProcessor_ConcurrentCallsNonceOfFirstCommittedBlock(t *testing.T) { @@ -3934,6 +4336,7 @@ func TestBaseProcessor_updateGasConsumptionLimitsIfNeeded(t *testing.T) { func TestCheckHeaderBodyCorrelationProposal(t *testing.T) { t.Parallel() + shardID := uint32(0) t.Run("different number of miniblock headers and miniblocks should error ", func(t *testing.T) { t.Parallel() @@ -3945,6 +4348,7 @@ func TestCheckHeaderBodyCorrelationProposal(t *testing.T) { &block.Body{MiniBlocks: []*block.MiniBlock{ {SenderShardID: 0}, }}, + shardID, ) require.Equal(t, process.ErrHeaderBodyMismatch, err) }) @@ -3961,6 +4365,7 @@ func TestCheckHeaderBodyCorrelationProposal(t *testing.T) { &block.Body{MiniBlocks: []*block.MiniBlock{ nil, }}, + shardID, ) require.Equal(t, process.ErrNilMiniBlock, err) }) @@ -3977,6 +4382,7 @@ func TestCheckHeaderBodyCorrelationProposal(t *testing.T) { &block.Body{MiniBlocks: []*block.MiniBlock{ {}, }}, + shardID, ) require.Equal(t, process.ErrNilMiniBlockHeader, err) }) @@ -3994,6 +4400,7 @@ func TestCheckHeaderBodyCorrelationProposal(t *testing.T) { &block.Body{MiniBlocks: []*block.MiniBlock{ {}, }}, + shardID, ) require.Equal(t, process.ErrHeaderBodyMismatch, err) }) @@ -4017,6 +4424,7 @@ func TestCheckHeaderBodyCorrelationProposal(t *testing.T) { &block.Body{MiniBlocks: []*block.MiniBlock{ miniBlock, }}, + shardID, ) require.Equal(t, process.ErrHeaderBodyMismatch, err) }) @@ -4042,6 +4450,7 @@ func TestCheckHeaderBodyCorrelationProposal(t *testing.T) { &block.Body{MiniBlocks: []*block.MiniBlock{ miniBlock, }}, + shardID, ) require.ErrorIs(t, err, process.ErrHeaderBodyMismatch) }) @@ -4069,6 +4478,7 @@ func TestCheckHeaderBodyCorrelationProposal(t *testing.T) { &block.Body{MiniBlocks: []*block.MiniBlock{ miniBlock, }}, + shardID, ) require.ErrorIs(t, err, process.ErrHeaderBodyMismatch) }) @@ -4078,8 +4488,11 @@ func TestCheckHeaderBodyCorrelationProposal(t *testing.T) { bp, _ := blproc.NewShardProcessor(arguments) miniBlock := &block.MiniBlock{ - SenderShardID: 0, ReceiverShardID: 2, + SenderShardID: 0, + TxHashes: [][]byte{[]byte("tx1"), []byte("tx2")}, + Type: block.TxBlock, + Reserved: nil, } mbHash, _ := core.CalculateHash(arguments.CoreComponents.InternalMarshalizer(), arguments.CoreComponents.Hasher(), miniBlock) @@ -4089,6 +4502,9 @@ func TestCheckHeaderBodyCorrelationProposal(t *testing.T) { Hash: mbHash, SenderShardID: 0, ReceiverShardID: 2, + TxCount: 2, + Type: block.TxBlock, + Reserved: nil, } _ = mbHeaders[0].SetConstructionState(int32(block.PartialExecuted)) @@ -4097,6 +4513,7 @@ func TestCheckHeaderBodyCorrelationProposal(t *testing.T) { &block.Body{MiniBlocks: []*block.MiniBlock{ miniBlock, }}, + shardID, ) require.Equal(t, process.ErrWrongMiniBlockConstructionState, err) }) @@ -4108,6 +4525,9 @@ func TestCheckHeaderBodyCorrelationProposal(t *testing.T) { miniBlock := &block.MiniBlock{ SenderShardID: 0, ReceiverShardID: 2, + TxHashes: [][]byte{[]byte("tx1"), []byte("tx2")}, + Type: block.TxBlock, + Reserved: nil, } mbHash, _ := core.CalculateHash(arguments.CoreComponents.InternalMarshalizer(), arguments.CoreComponents.Hasher(), miniBlock) @@ -4117,6 +4537,9 @@ func TestCheckHeaderBodyCorrelationProposal(t *testing.T) { Hash: mbHash, SenderShardID: 0, ReceiverShardID: 2, + TxCount: 2, + Type: block.TxBlock, + Reserved: nil, } _ = mbHeaders[0].SetConstructionState(int32(block.Proposed)) _ = mbHeaders[0].SetProcessingType(int32(block.Scheduled)) @@ -4126,6 +4549,7 @@ func TestCheckHeaderBodyCorrelationProposal(t *testing.T) { &block.Body{MiniBlocks: []*block.MiniBlock{ miniBlock, }}, + shardID, ) require.Equal(t, process.ErrWrongMiniBlockProcessingType, err) }) @@ -4137,6 +4561,9 @@ func TestCheckHeaderBodyCorrelationProposal(t *testing.T) { miniBlock := &block.MiniBlock{ SenderShardID: 0, ReceiverShardID: 2, + TxHashes: [][]byte{[]byte("tx1"), []byte("tx2")}, + Type: block.TxBlock, + Reserved: nil, } mbHash, _ := core.CalculateHash(arguments.CoreComponents.InternalMarshalizer(), arguments.CoreComponents.Hasher(), miniBlock) @@ -4146,6 +4573,9 @@ func TestCheckHeaderBodyCorrelationProposal(t *testing.T) { Hash: mbHash, SenderShardID: 0, ReceiverShardID: 2, + TxCount: 2, + Type: block.TxBlock, + Reserved: nil, } _ = mbHeaders[0].SetConstructionState(int32(block.Proposed)) _ = mbHeaders[0].SetProcessingType(int32(block.Normal)) @@ -4155,6 +4585,7 @@ func TestCheckHeaderBodyCorrelationProposal(t *testing.T) { &block.Body{MiniBlocks: []*block.MiniBlock{ miniBlock, }}, + shardID, ) require.NoError(t, err) }) diff --git a/process/block/export_test.go b/process/block/export_test.go index 4091e352393..9c9f924442e 100644 --- a/process/block/export_test.go +++ b/process/block/export_test.go @@ -293,6 +293,7 @@ func NewShardProcessorEmptyWith3shards( }, }, BlockTracker: mock.NewBlockTrackerMock(shardCoordinator, genesisBlocks), + MiniBlockTracker: &testscommon.MiniBlockTrackerStub{}, BlockSizeThrottler: &mock.BlockSizeThrottlerStub{}, Version: "softwareVersion", HistoryRepository: &dblookupext.HistoryRepositoryStub{}, @@ -439,7 +440,7 @@ func (mp *metaProcessor) CheckShardHeadersFinality(highestNonceHdrs map[uint32]d // CheckHeaderBodyCorrelation - func (mp *metaProcessor) CheckHeaderBodyCorrelation(hdr data.HeaderHandler, body *block.Body) error { - return mp.checkHeaderBodyCorrelation(hdr.GetMiniBlockHeaderHandlers(), body) + return mp.checkHeaderBodyCorrelation(hdr.GetMiniBlockHeaderHandlers(), body, hdr.GetShardID(), false) } // IsHdrConstructionValid - @@ -464,7 +465,7 @@ func (sp *shardProcessor) SaveLastNotarizedHeader(shardId uint32, processedHdrs // CheckHeaderBodyCorrelation - func (sp *shardProcessor) CheckHeaderBodyCorrelation(hdr data.HeaderHandler, body *block.Body) error { - return sp.checkHeaderBodyCorrelation(hdr.GetMiniBlockHeaderHandlers(), body) + return sp.checkHeaderBodyCorrelation(hdr.GetMiniBlockHeaderHandlers(), body, hdr.GetShardID(), false) } // CheckAndRequestIfMetaHeadersMissing - @@ -611,6 +612,11 @@ func (mp *metaProcessor) UpdateState(metaBlock data.MetaHeaderHandler, metaBlock mp.updateState(metaBlock, metaBlockHash) } +// CheckScheduledData - +func (bp *baseProcessor) CheckScheduledData(headerHandler data.HeaderHandler) error { + return bp.checkScheduledData(headerHandler) +} + // GasAndFeesDelta - func GasAndFeesDelta(initialGasAndFees, finalGasAndFees scheduled.GasAndFees) scheduled.GasAndFees { return gasAndFeesDelta(initialGasAndFees, finalGasAndFees) @@ -732,9 +738,9 @@ func (sp *shardProcessor) RollBackProcessedMiniBlocksInfo(headerHandler data.Hea sp.rollBackProcessedMiniBlocksInfo(headerHandler, mapMiniBlockHashes) } -// CheckConstructionStateAndIndexesCorrectness - -func (bp *baseProcessor) CheckConstructionStateAndIndexesCorrectness(mbh data.MiniBlockHeaderHandler) error { - return checkConstructionStateAndIndexesCorrectness(mbh) +// CheckConstructionStateProcessingTypeAndIndexesCorrectness - +func CheckConstructionStateProcessingTypeAndIndexesCorrectness(mbh data.MiniBlockHeaderHandler, miniBlock *block.MiniBlock, blockShardID uint32) error { + return checkConstructionStateProcessingTypeAndIndexesCorrectness(mbh, miniBlock, blockShardID) } // GetAllMarshalledTxs - @@ -836,8 +842,8 @@ func (bp *baseProcessor) SetMiniBlockSelectionSession(session MiniBlocksSelectio } // CheckHeaderBodyCorrelationProposal - -func (bp *baseProcessor) CheckHeaderBodyCorrelationProposal(miniBlockHeaders []data.MiniBlockHeaderHandler, body *block.Body) error { - return bp.checkHeaderBodyCorrelationProposal(miniBlockHeaders, body) +func (bp *baseProcessor) CheckHeaderBodyCorrelationProposal(miniBlockHeaders []data.MiniBlockHeaderHandler, body *block.Body, headerShardID uint32) error { + return bp.checkHeaderBodyCorrelation(miniBlockHeaders, body, headerShardID, true) } // GetFinalMiniBlocksFromExecutionResults - diff --git a/process/block/interceptedBlocks/interceptedMetaBlockHeader.go b/process/block/interceptedBlocks/interceptedMetaBlockHeader.go index e06848b97e3..e90d6f4978a 100644 --- a/process/block/interceptedBlocks/interceptedMetaBlockHeader.go +++ b/process/block/interceptedBlocks/interceptedMetaBlockHeader.go @@ -126,6 +126,10 @@ func (imh *InterceptedMetaHeader) isMetaHeaderEpochOutOfRange() bool { return false } + if imh.epochStartTrigger.Epoch() == 0 { + return false + } + if imh.hdr.GetEpoch() > imh.epochStartTrigger.Epoch()+1 { return true } diff --git a/process/block/interceptedBlocks/interceptedMiniblock.go b/process/block/interceptedBlocks/interceptedMiniblock.go index d62cc46673b..f528ceeb8ba 100644 --- a/process/block/interceptedBlocks/interceptedMiniblock.go +++ b/process/block/interceptedBlocks/interceptedMiniblock.go @@ -99,29 +99,7 @@ func (inMb *InterceptedMiniblock) IsForCurrentShard() bool { func (inMb *InterceptedMiniblock) integrity() error { miniblock := inMb.miniblock - receiverNotCurrentShard := miniblock.ReceiverShardID >= inMb.shardCoordinator.NumberOfShards() && - (miniblock.ReceiverShardID != core.MetachainShardId && miniblock.ReceiverShardID != core.AllShardId) - if receiverNotCurrentShard { - return process.ErrInvalidShardId - } - - senderNotCurrentShard := miniblock.SenderShardID >= inMb.shardCoordinator.NumberOfShards() && - miniblock.SenderShardID != core.MetachainShardId - if senderNotCurrentShard { - return process.ErrInvalidShardId - } - - for _, txHash := range miniblock.TxHashes { - if txHash == nil { - return process.ErrNilTxHash - } - } - - if len(miniblock.GetReserved()) > maxLenMiniBlockReservedField { - return process.ErrReservedFieldInvalid - } - - return nil + return process.CheckMiniBlock(miniblock, inMb.shardCoordinator) } // Type returns the type of this intercepted data diff --git a/process/block/interceptedBlocks/interceptedMiniblock_test.go b/process/block/interceptedBlocks/interceptedMiniblock_test.go index b592b742b96..cb87e263112 100644 --- a/process/block/interceptedBlocks/interceptedMiniblock_test.go +++ b/process/block/interceptedBlocks/interceptedMiniblock_test.go @@ -87,7 +87,7 @@ func TestInterceptedMiniblock_InvalidReceiverShardIdShouldErr(t *testing.T) { err := inMb.CheckValidity() - assert.Equal(t, process.ErrInvalidShardId, err) + assert.ErrorIs(t, err, process.ErrInvalidShardId) } func TestInterceptedMiniblock_InvalidSenderShardIdShouldErr(t *testing.T) { @@ -104,7 +104,7 @@ func TestInterceptedMiniblock_InvalidSenderShardIdShouldErr(t *testing.T) { err := inMb.CheckValidity() - assert.Equal(t, process.ErrInvalidShardId, err) + assert.ErrorIs(t, err, process.ErrInvalidShardId) } func TestInterceptedMiniblock_ContainsNilHashShouldErr(t *testing.T) { diff --git a/process/block/metablock.go b/process/block/metablock.go index c6bc335fb68..618817b7d67 100644 --- a/process/block/metablock.go +++ b/process/block/metablock.go @@ -16,12 +16,10 @@ import ( "github.com/multiversx/mx-chain-core-go/data/headerVersionData" logger "github.com/multiversx/mx-chain-logger-go" - epochStartMetaCommmon "github.com/multiversx/mx-chain-go/epochStart/metachain" - "github.com/multiversx/mx-chain-go/trie" - "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/common/holders" "github.com/multiversx/mx-chain-go/dataRetriever" + epochStartMetaCommmon "github.com/multiversx/mx-chain-go/epochStart/metachain" processOutport "github.com/multiversx/mx-chain-go/outport/process" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/process/asyncExecution/executionTrack" @@ -29,6 +27,7 @@ import ( "github.com/multiversx/mx-chain-go/process/block/helpers" "github.com/multiversx/mx-chain-go/process/block/processedMb" "github.com/multiversx/mx-chain-go/state" + "github.com/multiversx/mx-chain-go/trie" ) const ( @@ -198,7 +197,7 @@ func (mp *metaProcessor) ProcessBlock( return process.ErrWrongTypeAssertion } - err = mp.checkHeaderBodyCorrelation(header.GetMiniBlockHeaderHandlers(), body) + err = mp.checkHeaderBodyCorrelation(header.GetMiniBlockHeaderHandlers(), body, header.GetShardID(), false) if err != nil { return err } @@ -247,6 +246,11 @@ func (mp *metaProcessor) ProcessBlock( return err } + err = mp.verifyNonEpochStartMiniBlocks(header) + if err != nil { + return err + } + mp.txCoordinator.RequestBlockTransactions(body) mp.hdrsForCurrBlock.RequestShardHeaders(header) @@ -479,6 +483,17 @@ func (mp *metaProcessor) verifyEpochStartMiniBlocks(metaBlock data.MetaHeaderHan return nil } +func (mp *metaProcessor) verifyNonEpochStartMiniBlocks(header data.HeaderHandler) error { + for _, miniBlockHeader := range header.GetMiniBlockHeaderHandlers() { + if miniBlockHeader.GetTypeInt32() == int32(block.RewardsBlock) || + miniBlockHeader.GetTypeInt32() == int32(block.PeerBlock) { + return process.ErrInvalidMiniBlockType + } + } + + return nil +} + // SetNumProcessedObj will set the num of processed headers func (mp *metaProcessor) SetNumProcessedObj(numObj uint64) { mp.headersCounter.shardMBHeadersTotalProcessed = numObj @@ -2121,6 +2136,14 @@ func (mp *metaProcessor) saveLastNotarizedHeader(metaHeader data.MetaHeaderHandl hash := lastCrossNotarizedHeaderForShard[shardID].hash mp.blockTracker.AddCrossNotarizedHeader(shardID, hdr, hash) DisplayLastNotarized(mp.marshalizer, mp.hasher, hdr, shardID) + + // Per-shard threshold advance: commitAll already ran, so SCRs from shardID up to + // hdr.GetNonce() can be released. hdr.GetNonce()+1 releases items at the just- + // notarized nonce too, since they were processed in this metablock. + if !check.IfNil(hdr) && !check.IfNil(mp.miniBlockTracker) { + threshold := hdr.GetNonce() + 1 + mp.miniBlockTracker.ReleaseImmunityForCommittedShardBlocks(shardID, threshold) + } } return nil diff --git a/process/block/metablockProposal.go b/process/block/metablockProposal.go index ce9a5cdd114..59f98e6c978 100644 --- a/process/block/metablockProposal.go +++ b/process/block/metablockProposal.go @@ -247,7 +247,7 @@ func (mp *metaProcessor) VerifyBlockProposal( } } - err = mp.checkHeaderBodyCorrelationProposal(header.GetMiniBlockHeaderHandlers(), body) + err = mp.checkHeaderBodyCorrelation(header.GetMiniBlockHeaderHandlers(), body, header.GetShardID(), true) if err != nil { return err } diff --git a/process/block/metablock_test.go b/process/block/metablock_test.go index 9f2e9ec4ff1..1b13440a5cc 100644 --- a/process/block/metablock_test.go +++ b/process/block/metablock_test.go @@ -248,6 +248,7 @@ func createMockMetaArguments( }, }, BlockTracker: blockTracker, + MiniBlockTracker: &testscommon.MiniBlockTrackerStub{}, BlockSizeThrottler: &mock.BlockSizeThrottlerStub{}, HistoryRepository: &dblookupext.HistoryRepositoryStub{}, ScheduledTxsExecutionHandler: &testscommon.ScheduledTxsExecutionStub{}, @@ -990,6 +991,291 @@ func TestMetaProcessor_ProcessBlockWithErrOnVerifyStateRootCallShouldRevertState assert.True(t, wasCalled) } +func TestMetaProcessor_ProcessBlock_MiniBlockChecks(t *testing.T) { + t.Parallel() + + hash := []byte("hash1") + miniBlock1 := &block.MiniBlock{TxHashes: [][]byte{hash}} + + txCoordinator := &testscommon.TransactionCoordinatorMock{ + CreateMbsAndProcessCrossShardTransactionsDstMeCalled: func(header data.HeaderHandler, processedMiniBlocksInfo map[string]*processedMb.ProcessedMiniBlockInfo, haveTime func() bool, haveAdditionalTime func() bool, scheduledMode bool) (slices block.MiniBlockSlice, u uint32, b bool, err error) { + return block.MiniBlockSlice{miniBlock1}, 0, true, nil + }, + } + + blkc := &testscommon.ChainHandlerStub{ + GetCurrentBlockHeaderCalled: func() data.HeaderHandler { + return &block.MetaBlock{Nonce: 0, AccumulatedFeesInEpoch: big.NewInt(0), DevFeesInEpoch: big.NewInt(0)} + }, + GetCurrentBlockHeaderHashCalled: func() []byte { + return hash + }, + GetGenesisHeaderCalled: func() data.HeaderHandler { + return &block.Header{Nonce: 0} + }, + } + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + coreComponents.Hash = &hashingMocks.HasherMock{} + dataComponents.BlockChain = blkc + bootstrapComponents.VersionedHdrFactory = &testscommon.VersionedHeaderFactoryStub{ + CreateCalled: func(epoch uint32, round uint64) data.HeaderHandler { + return &block.MetaBlock{ + Epoch: 0, + Round: round, + } + }, + } + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + arguments.TxCoordinator = txCoordinator + + mp, _ := processBlock.NewMetaProcessor(arguments) + + t.Run("should work with valid miniblocks", func(t *testing.T) { + mb1 := &block.MiniBlock{ + TxHashes: [][]byte{[]byte("txHash1")}, + SenderShardID: core.MetachainShardId, + ReceiverShardID: 1, + Type: block.TxBlock, + } + + mbHash, _ := core.CalculateHash(coreComponents.IntMarsh, coreComponents.Hash, mb1) + + metaBlock := &block.MetaBlock{ + Nonce: 1, + Round: 1, + PrevHash: hash, + AccumulatedFees: big.NewInt(0), + AccumulatedFeesInEpoch: big.NewInt(0), + DeveloperFees: big.NewInt(0), + DevFeesInEpoch: big.NewInt(0), + TxCount: 1, + MiniBlockHeaders: []block.MiniBlockHeader{ + { + Hash: mbHash, + SenderShardID: core.MetachainShardId, + ReceiverShardID: 1, + Type: block.TxBlock, + TxCount: 1, + }, + }, + } + + body := &block.Body{ + MiniBlocks: []*block.MiniBlock{ + mb1, + }, + } + + err := mp.ProcessBlock(metaBlock, body, func() time.Duration { return time.Second }) + require.Nil(t, err) + }) + + t.Run("non epoch start should not have rewards mb", func(t *testing.T) { + mb1 := &block.MiniBlock{ + TxHashes: [][]byte{[]byte("txHash1")}, + SenderShardID: core.MetachainShardId, + ReceiverShardID: 1, + Type: block.RewardsBlock, + } + + mbHash, _ := core.CalculateHash(coreComponents.IntMarsh, coreComponents.Hash, mb1) + + metaBlock := &block.MetaBlock{ + Nonce: 1, + Round: 1, + PrevHash: hash, + AccumulatedFees: big.NewInt(0), + AccumulatedFeesInEpoch: big.NewInt(0), + DeveloperFees: big.NewInt(0), + DevFeesInEpoch: big.NewInt(0), + TxCount: 1, + MiniBlockHeaders: []block.MiniBlockHeader{ + { + Hash: mbHash, + SenderShardID: core.MetachainShardId, + ReceiverShardID: 1, + Type: block.RewardsBlock, + TxCount: 1, + }, + }, + } + + body := &block.Body{ + MiniBlocks: []*block.MiniBlock{ + mb1, + }, + } + + err := mp.ProcessBlock(metaBlock, body, func() time.Duration { return time.Second }) + require.Equal(t, process.ErrInvalidMiniBlockType, err) + }) + + t.Run("non epoch start should not have peer mb", func(t *testing.T) { + mb1 := &block.MiniBlock{ + TxHashes: [][]byte{[]byte("txHash1")}, + SenderShardID: core.MetachainShardId, + ReceiverShardID: 1, + Type: block.PeerBlock, + } + + mbHash, _ := core.CalculateHash(coreComponents.IntMarsh, coreComponents.Hash, mb1) + + metaBlock := &block.MetaBlock{ + Nonce: 1, + Round: 1, + PrevHash: hash, + AccumulatedFees: big.NewInt(0), + AccumulatedFeesInEpoch: big.NewInt(0), + DeveloperFees: big.NewInt(0), + DevFeesInEpoch: big.NewInt(0), + TxCount: 1, + MiniBlockHeaders: []block.MiniBlockHeader{ + { + Hash: mbHash, + SenderShardID: core.MetachainShardId, + ReceiverShardID: 1, + Type: block.PeerBlock, + TxCount: 1, + }, + }, + } + + body := &block.Body{ + MiniBlocks: []*block.MiniBlock{ + mb1, + }, + } + + err := mp.ProcessBlock(metaBlock, body, func() time.Duration { return time.Second }) + require.Equal(t, process.ErrInvalidMiniBlockType, err) + }) + + t.Run("epoch start should have rewards or peer mb", func(t *testing.T) { + mb1 := &block.MiniBlock{ + TxHashes: [][]byte{[]byte("txHash1")}, + SenderShardID: core.MetachainShardId, + ReceiverShardID: 1, + Type: block.RewardsBlock, + } + mb2 := &block.MiniBlock{ + TxHashes: [][]byte{[]byte("txHash2")}, + SenderShardID: core.MetachainShardId, + ReceiverShardID: core.AllShardId, + Type: block.PeerBlock, + } + + mbHash, _ := core.CalculateHash(coreComponents.IntMarsh, coreComponents.Hash, mb1) + mbHash2, _ := core.CalculateHash(coreComponents.IntMarsh, coreComponents.Hash, mb2) + + metaBlock := &block.MetaBlock{ + Nonce: 1, + Round: 1, + PrevHash: hash, + AccumulatedFees: big.NewInt(0), + AccumulatedFeesInEpoch: big.NewInt(0), + DeveloperFees: big.NewInt(0), + DevFeesInEpoch: big.NewInt(0), + TxCount: 1, + MiniBlockHeaders: []block.MiniBlockHeader{ + { + Hash: mbHash, + SenderShardID: core.MetachainShardId, + ReceiverShardID: 1, + Type: block.RewardsBlock, + TxCount: 1, + }, + { + Hash: mbHash2, + SenderShardID: core.MetachainShardId, + ReceiverShardID: core.AllShardId, + Type: block.PeerBlock, + TxCount: 1, + }, + }, + EpochStart: block.EpochStart{ + LastFinalizedHeaders: []block.EpochStartShardData{ + { + ShardID: 1, + }, + }, + }, + } + + body := &block.Body{ + MiniBlocks: []*block.MiniBlock{ + mb1, + mb2, + }, + } + + err := mp.ProcessBlock(metaBlock, body, func() time.Duration { return time.Second }) + require.Nil(t, err) + }) + + t.Run("epoch start should not have other mb types", func(t *testing.T) { + mb1 := &block.MiniBlock{ + TxHashes: [][]byte{[]byte("txHash1")}, + SenderShardID: core.MetachainShardId, + ReceiverShardID: 1, + Type: block.TxBlock, + } + mb2 := &block.MiniBlock{ + TxHashes: [][]byte{[]byte("txHash2")}, + SenderShardID: core.MetachainShardId, + ReceiverShardID: 1, + Type: block.ReceiptBlock, + } + + mbHash, _ := core.CalculateHash(coreComponents.IntMarsh, coreComponents.Hash, mb1) + mbHash2, _ := core.CalculateHash(coreComponents.IntMarsh, coreComponents.Hash, mb2) + + metaBlock := &block.MetaBlock{ + Nonce: 1, + Round: 1, + PrevHash: hash, + AccumulatedFees: big.NewInt(0), + AccumulatedFeesInEpoch: big.NewInt(0), + DeveloperFees: big.NewInt(0), + DevFeesInEpoch: big.NewInt(0), + TxCount: 1, + MiniBlockHeaders: []block.MiniBlockHeader{ + { + Hash: mbHash, + SenderShardID: core.MetachainShardId, + ReceiverShardID: 1, + Type: block.TxBlock, + TxCount: 1, + }, + { + Hash: mbHash2, + SenderShardID: core.MetachainShardId, + ReceiverShardID: 1, + Type: block.ReceiptBlock, + TxCount: 1, + }, + }, + EpochStart: block.EpochStart{ + LastFinalizedHeaders: []block.EpochStartShardData{ + { + ShardID: 1, + }, + }, + }, + } + + body := &block.Body{ + MiniBlocks: []*block.MiniBlock{ + mb1, + mb2, + }, + } + + err := mp.ProcessBlock(metaBlock, body, func() time.Duration { return time.Second }) + require.Equal(t, process.ErrInvalidMiniBlockType, err) + }) +} + // ------- CommitBlock func TestMetaProcessor_CommitBlockMarshalizerFailForHeaderShouldErr(t *testing.T) { @@ -2217,6 +2503,48 @@ func TestMetaProcessor_saveLastNotarizedHeader(t *testing.T) { }) } +func TestMetaProcessor_SaveLastNotarizedHeader_ReleasesImmunityForCommittedShardBlocks(t *testing.T) { + t.Parallel() + + pool := dataRetrieverMock.NewPoolsHolderMock() + noOfShards := uint32(3) + coreComponents, dataComponents, bootstrapComponents, statusComponents := createMockComponentHolders() + coreComponents.Hash = &hashingMocks.HasherMock{} + dataComponents.DataPool = pool + dataComponents.Storage = initStore() + bootstrapComponents.Coordinator = mock.NewMultiShardsCoordinatorMock(noOfShards) + arguments := createMockMetaArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + + startHeaders := createGenesisBlocks(bootstrapComponents.ShardCoordinator()) + arguments.BlockTracker = mock.NewBlockTrackerMock(bootstrapComponents.ShardCoordinator(), startHeaders) + + received := make(map[uint32]uint64) + var mu sync.Mutex + arguments.MiniBlockTracker = &testscommon.MiniBlockTrackerStub{ + ReleaseImmunityForCommittedShardBlocksCalled: func(senderShard uint32, threshold uint64) { + mu.Lock() + received[senderShard] = threshold + mu.Unlock() + }, + } + + mp, err := processBlock.NewMetaProcessor(arguments) + require.Nil(t, err) + + const baseNonce = uint64(44) + setLastNotarizedHdr(noOfShards, 9, baseNonce, []byte("randseed"), mp.NotarizedHdrs(), arguments.BlockTracker) + + err = mp.SaveLastNotarizedHeader(&block.MetaBlock{}) + require.Nil(t, err) + + mu.Lock() + defer mu.Unlock() + require.Equal(t, int(noOfShards), len(received), "every shard should receive a release") + for shardID := uint32(0); shardID < noOfShards; shardID++ { + require.Equal(t, baseNonce+1, received[shardID], "shard %d expected hdr.GetNonce()+1", shardID) + } +} + func TestMetaProcessor_CheckShardHeadersValidity(t *testing.T) { t.Parallel() diff --git a/process/block/preprocess/transactions.go b/process/block/preprocess/transactions.go index 91523b151e9..8e24b7f5066 100644 --- a/process/block/preprocess/transactions.go +++ b/process/block/preprocess/transactions.go @@ -1715,7 +1715,7 @@ func (txs *transactions) ProcessMiniBlock( numTXsProcessed++ } - if err != nil && !partialMbExecutionMode { + if err != nil && (!partialMbExecutionMode || scheduledMode) { return processedTxHashes, txIndex - 1, true, err } diff --git a/process/block/preprocess/transactions_test.go b/process/block/preprocess/transactions_test.go index 390f9584019..6e38f022606 100644 --- a/process/block/preprocess/transactions_test.go +++ b/process/block/preprocess/transactions_test.go @@ -1936,6 +1936,83 @@ func TestTransactionsPreprocessor_ProcessMiniBlockShouldErrMaxGasLimitUsedForDes assert.Equal(t, -1, indexOfLastTxProcessed) } +// Scheduled mode mid-MB break must always roll back the whole MB, even when +// partial-execution mode is on, so the resulting header never combines +// ProcessingType=Scheduled with ConstructionState=PartialExecuted. +func TestTransactionsPreprocessor_ProcessMiniBlockScheduledRollsBackOnError(t *testing.T) { + t.Parallel() + + tdp := &dataRetrieverMock.PoolsHolderStub{ + TransactionsCalled: func() dataRetriever.ShardedDataCacherNotifier { + return &testscommon.ShardedDataStub{ + ShardDataStoreCalled: func(id string) (c storage.Cacher) { + return &cache.CacherStub{ + PeekCalled: func(key []byte) (value interface{}, ok bool) { + return &transaction.Transaction{}, true + }, + } + }, + } + }, + } + + txHashes := [][]byte{[]byte("tx_hash1"), []byte("tx_hash2")} + miniBlock := &block.MiniBlock{ + ReceiverShardID: 0, + SenderShardID: 1, + TxHashes: txHashes, + Type: block.TxBlock, + } + preProcessorExecutionInfoHandlerMock := &testscommon.PreProcessorExecutionInfoHandlerMock{ + GetNumOfCrossInterMbsAndTxsCalled: getNumOfCrossInterMbsAndTxsZero, + } + + // haveTime returns true for the initial getAllTxsFromMiniBlock per-tx checks + // (one call per tx hash), then false so the per-tx processing loop breaks + // immediately with ErrTimeIsOut. haveAdditionalTime always returns false so + // both branches of the loop's time-out guard fail together. + makeHaveTimeAllowingFetch := func() func() bool { + remaining := len(txHashes) + return func() bool { + if remaining > 0 { + remaining-- + return true + } + return false + } + } + + cases := []struct { + name string + scheduledMode bool + partialMode bool + expectShouldRevert bool + }{ + {"non-scheduled non-partial revert", false, false, true}, + {"non-scheduled partial do not revert", false, true, false}, + {"scheduled non-partial revert", true, false, true}, + {"scheduled partial revert (mutual exclusion fix)", true, true, true}, + } + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + args := createDefaultTransactionsProcessorArgs() + args.DataPool = tdp.Transactions() + txs, err := NewTransactionPreprocessor(args) + require.NoError(t, err) + + _, _, shouldRevert, processErr := txs.ProcessMiniBlock( + miniBlock, makeHaveTimeAllowingFetch(), haveAdditionalTimeFalse, + tc.scheduledMode, tc.partialMode, -1, + preProcessorExecutionInfoHandlerMock, + ) + require.ErrorIs(t, processErr, process.ErrTimeIsOut) + require.Equal(t, tc.expectShouldRevert, shouldRevert) + }) + } +} + func TestTransactionsPreprocessor_ComputeGasProvidedShouldWork(t *testing.T) { t.Parallel() diff --git a/process/block/shardblock.go b/process/block/shardblock.go index 9e43d1140be..924189c4c26 100644 --- a/process/block/shardblock.go +++ b/process/block/shardblock.go @@ -121,7 +121,7 @@ func (sp *shardProcessor) ProcessBlock( sp.epochNotifier.CheckEpoch(headerHandler) sp.requestHandler.SetEpoch(headerHandler.GetEpoch()) - err = sp.checkScheduledRootHash(headerHandler) + err = sp.checkScheduledData(headerHandler) if err != nil { return err } @@ -145,7 +145,7 @@ func (sp *shardProcessor) ProcessBlock( go getMetricsFromBlockBody(body, sp.marshalizer, sp.appStatusHandler) - err = sp.checkHeaderBodyCorrelation(header.GetMiniBlockHeaderHandlers(), body) + err = sp.checkHeaderBodyCorrelation(header.GetMiniBlockHeaderHandlers(), body, header.GetShardID(), false) if err != nil { return err } @@ -1638,6 +1638,14 @@ func (sp *shardProcessor) saveLastNotarizedHeader(shardId uint32, processedHdrs sp.blockTracker.AddCrossNotarizedHeader(shardId, lastCrossNotarizedHeader, lastCrossNotarizedHeaderHash) DisplayLastNotarized(sp.marshalizer, sp.hasher, lastCrossNotarizedHeader, shardId) + // processedHdrs only contains fully-processed metablocks (see processedAll gate in + // getOrderedProcessedMetaBlocksFromMiniBlockHashes), so lastNonce+1 releases items + // from those metablocks now that the consuming shard block is being committed. + if shardId == core.MetachainShardId && !check.IfNil(lastCrossNotarizedHeader) && !check.IfNil(sp.miniBlockTracker) { + threshold := lastCrossNotarizedHeader.GetNonce() + 1 + sp.miniBlockTracker.ReleaseImmunityForCommittedMetaBlocks(threshold) + } + return nil } diff --git a/process/block/shardblockProposal.go b/process/block/shardblockProposal.go index ea6eea8b3b9..6e9618b061f 100644 --- a/process/block/shardblockProposal.go +++ b/process/block/shardblockProposal.go @@ -179,7 +179,7 @@ func (sp *shardProcessor) VerifyBlockProposal( return process.ErrWrongTypeAssertion } - err = sp.checkHeaderBodyCorrelationProposal(header.GetMiniBlockHeaderHandlers(), body) + err = sp.checkHeaderBodyCorrelation(header.GetMiniBlockHeaderHandlers(), body, header.GetShardID(), true) if err != nil { return err } diff --git a/process/block/shardblock_test.go b/process/block/shardblock_test.go index e93ca3454e3..e45fee0d8ed 100644 --- a/process/block/shardblock_test.go +++ b/process/block/shardblock_test.go @@ -3986,6 +3986,86 @@ func TestShardProcessor_RemoveAndSaveLastNotarizedMetaHdrNoDstMB(t *testing.T) { assert.Equal(t, currHdr, sp.LastNotarizedHdrForShard(core.MetachainShardId)) } +func TestShardProcessor_SaveLastNotarizedHeader_ReleasesImmunityForCommittedMetaBlocks(t *testing.T) { + t.Parallel() + + t.Run("processed headers advance the last cross-notarized nonce", func(t *testing.T) { + var received uint64 + var called atomicCore.Flag + coreComponents, dataComponents, bootstrapComponents, statusComponents := createComponentHolderMocks() + arguments := CreateMockArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + startHeaders := createGenesisBlocks(bootstrapComponents.ShardCoordinator()) + arguments.BlockTracker = mock.NewBlockTrackerMock(bootstrapComponents.ShardCoordinator(), startHeaders) + arguments.MiniBlockTracker = &testscommon.MiniBlockTrackerStub{ + ReleaseImmunityForCommittedMetaBlocksCalled: func(threshold uint64) { + atomic.StoreUint64(&received, threshold) + called.SetValue(true) + }, + } + + sp, err := blproc.NewShardProcessor(arguments) + require.Nil(t, err) + + arguments.BlockTracker.AddCrossNotarizedHeader(core.MetachainShardId, &block.MetaBlock{Nonce: 10}, nil) + + processedHdrs := []data.HeaderHandler{ + &block.MetaBlock{Nonce: 12}, + &block.MetaBlock{Nonce: 15}, + } + err = sp.SaveLastNotarizedHeader(core.MetachainShardId, processedHdrs) + require.Nil(t, err) + require.True(t, called.IsSet()) + require.Equal(t, uint64(16), atomic.LoadUint64(&received)) + }) + + t.Run("no processed headers still releases against the existing cross-notarized nonce", func(t *testing.T) { + var received uint64 + var called atomicCore.Flag + coreComponents, dataComponents, bootstrapComponents, statusComponents := createComponentHolderMocks() + arguments := CreateMockArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + startHeaders := createGenesisBlocks(bootstrapComponents.ShardCoordinator()) + arguments.BlockTracker = mock.NewBlockTrackerMock(bootstrapComponents.ShardCoordinator(), startHeaders) + arguments.MiniBlockTracker = &testscommon.MiniBlockTrackerStub{ + ReleaseImmunityForCommittedMetaBlocksCalled: func(threshold uint64) { + atomic.StoreUint64(&received, threshold) + called.SetValue(true) + }, + } + + sp, err := blproc.NewShardProcessor(arguments) + require.Nil(t, err) + + arguments.BlockTracker.AddCrossNotarizedHeader(core.MetachainShardId, &block.MetaBlock{Nonce: 7}, nil) + + err = sp.SaveLastNotarizedHeader(core.MetachainShardId, nil) + require.Nil(t, err) + require.True(t, called.IsSet()) + require.Equal(t, uint64(8), atomic.LoadUint64(&received)) + }) + + t.Run("non-meta shard does not invoke the hook", func(t *testing.T) { + var called atomicCore.Flag + coreComponents, dataComponents, bootstrapComponents, statusComponents := createComponentHolderMocks() + arguments := CreateMockArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + startHeaders := createGenesisBlocks(bootstrapComponents.ShardCoordinator()) + arguments.BlockTracker = mock.NewBlockTrackerMock(bootstrapComponents.ShardCoordinator(), startHeaders) + arguments.MiniBlockTracker = &testscommon.MiniBlockTrackerStub{ + ReleaseImmunityForCommittedMetaBlocksCalled: func(_ uint64) { + called.SetValue(true) + }, + } + + sp, err := blproc.NewShardProcessor(arguments) + require.Nil(t, err) + + arguments.BlockTracker.AddCrossNotarizedHeader(0, &block.Header{Nonce: 4}, nil) + + err = sp.SaveLastNotarizedHeader(0, []data.HeaderHandler{&block.Header{Nonce: 5}}) + require.Nil(t, err) + require.False(t, called.IsSet()) + }) +} + func createShardData(hasher hashing.Hasher, marshalizer marshal.Marshalizer, miniBlocks []block.MiniBlock) []block.ShardData { shardData := make([]block.ShardData, len(miniBlocks)) for i := 0; i < len(miniBlocks); i++ { diff --git a/process/common.go b/process/common.go index cfb0c1513aa..5cbca467666 100644 --- a/process/common.go +++ b/process/common.go @@ -25,6 +25,7 @@ import ( "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/dataRetriever" + "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/state" ) @@ -32,6 +33,7 @@ var log = logger.GetOrCreate("process") const maxSelfNotarizedLookback = 50 const VMStoragePrefix = "VM@" +const maxLenMiniBlockReservedField = 10 // ShardedCacheSearchMethod defines the algorithm for searching through a sharded cache type ShardedCacheSearchMethod byte @@ -1231,6 +1233,128 @@ func findSelfNotarizedMetaHeaderInBlock( return bestNonce, bestHeader, bestHash } +// CheckMiniBlock will check miniblock validity +func CheckMiniBlock( + miniBlock *block.MiniBlock, + shardCoordinator sharding.Coordinator, +) error { + senderShard := miniBlock.GetSenderShardID() + receiverShard := miniBlock.GetReceiverShardID() + + // shard id checks + receiverShardInvalid := receiverShard >= shardCoordinator.NumberOfShards() && + (receiverShard != core.MetachainShardId && receiverShard != core.AllShardId) + if receiverShardInvalid { + return fmt.Errorf("%w - receiver not for current shard: block type: %s, sender shard id: %d, receiver shard id: %d", + ErrInvalidShardId, + miniBlock.Type, + senderShard, + receiverShard) + } + + senderShardInvalid := senderShard >= shardCoordinator.NumberOfShards() && + senderShard != core.MetachainShardId + if senderShardInvalid { + return fmt.Errorf("%w - sender not for current shard: block type: %s, sender shard id: %d, receiver shard id: %d", + ErrInvalidShardId, + miniBlock.Type, + senderShard, + receiverShard) + } + + if senderShard != shardCoordinator.SelfId() && receiverShard != shardCoordinator.SelfId() && receiverShard != core.AllShardId { + return fmt.Errorf("%w - not valid shard ids: block type: %s, sender shard id: %d, receiver shard id: %d", + ErrInvalidShardId, + miniBlock.Type, + senderShard, + receiverShard) + } + + err := checkMiniBlockByType(miniBlock, shardCoordinator) + if err != nil { + return err + } + + for _, txHash := range miniBlock.TxHashes { + if txHash == nil { + return ErrNilTxHash + } + } + + if len(miniBlock.GetReserved()) > maxLenMiniBlockReservedField { + return ErrReservedFieldInvalid + } + + return nil +} + +func checkMiniBlockByType( + miniBlock *block.MiniBlock, + shardCoordinator sharding.Coordinator, +) error { + selfId := shardCoordinator.SelfId() + sender := miniBlock.GetSenderShardID() + receiver := miniBlock.GetReceiverShardID() + mbType := miniBlock.GetType() + + switch mbType { + case block.TxBlock: + if sender == core.MetachainShardId || receiver == core.AllShardId { + return fmt.Errorf("%w - TxBlock must be from shard to specific shard id: block type: %s, sender shard id: %d, receiver shard id: %d", + ErrInvalidShardId, + mbType, + sender, + receiver, + ) + } + + case block.SmartContractResultBlock: + if receiver == core.AllShardId { + return fmt.Errorf("%w - SCResultBlock cannot target AllShardId: block type: %s, sender shard id: %d, receiver shard id: %d", + ErrInvalidShardId, + mbType, + sender, + receiver, + ) + } + + case block.InvalidBlock, block.ReceiptBlock: + if sender != selfId || receiver != selfId { + return fmt.Errorf("%w - must be intra-shard: block type: %s, sender shard id: %d, receiver shard id: %d", + ErrInvalidShardId, + mbType, + sender, + receiver, + ) + } + + case block.PeerBlock: + if sender != core.MetachainShardId || receiver != core.AllShardId { + return fmt.Errorf("%w - PeerBlock must be from metachain to all shards: block type: %s, sender shard id: %d, receiver shard id: %d", + ErrInvalidShardId, + mbType, + sender, + receiver, + ) + } + + case block.RewardsBlock: + if sender != core.MetachainShardId || receiver == core.AllShardId { + return fmt.Errorf("%w - RewardsBlock must be from metachain to specific shard: block type: %s, sender shard id: %d, receiver shard id: %d", + ErrInvalidShardId, + mbType, + sender, + receiver, + ) + } + + default: + return fmt.Errorf("%w - unknown miniblock type %d", ErrInvalidShardId, int32(mbType)) + } + + return nil +} + // SetBaseExecutionResult sets the last notarized base execution result in the execution results tracker func SetBaseExecutionResult(executionManager ExecutionManager, blockChain data.ChainHandler) error { if check.IfNil(blockChain) { diff --git a/process/common_test.go b/process/common_test.go index e076d43090e..ba81d85758f 100644 --- a/process/common_test.go +++ b/process/common_test.go @@ -2446,6 +2446,345 @@ func TestShardedCacheSearchMethod_ToString(t *testing.T) { assert.Equal(t, "unknown method 166", str) } +func TestCheckMiniBlock(t *testing.T) { + t.Parallel() + + selfShardID := uint32(1) + + shardCoordinator := &mock.ShardCoordinatorStub{ + SelfIdCalled: func() uint32 { + return selfShardID + }, + NumberOfShardsCalled: func() uint32 { + return 3 + }, + } + + t.Run("not related to self shard, should fail", func(t *testing.T) { + t.Parallel() + + mb := &block.MiniBlock{SenderShardID: 2, ReceiverShardID: 3, Type: block.TxBlock} + err := process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{SenderShardID: 2, ReceiverShardID: core.MetachainShardId, Type: block.TxBlock} + err = process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{SenderShardID: core.MetachainShardId, ReceiverShardID: 3, Type: block.TxBlock} + err = process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + }) + + t.Run("peer miniblock should be from meta to all shards", func(t *testing.T) { + t.Parallel() + + shardCoordinator := &mock.ShardCoordinatorStub{} + + mb := &block.MiniBlock{SenderShardID: core.MetachainShardId, ReceiverShardID: core.AllShardId, Type: block.TxBlock} + err := process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{SenderShardID: 2, ReceiverShardID: core.AllShardId, Type: block.PeerBlock} + err = process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{SenderShardID: core.MetachainShardId, ReceiverShardID: 1, Type: block.PeerBlock} + err = process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{SenderShardID: core.MetachainShardId, ReceiverShardID: core.AllShardId, Type: block.PeerBlock} + err = process.CheckMiniBlock(mb, shardCoordinator) + require.Nil(t, err) + + mb = &block.MiniBlock{SenderShardID: shardCoordinator.SelfId(), ReceiverShardID: core.MetachainShardId, Type: block.PeerBlock} + err = process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + }) + + t.Run("rewards miniblock should be from meta", func(t *testing.T) { + t.Parallel() + + mb := &block.MiniBlock{ + SenderShardID: core.MetachainShardId, + ReceiverShardID: selfShardID, + Type: block.RewardsBlock, + } + err := process.CheckMiniBlock(mb, shardCoordinator) + require.Nil(t, err) + + mb = &block.MiniBlock{ + SenderShardID: core.MetachainShardId, + ReceiverShardID: 2, + Type: block.RewardsBlock, + } + err = process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{ + SenderShardID: 0, + ReceiverShardID: 2, + Type: block.RewardsBlock, + } + err = process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{ + SenderShardID: 2, + ReceiverShardID: selfShardID, + Type: block.RewardsBlock, + } + err = process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + }) + + t.Run("non peer miniblock should not be to all", func(t *testing.T) { + t.Parallel() + + shardCoordinator := &mock.ShardCoordinatorStub{} + + mb := &block.MiniBlock{SenderShardID: core.MetachainShardId, ReceiverShardID: core.AllShardId, Type: block.TxBlock} + err := process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{SenderShardID: 1, ReceiverShardID: core.AllShardId, Type: block.TxBlock} + err = process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{SenderShardID: 1, ReceiverShardID: core.AllShardId, Type: block.ReceiptBlock} + err = process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{SenderShardID: 1, ReceiverShardID: core.AllShardId, Type: block.RewardsBlock} + err = process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{SenderShardID: 1, ReceiverShardID: core.AllShardId, Type: block.SmartContractResultBlock} + err = process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + }) + + t.Run("wrong receiver shard id, should fail", func(t *testing.T) { + t.Parallel() + + wrongShardId := uint32(4) + shardCoordinator := &mock.ShardCoordinatorStub{ + NumberOfShardsCalled: func() uint32 { + return 2 + }, + } + + mb := &block.MiniBlock{SenderShardID: core.MetachainShardId, ReceiverShardID: wrongShardId, Type: block.TxBlock} + err := process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + }) + + t.Run("wrong sender shard id, should fail", func(t *testing.T) { + t.Parallel() + + wrongShardId := uint32(4) + shardCoordinator := &mock.ShardCoordinatorStub{ + NumberOfShardsCalled: func() uint32 { + return 2 + }, + } + + mb := &block.MiniBlock{SenderShardID: wrongShardId, ReceiverShardID: 1, Type: block.TxBlock} + err := process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{SenderShardID: core.AllShardId, ReceiverShardID: 1, Type: block.TxBlock} + err = process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + }) + + t.Run("nil tx hash, should fail", func(t *testing.T) { + t.Parallel() + + mb := &block.MiniBlock{ + SenderShardID: shardCoordinator.SelfId(), ReceiverShardID: 1, + Type: block.TxBlock, + TxHashes: [][]byte{[]byte("txHash0"), nil, []byte("txHash1")}, + } + + err := process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrNilTxHash) + }) + + t.Run("invalid reserved field, should fail", func(t *testing.T) { + t.Parallel() + + mb := &block.MiniBlock{ + SenderShardID: shardCoordinator.SelfId(), ReceiverShardID: 1, + Type: block.TxBlock, + Reserved: bytes.Repeat([]byte("A"), 100), + } + + err := process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrReservedFieldInvalid) + }) + + t.Run("tx block should be from shards", func(t *testing.T) { + t.Parallel() + + mb := &block.MiniBlock{ + SenderShardID: core.MetachainShardId, + ReceiverShardID: selfShardID, + Type: block.TxBlock, + } + err := process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{ + SenderShardID: selfShardID, + ReceiverShardID: 2, + Type: block.TxBlock, + } + err = process.CheckMiniBlock(mb, shardCoordinator) + require.Nil(t, err) + }) + + t.Run("scr block should be to specific shard", func(t *testing.T) { + t.Parallel() + + mb := &block.MiniBlock{ + SenderShardID: selfShardID, + ReceiverShardID: core.AllShardId, + Type: block.SmartContractResultBlock, + } + err := process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{ + SenderShardID: selfShardID, + ReceiverShardID: 2, + Type: block.SmartContractResultBlock, + } + err = process.CheckMiniBlock(mb, shardCoordinator) + require.Nil(t, err) + + mb = &block.MiniBlock{ + SenderShardID: selfShardID, + ReceiverShardID: core.MetachainShardId, + Type: block.SmartContractResultBlock, + } + err = process.CheckMiniBlock(mb, shardCoordinator) + require.Nil(t, err) + }) + + t.Run("invalid and receipts blocks must be intra shard", func(t *testing.T) { + t.Parallel() + + mb := &block.MiniBlock{ + SenderShardID: core.MetachainShardId, + ReceiverShardID: selfShardID, + Type: block.ReceiptBlock, + } + err := process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{ + SenderShardID: selfShardID, + ReceiverShardID: 2, + Type: block.ReceiptBlock, + } + err = process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{ + SenderShardID: selfShardID, + ReceiverShardID: selfShardID, + Type: block.ReceiptBlock, + } + err = process.CheckMiniBlock(mb, shardCoordinator) + require.Nil(t, err) + + mb = &block.MiniBlock{ + SenderShardID: core.MetachainShardId, + ReceiverShardID: selfShardID, + Type: block.InvalidBlock, + } + err = process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{ + SenderShardID: selfShardID, + ReceiverShardID: 2, + Type: block.InvalidBlock, + } + err = process.CheckMiniBlock(mb, shardCoordinator) + require.ErrorIs(t, err, process.ErrInvalidShardId) + + mb = &block.MiniBlock{ + SenderShardID: selfShardID, + ReceiverShardID: selfShardID, + Type: block.InvalidBlock, + } + err = process.CheckMiniBlock(mb, shardCoordinator) + require.Nil(t, err) + }) + + t.Run("should work", func(t *testing.T) { + t.Parallel() + + mb := &block.MiniBlock{ + SenderShardID: 2, + ReceiverShardID: selfShardID, + Type: block.TxBlock, + } + err := process.CheckMiniBlock(mb, shardCoordinator) + require.Nil(t, err) + + mb = &block.MiniBlock{ + SenderShardID: selfShardID, + ReceiverShardID: 2, + Type: block.TxBlock, + } + err = process.CheckMiniBlock(mb, shardCoordinator) + require.Nil(t, err) + + mb = &block.MiniBlock{ + SenderShardID: selfShardID, + ReceiverShardID: selfShardID, + Type: block.InvalidBlock, + } + err = process.CheckMiniBlock(mb, shardCoordinator) + require.Nil(t, err) + + mb = &block.MiniBlock{ + SenderShardID: selfShardID, + ReceiverShardID: selfShardID, + Type: block.ReceiptBlock, + } + err = process.CheckMiniBlock(mb, shardCoordinator) + require.Nil(t, err) + + mb = &block.MiniBlock{ + SenderShardID: selfShardID, + ReceiverShardID: core.MetachainShardId, + Type: block.SmartContractResultBlock, + } + err = process.CheckMiniBlock(mb, shardCoordinator) + require.Nil(t, err) + + mb = &block.MiniBlock{ + SenderShardID: core.MetachainShardId, + ReceiverShardID: core.AllShardId, + Type: block.PeerBlock, + } + err = process.CheckMiniBlock(mb, shardCoordinator) + require.Nil(t, err) + + mb = &block.MiniBlock{ + SenderShardID: core.MetachainShardId, + ReceiverShardID: selfShardID, + Type: block.RewardsBlock, + } + err = process.CheckMiniBlock(mb, shardCoordinator) + require.Nil(t, err) + }) +} + func Test_SetBaseExecutionResult(t *testing.T) { t.Parallel() diff --git a/process/coordinator/process.go b/process/coordinator/process.go index 9d387d8f1da..b5a27f108c9 100644 --- a/process/coordinator/process.go +++ b/process/coordinator/process.go @@ -440,7 +440,7 @@ func (tc *transactionCoordinator) processMiniBlocksFromMe( haveTime func() bool, ) error { for _, mb := range body.MiniBlocks { - err := tc.checkMiniBlock(mb) + err := process.CheckMiniBlock(mb, tc.shardCoordinator) if err != nil { return err } @@ -540,7 +540,7 @@ func (tc *transactionCoordinator) processMiniBlocksToMe( for mbIndex = 0; mbIndex < len(body.MiniBlocks); mbIndex++ { miniBlock := body.MiniBlocks[mbIndex] - err := tc.checkMiniBlock(miniBlock) + err := process.CheckMiniBlock(miniBlock, tc.shardCoordinator) if err != nil { return mbIndex, err } diff --git a/process/coordinator/process_test.go b/process/coordinator/process_test.go index 20e7e75b074..54fdd33fa75 100644 --- a/process/coordinator/process_test.go +++ b/process/coordinator/process_test.go @@ -4540,117 +4540,3 @@ func createDefaultTxCoordinatorArgs() ArgTransactionCoordinator { return txCoordinatorArgs } - -func TestTransactionCoordinator_checkMiniBlock(t *testing.T) { - t.Parallel() - - t.Run("valid miniblock should not error", func(t *testing.T) { - t.Parallel() - - argsTransactionCoordinator := createMockTransactionCoordinatorArguments() - - selfShardID := uint32(1) - argsTransactionCoordinator.ShardCoordinator = &mock.ShardCoordinatorStub{ - SelfIdCalled: func() uint32 { - return selfShardID - }, - } - - tc, err := NewTransactionCoordinator(argsTransactionCoordinator) - require.Nil(t, err) - require.NotNil(t, tc) - - mb := &block.MiniBlock{SenderShardID: 2, ReceiverShardID: selfShardID, Type: block.TxBlock} - err = tc.checkMiniBlock(mb) - require.Nil(t, err) - - mb = &block.MiniBlock{SenderShardID: selfShardID, ReceiverShardID: 2, Type: block.TxBlock} - err = tc.checkMiniBlock(mb) - require.Nil(t, err) - }) - - t.Run("not related to self shard, should fail", func(t *testing.T) { - t.Parallel() - - argsTransactionCoordinator := createMockTransactionCoordinatorArguments() - - selfShardID := uint32(1) - argsTransactionCoordinator.ShardCoordinator = &mock.ShardCoordinatorStub{ - SelfIdCalled: func() uint32 { - return selfShardID - }, - } - - tc, err := NewTransactionCoordinator(argsTransactionCoordinator) - require.Nil(t, err) - require.NotNil(t, tc) - - mb := &block.MiniBlock{SenderShardID: 2, ReceiverShardID: 3, Type: block.TxBlock} - err = tc.checkMiniBlock(mb) - require.ErrorIs(t, err, process.ErrInvalidShardId) - - mb = &block.MiniBlock{SenderShardID: 2, ReceiverShardID: core.MetachainShardId, Type: block.TxBlock} - err = tc.checkMiniBlock(mb) - require.ErrorIs(t, err, process.ErrInvalidShardId) - - mb = &block.MiniBlock{SenderShardID: core.MetachainShardId, ReceiverShardID: 3, Type: block.TxBlock} - err = tc.checkMiniBlock(mb) - require.ErrorIs(t, err, process.ErrInvalidShardId) - }) - - t.Run("peer miniblock should be from meta to all shards", func(t *testing.T) { - t.Parallel() - - argsTransactionCoordinator := createMockTransactionCoordinatorArguments() - - tc, err := NewTransactionCoordinator(argsTransactionCoordinator) - require.Nil(t, err) - require.NotNil(t, tc) - - mb := &block.MiniBlock{SenderShardID: core.MetachainShardId, ReceiverShardID: core.AllShardId, Type: block.TxBlock} - err = tc.checkMiniBlock(mb) - require.ErrorIs(t, err, process.ErrInvalidShardId) - - mb = &block.MiniBlock{SenderShardID: 2, ReceiverShardID: core.AllShardId, Type: block.PeerBlock} - err = tc.checkMiniBlock(mb) - require.ErrorIs(t, err, process.ErrInvalidShardId) - - mb = &block.MiniBlock{SenderShardID: core.MetachainShardId, ReceiverShardID: 1, Type: block.PeerBlock} - err = tc.checkMiniBlock(mb) - require.ErrorIs(t, err, process.ErrInvalidShardId) - - mb = &block.MiniBlock{SenderShardID: core.MetachainShardId, ReceiverShardID: core.AllShardId, Type: block.PeerBlock} - err = tc.checkMiniBlock(mb) - require.Nil(t, err) - }) - - t.Run("non peer miniblock should not be to all", func(t *testing.T) { - t.Parallel() - - argsTransactionCoordinator := createMockTransactionCoordinatorArguments() - - tc, err := NewTransactionCoordinator(argsTransactionCoordinator) - require.Nil(t, err) - require.NotNil(t, tc) - - mb := &block.MiniBlock{SenderShardID: core.MetachainShardId, ReceiverShardID: core.AllShardId, Type: block.TxBlock} - err = tc.checkMiniBlock(mb) - require.ErrorIs(t, err, process.ErrInvalidShardId) - - mb = &block.MiniBlock{SenderShardID: 1, ReceiverShardID: core.AllShardId, Type: block.TxBlock} - err = tc.checkMiniBlock(mb) - require.ErrorIs(t, err, process.ErrInvalidShardId) - - mb = &block.MiniBlock{SenderShardID: 1, ReceiverShardID: core.AllShardId, Type: block.ReceiptBlock} - err = tc.checkMiniBlock(mb) - require.ErrorIs(t, err, process.ErrInvalidShardId) - - mb = &block.MiniBlock{SenderShardID: 1, ReceiverShardID: core.AllShardId, Type: block.RewardsBlock} - err = tc.checkMiniBlock(mb) - require.ErrorIs(t, err, process.ErrInvalidShardId) - - mb = &block.MiniBlock{SenderShardID: 1, ReceiverShardID: core.AllShardId, Type: block.SmartContractResultBlock} - err = tc.checkMiniBlock(mb) - require.ErrorIs(t, err, process.ErrInvalidShardId) - }) -} diff --git a/process/errors.go b/process/errors.go index 483e7a827d5..dd76207b5bd 100644 --- a/process/errors.go +++ b/process/errors.go @@ -618,6 +618,9 @@ var ErrNilPeerShardMapper = errors.New("nil peer shard mapper") // ErrNilBlockTracker signals that a nil block tracker was provided var ErrNilBlockTracker = errors.New("nil block tracker") +// ErrNilMiniBlockTracker signals that a nil miniblock tracker was provided +var ErrNilMiniBlockTracker = errors.New("nil miniblock tracker") + // ErrHeaderIsBlackListed signals that the header provided is blacklisted var ErrHeaderIsBlackListed = errors.New("header is black listed") @@ -765,6 +768,9 @@ var ErrShardInfoOnEpochStartBlock = errors.New("epoch-start block should not con // ErrMiniBlockNotExecuted signals that a mini block was not executed locally var ErrMiniBlockNotExecuted = errors.New("mini block not executed") +// ErrProcessedMiniBlockNotInLeadingPrefix signals that a processed mini block was found outside the leading scheduled-executed prefix +var ErrProcessedMiniBlockNotInLeadingPrefix = errors.New("processed mini block found outside the leading scheduled-executed prefix") + // ErrNilRewardsHandler signals that rewards handler is nil var ErrNilRewardsHandler = errors.New("rewards handler is nil") @@ -1110,6 +1116,9 @@ var ErrNilTxMaxTotalCostHandler = errors.New("nil transaction max total cost") // ErrScheduledRootHashDoesNotMatch signals that scheduled root hash does not match var ErrScheduledRootHashDoesNotMatch = errors.New("scheduled root hash does not match") +// ErrScheduledGasAndFeesDoesNotMatch signals that scheduled gas and fees do not match +var ErrScheduledGasAndFeesDoesNotMatch = errors.New("scheduled gas and fees do not match") + // ErrNilAdditionalData signals that additional data is nil var ErrNilAdditionalData = errors.New("nil additional data") @@ -1170,6 +1179,24 @@ var ErrIndexDoesNotMatchWithPartialExecutedMiniBlock = errors.New("index does no // ErrIndexDoesNotMatchWithFullyExecutedMiniBlock signals that the given index does not match with a fully executed mini block var ErrIndexDoesNotMatchWithFullyExecutedMiniBlock = errors.New("index does not match with a fully executed mini block") +// ErrIndexOfFirstTxProcessedMismatch signals that the index of first tx processed from the header does not match the local processed mini blocks tracker +var ErrIndexOfFirstTxProcessedMismatch = errors.New("index of first tx processed does not match the local processed mini blocks tracker") + +// ErrInvalidMiniBlockProcessingType signals that an invalid miniblock processing type has been provided +var ErrInvalidMiniBlockProcessingType = errors.New("invalid miniblock processing type") + +// ErrInvalidMiniBlockProcessingTypeForType signals an invalid miniblock processing type for the given miniblock type +var ErrInvalidMiniBlockProcessingTypeForType = errors.New("invalid miniblock processing type for miniblock type") + +// ErrProcessingTypeBodyHeaderMismatch signals a processing type mismatch between the miniblock body and its header +var ErrProcessingTypeBodyHeaderMismatch = errors.New("processing type mismatch between miniblock body and miniblock header") + +// ErrInvalidConstructionState signals an invalid construction state for the given processing type and shard role +var ErrInvalidConstructionState = errors.New("invalid construction state for the given processing type and shard role") + +// ErrInvalidMiniBlockShardRole signals an invalid miniblock shard role for the given processing type +var ErrInvalidMiniBlockShardRole = errors.New("invalid miniblock shard role for the given processing type") + // ErrNilProcessedMiniBlocksTracker signals that a nil processed mini blocks tracker has been provided var ErrNilProcessedMiniBlocksTracker = errors.New("nil processed mini blocks tracker") @@ -1185,6 +1212,9 @@ var ErrNilESDTGlobalSettingsHandler = errors.New("nil esdt global settings handl // ErrNilEnableEpochsHandler signals that a nil enable epochs handler has been provided var ErrNilEnableEpochsHandler = errors.New("nil enable epochs handler") +// ErrNilWatchdog signals that a nil watchdog has been provided +var ErrNilWatchdog = errors.New("nil watchdog") + // ErrNilEpochChangeGracePeriodHandler signals that a nil epoch change grace period handler has been provided var ErrNilEpochChangeGracePeriodHandler = errors.New("nil epoch change grace period handler") @@ -1365,6 +1395,9 @@ var ErrDuplicatedHashInBlock = errors.New("duplicated hash in block") // ErrDoubleTransactionsFound signals that double transactions found var ErrDoubleTransactionsFound = errors.New("double transactions found") +// ErrPeerAlreadyAuthenticated signals that a peer authentication message was received for a peer that already has an existing mapping +var ErrPeerAlreadyAuthenticated = errors.New("peer already authenticated") + // ErrBadSelectionGasBandwidthIncreasePercent signals a bad txcache config var ErrBadSelectionGasBandwidthIncreasePercent = errors.New("bad selection gas bandwidth increase percent") diff --git a/process/factory/interceptorscontainer/args.go b/process/factory/interceptorscontainer/args.go index 40a9910cca8..050df66cc2a 100644 --- a/process/factory/interceptorscontainer/args.go +++ b/process/factory/interceptorscontainer/args.go @@ -1,10 +1,12 @@ package interceptorscontainer import ( + "time" + crypto "github.com/multiversx/mx-chain-crypto-go" - "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/common" + "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/heartbeat" "github.com/multiversx/mx-chain-go/process" @@ -15,36 +17,39 @@ import ( // CommonInterceptorsContainerFactoryArgs holds the arguments needed for the metachain/shard interceptors factories type CommonInterceptorsContainerFactoryArgs struct { - CoreComponents process.CoreComponentsHolder - CryptoComponents process.CryptoComponentsHolder - Accounts state.AccountsAdapter - ShardCoordinator sharding.Coordinator - NodesCoordinator nodesCoordinator.NodesCoordinator - MainMessenger process.TopicHandler - FullArchiveMessenger process.TopicHandler - Store dataRetriever.StorageService - DataPool dataRetriever.PoolsHolder - MaxTxNonceDeltaAllowed int - TxFeeHandler process.FeeHandler - BlockBlackList process.TimeCacher - HeaderSigVerifier process.InterceptedHeaderSigVerifier - HeaderIntegrityVerifier process.HeaderIntegrityVerifier - ValidityAttester process.ValidityAttester - EpochStartTrigger process.EpochStartTriggerHandler - WhiteListHandler process.WhiteListHandler - WhiteListerVerifiedTxs process.WhiteListHandler - AntifloodHandler process.P2PAntifloodHandler - ArgumentsParser process.ArgumentsParser - PreferredPeersHolder process.PreferredPeersHolderHandler - SizeCheckDelta uint32 - RequestHandler process.RequestHandler - PeerSignatureHandler crypto.PeerSignatureHandler - SignaturesHandler process.SignaturesHandler - HeartbeatExpiryTimespanInSec int64 - MainPeerShardMapper process.PeerShardMapper - FullArchivePeerShardMapper process.PeerShardMapper - HardforkTrigger heartbeat.HardforkTrigger - NodeOperationMode common.NodeOperation - InterceptedDataVerifierFactory process.InterceptedDataVerifierFactory - Config config.Config + CoreComponents process.CoreComponentsHolder + CryptoComponents process.CryptoComponentsHolder + Accounts state.AccountsAdapter + ShardCoordinator sharding.Coordinator + NodesCoordinator nodesCoordinator.NodesCoordinator + MainMessenger process.TopicHandler + FullArchiveMessenger process.TopicHandler + Store dataRetriever.StorageService + DataPool dataRetriever.PoolsHolder + MaxTxNonceDeltaAllowed int + TxFeeHandler process.FeeHandler + BlockBlackList process.TimeCacher + HeaderSigVerifier process.InterceptedHeaderSigVerifier + HeaderIntegrityVerifier process.HeaderIntegrityVerifier + ValidityAttester process.ValidityAttester + EpochStartTrigger process.EpochStartTriggerHandler + WhiteListHandler process.WhiteListHandler + WhiteListerVerifiedTxs process.WhiteListHandler + AntifloodHandler process.P2PAntifloodHandler + ArgumentsParser process.ArgumentsParser + PreferredPeersHolder process.PreferredPeersHolderHandler + SizeCheckDelta uint32 + RequestHandler process.RequestHandler + PeerSignatureHandler crypto.PeerSignatureHandler + SignaturesHandler process.SignaturesHandler + HeartbeatExpiryTimespanInSec int64 + PeerAuthenticationTimeBetweenSendsInSec int64 + MaxAllowedTrieNodeChunks uint32 + TrieNodeChunksInactivityTimeout time.Duration + MainPeerShardMapper process.PeerShardMapper + FullArchivePeerShardMapper process.PeerShardMapper + HardforkTrigger heartbeat.HardforkTrigger + NodeOperationMode common.NodeOperation + InterceptedDataVerifierFactory process.InterceptedDataVerifierFactory + Config config.Config } diff --git a/process/factory/interceptorscontainer/baseInterceptorsContainerFactory.go b/process/factory/interceptorscontainer/baseInterceptorsContainerFactory.go index 637d695d5bf..b5b6bd69d43 100644 --- a/process/factory/interceptorscontainer/baseInterceptorsContainerFactory.go +++ b/process/factory/interceptorscontainer/baseInterceptorsContainerFactory.go @@ -7,6 +7,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/hashing" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/dataRetriever" @@ -34,31 +35,33 @@ const ( ) type baseInterceptorsContainerFactory struct { - mainContainer process.InterceptorsContainer - fullArchiveContainer process.InterceptorsContainer - shardCoordinator sharding.Coordinator - accounts state.AccountsAdapter - store dataRetriever.StorageService - dataPool dataRetriever.PoolsHolder - mainMessenger process.TopicHandler - fullArchiveMessenger process.TopicHandler - nodesCoordinator nodesCoordinator.NodesCoordinator - blockBlackList process.TimeCacher - argInterceptorFactory *interceptorFactory.ArgInterceptedDataFactory - globalThrottler process.InterceptorThrottler - maxTxNonceDeltaAllowed int - antifloodHandler process.P2PAntifloodHandler - whiteListHandler process.WhiteListHandler - whiteListerVerifiedTxs process.WhiteListHandler - preferredPeersHolder process.PreferredPeersHolderHandler - hasher hashing.Hasher - requestHandler process.RequestHandler - mainPeerShardMapper process.PeerShardMapper - fullArchivePeerShardMapper process.PeerShardMapper - hardforkTrigger heartbeat.HardforkTrigger - nodeOperationMode common.NodeOperation - interceptedDataVerifierFactory process.InterceptedDataVerifierFactory - enableEpochsHandler common.EnableEpochsHandler + mainContainer process.InterceptorsContainer + fullArchiveContainer process.InterceptorsContainer + shardCoordinator sharding.Coordinator + accounts state.AccountsAdapter + store dataRetriever.StorageService + dataPool dataRetriever.PoolsHolder + mainMessenger process.TopicHandler + fullArchiveMessenger process.TopicHandler + nodesCoordinator nodesCoordinator.NodesCoordinator + blockBlackList process.TimeCacher + argInterceptorFactory *interceptorFactory.ArgInterceptedDataFactory + globalThrottler process.InterceptorThrottler + maxTxNonceDeltaAllowed int + antifloodHandler process.P2PAntifloodHandler + whiteListHandler process.WhiteListHandler + whiteListerVerifiedTxs process.WhiteListHandler + preferredPeersHolder process.PreferredPeersHolderHandler + hasher hashing.Hasher + requestHandler process.RequestHandler + maxAllowedTrieNodeChunks uint32 + trieNodeChunksInactivityTimeout time.Duration + mainPeerShardMapper process.PeerShardMapper + fullArchivePeerShardMapper process.PeerShardMapper + hardforkTrigger heartbeat.HardforkTrigger + nodeOperationMode common.NodeOperation + interceptedDataVerifierFactory process.InterceptedDataVerifierFactory + enableEpochsHandler common.EnableEpochsHandler config config.Config } @@ -219,7 +222,7 @@ func createTopicAndAssignHandlerOnMessenger( return messenger.RegisterMessageProcessor(topic, common.DefaultInterceptorsIdentifier, interceptor) } -// ------- tx interceptors +// ------- Tx interceptors func (bicf *baseInterceptorsContainerFactory) generateTxInterceptors() error { shardC := bicf.shardCoordinator @@ -726,11 +729,13 @@ func (bicf *baseInterceptorsContainerFactory) createOneTrieNodesInterceptor(topi } argChunkProcessor := processor.TrieNodesChunksProcessorArgs{ - Hasher: bicf.hasher, - ChunksCacher: bicf.dataPool.TrieNodesChunks(), - RequestInterval: chunksProcessorRequestInterval, - RequestHandler: bicf.requestHandler, - Topic: topic, + Hasher: bicf.hasher, + ChunksCacher: bicf.dataPool.TrieNodesChunks(), + RequestInterval: chunksProcessorRequestInterval, + RequestHandler: bicf.requestHandler, + Topic: topic, + MaxAllowedChunks: bicf.maxAllowedTrieNodeChunks, + ChunkInactivityTimeout: bicf.trieNodeChunksInactivityTimeout, } chunkProcessor, err := processor.NewTrieNodeChunksProcessor(argChunkProcessor) diff --git a/process/factory/interceptorscontainer/metaInterceptorsContainerFactory.go b/process/factory/interceptorscontainer/metaInterceptorsContainerFactory.go index 174e16e0a5f..8feccf3127b 100644 --- a/process/factory/interceptorscontainer/metaInterceptorsContainerFactory.go +++ b/process/factory/interceptorscontainer/metaInterceptorsContainerFactory.go @@ -5,6 +5,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/core/throttler" "github.com/multiversx/mx-chain-core-go/marshal" + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/process/factory" @@ -88,49 +89,54 @@ func NewMetaInterceptorsContainerFactory( } argInterceptorFactory := &interceptorFactory.ArgInterceptedDataFactory{ - CoreComponents: args.CoreComponents, - CryptoComponents: args.CryptoComponents, - ShardCoordinator: args.ShardCoordinator, - NodesCoordinator: args.NodesCoordinator, - FeeHandler: args.TxFeeHandler, - WhiteListerVerifiedTxs: args.WhiteListerVerifiedTxs, - HeaderSigVerifier: args.HeaderSigVerifier, - ValidityAttester: args.ValidityAttester, - HeaderIntegrityVerifier: args.HeaderIntegrityVerifier, - EpochStartTrigger: args.EpochStartTrigger, - ArgsParser: args.ArgumentsParser, - PeerSignatureHandler: args.PeerSignatureHandler, - SignaturesHandler: args.SignaturesHandler, - HeartbeatExpiryTimespanInSec: args.HeartbeatExpiryTimespanInSec, - PeerID: args.MainMessenger.ID(), + CoreComponents: args.CoreComponents, + CryptoComponents: args.CryptoComponents, + ShardCoordinator: args.ShardCoordinator, + NodesCoordinator: args.NodesCoordinator, + FeeHandler: args.TxFeeHandler, + WhiteListerVerifiedTxs: args.WhiteListerVerifiedTxs, + HeaderSigVerifier: args.HeaderSigVerifier, + ValidityAttester: args.ValidityAttester, + HeaderIntegrityVerifier: args.HeaderIntegrityVerifier, + EpochStartTrigger: args.EpochStartTrigger, + ArgsParser: args.ArgumentsParser, + PeerSignatureHandler: args.PeerSignatureHandler, + SignaturesHandler: args.SignaturesHandler, + HeartbeatExpiryTimespanInSec: args.HeartbeatExpiryTimespanInSec, + PeerID: args.MainMessenger.ID(), + PeerShardMapper: args.MainPeerShardMapper, + PeerAuthCacher: args.DataPool.PeerAuthentications(), + PeerAuthenticationTimeBetweenSendsInSec: args.PeerAuthenticationTimeBetweenSendsInSec, } base := &baseInterceptorsContainerFactory{ - mainContainer: containers.NewInterceptorsContainer(), - fullArchiveContainer: containers.NewInterceptorsContainer(), - shardCoordinator: args.ShardCoordinator, - mainMessenger: args.MainMessenger, - fullArchiveMessenger: args.FullArchiveMessenger, - store: args.Store, - dataPool: args.DataPool, - nodesCoordinator: args.NodesCoordinator, - blockBlackList: args.BlockBlackList, - argInterceptorFactory: argInterceptorFactory, - maxTxNonceDeltaAllowed: args.MaxTxNonceDeltaAllowed, - accounts: args.Accounts, - antifloodHandler: args.AntifloodHandler, - whiteListHandler: args.WhiteListHandler, - whiteListerVerifiedTxs: args.WhiteListerVerifiedTxs, - preferredPeersHolder: args.PreferredPeersHolder, - hasher: args.CoreComponents.Hasher(), - requestHandler: args.RequestHandler, - mainPeerShardMapper: args.MainPeerShardMapper, - fullArchivePeerShardMapper: args.FullArchivePeerShardMapper, - hardforkTrigger: args.HardforkTrigger, - nodeOperationMode: args.NodeOperationMode, - interceptedDataVerifierFactory: args.InterceptedDataVerifierFactory, - enableEpochsHandler: args.CoreComponents.EnableEpochsHandler(), - config: args.Config, + mainContainer: containers.NewInterceptorsContainer(), + fullArchiveContainer: containers.NewInterceptorsContainer(), + shardCoordinator: args.ShardCoordinator, + mainMessenger: args.MainMessenger, + fullArchiveMessenger: args.FullArchiveMessenger, + store: args.Store, + dataPool: args.DataPool, + nodesCoordinator: args.NodesCoordinator, + blockBlackList: args.BlockBlackList, + argInterceptorFactory: argInterceptorFactory, + maxTxNonceDeltaAllowed: args.MaxTxNonceDeltaAllowed, + accounts: args.Accounts, + antifloodHandler: args.AntifloodHandler, + whiteListHandler: args.WhiteListHandler, + whiteListerVerifiedTxs: args.WhiteListerVerifiedTxs, + preferredPeersHolder: args.PreferredPeersHolder, + hasher: args.CoreComponents.Hasher(), + requestHandler: args.RequestHandler, + maxAllowedTrieNodeChunks: args.MaxAllowedTrieNodeChunks, + trieNodeChunksInactivityTimeout: args.TrieNodeChunksInactivityTimeout, + mainPeerShardMapper: args.MainPeerShardMapper, + fullArchivePeerShardMapper: args.FullArchivePeerShardMapper, + hardforkTrigger: args.HardforkTrigger, + nodeOperationMode: args.NodeOperationMode, + interceptedDataVerifierFactory: args.InterceptedDataVerifierFactory, + enableEpochsHandler: args.CoreComponents.EnableEpochsHandler(), + config: args.Config, } icf := &metaInterceptorsContainerFactory{ diff --git a/process/factory/interceptorscontainer/metaInterceptorsContainerFactory_test.go b/process/factory/interceptorscontainer/metaInterceptorsContainerFactory_test.go index 7e0af6f9bb8..9ad09430f0e 100644 --- a/process/factory/interceptorscontainer/metaInterceptorsContainerFactory_test.go +++ b/process/factory/interceptorscontainer/metaInterceptorsContainerFactory_test.go @@ -4,6 +4,7 @@ import ( "errors" "strings" "testing" + "time" "github.com/multiversx/mx-chain-go/config" "github.com/stretchr/testify/assert" @@ -710,36 +711,39 @@ func getArgumentsMeta( cryptoComp *mock.CryptoComponentsMock, ) interceptorscontainer.CommonInterceptorsContainerFactoryArgs { return interceptorscontainer.CommonInterceptorsContainerFactoryArgs{ - CoreComponents: coreComp, - CryptoComponents: cryptoComp, - Accounts: &stateMock.AccountsStub{}, - ShardCoordinator: mock.NewOneShardCoordinatorMock(), - NodesCoordinator: shardingMocks.NewNodesCoordinatorMock(), - MainMessenger: &mock.TopicHandlerStub{}, - FullArchiveMessenger: &mock.TopicHandlerStub{}, - Store: createMetaStore(), - DataPool: createMetaDataPools(), - MaxTxNonceDeltaAllowed: maxTxNonceDeltaAllowed, - TxFeeHandler: &economicsmocks.EconomicsHandlerMock{}, - BlockBlackList: &testscommon.TimeCacheStub{}, - HeaderSigVerifier: &consensus.HeaderSigVerifierMock{}, - HeaderIntegrityVerifier: &mock.HeaderIntegrityVerifierStub{}, - ValidityAttester: &mock.ValidityAttesterStub{}, - EpochStartTrigger: &mock.EpochStartTriggerStub{}, - WhiteListHandler: &testscommon.WhiteListHandlerStub{}, - WhiteListerVerifiedTxs: &testscommon.WhiteListHandlerStub{}, - AntifloodHandler: &mock.P2PAntifloodHandlerStub{}, - ArgumentsParser: &testscommon.ArgumentParserMock{}, - PreferredPeersHolder: &p2pmocks.PeersHolderStub{}, - RequestHandler: &testscommon.RequestHandlerStub{}, - PeerSignatureHandler: &mock.PeerSignatureHandlerStub{}, - SignaturesHandler: &mock.SignaturesHandlerStub{}, - HeartbeatExpiryTimespanInSec: 30, - MainPeerShardMapper: &p2pmocks.NetworkShardingCollectorStub{}, - FullArchivePeerShardMapper: &p2pmocks.NetworkShardingCollectorStub{}, - HardforkTrigger: &testscommon.HardforkTriggerStub{}, - NodeOperationMode: common.NormalOperation, - InterceptedDataVerifierFactory: &mock.InterceptedDataVerifierFactoryMock{}, + CoreComponents: coreComp, + CryptoComponents: cryptoComp, + Accounts: &stateMock.AccountsStub{}, + ShardCoordinator: mock.NewOneShardCoordinatorMock(), + NodesCoordinator: shardingMocks.NewNodesCoordinatorMock(), + MainMessenger: &mock.TopicHandlerStub{}, + FullArchiveMessenger: &mock.TopicHandlerStub{}, + Store: createMetaStore(), + DataPool: createMetaDataPools(), + MaxTxNonceDeltaAllowed: maxTxNonceDeltaAllowed, + TxFeeHandler: &economicsmocks.EconomicsHandlerMock{}, + BlockBlackList: &testscommon.TimeCacheStub{}, + HeaderSigVerifier: &consensus.HeaderSigVerifierMock{}, + HeaderIntegrityVerifier: &mock.HeaderIntegrityVerifierStub{}, + ValidityAttester: &mock.ValidityAttesterStub{}, + EpochStartTrigger: &mock.EpochStartTriggerStub{}, + WhiteListHandler: &testscommon.WhiteListHandlerStub{}, + WhiteListerVerifiedTxs: &testscommon.WhiteListHandlerStub{}, + AntifloodHandler: &mock.P2PAntifloodHandlerStub{}, + ArgumentsParser: &testscommon.ArgumentParserMock{}, + PreferredPeersHolder: &p2pmocks.PeersHolderStub{}, + RequestHandler: &testscommon.RequestHandlerStub{}, + PeerSignatureHandler: &mock.PeerSignatureHandlerStub{}, + SignaturesHandler: &mock.SignaturesHandlerStub{}, + HeartbeatExpiryTimespanInSec: 30, + PeerAuthenticationTimeBetweenSendsInSec: 60, + MaxAllowedTrieNodeChunks: 10, + TrieNodeChunksInactivityTimeout: 10 * time.Second, + MainPeerShardMapper: &p2pmocks.NetworkShardingCollectorStub{}, + FullArchivePeerShardMapper: &p2pmocks.NetworkShardingCollectorStub{}, + HardforkTrigger: &testscommon.HardforkTriggerStub{}, + NodeOperationMode: common.NormalOperation, + InterceptedDataVerifierFactory: &mock.InterceptedDataVerifierFactoryMock{}, Config: config.Config{ InterceptedDataVerifier: config.InterceptedDataVerifierConfig{ CacheSpanInSec: 1, diff --git a/process/factory/interceptorscontainer/shardInterceptorsContainerFactory.go b/process/factory/interceptorscontainer/shardInterceptorsContainerFactory.go index 1922c3619db..e0b95b3f87d 100644 --- a/process/factory/interceptorscontainer/shardInterceptorsContainerFactory.go +++ b/process/factory/interceptorscontainer/shardInterceptorsContainerFactory.go @@ -89,49 +89,54 @@ func NewShardInterceptorsContainerFactory( } argInterceptorFactory := &interceptorFactory.ArgInterceptedDataFactory{ - CoreComponents: args.CoreComponents, - CryptoComponents: args.CryptoComponents, - ShardCoordinator: args.ShardCoordinator, - NodesCoordinator: args.NodesCoordinator, - FeeHandler: args.TxFeeHandler, - HeaderSigVerifier: args.HeaderSigVerifier, - HeaderIntegrityVerifier: args.HeaderIntegrityVerifier, - ValidityAttester: args.ValidityAttester, - EpochStartTrigger: args.EpochStartTrigger, - WhiteListerVerifiedTxs: args.WhiteListerVerifiedTxs, - ArgsParser: args.ArgumentsParser, - PeerSignatureHandler: args.PeerSignatureHandler, - SignaturesHandler: args.SignaturesHandler, - HeartbeatExpiryTimespanInSec: args.HeartbeatExpiryTimespanInSec, - PeerID: args.MainMessenger.ID(), + CoreComponents: args.CoreComponents, + CryptoComponents: args.CryptoComponents, + ShardCoordinator: args.ShardCoordinator, + NodesCoordinator: args.NodesCoordinator, + FeeHandler: args.TxFeeHandler, + HeaderSigVerifier: args.HeaderSigVerifier, + HeaderIntegrityVerifier: args.HeaderIntegrityVerifier, + ValidityAttester: args.ValidityAttester, + EpochStartTrigger: args.EpochStartTrigger, + WhiteListerVerifiedTxs: args.WhiteListerVerifiedTxs, + ArgsParser: args.ArgumentsParser, + PeerSignatureHandler: args.PeerSignatureHandler, + SignaturesHandler: args.SignaturesHandler, + HeartbeatExpiryTimespanInSec: args.HeartbeatExpiryTimespanInSec, + PeerID: args.MainMessenger.ID(), + PeerShardMapper: args.MainPeerShardMapper, + PeerAuthCacher: args.DataPool.PeerAuthentications(), + PeerAuthenticationTimeBetweenSendsInSec: args.PeerAuthenticationTimeBetweenSendsInSec, } base := &baseInterceptorsContainerFactory{ - mainContainer: containers.NewInterceptorsContainer(), - fullArchiveContainer: containers.NewInterceptorsContainer(), - accounts: args.Accounts, - shardCoordinator: args.ShardCoordinator, - mainMessenger: args.MainMessenger, - fullArchiveMessenger: args.FullArchiveMessenger, - store: args.Store, - dataPool: args.DataPool, - nodesCoordinator: args.NodesCoordinator, - argInterceptorFactory: argInterceptorFactory, - blockBlackList: args.BlockBlackList, - maxTxNonceDeltaAllowed: args.MaxTxNonceDeltaAllowed, - antifloodHandler: args.AntifloodHandler, - whiteListHandler: args.WhiteListHandler, - whiteListerVerifiedTxs: args.WhiteListerVerifiedTxs, - preferredPeersHolder: args.PreferredPeersHolder, - hasher: args.CoreComponents.Hasher(), - requestHandler: args.RequestHandler, - mainPeerShardMapper: args.MainPeerShardMapper, - fullArchivePeerShardMapper: args.FullArchivePeerShardMapper, - hardforkTrigger: args.HardforkTrigger, - nodeOperationMode: args.NodeOperationMode, - interceptedDataVerifierFactory: args.InterceptedDataVerifierFactory, - enableEpochsHandler: args.CoreComponents.EnableEpochsHandler(), - config: args.Config, + mainContainer: containers.NewInterceptorsContainer(), + fullArchiveContainer: containers.NewInterceptorsContainer(), + accounts: args.Accounts, + shardCoordinator: args.ShardCoordinator, + mainMessenger: args.MainMessenger, + fullArchiveMessenger: args.FullArchiveMessenger, + store: args.Store, + dataPool: args.DataPool, + nodesCoordinator: args.NodesCoordinator, + argInterceptorFactory: argInterceptorFactory, + blockBlackList: args.BlockBlackList, + maxTxNonceDeltaAllowed: args.MaxTxNonceDeltaAllowed, + antifloodHandler: args.AntifloodHandler, + whiteListHandler: args.WhiteListHandler, + whiteListerVerifiedTxs: args.WhiteListerVerifiedTxs, + preferredPeersHolder: args.PreferredPeersHolder, + hasher: args.CoreComponents.Hasher(), + requestHandler: args.RequestHandler, + maxAllowedTrieNodeChunks: args.MaxAllowedTrieNodeChunks, + trieNodeChunksInactivityTimeout: args.TrieNodeChunksInactivityTimeout, + mainPeerShardMapper: args.MainPeerShardMapper, + fullArchivePeerShardMapper: args.FullArchivePeerShardMapper, + hardforkTrigger: args.HardforkTrigger, + nodeOperationMode: args.NodeOperationMode, + interceptedDataVerifierFactory: args.InterceptedDataVerifierFactory, + enableEpochsHandler: args.CoreComponents.EnableEpochsHandler(), + config: args.Config, } icf := &shardInterceptorsContainerFactory{ @@ -211,6 +216,11 @@ func (sicf *shardInterceptorsContainerFactory) Create() (process.InterceptorsCon return sicf.mainContainer, sicf.fullArchiveContainer, nil } +// AddShardTrieNodeInterceptors returns nil +func (sicf *shardInterceptorsContainerFactory) AddShardTrieNodeInterceptors(_ process.InterceptorsContainer) error { + return nil +} + func (sicf *shardInterceptorsContainerFactory) generateTrieNodesInterceptors() error { shardC := sicf.shardCoordinator diff --git a/process/factory/interceptorscontainer/shardInterceptorsContainerFactory_test.go b/process/factory/interceptorscontainer/shardInterceptorsContainerFactory_test.go index 2d80286da1f..1b9689124a0 100644 --- a/process/factory/interceptorscontainer/shardInterceptorsContainerFactory_test.go +++ b/process/factory/interceptorscontainer/shardInterceptorsContainerFactory_test.go @@ -4,6 +4,7 @@ import ( "errors" "strings" "testing" + "time" "github.com/multiversx/mx-chain-core-go/core/versioning" "github.com/stretchr/testify/assert" @@ -742,36 +743,39 @@ func getArgumentsShard( cryptoComp *mock.CryptoComponentsMock, ) interceptorscontainer.CommonInterceptorsContainerFactoryArgs { return interceptorscontainer.CommonInterceptorsContainerFactoryArgs{ - CoreComponents: coreComp, - CryptoComponents: cryptoComp, - Accounts: &stateMock.AccountsStub{}, - ShardCoordinator: mock.NewOneShardCoordinatorMock(), - NodesCoordinator: shardingMocks.NewNodesCoordinatorMock(), - MainMessenger: &mock.TopicHandlerStub{}, - FullArchiveMessenger: &mock.TopicHandlerStub{}, - Store: createShardStore(), - DataPool: createShardDataPools(), - MaxTxNonceDeltaAllowed: maxTxNonceDeltaAllowed, - TxFeeHandler: &economicsmocks.EconomicsHandlerMock{}, - BlockBlackList: &testscommon.TimeCacheStub{}, - HeaderSigVerifier: &consensus.HeaderSigVerifierMock{}, - HeaderIntegrityVerifier: &mock.HeaderIntegrityVerifierStub{}, - SizeCheckDelta: 0, - ValidityAttester: &mock.ValidityAttesterStub{}, - EpochStartTrigger: &mock.EpochStartTriggerStub{}, - AntifloodHandler: &mock.P2PAntifloodHandlerStub{}, - WhiteListHandler: &testscommon.WhiteListHandlerStub{}, - WhiteListerVerifiedTxs: &testscommon.WhiteListHandlerStub{}, - ArgumentsParser: &testscommon.ArgumentParserMock{}, - PreferredPeersHolder: &p2pmocks.PeersHolderStub{}, - RequestHandler: &testscommon.RequestHandlerStub{}, - PeerSignatureHandler: &mock.PeerSignatureHandlerStub{}, - SignaturesHandler: &mock.SignaturesHandlerStub{}, - HeartbeatExpiryTimespanInSec: 30, - MainPeerShardMapper: &p2pmocks.NetworkShardingCollectorStub{}, - FullArchivePeerShardMapper: &p2pmocks.NetworkShardingCollectorStub{}, - HardforkTrigger: &testscommon.HardforkTriggerStub{}, - InterceptedDataVerifierFactory: &mock.InterceptedDataVerifierFactoryMock{}, + CoreComponents: coreComp, + CryptoComponents: cryptoComp, + Accounts: &stateMock.AccountsStub{}, + ShardCoordinator: mock.NewOneShardCoordinatorMock(), + NodesCoordinator: shardingMocks.NewNodesCoordinatorMock(), + MainMessenger: &mock.TopicHandlerStub{}, + FullArchiveMessenger: &mock.TopicHandlerStub{}, + Store: createShardStore(), + DataPool: createShardDataPools(), + MaxTxNonceDeltaAllowed: maxTxNonceDeltaAllowed, + TxFeeHandler: &economicsmocks.EconomicsHandlerMock{}, + BlockBlackList: &testscommon.TimeCacheStub{}, + HeaderSigVerifier: &consensus.HeaderSigVerifierMock{}, + HeaderIntegrityVerifier: &mock.HeaderIntegrityVerifierStub{}, + SizeCheckDelta: 0, + ValidityAttester: &mock.ValidityAttesterStub{}, + EpochStartTrigger: &mock.EpochStartTriggerStub{}, + AntifloodHandler: &mock.P2PAntifloodHandlerStub{}, + WhiteListHandler: &testscommon.WhiteListHandlerStub{}, + WhiteListerVerifiedTxs: &testscommon.WhiteListHandlerStub{}, + ArgumentsParser: &testscommon.ArgumentParserMock{}, + PreferredPeersHolder: &p2pmocks.PeersHolderStub{}, + RequestHandler: &testscommon.RequestHandlerStub{}, + PeerSignatureHandler: &mock.PeerSignatureHandlerStub{}, + SignaturesHandler: &mock.SignaturesHandlerStub{}, + HeartbeatExpiryTimespanInSec: 30, + PeerAuthenticationTimeBetweenSendsInSec: 60, + MaxAllowedTrieNodeChunks: 10, + TrieNodeChunksInactivityTimeout: 10 * time.Second, + MainPeerShardMapper: &p2pmocks.NetworkShardingCollectorStub{}, + FullArchivePeerShardMapper: &p2pmocks.NetworkShardingCollectorStub{}, + HardforkTrigger: &testscommon.HardforkTriggerStub{}, + InterceptedDataVerifierFactory: &mock.InterceptedDataVerifierFactoryMock{}, Config: config.Config{ InterceptedDataVerifier: config.InterceptedDataVerifierConfig{ CacheSpanInSec: 1, diff --git a/process/headerCheck/headerSignatureVerify_test.go b/process/headerCheck/headerSignatureVerify_test.go index 19220acedac..00f3b0cac50 100644 --- a/process/headerCheck/headerSignatureVerify_test.go +++ b/process/headerCheck/headerSignatureVerify_test.go @@ -658,7 +658,7 @@ func TestHeaderSigVerifier_VerifySignatureNotEnoughSigsShouldErr(t *testing.T) { hdrSigVerifier, _ := NewHeaderSigVerifier(args) header := &dataBlock.Header{ - PubKeysBitmap: []byte("A"), + PubKeysBitmap: []byte{0x03}, RandSeed: []byte("randSeed"), PrevRandSeed: []byte("prevRandSeed"), } @@ -689,7 +689,7 @@ func TestHeaderSigVerifier_VerifySignatureOk(t *testing.T) { hdrSigVerifier, _ := NewHeaderSigVerifier(args) header := &dataBlock.Header{ - PubKeysBitmap: []byte("1"), + PubKeysBitmap: []byte{0x01}, PrevRandSeed: []byte("prevRandSeed"), } @@ -728,7 +728,7 @@ func TestHeaderSigVerifier_VerifySignatureNotEnoughSigsShouldErrWhenFallbackThre hdrSigVerifier, _ := NewHeaderSigVerifier(args) header := &dataBlock.MetaBlock{ - PubKeysBitmap: []byte("C"), + PubKeysBitmap: []byte{0x03}, PrevRandSeed: []byte("prevRandSeed"), } @@ -835,9 +835,10 @@ func TestHeaderSigVerifier_VerifySignatureWithEquivalentProofsActivated(t *testi require.Nil(t, err) require.False(t, wasCalled) + var bitmap byte = 1< proc.maxAllowedChunks { + return false, fmt.Errorf("%w, trie node batch max chunks %d exceeds configured limit %d", + process.ErrInvalidValue, b.MaxChunks, proc.maxAllowedChunks) + } if len(b.Reference) != proc.hasher.Size() { return false, process.ErrIncompatibleReference } @@ -234,6 +256,24 @@ func (proc *trieNodeChunksProcessor) requestMissingForReference(reference []byte if !ok { return } + if chunkData.MaxChunks() > proc.maxAllowedChunks { + log.Warn("dropping cached trie node chunk tracker above configured limit", + "reference", reference, + "maxChunks", chunkData.MaxChunks(), + "configuredLimit", proc.maxAllowedChunks, + ) + proc.chunksCacher.Remove(reference) + return + } + if time.Since(chunkData.LastUpdated()) > proc.chunkInactivityTimeout { + log.Warn("dropping stale trie node chunk tracker after inactivity timeout", + "reference", reference, + "lastUpdated", chunkData.LastUpdated(), + "inactivityTimeout", proc.chunkInactivityTimeout, + ) + proc.chunksCacher.Remove(reference) + return + } missing := chunkData.GetAllMissingChunkIndexes() for _, missingChunkIndex := range missing { diff --git a/process/interceptors/processor/trieNodeChunksProcessor_test.go b/process/interceptors/processor/trieNodeChunksProcessor_test.go index 97260694fb5..98438d595e6 100644 --- a/process/interceptors/processor/trieNodeChunksProcessor_test.go +++ b/process/interceptors/processor/trieNodeChunksProcessor_test.go @@ -2,6 +2,7 @@ package processor import ( "bytes" + "context" "errors" "sync/atomic" "testing" @@ -9,9 +10,11 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data/batch" + "github.com/multiversx/mx-chain-go/p2p" "github.com/multiversx/mx-chain-go/process" + "github.com/multiversx/mx-chain-go/process/interceptors/processor/chunk" "github.com/multiversx/mx-chain-go/testscommon" "github.com/multiversx/mx-chain-go/testscommon/cache" @@ -35,10 +38,12 @@ func createMockTrieNodesChunksProcessorArgs() TrieNodesChunksProcessorArgs { return 32 }, }, - ChunksCacher: cache.NewCacherMock(), - RequestInterval: time.Second, - RequestHandler: &testscommon.RequestHandlerStub{}, - Topic: "topic", + ChunksCacher: cache.NewCacherMock(), + RequestInterval: time.Second, + RequestHandler: &testscommon.RequestHandlerStub{}, + Topic: "topic", + MaxAllowedChunks: 3, + ChunkInactivityTimeout: 10 * time.Second, } } @@ -92,6 +97,26 @@ func TestNewTrieNodeChunksProcessor_EmptyTopic(t *testing.T) { assert.True(t, check.IfNil(tncp)) } +func TestNewTrieNodeChunksProcessor_InvalidMaxAllowedChunks(t *testing.T) { + t.Parallel() + + args := createMockTrieNodesChunksProcessorArgs() + args.MaxAllowedChunks = 1 + tncp, err := NewTrieNodeChunksProcessor(args) + assert.True(t, errors.Is(err, process.ErrInvalidValue)) + assert.True(t, check.IfNil(tncp)) +} + +func TestNewTrieNodeChunksProcessor_InvalidChunkInactivityTimeout(t *testing.T) { + t.Parallel() + + args := createMockTrieNodesChunksProcessorArgs() + args.ChunkInactivityTimeout = 0 + tncp, err := NewTrieNodeChunksProcessor(args) + assert.True(t, errors.Is(err, process.ErrInvalidValue)) + assert.True(t, check.IfNil(tncp)) +} + func TestNewTrieNodeChunksProcessor_ShouldWork(t *testing.T) { t.Parallel() @@ -135,6 +160,19 @@ func TestTrieNodeChunksProcessor_CheckBatchInvalidBatch(t *testing.T) { assert.Equal(t, err, process.ErrIncompatibleReference) assert.Equal(t, emptyCheckedChunkResult, chunkResult) + chunkResult, err = tncp.CheckBatch( + &batch.Batch{ + Data: make([][]byte, 1), + Reference: make([]byte, 32), + ChunkIndex: 0, + MaxChunks: 4, + }, + createMockWhiteLister(true), + p2p.Broadcast, + ) + assert.True(t, errors.Is(err, process.ErrInvalidValue)) + assert.Equal(t, emptyCheckedChunkResult, chunkResult) + chunkResult, err = tncp.CheckBatch( &batch.Batch{ Data: nil, @@ -288,6 +326,54 @@ func TestTrieNodeChunksProcessor_CheckBatchNotTheFirstBatch(t *testing.T) { assert.Equal(t, 1, args.ChunksCacher.Len()) } +func TestTrieNodeChunksProcessor_RequestMissingForReferenceShouldDropCachedChunkAboveConfiguredLimit(t *testing.T) { + t.Parallel() + + args := createMockTrieNodesChunksProcessorArgs() + numRequested := uint32(0) + args.RequestHandler = &testscommon.RequestHandlerStub{ + RequestTrieNodeCalled: func(_ []byte, _ string, _ uint32) { + atomic.AddUint32(&numRequested, 1) + }, + } + + tncp, _ := NewTrieNodeChunksProcessor(args) + args.ChunksCacher.Put(reference, chunk.NewChunk(args.MaxAllowedChunks+1, reference), 0) + + tncp.requestMissingForReference(reference, context.Background()) + + assert.Equal(t, 0, args.ChunksCacher.Len()) + assert.Equal(t, uint32(0), atomic.LoadUint32(&numRequested)) + + _ = tncp.Close() +} + +func TestTrieNodeChunksProcessor_RequestMissingForReferenceShouldDropStaleCachedChunk(t *testing.T) { + t.Parallel() + + args := createMockTrieNodesChunksProcessorArgs() + args.ChunkInactivityTimeout = 10 * time.Millisecond + numRequested := uint32(0) + args.RequestHandler = &testscommon.RequestHandlerStub{ + RequestTrieNodeCalled: func(_ []byte, _ string, _ uint32) { + atomic.AddUint32(&numRequested, 1) + }, + } + + tncp, _ := NewTrieNodeChunksProcessor(args) + staleChunk := chunk.NewChunk(args.MaxAllowedChunks, reference) + staleChunk.Put(0, []byte("buff1")) + args.ChunksCacher.Put(reference, staleChunk, staleChunk.Size()) + + time.Sleep(args.ChunkInactivityTimeout + 5*time.Millisecond) + tncp.requestMissingForReference(reference, context.Background()) + + assert.Equal(t, 0, args.ChunksCacher.Len()) + assert.Equal(t, uint32(0), atomic.LoadUint32(&numRequested)) + + _ = tncp.Close() +} + func TestTrieNodeChunksProcessor_CheckBatchComponentClosed(t *testing.T) { t.Parallel() diff --git a/process/interceptors/singleDataInterceptor.go b/process/interceptors/singleDataInterceptor.go index f39da2a261d..6ce80edce3f 100644 --- a/process/interceptors/singleDataInterceptor.go +++ b/process/interceptors/singleDataInterceptor.go @@ -143,8 +143,7 @@ func (sdi *SingleDataInterceptor) ProcessReceivedMessage(message p2p.MessageP2P, } isForCurrentShard := interceptedData.IsForCurrentShard() - shouldProcess := isForCurrentShard || isWhiteListed - if !shouldProcess { + if !isForCurrentShard { sdi.throttler.EndProcessing() log.Trace("intercepted data is for other shards", "pid", p2p.MessageOriginatorPid(message), @@ -152,10 +151,9 @@ func (sdi *SingleDataInterceptor) ProcessReceivedMessage(message p2p.MessageP2P, "topic", message.Topic(), "hash", interceptedData.Hash(), "is for current shard", isForCurrentShard, - "is white listed", isWhiteListed, ) - return messageID, nil + return messageID, process.ErrInterceptedDataNotForCurrentShard } go func() { diff --git a/process/interceptors/singleDataInterceptor_test.go b/process/interceptors/singleDataInterceptor_test.go index 58043a2c9a5..3bae2dd57b0 100644 --- a/process/interceptors/singleDataInterceptor_test.go +++ b/process/interceptors/singleDataInterceptor_test.go @@ -251,7 +251,7 @@ func TestSingleDataInterceptor_ProcessReceivedMessageIsNotValidShouldNotCallProc func TestSingleDataInterceptor_ProcessReceivedMessageIsNotForCurrentShardShouldNotCallProcess(t *testing.T) { t.Parallel() - testProcessReceiveMessage(t, false, nil, 0) + testProcessReceiveMessage(t, false, process.ErrInterceptedDataNotForCurrentShard, 0) } func TestSingleDataInterceptor_ProcessReceivedMessageShouldWork(t *testing.T) { @@ -310,7 +310,7 @@ func TestSingleDataInterceptor_ProcessReceivedMessageWhitelistedShouldWork(t *te return nil }, IsForCurrentShardCalled: func() bool { - return false + return true }, HashCalled: func() []byte { return msgHash @@ -368,7 +368,7 @@ func processReceivedMessageSingleDataInvalidVersion(t *testing.T, expectedErr er return expectedErr }, IsForCurrentShardCalled: func() bool { - return false + return true }, } @@ -424,7 +424,7 @@ func TestSingleDataInterceptor_ProcessReceivedMessageWithOriginator(t *testing.T return nil }, IsForCurrentShardCalled: func() bool { - return false + return true }, HashCalled: func() []byte { return msgHash diff --git a/process/interface.go b/process/interface.go index a3f55e788d4..4c38cf6b81d 100644 --- a/process/interface.go +++ b/process/interface.go @@ -500,6 +500,7 @@ type InterceptorsContainer interface { // InterceptorsContainerFactory defines the functionality to create an interceptors container type InterceptorsContainerFactory interface { Create() (InterceptorsContainer, InterceptorsContainer, error) + AddShardTrieNodeInterceptors(container InterceptorsContainer) error IsInterfaceNil() bool } @@ -915,7 +916,7 @@ type PeerBlackListCacher interface { // PeerShardMapper can return the public key of a provided peer ID type PeerShardMapper interface { - UpdatePeerIDPublicKeyPair(pid core.PeerID, pk []byte) + UpdatePeerIDPublicKeyPair(pid core.PeerID, pk []byte, timestamp int64) PutPeerIdShardId(pid core.PeerID, shardID uint32) PutPeerIdSubType(pid core.PeerID, peerSubType core.P2PPeerSubType) GetPeerInfo(pid core.PeerID) core.P2PPeerInfo @@ -1053,6 +1054,26 @@ type BlockTracker interface { IsInterfaceNil() bool } +// MiniBlockTracker tracks the confirmation status of cross-shard miniblocks so that +// their referenced transactions can be granted immunity in the pool on metablock +// arrival and released from immunity on this shard's commit. +type MiniBlockTracker interface { + // ReleaseImmunityForCommittedMetaBlocks is called by the shard processor after + // metablocks up to (threshold-1) have been fully processed. It advances the + // immunity threshold for every cache on every pool and drops stale registry + // entries whose tracked nonce is strictly below `threshold`. + ReleaseImmunityForCommittedMetaBlocks(threshold uint64) + + // ReleaseImmunityForCommittedShardBlocks is called by the meta processor after + // shard headers from `senderShard` up to (threshold-1) have been fully processed. + // It advances the immunity threshold for caches whose senderShardID matches + // `senderShard` and receiver is the metachain, and drops the corresponding stale + // registry entries. + ReleaseImmunityForCommittedShardBlocks(senderShard uint32, threshold uint64) + + IsInterfaceNil() bool +} + // FloodPreventer defines the behavior of a component that is able to signal that too many events occurred // on a provided identifier between Reset calls type FloodPreventer interface { diff --git a/process/mock/peerShardMapperStub.go b/process/mock/peerShardMapperStub.go index 8c73a582904..364c19dd0ab 100644 --- a/process/mock/peerShardMapperStub.go +++ b/process/mock/peerShardMapperStub.go @@ -9,7 +9,7 @@ type PeerShardMapperStub struct { UpdatePeerIdPublicKeyCalled func(pid core.PeerID, pk []byte) UpdatePublicKeyShardIdCalled func(pk []byte, shardId uint32) PutPeerIdShardIdCalled func(pid core.PeerID, shardId uint32) - UpdatePeerIDPublicKeyPairCalled func(pid core.PeerID, pk []byte) + UpdatePeerIDPublicKeyPairCalled func(pid core.PeerID, pk []byte, timestamp int64) PutPeerIdSubTypeCalled func(pid core.PeerID, peerSubType core.P2PPeerSubType) } @@ -32,9 +32,9 @@ func (psms *PeerShardMapperStub) GetPeerInfo(pid core.PeerID) core.P2PPeerInfo { } // UpdatePeerIDPublicKeyPair - -func (psms *PeerShardMapperStub) UpdatePeerIDPublicKeyPair(pid core.PeerID, pk []byte) { +func (psms *PeerShardMapperStub) UpdatePeerIDPublicKeyPair(pid core.PeerID, pk []byte, timestamp int64) { if psms.UpdatePeerIDPublicKeyPairCalled != nil { - psms.UpdatePeerIDPublicKeyPairCalled(pid, pk) + psms.UpdatePeerIDPublicKeyPairCalled(pid, pk, timestamp) } } diff --git a/process/sync/argBootstrapper.go b/process/sync/argBootstrapper.go index 1e7c35bd11c..25f475b9a22 100644 --- a/process/sync/argBootstrapper.go +++ b/process/sync/argBootstrapper.go @@ -8,6 +8,7 @@ import ( "github.com/multiversx/mx-chain-core-go/data/typeConverters" "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/consensus" "github.com/multiversx/mx-chain-go/dataRetriever" @@ -68,4 +69,5 @@ type ArgMetaBootstrapper struct { EpochBootstrapper process.EpochBootstrapper ValidatorStatisticsDBSyncer process.AccountsDBSyncer ValidatorAccountsDB state.AccountsAdapter + Watchdog core.WatchdogTimer } diff --git a/process/sync/export_test.go b/process/sync/export_test.go index 427e2369864..0a093fb384c 100644 --- a/process/sync/export_test.go +++ b/process/sync/export_test.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/common" "github.com/multiversx/mx-chain-go/process" ) @@ -35,6 +36,16 @@ func (boot *MetaBootstrap) ReceivedProof(header data.HeaderProofHandler) { boot.processReceivedProof(header) } +// RequestEpochStartBlockIfStuck - +func (boot *MetaBootstrap) RequestEpochStartBlockIfStuck() { + boot.requestEpochStartBlockIfStuck() +} + +// SetWatchdogLastNonce - +func (boot *MetaBootstrap) SetWatchdogLastNonce(nonce uint64) { + boot.watchdogLastNonce = nonce +} + // SetRcvHdrNonce - func (boot *MetaBootstrap) SetRcvHdrNonce() { boot.chRcvHdrNonce <- true diff --git a/process/sync/metablock.go b/process/sync/metablock.go index 71b12d956a4..1b5a1936ba3 100644 --- a/process/sync/metablock.go +++ b/process/sync/metablock.go @@ -8,6 +8,8 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" + + "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/state" @@ -15,12 +17,21 @@ import ( "github.com/multiversx/mx-chain-go/trie/storageMarker" ) +const ( + numRoundsWithoutCommittedBlock = 5 + metaSyncEpochStartWatchdogID = "metaSyncEpochStartWatchdog" +) + // MetaBootstrap implements the bootstrap mechanism type MetaBootstrap struct { *baseBootstrap epochBootstrapper process.EpochBootstrapper validatorStatisticsDBSyncer process.AccountsDBSyncer validatorAccountsDB state.AccountsAdapter + + watchdog core.WatchdogTimer + watchdogCtx context.Context + watchdogLastNonce uint64 } // NewMetaBootstrap creates a new Bootstrap object @@ -46,6 +57,9 @@ func NewMetaBootstrap(arguments ArgMetaBootstrapper) (*MetaBootstrap, error) { if check.IfNil(arguments.ValidatorAccountsDB) { return nil, process.ErrNilPeerAccountsAdapter } + if check.IfNil(arguments.Watchdog) { + return nil, process.ErrNilWatchdog + } err := checkBaseBootstrapParameters(arguments.ArgBaseBootstrapper) if err != nil { @@ -98,6 +112,7 @@ func NewMetaBootstrap(arguments ArgMetaBootstrapper) (*MetaBootstrap, error) { epochBootstrapper: arguments.EpochBootstrapper, validatorStatisticsDBSyncer: arguments.ValidatorStatisticsDBSyncer, validatorAccountsDB: arguments.ValidatorAccountsDB, + watchdog: arguments.Watchdog, } base.blockBootstrapper = &boot @@ -151,11 +166,91 @@ func (boot *MetaBootstrap) StartSyncingBlocks() error { var ctx context.Context ctx, boot.cancelFunc = context.WithCancel(context.Background()) + boot.watchdogCtx = ctx go boot.syncBlocks(ctx) + boot.armEpochStartWatchdog() + return nil } +func (boot *MetaBootstrap) armEpochStartWatchdog() { + if boot.watchdogCtx == nil || boot.watchdogCtx.Err() != nil { + return + } + + timeout := boot.roundHandler.TimeDuration() * numRoundsWithoutCommittedBlock + if timeout <= 0 { + return + } + + // capture the baseline nonce now so the alarm measures progress over a single interval + boot.watchdogLastNonce = boot.currentBlockNonce() + boot.watchdog.Set(boot.epochStartWatchdogCallback, timeout, metaSyncEpochStartWatchdogID) +} + +func (boot *MetaBootstrap) currentBlockNonce() uint64 { + currentHeader := boot.chainHandler.GetCurrentBlockHeader() + if check.IfNil(currentHeader) { + return 0 + } + + return currentHeader.GetNonce() +} + +func (boot *MetaBootstrap) epochStartWatchdogCallback(_ string) { + if boot.watchdogCtx == nil || boot.watchdogCtx.Err() != nil { + return + } + defer boot.armEpochStartWatchdog() + + boot.requestEpochStartBlockIfStuck() +} + +func (boot *MetaBootstrap) requestEpochStartBlockIfStuck() { + currentHeader := boot.chainHandler.GetCurrentBlockHeader() + if check.IfNil(currentHeader) { + return + } + + currentNonce := currentHeader.GetNonce() + if currentNonce != boot.watchdogLastNonce { + return + } + + currentEpoch := currentHeader.GetEpoch() + targetEpoch := currentEpoch + 1 + if !boot.enableEpochsHandler.IsFlagEnabledInEpoch(common.AndromedaFlag, targetEpoch) { + return + } + + targetNonce := currentNonce + 1 + + header, headerHash, err := process.GetMetaHeaderFromPoolWithNonce(targetNonce, boot.headers) + if err == nil && !check.IfNil(header) { + if boot.proofs.HasProof(core.MetachainShardId, headerHash) { + return + } + + log.Debug("epoch start watchdog: header present without proof, requesting proof by hash", + "nonce", targetNonce, + "epoch", header.GetEpoch(), + "hash", headerHash, + ) + boot.requestHandler.SetEpoch(header.GetEpoch()) + boot.requestHandler.RequestEquivalentProofByHash(core.MetachainShardId, headerHash) + return + } + + log.Debug("epoch start watchdog: stuck without epoch change metablock, requesting header and proof", + "nonce", targetNonce, + "epoch", targetEpoch, + ) + boot.requestHandler.SetEpoch(targetEpoch) + boot.requestHandler.RequestStartOfEpochMetaBlock(targetEpoch) + boot.requestHandler.RequestEquivalentProofByNonce(core.MetachainShardId, targetNonce) +} + func (boot *MetaBootstrap) setLastEpochStartRound() { hdr := boot.chainHandler.GetCurrentBlockHeader() if check.IfNil(hdr) || hdr.GetEpoch() < 1 { @@ -220,6 +315,10 @@ func (boot *MetaBootstrap) Close() error { return nil } + if !check.IfNil(boot.watchdog) { + boot.watchdog.Stop(metaSyncEpochStartWatchdogID) + } + return boot.baseBootstrap.Close() } diff --git a/process/sync/metablock_test.go b/process/sync/metablock_test.go index 3ac8b1ace9b..ae6e312e53a 100644 --- a/process/sync/metablock_test.go +++ b/process/sync/metablock_test.go @@ -102,11 +102,164 @@ func CreateMetaBootstrapMockArguments() sync.ArgMetaBootstrapper { EpochBootstrapper: &mock.EpochStartTriggerStub{}, ValidatorAccountsDB: &stateMock.AccountsStub{}, ValidatorStatisticsDBSyncer: &mock.AccountsDBSyncerStub{}, + Watchdog: &testscommon.WatchdogMock{}, } return argsMetaBootstrapper } +func TestMetaBootstrap_RequestEpochStartBlockIfStuck(t *testing.T) { + t.Parallel() + + const currentNonce = uint64(100) + const currentEpoch = uint32(0) + + newArgs := func() (sync.ArgMetaBootstrapper, *headerRequestsRecorder) { + recorder := &headerRequestsRecorder{} + args := CreateMetaBootstrapMockArguments() + args.ChainHandler = &testscommon.ChainHandlerStub{ + GetCurrentBlockHeaderCalled: func() data.HeaderHandler { + return &block.MetaBlock{Nonce: currentNonce, Epoch: currentEpoch} + }, + } + args.RequestHandler = &testscommon.RequestHandlerStub{ + RequestStartOfEpochMetaBlockCalled: func(epoch uint32) { + recorder.startOfEpochCalls++ + recorder.startOfEpochArg = epoch + }, + RequestEquivalentProofByHashCalled: func(headerShard uint32, headerHash []byte) { + recorder.proofByHashCalls++ + recorder.proofByHashArg = headerHash + }, + RequestEquivalentProofByNonceCalled: func(headerShard uint32, headerNonce uint64) { + recorder.proofByNonceCalls++ + recorder.proofByNonceArg = headerNonce + }, + } + args.EnableEpochsHandler = &enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return flag == common.AndromedaFlag + }, + } + return args, recorder + } + + t.Run("not stuck (nonce advanced since last arm) does not request anything", func(t *testing.T) { + t.Parallel() + + args, recorder := newArgs() + boot, err := sync.NewMetaBootstrap(args) + require.Nil(t, err) + + // baseline is behind the current nonce -> progress was made during the interval + boot.SetWatchdogLastNonce(currentNonce - 1) + boot.RequestEpochStartBlockIfStuck() + require.Zero(t, recorder.total()) + }) + + t.Run("stuck, header absent, requests start of epoch block and proof by nonce", func(t *testing.T) { + t.Parallel() + + args, recorder := newArgs() + boot, err := sync.NewMetaBootstrap(args) + require.Nil(t, err) + + boot.SetWatchdogLastNonce(currentNonce) + boot.RequestEpochStartBlockIfStuck() + + require.Equal(t, 1, recorder.startOfEpochCalls) + require.Equal(t, currentEpoch+1, recorder.startOfEpochArg) + require.Equal(t, 1, recorder.proofByNonceCalls) + require.Equal(t, currentNonce+1, recorder.proofByNonceArg) + require.Equal(t, 0, recorder.proofByHashCalls) + }) + + t.Run("stuck, header present without proof, requests proof by hash", func(t *testing.T) { + t.Parallel() + + expectedHash := []byte("epoch-change-hash") + args, recorder := newArgs() + args.PoolsHolder = poolsWithMetaHeader(currentNonce+1, currentEpoch+1, expectedHash, false) + boot, err := sync.NewMetaBootstrap(args) + require.Nil(t, err) + + boot.SetWatchdogLastNonce(currentNonce) + boot.RequestEpochStartBlockIfStuck() + + require.Equal(t, 1, recorder.proofByHashCalls) + require.Equal(t, expectedHash, recorder.proofByHashArg) + require.Equal(t, 0, recorder.startOfEpochCalls) + require.Equal(t, 0, recorder.proofByNonceCalls) + }) + + t.Run("stuck, header present with proof, does not request anything", func(t *testing.T) { + t.Parallel() + + args, recorder := newArgs() + args.PoolsHolder = poolsWithMetaHeader(currentNonce+1, currentEpoch+1, []byte("hash"), true) + boot, err := sync.NewMetaBootstrap(args) + require.Nil(t, err) + + boot.SetWatchdogLastNonce(currentNonce) + boot.RequestEpochStartBlockIfStuck() + + require.Zero(t, recorder.total()) + }) + + t.Run("stuck but andromeda not enabled does not request anything", func(t *testing.T) { + t.Parallel() + + args, recorder := newArgs() + args.EnableEpochsHandler = &enableEpochsHandlerMock.EnableEpochsHandlerStub{ + IsFlagEnabledInEpochCalled: func(flag core.EnableEpochFlag, epoch uint32) bool { + return false + }, + } + boot, err := sync.NewMetaBootstrap(args) + require.Nil(t, err) + + boot.SetWatchdogLastNonce(currentNonce) + boot.RequestEpochStartBlockIfStuck() + + require.Zero(t, recorder.total()) + }) +} + +type headerRequestsRecorder struct { + startOfEpochCalls int + startOfEpochArg uint32 + proofByHashCalls int + proofByHashArg []byte + proofByNonceCalls int + proofByNonceArg uint64 +} + +func (r *headerRequestsRecorder) total() int { + return r.startOfEpochCalls + r.proofByHashCalls + r.proofByNonceCalls +} + +func poolsWithMetaHeader(nonce uint64, epoch uint32, hash []byte, hasProof bool) *dataRetrieverMock.PoolsHolderStub { + pools := createMockPools() + pools.HeadersCalled = func() dataRetriever.HeadersPool { + return &mock.HeadersCacherStub{ + GetHeaderByNonceAndShardIdCalled: func(hdrNonce uint64, shardId uint32) ([]data.HeaderHandler, [][]byte, error) { + if hdrNonce != nonce { + return nil, nil, errors.New("not found") + } + return []data.HeaderHandler{&block.MetaBlock{Nonce: nonce, Epoch: epoch}}, [][]byte{hash}, nil + }, + } + } + pools.ProofsCalled = func() dataRetriever.ProofsPool { + return &dataRetrieverMock.ProofsPoolMock{ + HasProofCalled: func(shardID uint32, headerHash []byte) bool { + return hasProof + }, + } + } + return pools +} + // ------- NewMetaBootstrap func TestNewMetaBootstrap_NilPoolsHolderShouldErr(t *testing.T) { @@ -121,6 +274,18 @@ func TestNewMetaBootstrap_NilPoolsHolderShouldErr(t *testing.T) { assert.Equal(t, process.ErrNilPoolsHolder, err) } +func TestNewMetaBootstrap_NilWatchdogShouldErr(t *testing.T) { + t.Parallel() + + args := CreateMetaBootstrapMockArguments() + args.Watchdog = nil + + bs, err := sync.NewMetaBootstrap(args) + + assert.True(t, check.IfNil(bs)) + assert.Equal(t, process.ErrNilWatchdog, err) +} + func TestNewMetaBootstrap_NilValidatorDBShouldErr(t *testing.T) { t.Parallel() diff --git a/process/track/export_test.go b/process/track/export_test.go index 9c251bfc7f2..cb12709d15a 100644 --- a/process/track/export_test.go +++ b/process/track/export_test.go @@ -4,6 +4,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" + "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/sharding" @@ -291,3 +292,12 @@ func (mbt *miniBlockTrack) GetTransactionPool(mbType block.Type) dataRetriever.S func (mbt *miniBlockTrack) SetBlockTransactionsPool(blockTransactionsPool dataRetriever.ShardedDataCacherNotifier) { mbt.blockTransactionsPool = blockTransactionsPool } + +// GetConfirmedMiniBlockInfo - test accessor for the local registry +func (mbt *miniBlockTrack) GetConfirmedMiniBlockInfo(miniBlockHash []byte) (cacheID string, nonce uint64, ok bool) { + info, found := mbt.getConfirmedMiniBlockInfo(miniBlockHash) + if !found { + return "", 0, false + } + return info.cacheID, info.nonce, true +} diff --git a/process/track/miniBlockTrack.go b/process/track/miniBlockTrack.go index 900846f67ff..c7cd47dad27 100644 --- a/process/track/miniBlockTrack.go +++ b/process/track/miniBlockTrack.go @@ -1,15 +1,25 @@ package track import ( + "sync" + "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" + "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/storage" ) +type confirmedMiniBlockInfo struct { + cacheID string + mbType block.Type + nonce uint64 +} + type miniBlockTrack struct { blockTransactionsPool dataRetriever.ShardedDataCacherNotifier rewardTransactionsPool dataRetriever.ShardedDataCacherNotifier @@ -17,11 +27,14 @@ type miniBlockTrack struct { miniBlocksPool storage.Cacher shardCoordinator sharding.Coordinator whitelistHandler process.WhiteListHandler + mutConfirmedMiniBlocks sync.RWMutex + confirmedMiniBlocks map[string]confirmedMiniBlockInfo } // NewMiniBlockTrack creates an object for tracking the received mini blocks func NewMiniBlockTrack( dataPool dataRetriever.PoolsHolder, + blockTracker process.BlockTracker, shardCoordinator sharding.Coordinator, whitelistHandler process.WhiteListHandler, ) (*miniBlockTrack, error) { @@ -41,6 +54,9 @@ func NewMiniBlockTrack( if check.IfNil(dataPool.MiniBlocks()) { return nil, process.ErrNilMiniBlockPool } + if check.IfNil(blockTracker) { + return nil, process.ErrNilBlockTracker + } if check.IfNil(shardCoordinator) { return nil, process.ErrNilShardCoordinator } @@ -55,9 +71,11 @@ func NewMiniBlockTrack( miniBlocksPool: dataPool.MiniBlocks(), shardCoordinator: shardCoordinator, whitelistHandler: whitelistHandler, + confirmedMiniBlocks: make(map[string]confirmedMiniBlockInfo), } mbt.miniBlocksPool.RegisterHandler(mbt.receivedMiniBlock, core.UniqueIdentifier()) + mbt.registerBlockTrackerHandlers(blockTracker) return &mbt, nil } @@ -84,16 +102,7 @@ func (mbt *miniBlockTrack) receivedMiniBlock(key []byte, value interface{}) { return } - // TODO - stop reusing miniBlock.TxHashes for peer changes, add new fields - transactionPool := mbt.getTransactionPool(miniBlock.Type) - if check.IfNil(transactionPool) { - return - } - - mbt.whitelistHandler.Add(miniBlock.TxHashes) - - strCache := process.ShardCacherIdentifier(miniBlock.SenderShardID, miniBlock.ReceiverShardID) - transactionPool.ImmunizeSetOfDataAgainstEviction(miniBlock.TxHashes, strCache) + mbt.immunizeMiniBlock(key, miniBlock) } func (mbt *miniBlockTrack) getTransactionPool(mbType block.Type) dataRetriever.ShardedDataCacherNotifier { @@ -108,3 +117,215 @@ func (mbt *miniBlockTrack) getTransactionPool(mbType block.Type) dataRetriever.S return nil } + +func (mbt *miniBlockTrack) registerBlockTrackerHandlers(blockTracker process.BlockTracker) { + if mbt.shardCoordinator.SelfId() == core.MetachainShardId { + blockTracker.RegisterCrossNotarizedHeadersHandler(func(_ uint32, headers []data.HeaderHandler, _ [][]byte) { + mbt.registerConfirmedMiniBlocks(headers) + }) + return + } + + blockTracker.RegisterFinalMetachainHeadersHandler(func(_ uint32, headers []data.HeaderHandler, _ [][]byte) { + mbt.registerConfirmedMiniBlocks(headers) + }) +} + +func (mbt *miniBlockTrack) registerConfirmedMiniBlocks(headers []data.HeaderHandler) { + for _, header := range headers { + mbt.registerConfirmedMiniBlocksForHeader(header) + } +} + +func (mbt *miniBlockTrack) registerConfirmedMiniBlocksForHeader(header data.HeaderHandler) { + if check.IfNil(header) { + return + } + + switch typedHeader := header.(type) { + case data.MetaHeaderHandler: + mbt.registerFromMiniBlockHeaders(typedHeader.GetNonce(), core.MetachainShardId, typedHeader.GetMiniBlockHeaderHandlers()) + for _, shardInfo := range typedHeader.GetShardInfoHandlers() { + mbt.registerFromMiniBlockHeaders(typedHeader.GetNonce(), shardInfo.GetShardID(), shardInfo.GetShardMiniBlockHeaderHandlers()) + } + case data.ShardHeaderHandler: + mbt.registerFromMiniBlockHeaders(typedHeader.GetNonce(), typedHeader.GetShardID(), typedHeader.GetMiniBlockHeaderHandlers()) + } +} + +func (mbt *miniBlockTrack) registerFromMiniBlockHeaders( + nonce uint64, + processingShard uint32, + miniBlockHeaders []data.MiniBlockHeaderHandler, +) { + selfShardID := mbt.shardCoordinator.SelfId() + for _, miniBlockHeader := range miniBlockHeaders { + receiverShard := miniBlockHeader.GetReceiverShardID() + // AllShardId from a metaheader (e.g. rewards) is treated as receiver = self. + receiverIsAllShardsMiniBlockFromMetaHeader := receiverShard == core.AllShardId && processingShard == core.MetachainShardId + receiverIsRelevantForCurrentShard := receiverShard == selfShardID || receiverIsAllShardsMiniBlockFromMetaHeader + senderShard := miniBlockHeader.GetSenderShardID() + senderIsSelfShard := senderShard == selfShardID + if !receiverIsRelevantForCurrentShard || senderIsSelfShard { + continue + } + + cacheID := process.ShardCacherIdentifier(senderShard, receiverShard) + mbInfo := confirmedMiniBlockInfo{ + cacheID: cacheID, + mbType: block.Type(miniBlockHeader.GetTypeInt32()), + nonce: nonce, + } + + transactionPool := mbt.getTransactionPool(mbInfo.mbType) + if check.IfNil(transactionPool) { + continue + } + + // Threshold advance is deferred to commit (see ReleaseImmunityForCommittedMetaBlocks). + // Advancing here would release items from older metablocks before this shard executes them. + mbt.storeConfirmedMiniBlockInfo(miniBlockHeader.GetHash(), mbInfo) + mbt.tryProcessStoredMiniBlock(miniBlockHeader.GetHash()) + } +} + +func (mbt *miniBlockTrack) tryProcessStoredMiniBlock(miniBlockHash []byte) { + value, ok := mbt.miniBlocksPool.Peek(miniBlockHash) + if !ok { + return + } + + miniBlock, ok := value.(*block.MiniBlock) + if !ok { + return + } + + mbt.immunizeMiniBlock(miniBlockHash, miniBlock) +} + +func (mbt *miniBlockTrack) immunizeMiniBlock(miniBlockHash []byte, miniBlock *block.MiniBlock) { + // TODO - stop reusing miniBlock.TxHashes for peer changes, add new fields + transactionPool := mbt.getTransactionPool(miniBlock.Type) + if check.IfNil(transactionPool) { + return + } + + confirmationInfo, ok := mbt.getConfirmedMiniBlockInfo(miniBlockHash) + if !ok { + return + } + + mbt.whitelistHandler.Add(miniBlock.TxHashes) + transactionPool.ImmunizeSetOfDataAgainstEviction(miniBlock.TxHashes, confirmationInfo.cacheID, confirmationInfo.nonce) + mbt.removeConfirmedMiniBlockInfo(miniBlockHash, confirmationInfo.nonce) +} + +func (mbt *miniBlockTrack) storeConfirmedMiniBlockInfo(miniBlockHash []byte, info confirmedMiniBlockInfo) { + mbt.mutConfirmedMiniBlocks.Lock() + defer mbt.mutConfirmedMiniBlocks.Unlock() + + key := string(miniBlockHash) + existingInfo, exists := mbt.confirmedMiniBlocks[key] + if exists && existingInfo.nonce >= info.nonce { + return + } + + mbt.confirmedMiniBlocks[key] = info +} + +func (mbt *miniBlockTrack) getConfirmedMiniBlockInfo(miniBlockHash []byte) (confirmedMiniBlockInfo, bool) { + mbt.mutConfirmedMiniBlocks.RLock() + defer mbt.mutConfirmedMiniBlocks.RUnlock() + + info, ok := mbt.confirmedMiniBlocks[string(miniBlockHash)] + return info, ok +} + +func (mbt *miniBlockTrack) removeConfirmedMiniBlockInfo(miniBlockHash []byte, nonce uint64) { + mbt.mutConfirmedMiniBlocks.Lock() + defer mbt.mutConfirmedMiniBlocks.Unlock() + + key := string(miniBlockHash) + info, ok := mbt.confirmedMiniBlocks[key] + if !ok { + return + } + if info.nonce > nonce { + return + } + + delete(mbt.confirmedMiniBlocks, key) +} + +// CleanupConfirmedMiniBlocksBelow drops every tracked confirmation whose nonce +// is strictly below `threshold`. Called from the shard's commit path alongside +// SetOldestImmuneNonceForAllCaches so that the local registry doesn't accumulate +// stale entries for miniblocks that never arrived in the pool. +func (mbt *miniBlockTrack) CleanupConfirmedMiniBlocksBelow(threshold uint64) { + mbt.mutConfirmedMiniBlocks.Lock() + defer mbt.mutConfirmedMiniBlocks.Unlock() + + for key, info := range mbt.confirmedMiniBlocks { + if info.nonce >= threshold { + continue + } + + delete(mbt.confirmedMiniBlocks, key) + } +} + +// CleanupConfirmedMiniBlocksBelowForCacheID drops every tracked confirmation whose +// cacheID matches and nonce is strictly below `threshold`. Used by the meta commit +// path where the threshold is per-sender-shard rather than uniform. +func (mbt *miniBlockTrack) CleanupConfirmedMiniBlocksBelowForCacheID(cacheID string, threshold uint64) { + mbt.mutConfirmedMiniBlocks.Lock() + defer mbt.mutConfirmedMiniBlocks.Unlock() + + for key, info := range mbt.confirmedMiniBlocks { + if info.cacheID != cacheID || info.nonce >= threshold { + continue + } + + delete(mbt.confirmedMiniBlocks, key) + } +} + +// ReleaseImmunityForCommittedMetaBlocks advances the immunity threshold uniformly +// across every tx-pool cache and prunes the local registry for entries below +// `threshold`. Called from the shard's commit path once the cross-notarized +// metablock has advanced past (threshold-1). +func (mbt *miniBlockTrack) ReleaseImmunityForCommittedMetaBlocks(threshold uint64) { + if !check.IfNil(mbt.blockTransactionsPool) { + mbt.blockTransactionsPool.SetOldestImmuneNonceForAllCaches(threshold) + } + if !check.IfNil(mbt.rewardTransactionsPool) { + mbt.rewardTransactionsPool.SetOldestImmuneNonceForAllCaches(threshold) + } + if !check.IfNil(mbt.unsignedTransactionsPool) { + mbt.unsignedTransactionsPool.SetOldestImmuneNonceForAllCaches(threshold) + } + mbt.CleanupConfirmedMiniBlocksBelow(threshold) +} + +// ReleaseImmunityForCommittedShardBlocks advances the immunity threshold only on +// caches with senderShardID = `senderShard` and receiver = metachain, and prunes +// the local registry for matching entries below `threshold`. Called from the +// meta processor after its cross-notarized shard header has advanced for `senderShard`. +func (mbt *miniBlockTrack) ReleaseImmunityForCommittedShardBlocks(senderShard uint32, threshold uint64) { + cacheID := process.ShardCacherIdentifier(senderShard, core.MetachainShardId) + if !check.IfNil(mbt.blockTransactionsPool) { + mbt.blockTransactionsPool.SetOldestImmuneNonce(cacheID, threshold) + } + if !check.IfNil(mbt.rewardTransactionsPool) { + mbt.rewardTransactionsPool.SetOldestImmuneNonce(cacheID, threshold) + } + if !check.IfNil(mbt.unsignedTransactionsPool) { + mbt.unsignedTransactionsPool.SetOldestImmuneNonce(cacheID, threshold) + } + mbt.CleanupConfirmedMiniBlocksBelowForCacheID(cacheID, threshold) +} + +// IsInterfaceNil returns true if the receiver is a nil interface +func (mbt *miniBlockTrack) IsInterfaceNil() bool { + return mbt == nil +} diff --git a/process/track/miniBlockTrack_test.go b/process/track/miniBlockTrack_test.go index 6a72d7ad9d0..89861bb89cb 100644 --- a/process/track/miniBlockTrack_test.go +++ b/process/track/miniBlockTrack_test.go @@ -3,6 +3,8 @@ package track_test import ( "testing" + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" "github.com/stretchr/testify/assert" @@ -19,7 +21,7 @@ import ( func TestNewMiniBlockTrack_NilDataPoolHolderErr(t *testing.T) { t.Parallel() - mbt, err := track.NewMiniBlockTrack(nil, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) + mbt, err := track.NewMiniBlockTrack(nil, &mock.BlockTrackerMock{}, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) assert.Nil(t, mbt) assert.Equal(t, process.ErrNilPoolsHolder, err) @@ -33,7 +35,7 @@ func TestNewMiniBlockTrack_NilTxsPoolErr(t *testing.T) { return nil }, } - mbt, err := track.NewMiniBlockTrack(dataPool, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) + mbt, err := track.NewMiniBlockTrack(dataPool, &mock.BlockTrackerMock{}, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) assert.Nil(t, mbt) assert.Equal(t, process.ErrNilTransactionPool, err) @@ -50,7 +52,7 @@ func TestNewMiniBlockTrack_NilRewardTxsPoolErr(t *testing.T) { return nil }, } - mbt, err := track.NewMiniBlockTrack(dataPool, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) + mbt, err := track.NewMiniBlockTrack(dataPool, &mock.BlockTrackerMock{}, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) assert.Nil(t, mbt) assert.Equal(t, process.ErrNilRewardTxDataPool, err) @@ -70,7 +72,7 @@ func TestNewMiniBlockTrack_NilUnsignedTxsPoolErr(t *testing.T) { return nil }, } - mbt, err := track.NewMiniBlockTrack(dataPool, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) + mbt, err := track.NewMiniBlockTrack(dataPool, &mock.BlockTrackerMock{}, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) assert.Nil(t, mbt) assert.Equal(t, process.ErrNilUnsignedTxDataPool, err) @@ -93,17 +95,27 @@ func TestNewMiniBlockTrack_NilMiniBlockPoolShouldErr(t *testing.T) { return nil }, } - mbt, err := track.NewMiniBlockTrack(dataPool, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) + mbt, err := track.NewMiniBlockTrack(dataPool, &mock.BlockTrackerMock{}, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) assert.Nil(t, mbt) assert.Equal(t, process.ErrNilMiniBlockPool, err) } +func TestNewMiniBlockTrack_NilBlockTrackerErr(t *testing.T) { + t.Parallel() + + dataPool := createDataPool() + miniBlockTrack, err := track.NewMiniBlockTrack(dataPool, nil, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) + + assert.Nil(t, miniBlockTrack) + assert.Equal(t, process.ErrNilBlockTracker, err) +} + func TestNewMiniBlockTrack_NilShardCoordinatorErr(t *testing.T) { t.Parallel() dataPool := createDataPool() - miniBlockTrack, err := track.NewMiniBlockTrack(dataPool, nil, &testscommon.WhiteListHandlerStub{}) + miniBlockTrack, err := track.NewMiniBlockTrack(dataPool, &mock.BlockTrackerMock{}, nil, &testscommon.WhiteListHandlerStub{}) assert.Nil(t, miniBlockTrack) assert.Equal(t, process.ErrNilShardCoordinator, err) @@ -113,7 +125,7 @@ func TestNewMiniBlockTrack_NilWhitelistHandlerErr(t *testing.T) { t.Parallel() dataPool := createDataPool() - miniBlockTrack, err := track.NewMiniBlockTrack(dataPool, mock.NewMultipleShardsCoordinatorMock(), nil) + miniBlockTrack, err := track.NewMiniBlockTrack(dataPool, &mock.BlockTrackerMock{}, mock.NewMultipleShardsCoordinatorMock(), nil) assert.Nil(t, miniBlockTrack) assert.Equal(t, process.ErrNilWhiteListHandler, err) @@ -123,7 +135,7 @@ func TestNewMiniBlockTrack_ShouldWork(t *testing.T) { t.Parallel() dataPool := createDataPool() - mbt, err := track.NewMiniBlockTrack(dataPool, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) + mbt, err := track.NewMiniBlockTrack(dataPool, &mock.BlockTrackerMock{}, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) assert.Nil(t, err) assert.NotNil(t, mbt) @@ -133,11 +145,11 @@ func TestReceivedMiniBlock_ShouldReturnIfKeyIsNil(t *testing.T) { t.Parallel() dataPool := createDataPool() - mbt, _ := track.NewMiniBlockTrack(dataPool, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) + mbt, _ := track.NewMiniBlockTrack(dataPool, &mock.BlockTrackerMock{}, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) wasCalled := false blockTransactionsPool := &testscommon.ShardedDataStub{ - ImmunizeSetOfDataAgainstEvictionCalled: func(keys [][]byte, destCacheId string) { + ImmunizeSetOfDataAgainstEvictionCalled: func(keys [][]byte, destCacheId string, nonce uint64) { wasCalled = true }, } @@ -151,11 +163,11 @@ func TestReceivedMiniBlock_ShouldReturnIfWrongTypeAssertion(t *testing.T) { t.Parallel() dataPool := createDataPool() - mbt, _ := track.NewMiniBlockTrack(dataPool, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) + mbt, _ := track.NewMiniBlockTrack(dataPool, &mock.BlockTrackerMock{}, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) wasCalled := false blockTransactionsPool := &testscommon.ShardedDataStub{ - ImmunizeSetOfDataAgainstEvictionCalled: func(keys [][]byte, destCacheId string) { + ImmunizeSetOfDataAgainstEvictionCalled: func(keys [][]byte, destCacheId string, nonce uint64) { wasCalled = true }, } @@ -169,11 +181,11 @@ func TestReceivedMiniBlock_ShouldReturnIfMiniBlockIsNotCrossShardDestMe(t *testi t.Parallel() dataPool := createDataPool() - mbt, _ := track.NewMiniBlockTrack(dataPool, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) + mbt, _ := track.NewMiniBlockTrack(dataPool, &mock.BlockTrackerMock{}, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) wasCalled := false blockTransactionsPool := &testscommon.ShardedDataStub{ - ImmunizeSetOfDataAgainstEvictionCalled: func(keys [][]byte, destCacheId string) { + ImmunizeSetOfDataAgainstEvictionCalled: func(keys [][]byte, destCacheId string, nonce uint64) { wasCalled = true }, } @@ -187,11 +199,11 @@ func TestReceivedMiniBlock_ShouldReturnIfMiniBlockTypeIsWrong(t *testing.T) { t.Parallel() dataPool := createDataPool() - mbt, _ := track.NewMiniBlockTrack(dataPool, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) + mbt, _ := track.NewMiniBlockTrack(dataPool, &mock.BlockTrackerMock{}, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) wasCalled := false blockTransactionsPool := &testscommon.ShardedDataStub{ - ImmunizeSetOfDataAgainstEvictionCalled: func(keys [][]byte, destCacheId string) { + ImmunizeSetOfDataAgainstEvictionCalled: func(keys [][]byte, destCacheId string, nonce uint64) { wasCalled = true }, } @@ -206,15 +218,15 @@ func TestReceivedMiniBlock_ShouldReturnIfMiniBlockTypeIsWrong(t *testing.T) { assert.False(t, wasCalled) } -func TestReceivedMiniBlock_ShouldWork(t *testing.T) { +func TestReceivedMiniBlock_ShouldNotImmunizeUnconfirmedMiniBlock(t *testing.T) { t.Parallel() dataPool := createDataPool() - mbt, _ := track.NewMiniBlockTrack(dataPool, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) + mbt, _ := track.NewMiniBlockTrack(dataPool, &mock.BlockTrackerMock{}, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) wasCalled := false blockTransactionsPool := &testscommon.ShardedDataStub{ - ImmunizeSetOfDataAgainstEvictionCalled: func(keys [][]byte, destCacheId string) { + ImmunizeSetOfDataAgainstEvictionCalled: func(keys [][]byte, destCacheId string, nonce uint64) { wasCalled = true }, } @@ -226,7 +238,73 @@ func TestReceivedMiniBlock_ShouldWork(t *testing.T) { Type: block.TxBlock, }) + assert.False(t, wasCalled) +} + +func TestReceivedMiniBlock_ShouldImmunizeConfirmedMiniBlock(t *testing.T) { + t.Parallel() + + dataPool := createDataPool() + blockTracker := &mock.BlockTrackerMock{} + + var finalMetachainHeadersHandler func(shardID uint32, headers []data.HeaderHandler, headersHashes [][]byte) + blockTracker.RegisterFinalMetachainHeadersHandlerCalled = func(handler func(shardID uint32, headers []data.HeaderHandler, headersHashes [][]byte)) { + finalMetachainHeadersHandler = handler + } + + whitelistCalled := false + whiteListHandler := &testscommon.WhiteListHandlerStub{ + AddCalled: func(keys [][]byte) { + whitelistCalled = true + }, + } + mbt, _ := track.NewMiniBlockTrack(dataPool, blockTracker, mock.NewMultipleShardsCoordinatorMock(), whiteListHandler) + + var cacheID string + var nonce uint64 + wasCalled := false + blockTransactionsPool := &testscommon.ShardedDataStub{ + ImmunizeSetOfDataAgainstEvictionCalled: func(keys [][]byte, destCacheId string, providedNonce uint64) { + wasCalled = true + cacheID = destCacheId + nonce = providedNonce + }, + } + mbt.SetBlockTransactionsPool(blockTransactionsPool) + + finalMetachainHeadersHandler(core.MetachainShardId, []data.HeaderHandler{ + &block.MetaBlock{ + Nonce: 7, + ShardInfo: []block.ShardData{ + { + ShardID: 1, + ShardMiniBlockHeaders: []block.MiniBlockHeader{ + { + Hash: []byte("mb_hash"), + SenderShardID: 1, + ReceiverShardID: 0, + Type: block.TxBlock, + }, + }, + }, + }, + }, + }, nil) + + mbt.ReceivedMiniBlock( + []byte("mb_hash"), + &block.MiniBlock{ + SenderShardID: 1, + ReceiverShardID: 0, + Type: block.TxBlock, + TxHashes: [][]byte{[]byte("txHash")}, + }, + ) + assert.True(t, wasCalled) + assert.True(t, whitelistCalled) + assert.Equal(t, process.ShardCacherIdentifier(1, 0), cacheID) + assert.Equal(t, uint64(7), nonce) } func TestGetTransactionPool_ShouldWork(t *testing.T) { @@ -261,7 +339,7 @@ func TestGetTransactionPool_ShouldWork(t *testing.T) { return cache.NewCacherStub() }, } - mbt, _ := track.NewMiniBlockTrack(dataPool, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) + mbt, _ := track.NewMiniBlockTrack(dataPool, &mock.BlockTrackerMock{}, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) tp := mbt.GetTransactionPool(block.TxBlock) assert.Equal(t, blockTransactionsPool, tp) @@ -292,3 +370,267 @@ func createDataPool() dataRetriever.PoolsHolder { }, } } + +func TestRegisterConfirmedMiniBlocksForHeader_ShouldImmunizeStoredMiniBlock(t *testing.T) { + t.Parallel() + + miniBlockHash := []byte("mb_hash") + txHashes := [][]byte{[]byte("txHash")} + storedMiniBlock := &block.MiniBlock{ + SenderShardID: 1, + ReceiverShardID: 0, + Type: block.TxBlock, + TxHashes: txHashes, + } + + miniBlocksPool := cache.NewCacherStub() + miniBlocksPool.PeekCalled = func(key []byte) (value interface{}, ok bool) { + if string(key) != string(miniBlockHash) { + return nil, false + } + + return storedMiniBlock, true + } + + dataPool := &dataRetrieverMock.PoolsHolderStub{ + TransactionsCalled: func() dataRetriever.ShardedDataCacherNotifier { + return testscommon.NewShardedDataStub() + }, + RewardTransactionsCalled: func() dataRetriever.ShardedDataCacherNotifier { + return testscommon.NewShardedDataStub() + }, + UnsignedTransactionsCalled: func() dataRetriever.ShardedDataCacherNotifier { + return testscommon.NewShardedDataStub() + }, + MiniBlocksCalled: func() storage.Cacher { + return miniBlocksPool + }, + } + + blockTracker := &mock.BlockTrackerMock{} + var finalMetachainHeadersHandler func(shardID uint32, headers []data.HeaderHandler, headersHashes [][]byte) + blockTracker.RegisterFinalMetachainHeadersHandlerCalled = func(handler func(shardID uint32, headers []data.HeaderHandler, headersHashes [][]byte)) { + finalMetachainHeadersHandler = handler + } + + var whitelistedKeys [][]byte + whiteListHandler := &testscommon.WhiteListHandlerStub{ + AddCalled: func(keys [][]byte) { + whitelistedKeys = keys + }, + } + var immunizedKeys [][]byte + var immunizedCacheID string + var immunizedNonce uint64 + setOldestImmuneNonceCalled := false + blockTransactionsPool := &testscommon.ShardedDataStub{ + SetOldestImmuneNonceCalled: func(cacheID string, nonce uint64) { + setOldestImmuneNonceCalled = true + }, + ImmunizeSetOfDataAgainstEvictionCalled: func(keys [][]byte, destCacheID string, nonce uint64) { + immunizedKeys = keys + immunizedCacheID = destCacheID + immunizedNonce = nonce + }, + } + + mbt, _ := track.NewMiniBlockTrack(dataPool, blockTracker, mock.NewMultipleShardsCoordinatorMock(), whiteListHandler) + mbt.SetBlockTransactionsPool(blockTransactionsPool) + + finalMetachainHeadersHandler(core.MetachainShardId, []data.HeaderHandler{ + &block.MetaBlock{ + Nonce: 7, + ShardInfo: []block.ShardData{ + { + ShardID: 1, + ShardMiniBlockHeaders: []block.MiniBlockHeader{ + { + Hash: miniBlockHash, + SenderShardID: 1, + ReceiverShardID: 0, + Type: block.TxBlock, + }, + }, + }, + }, + }, + }, nil) + + // Immunization happens on metablock arrival. + assert.Equal(t, txHashes, whitelistedKeys) + assert.Equal(t, txHashes, immunizedKeys) + assert.Equal(t, process.ShardCacherIdentifier(1, 0), immunizedCacheID) + assert.Equal(t, uint64(7), immunizedNonce) + // Regression guard: threshold advance is deferred to commit. + assert.False(t, setOldestImmuneNonceCalled, "SetOldestImmuneNonce must not be called from metablock arrival path") +} + +func TestMiniBlockTrack_CleanupConfirmedMiniBlocksBelow(t *testing.T) { + t.Parallel() + + dataPool := createDataPool() + blockTracker := &mock.BlockTrackerMock{} + var finalMetachainHeadersHandler func(shardID uint32, headers []data.HeaderHandler, headersHashes [][]byte) + blockTracker.RegisterFinalMetachainHeadersHandlerCalled = func(handler func(shardID uint32, headers []data.HeaderHandler, headersHashes [][]byte)) { + finalMetachainHeadersHandler = handler + } + + mbt, _ := track.NewMiniBlockTrack(dataPool, blockTracker, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) + + // Two confirmed miniblocks at different nonces, neither arriving in pool. + finalMetachainHeadersHandler(core.MetachainShardId, []data.HeaderHandler{ + &block.MetaBlock{ + Nonce: 5, + ShardInfo: []block.ShardData{ + { + ShardID: 1, + ShardMiniBlockHeaders: []block.MiniBlockHeader{ + {Hash: []byte("mb_old"), SenderShardID: 1, ReceiverShardID: 0, Type: block.TxBlock}, + }, + }, + }, + }, + }, nil) + finalMetachainHeadersHandler(core.MetachainShardId, []data.HeaderHandler{ + &block.MetaBlock{ + Nonce: 10, + ShardInfo: []block.ShardData{ + { + ShardID: 1, + ShardMiniBlockHeaders: []block.MiniBlockHeader{ + {Hash: []byte("mb_new"), SenderShardID: 1, ReceiverShardID: 0, Type: block.TxBlock}, + }, + }, + }, + }, + }, nil) + + // Cleanup with threshold 8 should drop the nonce-5 entry but keep nonce-10. + mbt.CleanupConfirmedMiniBlocksBelow(8) + + _, _, hasOld := mbt.GetConfirmedMiniBlockInfo([]byte("mb_old")) + _, _, hasNew := mbt.GetConfirmedMiniBlockInfo([]byte("mb_new")) + assert.False(t, hasOld) + assert.True(t, hasNew) +} + +func TestMiniBlockTrack_ReleaseImmunityForCommittedMetaBlocks(t *testing.T) { + t.Parallel() + + miniBlocksPool := cache.NewCacherStub() + miniBlocksPool.PeekCalled = func(_ []byte) (interface{}, bool) { return nil, false } + + var blockPoolThreshold, rewardPoolThreshold, unsignedPoolThreshold uint64 + blockPool := &testscommon.ShardedDataStub{ + SetOldestImmuneNonceForAllCachesCalled: func(nonce uint64) { blockPoolThreshold = nonce }, + } + rewardPool := &testscommon.ShardedDataStub{ + SetOldestImmuneNonceForAllCachesCalled: func(nonce uint64) { rewardPoolThreshold = nonce }, + } + unsignedPool := &testscommon.ShardedDataStub{ + SetOldestImmuneNonceForAllCachesCalled: func(nonce uint64) { unsignedPoolThreshold = nonce }, + } + + dataPool := &dataRetrieverMock.PoolsHolderStub{ + TransactionsCalled: func() dataRetriever.ShardedDataCacherNotifier { return blockPool }, + RewardTransactionsCalled: func() dataRetriever.ShardedDataCacherNotifier { return rewardPool }, + UnsignedTransactionsCalled: func() dataRetriever.ShardedDataCacherNotifier { return unsignedPool }, + MiniBlocksCalled: func() storage.Cacher { return miniBlocksPool }, + } + + blockTracker := &mock.BlockTrackerMock{} + var headersHandler func(uint32, []data.HeaderHandler, [][]byte) + blockTracker.RegisterFinalMetachainHeadersHandlerCalled = func(handler func(uint32, []data.HeaderHandler, [][]byte)) { + headersHandler = handler + } + + mbt, _ := track.NewMiniBlockTrack(dataPool, blockTracker, mock.NewMultipleShardsCoordinatorMock(), &testscommon.WhiteListHandlerStub{}) + + // Seed registry with entries at two different nonces. + headersHandler(core.MetachainShardId, []data.HeaderHandler{ + &block.MetaBlock{Nonce: 5, ShardInfo: []block.ShardData{{ShardID: 1, ShardMiniBlockHeaders: []block.MiniBlockHeader{ + {Hash: []byte("mb_old"), SenderShardID: 1, ReceiverShardID: 0, Type: block.TxBlock}, + }}}}, + }, nil) + headersHandler(core.MetachainShardId, []data.HeaderHandler{ + &block.MetaBlock{Nonce: 10, ShardInfo: []block.ShardData{{ShardID: 1, ShardMiniBlockHeaders: []block.MiniBlockHeader{ + {Hash: []byte("mb_new"), SenderShardID: 1, ReceiverShardID: 0, Type: block.TxBlock}, + }}}}, + }, nil) + + mbt.ReleaseImmunityForCommittedMetaBlocks(8) + + // All three pools should have received the threshold uniformly. + assert.Equal(t, uint64(8), blockPoolThreshold) + assert.Equal(t, uint64(8), rewardPoolThreshold) + assert.Equal(t, uint64(8), unsignedPoolThreshold) + + // Registry pruned below threshold. + _, _, hasOld := mbt.GetConfirmedMiniBlockInfo([]byte("mb_old")) + _, _, hasNew := mbt.GetConfirmedMiniBlockInfo([]byte("mb_new")) + assert.False(t, hasOld) + assert.True(t, hasNew) +} + +func TestMiniBlockTrack_ReleaseImmunityForCommittedShardBlocks(t *testing.T) { + t.Parallel() + + miniBlocksPool := cache.NewCacherStub() + miniBlocksPool.PeekCalled = func(_ []byte) (interface{}, bool) { return nil, false } + + type call struct { + cacheID string + nonce uint64 + } + var blockCalls, rewardCalls, unsignedCalls []call + blockPool := &testscommon.ShardedDataStub{ + SetOldestImmuneNonceCalled: func(c string, n uint64) { blockCalls = append(blockCalls, call{c, n}) }, + } + rewardPool := &testscommon.ShardedDataStub{ + SetOldestImmuneNonceCalled: func(c string, n uint64) { rewardCalls = append(rewardCalls, call{c, n}) }, + } + unsignedPool := &testscommon.ShardedDataStub{ + SetOldestImmuneNonceCalled: func(c string, n uint64) { unsignedCalls = append(unsignedCalls, call{c, n}) }, + } + + dataPool := &dataRetrieverMock.PoolsHolderStub{ + TransactionsCalled: func() dataRetriever.ShardedDataCacherNotifier { return blockPool }, + RewardTransactionsCalled: func() dataRetriever.ShardedDataCacherNotifier { return rewardPool }, + UnsignedTransactionsCalled: func() dataRetriever.ShardedDataCacherNotifier { return unsignedPool }, + MiniBlocksCalled: func() storage.Cacher { return miniBlocksPool }, + } + + shardCoordinator := mock.NewMultipleShardsCoordinatorMock() + shardCoordinator.CurrentShard = core.MetachainShardId + + blockTracker := &mock.BlockTrackerMock{} + var crossHeadersHandler func(uint32, []data.HeaderHandler, [][]byte) + blockTracker.RegisterCrossNotarizedHeadersHandlerCalled = func(handler func(uint32, []data.HeaderHandler, [][]byte)) { + crossHeadersHandler = handler + } + + mbt, _ := track.NewMiniBlockTrack(dataPool, blockTracker, shardCoordinator, &testscommon.WhiteListHandlerStub{}) + + // Seed registry: SCR from shard 1 to meta at nonce 5, and unrelated entry from shard 2 to meta at nonce 5. + crossHeadersHandler(0, []data.HeaderHandler{ + &block.Header{Nonce: 5, ShardID: 1, MiniBlockHeaders: []block.MiniBlockHeader{ + {Hash: []byte("scr_shard1"), SenderShardID: 1, ReceiverShardID: core.MetachainShardId, Type: block.SmartContractResultBlock}, + }}, + &block.Header{Nonce: 5, ShardID: 2, MiniBlockHeaders: []block.MiniBlockHeader{ + {Hash: []byte("scr_shard2"), SenderShardID: 2, ReceiverShardID: core.MetachainShardId, Type: block.SmartContractResultBlock}, + }}, + }, nil) + + mbt.ReleaseImmunityForCommittedShardBlocks(1, 6) + + expectedCacheID := process.ShardCacherIdentifier(1, core.MetachainShardId) + assert.Equal(t, []call{{expectedCacheID, 6}}, blockCalls) + assert.Equal(t, []call{{expectedCacheID, 6}}, rewardCalls) + assert.Equal(t, []call{{expectedCacheID, 6}}, unsignedCalls) + + // Only the shard-1 registry entry should be pruned. + _, _, hasShard1 := mbt.GetConfirmedMiniBlockInfo([]byte("scr_shard1")) + _, _, hasShard2 := mbt.GetConfirmedMiniBlockInfo([]byte("scr_shard2")) + assert.False(t, hasShard1) + assert.True(t, hasShard2) +} diff --git a/sharding/networksharding/peerShardMapper.go b/sharding/networksharding/peerShardMapper.go index f66bbf52742..4e2fd1bfd5f 100644 --- a/sharding/networksharding/peerShardMapper.go +++ b/sharding/networksharding/peerShardMapper.go @@ -18,6 +18,7 @@ import ( const maxNumPidsPerPk = 3 const uint32Size = 4 +const int64Size = 8 const defaultShardId = uint32(0) const indexNotFound = -1 @@ -38,6 +39,7 @@ var _ p2p.PeerShardResolver = (*PeerShardMapper)(nil) type PeerShardMapper struct { peerIdPkCache storage.Cacher pkPeerIdCache storage.Cacher + pkTimestampCache storage.Cacher fallbackPkShardCache storage.Cacher fallbackPidShardCache storage.Cacher peerIdSubTypeCache storage.Cacher @@ -85,9 +87,15 @@ func NewPeerShardMapper(arg ArgPeerShardMapper) (*PeerShardMapper, error) { return nil, err } + pkTimestamp, err := cache.NewLRUCache(arg.PeerIdPkCache.MaxSize()) + if err != nil { + return nil, err + } + return &PeerShardMapper{ peerIdPkCache: arg.PeerIdPkCache, pkPeerIdCache: pkPeerId, + pkTimestampCache: pkTimestamp, fallbackPkShardCache: arg.FallbackPkShardCache, fallbackPidShardCache: arg.FallbackPidShardCache, peerIdSubTypeCache: peerIdSubTypeCache, @@ -161,12 +169,27 @@ func (psm *PeerShardMapper) getPeerInfoWithNodesCoordinator(pid core.PeerID) (*c } return &core.P2PPeerInfo{ - PeerType: core.ValidatorPeer, - ShardID: shardId, - PkBytes: pkBuff, + PeerType: core.ValidatorPeer, + ShardID: shardId, + PkBytes: pkBuff, + AuthTimestamp: psm.getTimestampForPk(pkBuff), }, true } +func (psm *PeerShardMapper) getTimestampForPk(pkBytes []byte) int64 { + timestamp, ok := psm.pkTimestampCache.Get(pkBytes) + if !ok { + return 0 + } + + timestampInt, ok := timestamp.(int64) + if !ok { + return 0 + } + + return timestampInt +} + func (psm *PeerShardMapper) getShardIDSearchingPkInFallbackCache(pkBuff []byte) (shardId uint32, ok bool) { if len(pkBuff) == 0 { return defaultShardId, false @@ -231,11 +254,13 @@ func (psm *PeerShardMapper) getPeerInfoSearchingPidInFallbackCache(pid core.Peer // UpdatePeerIDPublicKeyPair updates the public key - peer ID pair in the corresponding maps // It also uses the intermediate pkPeerId cache that will prevent having thousands of peer ID's with // the same MultiversX PK that will make the node prone to an eclipse attack -func (psm *PeerShardMapper) UpdatePeerIDPublicKeyPair(pid core.PeerID, pk []byte) { +func (psm *PeerShardMapper) UpdatePeerIDPublicKeyPair(pid core.PeerID, pk []byte, timestamp int64) { isNew := psm.updatePeerIDPublicKey(pid, pk) if isNew { peerLog.Trace("new peer mapping", "pid", pid.Pretty(), "pk", pk) } + + psm.pkTimestampCache.Put(pk, timestamp, int64Size) } // UpdatePeerIDInfo updates the public keys and the shard ID for the peer ID in the corresponding maps diff --git a/sharding/networksharding/peerShardMapper_test.go b/sharding/networksharding/peerShardMapper_test.go index 6b03abe6805..17cdbd47464 100644 --- a/sharding/networksharding/peerShardMapper_test.go +++ b/sharding/networksharding/peerShardMapper_test.go @@ -231,7 +231,7 @@ func TestPeerShardMapper_UpdatePeerIDPublicKeyPairShouldWork(t *testing.T) { pid := core.PeerID("dummy peer ID") pk := []byte("dummy pk") - psm.UpdatePeerIDPublicKeyPair(pid, pk) + psm.UpdatePeerIDPublicKeyPair(pid, pk, 0) pkRecovered := psm.GetPkFromPidPk(pid) assert.Equal(t, pk, pkRecovered) diff --git a/storage/database/db.go b/storage/database/db.go index 7e677ed954c..52c720d572f 100644 --- a/storage/database/db.go +++ b/storage/database/db.go @@ -23,14 +23,14 @@ func NewlruDB(size uint32) (storage.Persister, error) { // NewLevelDB is a constructor for the leveldb persister // It creates the files in the location given as parameter -func NewLevelDB(path string, batchDelaySeconds int, maxBatchSize int, maxOpenFiles int) (s *leveldb.DB, err error) { - return leveldb.NewDB(path, batchDelaySeconds, maxBatchSize, maxOpenFiles) +func NewLevelDB(path string, batchDelaySeconds int, maxBatchSize int, maxOpenFiles int, bloomFilterSize int) (s *leveldb.DB, err error) { + return leveldb.NewDB(path, batchDelaySeconds, maxBatchSize, maxOpenFiles, bloomFilterSize) } // NewSerialDB is a constructor for the leveldb persister // It creates the files in the location given as parameter -func NewSerialDB(path string, batchDelaySeconds int, maxBatchSize int, maxOpenFiles int) (s *leveldb.SerialDB, err error) { - return leveldb.NewSerialDB(path, batchDelaySeconds, maxBatchSize, maxOpenFiles) +func NewSerialDB(path string, batchDelaySeconds int, maxBatchSize int, maxOpenFiles int, bloomFilterSize int) (s *leveldb.SerialDB, err error) { + return leveldb.NewSerialDB(path, batchDelaySeconds, maxBatchSize, maxOpenFiles, bloomFilterSize) } // NewShardIDProvider is a constructor for shard id provider diff --git a/storage/database/db_test.go b/storage/database/db_test.go index d04aaa2a78d..1909ff53c6b 100644 --- a/storage/database/db_test.go +++ b/storage/database/db_test.go @@ -38,14 +38,14 @@ func TestNewLevelDB(t *testing.T) { t.Run("invalid argument should error", func(t *testing.T) { t.Parallel() - instance, err := NewLevelDB(t.TempDir(), 0, 0, 0) + instance, err := NewLevelDB(t.TempDir(), 0, 0, 0, 0) assert.Nil(t, instance) assert.NotNil(t, err) }) t.Run("should work", func(t *testing.T) { t.Parallel() - instance, err := NewLevelDB(t.TempDir(), 1, 1, 1) + instance, err := NewLevelDB(t.TempDir(), 1, 1, 1, 0) assert.NotNil(t, instance) assert.Nil(t, err) _ = instance.Close() @@ -58,14 +58,14 @@ func TestNewSerialDB(t *testing.T) { t.Run("invalid argument should error", func(t *testing.T) { t.Parallel() - instance, err := NewSerialDB(t.TempDir(), 0, 0, 0) + instance, err := NewSerialDB(t.TempDir(), 0, 0, 0, 0) assert.Nil(t, instance) assert.NotNil(t, err) }) t.Run("should work", func(t *testing.T) { t.Parallel() - instance, err := NewSerialDB(t.TempDir(), 1, 1, 1) + instance, err := NewSerialDB(t.TempDir(), 1, 1, 1, 0) assert.NotNil(t, instance) assert.Nil(t, err) _ = instance.Close() diff --git a/storage/factory/persisterCreator.go b/storage/factory/persisterCreator.go index 0d17287815e..a1fc19cd3e8 100644 --- a/storage/factory/persisterCreator.go +++ b/storage/factory/persisterCreator.go @@ -43,11 +43,12 @@ func (pc *persisterCreator) CreateBasePersister(path string) (storage.Persister, var dbType = storageunit.DBType(pc.conf.Type) argsDB := factory.ArgDB{ - DBType: dbType, - Path: path, - BatchDelaySeconds: pc.conf.BatchDelaySeconds, - MaxBatchSize: pc.conf.MaxBatchSize, - MaxOpenFiles: pc.conf.MaxOpenFiles, + DBType: dbType, + Path: path, + BatchDelaySeconds: pc.conf.BatchDelaySeconds, + MaxBatchSize: pc.conf.MaxBatchSize, + MaxOpenFiles: pc.conf.MaxOpenFiles, + BloomFilterBitsPerKey: pc.conf.BloomFilterBitsPerKey, } return storageunit.NewDB(argsDB) diff --git a/storage/pruning/pruningStorer_test.go b/storage/pruning/pruningStorer_test.go index 994de340cdc..260dbdd01b7 100644 --- a/storage/pruning/pruningStorer_test.go +++ b/storage/pruning/pruningStorer_test.go @@ -99,7 +99,7 @@ func getDefaultArgsSerialDB() pruning.StorerArgs { cacheConf.Capacity = 40 persisterFactory := &mock.PersisterFactoryStub{ CreateCalled: func(path string) (storage.Persister, error) { - return database.NewSerialDB(path, 1, 20, 10) + return database.NewSerialDB(path, 1, 20, 10, 10) }, } pathManager := &testscommon.PathManagerStub{PathForEpochCalled: func(shardId string, epoch uint32, identifier string) string { diff --git a/testscommon/cache/immunityCacheSpy.go b/testscommon/cache/immunityCacheSpy.go new file mode 100644 index 00000000000..1f31bb273ce --- /dev/null +++ b/testscommon/cache/immunityCacheSpy.go @@ -0,0 +1,21 @@ +package cache + +// ImmunityCacheSpy is a spy for the ImmunityCache +type ImmunityCacheSpy struct { + *CacherStub + SetOldestImmuneNonceCalled func(uint64) +} + +// ImmunizeKeys is a spy for the ImmunizeKeys method of the ImmunityCache +func (c *ImmunityCacheSpy) ImmunizeKeys(_ [][]byte, _ uint64) (int, int) { return 0, 0 } + +// SetOldestImmuneNonce is a spy for the SetOldestImmuneNonce method of the ImmunityCache +func (c *ImmunityCacheSpy) SetOldestImmuneNonce(nonce uint64) { + if c.SetOldestImmuneNonceCalled != nil { + c.SetOldestImmuneNonceCalled(nonce) + } +} + +func (c *ImmunityCacheSpy) RemoveWithResult(_ []byte) bool { return false } +func (c *ImmunityCacheSpy) NumBytes() int { return 0 } +func (c *ImmunityCacheSpy) Diagnose(_ bool) {} diff --git a/testscommon/dataRetriever/poolFactory.go b/testscommon/dataRetriever/poolFactory.go index 7e8b8e0699c..03929792a9e 100644 --- a/testscommon/dataRetriever/poolFactory.go +++ b/testscommon/dataRetriever/poolFactory.go @@ -16,7 +16,7 @@ import ( "github.com/multiversx/mx-chain-go/storage/cache" "github.com/multiversx/mx-chain-go/storage/storageunit" "github.com/multiversx/mx-chain-go/testscommon" - "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" "github.com/multiversx/mx-chain-go/trie/factory" ) @@ -41,7 +41,7 @@ func CreateTxPool(numShards uint32, selfShard uint32) (dataRetriever.ShardedData }, NumberOfShards: numShards, SelfShardID: selfShard, - TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + TxGasHandler: mempool.NewTxGasHandlerMock(), Marshalizer: &marshal.GogoProtoMarshalizer{}, TxCacheBoundsConfig: config.TxCacheBoundsConfig{ MaxNumBytesPerSenderUpperBound: 33_554_432, diff --git a/testscommon/dataRetriever/poolsHolderMock.go b/testscommon/dataRetriever/poolsHolderMock.go index 903cc92fb3e..5aa28f7c430 100644 --- a/testscommon/dataRetriever/poolsHolderMock.go +++ b/testscommon/dataRetriever/poolsHolderMock.go @@ -16,7 +16,7 @@ import ( "github.com/multiversx/mx-chain-go/storage" "github.com/multiversx/mx-chain-go/storage/cache" "github.com/multiversx/mx-chain-go/storage/storageunit" - "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" ) // PoolsHolderMock - @@ -55,7 +55,7 @@ func NewPoolsHolderMock() *PoolsHolderMock { SizeInBytesPerSender: 10000000, Shards: 16, }, - TxGasHandler: txcachemocks.NewTxGasHandlerMock(), + TxGasHandler: mempool.NewTxGasHandlerMock(), Marshalizer: &marshal.GogoProtoMarshalizer{}, NumberOfShards: 1, TxCacheBoundsConfig: config.TxCacheBoundsConfig{ diff --git a/testscommon/generalConfig.go b/testscommon/generalConfig.go index df01b6ec29c..c6da668dc98 100644 --- a/testscommon/generalConfig.go +++ b/testscommon/generalConfig.go @@ -315,7 +315,7 @@ func GetGeneralConfig() config.Config { }, }, HeartbeatV2: config.HeartbeatV2Config{ - PeerAuthenticationTimeBetweenSendsInSec: 1, + PeerAuthenticationTimeBetweenSendsInSec: 10, PeerAuthenticationTimeBetweenSendsWhenErrorInSec: 1, PeerAuthenticationTimeThresholdBetweenSends: 0.1, HeartbeatTimeBetweenSendsInSec: 1, @@ -522,7 +522,9 @@ func getLRUCacheConfig() config.CacheConfig { // GetDefaultAntifloodConfig - func GetDefaultAntifloodConfig() config.AntifloodConfig { return config.AntifloodConfig{ - Enabled: true, + Enabled: true, + MaxAllowedTrieNodeChunks: 10, + TrieNodeChunksInactivityTimeoutInSec: 10, ConfigsByRound: []config.AntifloodConfigByRound{ { Round: 0, diff --git a/testscommon/miniBlockTrackerStub.go b/testscommon/miniBlockTrackerStub.go new file mode 100644 index 00000000000..6d30d72d01c --- /dev/null +++ b/testscommon/miniBlockTrackerStub.go @@ -0,0 +1,26 @@ +package testscommon + +// MiniBlockTrackerStub is a stub for process.MiniBlockTracker +type MiniBlockTrackerStub struct { + ReleaseImmunityForCommittedMetaBlocksCalled func(threshold uint64) + ReleaseImmunityForCommittedShardBlocksCalled func(senderShard uint32, threshold uint64) +} + +// ReleaseImmunityForCommittedMetaBlocks - +func (s *MiniBlockTrackerStub) ReleaseImmunityForCommittedMetaBlocks(threshold uint64) { + if s.ReleaseImmunityForCommittedMetaBlocksCalled != nil { + s.ReleaseImmunityForCommittedMetaBlocksCalled(threshold) + } +} + +// ReleaseImmunityForCommittedShardBlocks - +func (s *MiniBlockTrackerStub) ReleaseImmunityForCommittedShardBlocks(senderShard uint32, threshold uint64) { + if s.ReleaseImmunityForCommittedShardBlocksCalled != nil { + s.ReleaseImmunityForCommittedShardBlocksCalled(senderShard, threshold) + } +} + +// IsInterfaceNil returns true if the receiver is nil +func (s *MiniBlockTrackerStub) IsInterfaceNil() bool { + return s == nil +} diff --git a/testscommon/p2pmocks/networkShardingCollectorStub.go b/testscommon/p2pmocks/networkShardingCollectorStub.go index b7b1d3fb21b..bee8f438d46 100644 --- a/testscommon/p2pmocks/networkShardingCollectorStub.go +++ b/testscommon/p2pmocks/networkShardingCollectorStub.go @@ -6,7 +6,7 @@ import ( // NetworkShardingCollectorStub - type NetworkShardingCollectorStub struct { - UpdatePeerIDPublicKeyPairCalled func(pid core.PeerID, pk []byte) + UpdatePeerIDPublicKeyPairCalled func(pid core.PeerID, pk []byte, timestamp int64) UpdatePeerIDInfoCalled func(pid core.PeerID, pk []byte, shardID uint32) PutPeerIdShardIdCalled func(pid core.PeerID, shardId uint32) PutPeerIdSubTypeCalled func(pid core.PeerID, peerSubType core.P2PPeerSubType) @@ -15,9 +15,9 @@ type NetworkShardingCollectorStub struct { } // UpdatePeerIDPublicKeyPair - -func (nscs *NetworkShardingCollectorStub) UpdatePeerIDPublicKeyPair(pid core.PeerID, pk []byte) { +func (nscs *NetworkShardingCollectorStub) UpdatePeerIDPublicKeyPair(pid core.PeerID, pk []byte, timestamp int64) { if nscs.UpdatePeerIDPublicKeyPairCalled != nil { - nscs.UpdatePeerIDPublicKeyPairCalled(pid, pk) + nscs.UpdatePeerIDPublicKeyPairCalled(pid, pk, timestamp) } } diff --git a/testscommon/shardedDataCacheNotifierMock.go b/testscommon/shardedDataCacheNotifierMock.go index 93157be03cf..144ee0a577e 100644 --- a/testscommon/shardedDataCacheNotifierMock.go +++ b/testscommon/shardedDataCacheNotifierMock.go @@ -91,7 +91,15 @@ func (mock *ShardedDataCacheNotifierMock) RemoveSetOfDataFromPool(keys [][]byte, } // ImmunizeSetOfDataAgainstEviction - -func (mock *ShardedDataCacheNotifierMock) ImmunizeSetOfDataAgainstEviction(_ [][]byte, _ string) { +func (mock *ShardedDataCacheNotifierMock) ImmunizeSetOfDataAgainstEviction(_ [][]byte, _ string, _ uint64) { +} + +// SetOldestImmuneNonce - +func (mock *ShardedDataCacheNotifierMock) SetOldestImmuneNonce(_ string, _ uint64) { +} + +// SetOldestImmuneNonceForAllCaches - +func (mock *ShardedDataCacheNotifierMock) SetOldestImmuneNonceForAllCaches(_ uint64) { } // RemoveDataFromAllShards - diff --git a/testscommon/shardedDataStub.go b/testscommon/shardedDataStub.go index dd2ad968515..949a33844f6 100644 --- a/testscommon/shardedDataStub.go +++ b/testscommon/shardedDataStub.go @@ -24,7 +24,9 @@ type ShardedDataStub struct { ClearCalled func() ClearShardStoreCalled func(cacheID string) RemoveSetOfDataFromPoolCalled func(keys [][]byte, destCacheID string) - ImmunizeSetOfDataAgainstEvictionCalled func(keys [][]byte, cacheID string) + ImmunizeSetOfDataAgainstEvictionCalled func(keys [][]byte, cacheID string, nonce uint64) + SetOldestImmuneNonceCalled func(cacheID string, nonce uint64) + SetOldestImmuneNonceForAllCachesCalled func(nonce uint64) CreateShardStoreCalled func(destCacheID string) GetCountsCalled func() counting.CountsWithSize KeysCalled func() [][]byte @@ -120,9 +122,23 @@ func (sd *ShardedDataStub) RemoveSetOfDataFromPool(keys [][]byte, cacheID string } // ImmunizeSetOfDataAgainstEviction - -func (sd *ShardedDataStub) ImmunizeSetOfDataAgainstEviction(keys [][]byte, cacheID string) { +func (sd *ShardedDataStub) ImmunizeSetOfDataAgainstEviction(keys [][]byte, cacheID string, nonce uint64) { if sd.ImmunizeSetOfDataAgainstEvictionCalled != nil { - sd.ImmunizeSetOfDataAgainstEvictionCalled(keys, cacheID) + sd.ImmunizeSetOfDataAgainstEvictionCalled(keys, cacheID, nonce) + } +} + +// SetOldestImmuneNonce - +func (sd *ShardedDataStub) SetOldestImmuneNonce(cacheID string, nonce uint64) { + if sd.SetOldestImmuneNonceCalled != nil { + sd.SetOldestImmuneNonceCalled(cacheID, nonce) + } +} + +// SetOldestImmuneNonceForAllCaches - +func (sd *ShardedDataStub) SetOldestImmuneNonceForAllCaches(nonce uint64) { + if sd.SetOldestImmuneNonceForAllCachesCalled != nil { + sd.SetOldestImmuneNonceForAllCachesCalled(nonce) } } diff --git a/testscommon/txcachemocks/accountNonceAndBalanceProviderMock.go b/testscommon/txcachemocks/mempool/accountNonceAndBalanceProviderMock.go similarity index 99% rename from testscommon/txcachemocks/accountNonceAndBalanceProviderMock.go rename to testscommon/txcachemocks/mempool/accountNonceAndBalanceProviderMock.go index 522b5b75346..2cd8704db90 100644 --- a/testscommon/txcachemocks/accountNonceAndBalanceProviderMock.go +++ b/testscommon/txcachemocks/mempool/accountNonceAndBalanceProviderMock.go @@ -1,10 +1,11 @@ -package txcachemocks +package mempool import ( "math/big" "sync" "github.com/multiversx/mx-chain-core-go/core/check" + stateMock "github.com/multiversx/mx-chain-go/testscommon/state" ) diff --git a/testscommon/txcachemocks/mempoolHostMock.go b/testscommon/txcachemocks/mempool/mempoolHostMock.go similarity index 99% rename from testscommon/txcachemocks/mempoolHostMock.go rename to testscommon/txcachemocks/mempool/mempoolHostMock.go index e90d7115d1a..f7e905f9799 100644 --- a/testscommon/txcachemocks/mempoolHostMock.go +++ b/testscommon/txcachemocks/mempool/mempoolHostMock.go @@ -1,4 +1,4 @@ -package txcachemocks +package mempool import ( "math/big" diff --git a/testscommon/txcachemocks/selectionSessionMock.go b/testscommon/txcachemocks/mempool/selectionSessionMock.go similarity index 99% rename from testscommon/txcachemocks/selectionSessionMock.go rename to testscommon/txcachemocks/mempool/selectionSessionMock.go index ba8c7aa51eb..892cfbcbc43 100644 --- a/testscommon/txcachemocks/selectionSessionMock.go +++ b/testscommon/txcachemocks/mempool/selectionSessionMock.go @@ -1,4 +1,4 @@ -package txcachemocks +package mempool import ( "math/big" @@ -6,6 +6,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data" + stateMock "github.com/multiversx/mx-chain-go/testscommon/state" ) diff --git a/testscommon/txcachemocks/txGasHandlerMock.go b/testscommon/txcachemocks/mempool/txGasHandlerMock.go similarity index 99% rename from testscommon/txcachemocks/txGasHandlerMock.go rename to testscommon/txcachemocks/mempool/txGasHandlerMock.go index a624e29372a..afec960bab0 100644 --- a/testscommon/txcachemocks/txGasHandlerMock.go +++ b/testscommon/txcachemocks/mempool/txGasHandlerMock.go @@ -1,4 +1,4 @@ -package txcachemocks +package mempool import ( "math/big" diff --git a/testscommon/txcachemocks/txCacheMock.go b/testscommon/txcachemocks/txCacheMock.go index c34db2d53b0..ff5a5e52bc3 100644 --- a/testscommon/txcachemocks/txCacheMock.go +++ b/testscommon/txcachemocks/txCacheMock.go @@ -1,32 +1,97 @@ package txcachemocks -import "github.com/multiversx/mx-chain-storage-go/txcache" +import ( + "time" + + "github.com/multiversx/mx-chain-core-go/data" + + "github.com/multiversx/mx-chain-go/common" + "github.com/multiversx/mx-chain-go/txcache" +) // TxCacheMock - type TxCacheMock struct { - ClearCalled func() - PutCalled func(key []byte, value interface{}, sizeInBytes int) (evicted bool) - GetCalled func(key []byte) (value interface{}, ok bool) - HasCalled func(key []byte) bool - PeekCalled func(key []byte) (value interface{}, ok bool) - HasOrAddCalled func(key []byte, value interface{}, sizeInBytes int) (has, added bool) - RemoveCalled func(key []byte) - RemoveOldestCalled func() - KeysCalled func() [][]byte - LenCalled func() int - MaxSizeCalled func() int - RegisterHandlerCalled func(func(key []byte, value interface{})) - UnRegisterHandlerCalled func(id string) - CloseCalled func() error - + ClearCalled func() + PutCalled func(key []byte, value interface{}, sizeInBytes int) (evicted bool) + GetCalled func(key []byte) (value interface{}, ok bool) + HasCalled func(key []byte) bool + PeekCalled func(key []byte) (value interface{}, ok bool) + HasOrAddCalled func(key []byte, value interface{}, sizeInBytes int) (has, added bool) + RemoveCalled func(key []byte) + RemoveOldestCalled func() + KeysCalled func() [][]byte + LenCalled func() int + MaxSizeCalled func() int + RegisterHandlerCalled func(func(key []byte, value interface{})) + UnRegisterHandlerCalled func(id string) + CloseCalled func() error AddTxCalled func(tx *txcache.WrappedTransaction) (ok bool, added bool) GetByTxHashCalled func(txHash []byte) (*txcache.WrappedTransaction, bool) RemoveTxByHashCalled func(txHash []byte) bool - ImmunizeTxsAgainstEvictionCalled func(keys [][]byte) + ImmunizeTxsAgainstEvictionCalled func(keys [][]byte, nonce uint64) + SetOldestImmuneNonceCalled func(nonce uint64) ForEachTransactionCalled func(txcache.ForEachTransaction) NumBytesCalled func() int DiagnoseCalled func(deep bool) GetTransactionsPoolForSenderCalled func(sender string) []*txcache.WrappedTransaction + GetTrackerDiagnosisCalled func() txcache.TrackerDiagnosis + OnProposedBlockCalled func(blockHash []byte, blockBody data.BodyHandler, blockHeader data.HeaderHandler, accountsProvider common.AccountNonceAndBalanceProvider, latestExecutedHash []byte) error + OnBackfilledBlockCalled func(blockHash []byte, blockBody data.BodyHandler, blockHeader data.HeaderHandler) error + OnExecutedBlockCalled func(blockHeader data.HeaderHandler, rootHash []byte) error + ResetTrackerCalled func() + CleanupCalled func(accountsProvider common.AccountNonceProvider, randomness uint64, maxNum int, cleanupLoopMaximumDurationMs time.Duration) uint64 +} + +// GetTrackerDiagnosis - +func (cache *TxCacheMock) GetTrackerDiagnosis() txcache.TrackerDiagnosis { + if cache.GetTrackerDiagnosisCalled != nil { + return cache.GetTrackerDiagnosisCalled() + } + + return nil +} + +// OnProposedBlock - +func (cache *TxCacheMock) OnProposedBlock(blockHash []byte, blockBody data.BodyHandler, blockHeader data.HeaderHandler, accountsProvider common.AccountNonceAndBalanceProvider, latestExecutedHash []byte) error { + if cache.OnProposedBlockCalled != nil { + return cache.OnProposedBlockCalled(blockHash, blockBody, blockHeader, accountsProvider, latestExecutedHash) + } + + return nil +} + +// OnBackfilledBlock - +func (cache *TxCacheMock) OnBackfilledBlock(blockHash []byte, blockBody data.BodyHandler, blockHeader data.HeaderHandler) error { + if cache.OnBackfilledBlockCalled != nil { + return cache.OnBackfilledBlockCalled(blockHash, blockBody, blockHeader) + } + + return nil +} + +// OnExecutedBlock - +func (cache *TxCacheMock) OnExecutedBlock(blockHeader data.HeaderHandler, rootHash []byte) error { + if cache.OnExecutedBlockCalled != nil { + return cache.OnExecutedBlockCalled(blockHeader, rootHash) + } + + return nil +} + +// ResetTracker - +func (cache *TxCacheMock) ResetTracker() { + if cache.ResetTrackerCalled != nil { + cache.ResetTrackerCalled() + } +} + +// Cleanup - +func (cache *TxCacheMock) Cleanup(accountsProvider common.AccountNonceProvider, randomness uint64, maxNum int, cleanupLoopMaximumDurationMs time.Duration) uint64 { + if cache.CleanupCalled != nil { + return cache.CleanupCalled(accountsProvider, randomness, maxNum, cleanupLoopMaximumDurationMs) + } + + return 0 } // NewTxCacheStub - @@ -176,9 +241,16 @@ func (cache *TxCacheMock) RemoveTxByHash(txHash []byte) bool { } // ImmunizeTxsAgainstEviction - -func (cache *TxCacheMock) ImmunizeTxsAgainstEviction(keys [][]byte) { +func (cache *TxCacheMock) ImmunizeTxsAgainstEviction(keys [][]byte, nonce uint64) { if cache.ImmunizeTxsAgainstEvictionCalled != nil { - cache.ImmunizeTxsAgainstEvictionCalled(keys) + cache.ImmunizeTxsAgainstEvictionCalled(keys, nonce) + } +} + +// SetOldestImmuneNonce - +func (cache *TxCacheMock) SetOldestImmuneNonce(nonce uint64) { + if cache.SetOldestImmuneNonceCalled != nil { + cache.SetOldestImmuneNonceCalled(nonce) } } diff --git a/txcache/autoClean_test.go b/txcache/autoClean_test.go index 8c7b05aed76..f43cf29c847 100644 --- a/txcache/autoClean_test.go +++ b/txcache/autoClean_test.go @@ -6,7 +6,9 @@ import ( "testing" "github.com/multiversx/mx-chain-core-go/data/block" - "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -112,7 +114,7 @@ func TestTxCache_Cleanup(t *testing.T) { t.Run("with GetAccountNonce errors", func(t *testing.T) { boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() accountsProvider.GetAccountNonceCalled = func(address []byte) (uint64, bool, error) { switch string(address) { case "alice": @@ -147,7 +149,7 @@ func TestTxCache_Cleanup(t *testing.T) { t.Run("with nonce equal 0", func(t *testing.T) { boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() accountsProvider.SetNonce([]byte("alice"), 0) cache.AddTx(createTx([]byte("hash-alice-1"), "alice", 1)) @@ -163,7 +165,7 @@ func TestTxCache_Cleanup(t *testing.T) { boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() accountsProvider.SetNonce([]byte("alice"), 3) accountsProvider.SetNonce([]byte("bob"), 42) @@ -186,7 +188,7 @@ func TestTxCache_Cleanup(t *testing.T) { t.Run("with cleanupLoopMaximumDuration cap reached", func(t *testing.T) { boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() accountsProvider.SetNonce([]byte("alice"), 4) accountsProvider.SetNonce([]byte("bob"), 43) accountsProvider.SetNonce([]byte("carol"), 9) @@ -211,7 +213,7 @@ func TestTxCache_Cleanup(t *testing.T) { boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() accountsProvider.SetNonce([]byte("alice"), 2) accountsProvider.SetNonce([]byte("bob"), 42) accountsProvider.SetNonce([]byte("carol"), 7) @@ -240,7 +242,7 @@ func TestTxCache_Cleanup(t *testing.T) { boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() accountsProvider.SetNonce([]byte("alice"), 2) accountsProvider.SetNonce([]byte("bob"), 42) accountsProvider.SetNonce([]byte("carol"), 7) @@ -325,7 +327,7 @@ func TestTxCache_Cleanup(t *testing.T) { } // helper function for creating a new unconstrained cache with a given size -func newTxPoolWithN(size int, accountsProvider *txcachemocks.AccountNonceAndBalanceProviderMock) *TxCache { +func newTxPoolWithN(size int, accountsProvider *mempool.AccountNonceAndBalanceProviderMock) *TxCache { boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) for i := 0; i < size; i++ { @@ -340,7 +342,7 @@ func BenchmarkAddressShuffling(b *testing.B) { for _, size := range sizes { b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) { // prepare pool - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() cache := newTxPoolWithN(size, accountsProvider) b.ResetTimer() @@ -359,7 +361,7 @@ func BenchmarkCleanup(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { b.StopTimer() - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() cache := newTxPoolWithN(size, accountsProvider) b.StartTimer() diff --git a/txcache/crossTxCache.go b/txcache/crossTxCache.go index eef9622bfbf..b543eb65398 100644 --- a/txcache/crossTxCache.go +++ b/txcache/crossTxCache.go @@ -48,18 +48,24 @@ func NewCrossTxCache(config ConfigDestinationMe) (*CrossTxCache, error) { return &cache, nil } -// ImmunizeTxsAgainstEviction marks items as non-evictable -func (cache *CrossTxCache) ImmunizeTxsAgainstEviction(keys [][]byte) { - numNow, numFuture := cache.ImmunityCache.ImmunizeKeys(keys) +// ImmunizeTxsAgainstEviction marks items as non-evictable for the provided confirmation nonce +func (cache *CrossTxCache) ImmunizeTxsAgainstEviction(keys [][]byte, nonce uint64) { + numNow, numFuture := cache.ImmunityCache.ImmunizeKeys(keys, nonce) log.Trace("CrossTxCache.ImmunizeTxsAgainstEviction", "name", cache.config.Name, "len(keys)", len(keys), "numNow", numNow, "numFuture", numFuture, + "nonce", nonce, ) cache.Diagnose(false) } +// SetOldestImmuneNonce deactivates immunity below the provided nonce +func (cache *CrossTxCache) SetOldestImmuneNonce(nonce uint64) { + cache.ImmunityCache.SetOldestImmuneNonce(nonce) +} + // AddTx adds a transaction in the cache func (cache *CrossTxCache) AddTx(tx *WrappedTransaction) (has, added bool) { log.Trace("CrossTxCache.AddTx", "name", cache.config.Name, "txHash", tx.TxHash) diff --git a/txcache/crossTxCache_test.go b/txcache/crossTxCache_test.go index d657e5684fb..74af4db106a 100644 --- a/txcache/crossTxCache_test.go +++ b/txcache/crossTxCache_test.go @@ -51,9 +51,7 @@ func TestCrossTxCache_DoImmunizeTxsAgainstEviction(t *testing.T) { cache := newCrossTxCacheToTest(1, 8, math.MaxUint16) cache.addTestTxs("a", "b", "c", "d") - numNow, numFuture := cache.ImmunizeKeys(hashesAsBytes([]string{"a", "b", "e", "f"})) - require.Equal(t, 2, numNow) - require.Equal(t, 2, numFuture) + cache.ImmunizeTxsAgainstEviction(hashesAsBytes([]string{"a", "b", "e", "f"}), 7) require.Equal(t, 4, cache.Len()) cache.addTestTxs("e", "f", "g", "h") diff --git a/txcache/disabledCache.go b/txcache/disabledCache.go index 874cbb0d5f2..3034fc6d54a 100644 --- a/txcache/disabledCache.go +++ b/txcache/disabledCache.go @@ -111,7 +111,11 @@ func (cache *DisabledCache) UnRegisterHandler(string) { } // ImmunizeTxsAgainstEviction does nothing -func (cache *DisabledCache) ImmunizeTxsAgainstEviction(_ [][]byte) { +func (cache *DisabledCache) ImmunizeTxsAgainstEviction(_ [][]byte, _ uint64) { +} + +// SetOldestImmuneNonce does nothing +func (cache *DisabledCache) SetOldestImmuneNonce(_ uint64) { } // Diagnose does nothing diff --git a/txcache/eviction_test.go b/txcache/eviction_test.go index 00d7ea447dc..958fc603dc1 100644 --- a/txcache/eviction_test.go +++ b/txcache/eviction_test.go @@ -7,7 +7,9 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data/block" - "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" + "github.com/stretchr/testify/require" ) @@ -28,7 +30,7 @@ func TestTxCache_DoEviction_BecauseOfCount(t *testing.T) { TxCacheBoundsConfig: createMockTxBoundsConfig(), } - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() cache, err := NewTxCache(config, host, 0) require.Nil(t, err) @@ -64,7 +66,7 @@ func TestTxCache_DoEviction_BecauseOfSize(t *testing.T) { TxCacheBoundsConfig: createMockTxBoundsConfig(), } - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() cache, err := NewTxCache(config, host, 0) require.Nil(t, err) @@ -101,13 +103,13 @@ func TestTxCache_DoEviction_WithTrackedTxs(t *testing.T) { TxCacheBoundsConfig: createMockTxBoundsConfig(), } - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() cache, err := NewTxCache(config, host, 0) require.Nil(t, err) require.NotNil(t, cache) - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() accountsProvider.SetNonce([]byte("alice"), 1) accountsProvider.SetNonce([]byte("bob"), 1) accountsProvider.SetNonce([]byte("carol"), 1) @@ -165,7 +167,7 @@ func TestTxCache_DoEviction_DoesNothingWhenAlreadyInProgress(t *testing.T) { TxCacheBoundsConfig: createMockTxBoundsConfig(), } - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() cache, err := NewTxCache(config, host, 0) require.Nil(t, err) @@ -205,7 +207,7 @@ func TestBenchmarkTxCache_DoEviction(t *testing.T) { TxCacheBoundsConfig: createMockTxBoundsConfig(), } - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() sw := core.NewStopWatch() diff --git a/txcache/selectionTracker_test.go b/txcache/selectionTracker_test.go index f1f68953eac..3455dba9a4a 100644 --- a/txcache/selectionTracker_test.go +++ b/txcache/selectionTracker_test.go @@ -15,7 +15,7 @@ import ( "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/common/holders" - "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" ) func proposeBlocks(t *testing.T, numOfBlocks int, selectionTracker *selectionTracker, accountsProvider common.AccountNonceAndBalanceProvider) { @@ -158,7 +158,7 @@ func TestSelectionTracker_OnProposedBlockShouldErr(t *testing.T) { }, } - accountsProvider := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsProvider := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 1, big.NewInt(20), true, nil }, @@ -207,7 +207,7 @@ func TestSelectionTracker_OnProposedBlockShouldErr(t *testing.T) { }, } - accountsProvider := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsProvider := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 1, big.NewInt(20), true, nil }, @@ -262,7 +262,7 @@ func TestSelectionTracker_OnProposedBlockShouldErr(t *testing.T) { }, } - accountsProvider := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsProvider := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 1, big.NewInt(20), true, nil }, @@ -304,7 +304,7 @@ func TestSelectionTracker_OnProposedBlockShouldErr(t *testing.T) { }, } - accountsProvider := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsProvider := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, nil, false, expectedErr }, @@ -337,7 +337,7 @@ func TestSelectionTracker_OnProposedBlockShouldErr(t *testing.T) { }, } - accountsProvider := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsProvider := &mempool.AccountNonceAndBalanceProviderMock{ GetRootHashCalled: func() ([]byte, error) { return []byte("rootHash1"), nil }, @@ -362,7 +362,7 @@ func TestSelectionTracker_OnProposedBlockShouldWork(t *testing.T) { require.Nil(t, err) numOfBlocks := 20 - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() proposeBlocks(t, numOfBlocks, tracker, accountsProvider) require.Equal(t, 20, len(tracker.blocks)) @@ -376,7 +376,7 @@ func TestSelectionTracker_OnProposedBlockWhenMaxTrackedBlocksIsReached(t *testin require.Nil(t, err) numOfBlocks := 3 - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() proposeBlocks(t, numOfBlocks, tracker, accountsProvider) @@ -447,7 +447,7 @@ func TestSelectionTracker_OnProposedBlockWhenMaxTrackedBlocksIsReached(t *testin func Test_CompleteFlowShouldWork(t *testing.T) { t.Parallel() - accountsProvider := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsProvider := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 11, big.NewInt(8 * 100000 * oneBillion), true, nil }, @@ -465,7 +465,7 @@ func Test_CompleteFlowShouldWork(t *testing.T) { TxCacheBoundsConfig: createMockTxBoundsConfig(), } - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() cache, err := NewTxCache(config, host, 0) require.Nil(t, err) @@ -569,7 +569,7 @@ func Test_CompleteFlowShouldWork(t *testing.T) { require.True(t, ok) require.Equal(t, expectedBreadcrumbs, tb.breadcrumbsByAddress) - selectionSession := &txcachemocks.SelectionSessionMock{ + selectionSession := &mempool.SelectionSessionMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 11, big.NewInt(8 * 100000 * oneBillion), true, nil }, @@ -600,7 +600,7 @@ func Test_CompleteFlowShouldWork(t *testing.T) { } // update the session nonce - selectionSession = &txcachemocks.SelectionSessionMock{ + selectionSession = &mempool.SelectionSessionMock{ GetRootHashCalled: func() ([]byte, error) { return []byte("rootHash0"), nil }, @@ -667,7 +667,7 @@ func TestSelectionTracker_OnExecutedBlockShouldWork(t *testing.T) { require.Nil(t, err) numOfBlocks := 20 - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() proposeBlocks(t, numOfBlocks, tracker, accountsProvider) require.Equal(t, numOfBlocks, len(tracker.blocks)) @@ -682,7 +682,7 @@ func TestSelectionTracker_OnExecutedBlockShouldDeleteAllBlocksBelowSpecificNonce t.Parallel() txCache := newCacheToTest(maxNumBytesPerSenderUpperBoundTest, 3) - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() tracker, err := NewSelectionTracker(txCache, 0, maxTrackedBlocks) require.Nil(t, err) @@ -930,7 +930,7 @@ func TestSelectionTracker_deriveVirtualSelectionSessionShouldErr(t *testing.T) { t.Run("get roothash returns error, should error", func(t *testing.T) { expectedErr := errors.New("expected err") - session := txcachemocks.SelectionSessionMock{} + session := mempool.SelectionSessionMock{} session.GetRootHashCalled = func() ([]byte, error) { return nil, expectedErr } @@ -939,7 +939,7 @@ func TestSelectionTracker_deriveVirtualSelectionSessionShouldErr(t *testing.T) { require.Equal(t, expectedErr, actualErr) }) t.Run("cannot do simulation error on wrong nonce, returns error", func(t *testing.T) { - session := txcachemocks.SelectionSessionMock{} + session := mempool.SelectionSessionMock{} session.GetRootHashCalled = func() ([]byte, error) { return []byte("root hash"), nil } @@ -999,7 +999,7 @@ func TestSelectionTracker_deriveVirtualSelectionSessionShouldDeleteProposedBlock tracker.blocks = createDummyTrackedBlocks() require.Equal(t, 3, len(tracker.blocks)) - session := txcachemocks.SelectionSessionMock{} + session := mempool.SelectionSessionMock{} session.GetRootHashCalled = func() ([]byte, error) { return nil, nil } @@ -1019,7 +1019,7 @@ func TestSelectionTracker_deriveVirtualSelectionSessionShouldNotDeleteProposedBl require.Nil(t, err) require.Equal(t, 3, len(tracker.blocks)) - session := txcachemocks.SelectionSessionMock{} + session := mempool.SelectionSessionMock{} session.GetRootHashCalled = func() ([]byte, error) { return nil, nil } @@ -1072,7 +1072,7 @@ func TestSelectionTracker_validateTrackedBlocks(t *testing.T) { }, } - mockSelectionSession := txcachemocks.SelectionSessionMock{ + mockSelectionSession := mempool.SelectionSessionMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, big.NewInt(20), true, nil }, @@ -1128,7 +1128,7 @@ func TestSelectionTracker_validateTrackedBlocks(t *testing.T) { }, } - mockSelectionSession := txcachemocks.SelectionSessionMock{ + mockSelectionSession := mempool.SelectionSessionMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, big.NewInt(5), true, nil }, @@ -1184,7 +1184,7 @@ func TestSelectionTracker_validateTrackedBlocks(t *testing.T) { }, } - mockSelectionSession := txcachemocks.SelectionSessionMock{ + mockSelectionSession := mempool.SelectionSessionMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, big.NewInt(2), true, nil }, @@ -1272,7 +1272,7 @@ func Test_isTransactionTracked(t *testing.T) { require.Nil(t, err) txCache.tracker = tracker - accountsProvider := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsProvider := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 11, big.NewInt(6 * 100000 * oneBillion), true, nil }, @@ -1415,7 +1415,7 @@ func TestSelectionTracker_IsTransactionTracked(t *testing.T) { txCache.tracker = tracker - accountsProvider := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsProvider := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 11, big.NewInt(6 * 100000 * oneBillion), true, nil }, @@ -1606,7 +1606,7 @@ func TestSelectionTracker_MaxUniqueAccounts(t *testing.T) { Nonce: 10, } - accProvider := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accProvider := &mempool.AccountNonceAndBalanceProviderMock{ GetRootHashCalled: func() ([]byte, error) { return defaultLatestExecutedHash, nil }, @@ -1732,7 +1732,7 @@ func TestSelectionTracker_OnExecutedBlock_multipleBlocksWithSharedSender(t *test txCache.tracker = tracker aliceInitialNonce := uint64(1) - accountsProvider := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsProvider := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return aliceInitialNonce, big.NewInt(8 * 100000 * oneBillion), true, nil }, @@ -1934,7 +1934,7 @@ func TestSelectionTracker_validateBreadcrumbsToleratesPredecessorDiscontinuity(t }, } - accountsProvider := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsProvider := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, big.NewInt(1000), true, nil }, @@ -1990,7 +1990,7 @@ func TestSelectionTracker_validateBreadcrumbsToleratesPredecessorDiscontinuity(t }, } - accountsProvider := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsProvider := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, big.NewInt(1000), true, nil }, @@ -2028,7 +2028,7 @@ func TestSelectionTracker_validateBreadcrumbsToleratesPredecessorDiscontinuity(t }, } - accountsProvider := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsProvider := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, big.NewInt(1000), true, nil }, @@ -2101,7 +2101,7 @@ func TestSelectionTracker_validateBreadcrumbsToleratesPredecessorDiscontinuity(t }, } - accountsProvider := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsProvider := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, big.NewInt(1000), true, nil }, @@ -2140,7 +2140,7 @@ func TestSelectionTracker_SelectionSkipsDiscontinuousAccounts(t *testing.T) { TxCacheBoundsConfig: createMockTxBoundsConfig(), } - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() cache, err := NewTxCache(config, host, 0) require.Nil(t, err) @@ -2189,7 +2189,7 @@ func TestSelectionTracker_SelectionSkipsDiscontinuousAccounts(t *testing.T) { cache.tracker.latestRootHash = []byte("rootHash0") cache.tracker.latestNonce = 99 - selectionSession := &txcachemocks.SelectionSessionMock{ + selectionSession := &mempool.SelectionSessionMock{ GetRootHashCalled: func() ([]byte, error) { return []byte("rootHash0"), nil }, @@ -2234,7 +2234,7 @@ func TestSelectionTracker_RecoveryFromDiscontinuousBreadcrumbs(t *testing.T) { "bob": 0, } - accountsProvider := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsProvider := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { nonce := accountNonces[string(address)] return nonce, big.NewInt(8 * 100000 * oneBillion), true, nil @@ -2256,7 +2256,7 @@ func TestSelectionTracker_RecoveryFromDiscontinuousBreadcrumbs(t *testing.T) { TxCacheBoundsConfig: createMockTxBoundsConfig(), } - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() cache, err := NewTxCache(config, host, 0) require.Nil(t, err) @@ -2329,7 +2329,7 @@ func TestSelectionTracker_RecoveryFromDiscontinuousBreadcrumbs(t *testing.T) { // Step 4: After the stale block is removed, alice's breadcrumbs are no longer in any tracked block // Now verify alice can be selected in the next selection - selectionSession := &txcachemocks.SelectionSessionMock{ + selectionSession := &mempool.SelectionSessionMock{ GetRootHashCalled: func() ([]byte, error) { return []byte("rootHash1"), nil }, diff --git a/txcache/selection_test.go b/txcache/selection_test.go index ca11769fb77..658184a4c80 100644 --- a/txcache/selection_test.go +++ b/txcache/selection_test.go @@ -17,7 +17,7 @@ import ( "github.com/multiversx/mx-chain-go/common" "github.com/multiversx/mx-chain-go/common/holders" "github.com/multiversx/mx-chain-go/config" - "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" ) var expectedError = errors.New("expected error") @@ -69,7 +69,7 @@ func TestTxCache_SelectTransactions(t *testing.T) { options := createMockTxSelectionOptions(math.MaxUint64, math.MaxInt) boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - session := &txcachemocks.SelectionSessionMock{ + session := &mempool.SelectionSessionMock{ GetRootHashCalled: func() ([]byte, error) { return nil, expectedError }, @@ -85,7 +85,7 @@ func TestTxCache_SelectTransactions_Dummy(t *testing.T) { options := createMockTxSelectionOptions(math.MaxUint64, math.MaxInt) boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() session.SetNonce([]byte("alice"), 1) session.SetNonce([]byte("bob"), 5) session.SetNonce([]byte("carol"), 1) @@ -120,7 +120,7 @@ func TestTxCache_SelectTransactions_Dummy(t *testing.T) { boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() session.SetNonce([]byte("alice"), 1) session.SetNonce([]byte("bob"), 5) session.SetNonce([]byte("carol"), 3) @@ -147,7 +147,7 @@ func TestTxCache_SelectTransactionsWithBandwidth_Dummy(t *testing.T) { boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() session.SetNonce([]byte("alice"), 1) session.SetNonce([]byte("bob"), 5) session.SetNonce([]byte("carol"), 1) @@ -181,7 +181,7 @@ func TestTxCache_SelectTransactions_HandlesNotExecutableTransactions(t *testing. boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() session.SetNonce([]byte("alice"), 1) session.SetNonce([]byte("bob"), 42) session.SetNonce([]byte("carol"), 7) @@ -211,7 +211,7 @@ func TestTxCache_SelectTransactions_HandlesNotExecutableTransactions(t *testing. boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() session.SetNonce([]byte("alice"), 1) session.SetNonce([]byte("bob"), 42) session.SetNonce([]byte("carol"), 7) @@ -243,7 +243,7 @@ func TestTxCache_SelectTransactions_HandlesNotExecutableTransactions(t *testing. boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() session.SetNonce([]byte("alice"), 1) session.SetNonce([]byte("bob"), 42) session.SetNonce([]byte("carol"), 7) @@ -275,7 +275,7 @@ func TestTxCache_SelectTransactions_HandlesNotExecutableTransactions(t *testing. boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() session.SetNonce([]byte("alice"), 1) cache.AddTx(createTx([]byte("hash-alice-1"), "alice", 1).withValue(big.NewInt(0))) @@ -301,7 +301,7 @@ func TestTxCache_SelectTransactions_HandlesNotExecutableTransactions(t *testing. boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() session.SetNonce([]byte("alice"), 1) session.SetBalance([]byte("alice"), big.NewInt(150000000000000)) session.SetNonce([]byte("bob"), 42) @@ -330,7 +330,7 @@ func TestTxCache_SelectTransactions_HandlesNotExecutableTransactions(t *testing. boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() session.SetNonce([]byte("alice"), 1) session.SetNonce([]byte("bob"), 42) @@ -358,7 +358,7 @@ func TestTxCache_SelectTransactions_HandlesNotExecutableTransactions(t *testing. boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() session.SetNonce([]byte("alice"), 1) session.IsIncorrectlyGuardedCalled = func(tx data.TransactionHandler) bool { @@ -388,7 +388,7 @@ func TestTxCache_SelectTransactions_WhenTransactionsAddedInReversedNonceOrder(t boundsConfig := createMockTxBoundsConfig() cache := newUnconstrainedCacheToTest(boundsConfig) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() // Add "nSenders" * "nTransactionsPerSender" transactions in the cache (in reversed nonce order) nSenders := 1000 @@ -427,7 +427,7 @@ func TestTxCache_SelectTransactions_WhenTransactionsAddedInReversedNonceOrder(t func TestTxCache_selectTransactionsFromBunches(t *testing.T) { t.Run("empty cache", func(t *testing.T) { - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() virtualSession := newVirtualSelectionSession(session, make(map[string]*virtualAccountRecord)) options := createMockTxSelectionOptions(10_000_000_000, math.MaxInt) selected, accumulatedGas := selectTransactionsFromBunches(virtualSession, []bunchOfTransactions{}, options, 0) @@ -450,7 +450,7 @@ func TestBenchmarkTxCache_acquireBunchesOfTransactions(t *testing.T) { TxCacheBoundsConfig: createMockTxBoundsConfig(), } - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() sw := core.NewStopWatch() @@ -545,7 +545,7 @@ func TestBenchmarkTxCache_selectTransactionsFromBunches(t *testing.T) { t.Run("numSenders = 1000, numTransactions = 1000", func(t *testing.T) { options := createMockTxSelectionOptions(10_000_000_000, math.MaxInt) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() virtualSession := newVirtualSelectionSession(session, make(map[string]*virtualAccountRecord)) bunches := createBunchesOfTransactionsWithUniformDistribution(1000, 1000) @@ -559,7 +559,7 @@ func TestBenchmarkTxCache_selectTransactionsFromBunches(t *testing.T) { t.Run("numSenders = 10000, numTransactions = 100", func(t *testing.T) { options := createMockTxSelectionOptions(10_000_000_000, math.MaxInt) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() virtualSession := newVirtualSelectionSession(session, make(map[string]*virtualAccountRecord)) bunches := createBunchesOfTransactionsWithUniformDistribution(1000, 1000) @@ -573,7 +573,7 @@ func TestBenchmarkTxCache_selectTransactionsFromBunches(t *testing.T) { t.Run("numSenders = 100000, numTransactions = 3", func(t *testing.T) { options := createMockTxSelectionOptions(10_000_000_000, math.MaxInt) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() virtualSession := newVirtualSelectionSession(session, make(map[string]*virtualAccountRecord)) bunches := createBunchesOfTransactionsWithUniformDistribution(100000, 3) @@ -589,7 +589,7 @@ func TestBenchmarkTxCache_selectTransactionsFromBunches(t *testing.T) { t.Run("numSenders = 300000, numTransactions = 1", func(t *testing.T) { options := createMockTxSelectionOptions(10_000_000_000, math.MaxInt) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() virtualSession := newVirtualSelectionSession(session, make(map[string]*virtualAccountRecord)) bunches := createBunchesOfTransactionsWithUniformDistribution(300000, 1) @@ -623,7 +623,7 @@ func TestBenchmarkTxCache_selectTransactionsFromBunches(t *testing.T) { func TestTxCache_selectTransactionsFromBunches_loopBreaks_whenTakesTooLong(t *testing.T) { t.Run("numSenders = 300000, numTransactions = 1", func(t *testing.T) { - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() virtualSession := newVirtualSelectionSession(session, make(map[string]*virtualAccountRecord)) options := createMockTxSelectionOptionsWithTimeFunc(10_000_000_000, 50_000, haveTimeFalseForSelection) bunches := createBunchesOfTransactionsWithUniformDistribution(300000, 1) @@ -648,8 +648,8 @@ func TestBenchmarkTxCache_doSelectTransactions(t *testing.T) { TxCacheBoundsConfig: createMockTxBoundsConfig(), } - host := txcachemocks.NewMempoolHostMock() - session := txcachemocks.NewSelectionSessionMock() + host := mempool.NewMempoolHostMock() + session := mempool.NewSelectionSessionMock() sw := core.NewStopWatch() @@ -928,7 +928,7 @@ func TestTxCache_PropagationGracePeriod(t *testing.T) { PropagationGracePeriodMs: 0, } cache := newUnconstrainedCacheToTest(boundsConfig) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() session.SetNonce([]byte("alice"), 1) session.SetNonce([]byte("bob"), 5) @@ -951,7 +951,7 @@ func TestTxCache_PropagationGracePeriod(t *testing.T) { PropagationGracePeriodMs: 500, } cache := newUnconstrainedCacheToTest(boundsConfig) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() session.SetNonce([]byte("alice"), 1) session.SetNonce([]byte("bob"), 5) @@ -974,7 +974,7 @@ func TestTxCache_PropagationGracePeriod(t *testing.T) { PropagationGracePeriodMs: 500, } cache := newUnconstrainedCacheToTest(boundsConfig) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() session.SetNonce([]byte("alice"), 1) session.SetNonce([]byte("bob"), 5) @@ -1001,7 +1001,7 @@ func TestTxCache_PropagationGracePeriod(t *testing.T) { PropagationGracePeriodMs: 500, } cache := newUnconstrainedCacheToTest(boundsConfig) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() session.SetNonce([]byte("alice"), 1) cache.AddTx(createRelayedTx([]byte("hash-alice-1"), "alice", "relayer", 1)) @@ -1029,7 +1029,7 @@ func TestTxCache_PropagationGracePeriod(t *testing.T) { PropagationGracePeriodMs: 200, } cache := newUnconstrainedCacheToTest(boundsConfig) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() session.SetNonce([]byte("alice"), 1) session.SetNonce([]byte("bob"), 5) diff --git a/txcache/testutils_test.go b/txcache/testutils_test.go index 645eb5fc1c5..c59708bbbcd 100644 --- a/txcache/testutils_test.go +++ b/txcache/testutils_test.go @@ -10,7 +10,8 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/data/transaction" - "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" ) const oneMilion = 1000000 @@ -158,7 +159,7 @@ func addManyTransactionsWithUniformDistribution(cache *TxCache, nSenders int, nT func createBunchesOfTransactionsWithUniformDistribution(nSenders int, nTransactionsPerSender int) []bunchOfTransactions { bunches := make([]bunchOfTransactions, 0, nSenders) - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() for senderTag := 0; senderTag < nSenders; senderTag++ { bunch := make(bunchOfTransactions, 0, nTransactionsPerSender) diff --git a/txcache/transactionsHeapItem_test.go b/txcache/transactionsHeapItem_test.go index 267d76a85b2..9131c297c31 100644 --- a/txcache/transactionsHeapItem_test.go +++ b/txcache/transactionsHeapItem_test.go @@ -4,7 +4,9 @@ import ( "testing" "github.com/multiversx/mx-chain-core-go/data" - "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" + "github.com/stretchr/testify/require" ) @@ -34,7 +36,7 @@ func TestNewTransactionsHeapItem(t *testing.T) { } func TestTransactionsHeapItem_selectTransaction(t *testing.T) { - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() a := createTx([]byte("tx-1"), "alice", 42) b := createTx([]byte("tx-2"), "alice", 43) @@ -157,7 +159,7 @@ func TestTransactionsHeapItem_detectNonceDuplicate(t *testing.T) { func TestTransactionsHeapItem_detectIncorrectlyGuarded(t *testing.T) { t.Run("is correctly guarded", func(t *testing.T) { - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() virtualSession := newVirtualSelectionSession(session, make(map[string]*virtualAccountRecord)) session.IsIncorrectlyGuardedCalled = func(tx data.TransactionHandler) bool { @@ -171,7 +173,7 @@ func TestTransactionsHeapItem_detectIncorrectlyGuarded(t *testing.T) { }) t.Run("is incorrectly guarded", func(t *testing.T) { - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() session.IsIncorrectlyGuardedCalled = func(tx data.TransactionHandler) bool { return true } diff --git a/txcache/txCache.go b/txcache/txCache.go index 73aa1ee5078..6d92d0d625c 100644 --- a/txcache/txCache.go +++ b/txcache/txCache.go @@ -401,7 +401,11 @@ func (cache *TxCache) UnRegisterHandler(string) { } // ImmunizeTxsAgainstEviction does nothing for this type of cache -func (cache *TxCache) ImmunizeTxsAgainstEviction(_ [][]byte) { +func (cache *TxCache) ImmunizeTxsAgainstEviction(_ [][]byte, _ uint64) { +} + +// SetOldestImmuneNonce does nothing for this type of cache +func (cache *TxCache) SetOldestImmuneNonce(_ uint64) { } // Close does nothing for this cacher implementation diff --git a/txcache/txCache_test.go b/txcache/txCache_test.go index bd162c046cc..17d9d00e2ae 100644 --- a/txcache/txCache_test.go +++ b/txcache/txCache_test.go @@ -13,8 +13,10 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data/block" + "github.com/multiversx/mx-chain-go/config" - "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" + "github.com/multiversx/mx-chain-storage-go/common" "github.com/multiversx/mx-chain-storage-go/types" "github.com/stretchr/testify/require" @@ -33,7 +35,7 @@ func Test_NewTxCache(t *testing.T) { TxCacheBoundsConfig: createMockTxBoundsConfig(), } - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() cache, err := NewTxCache(config, host, 0) require.Nil(t, err) @@ -143,7 +145,7 @@ func Test_AddTx_AppliesSizeConstraintsPerSenderForNumTransactions(t *testing.T) cache := newCacheToTest(maxNumBytesPerSenderUpperBoundTest, 3) - accountsProvider := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsProvider := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 1, big.NewInt(3 * 1500000 * oneBillion), true, nil }, @@ -394,7 +396,7 @@ func Test_Keys(t *testing.T) { } func Test_AddWithEviction_UniformDistributionOfTxsPerSender(t *testing.T) { - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() t.Run("numSenders = 11, numTransactions = 10, countThreshold = 100, numItemsToPreemptivelyEvict = 1", func(t *testing.T) { config := ConfigSourceMe{ @@ -572,7 +574,7 @@ func TestTxCache_GetDimensionOfTrackedBlocks(t *testing.T) { require.Nil(t, err) txCache.tracker = tracker - accountsProvider := txcachemocks.NewAccountNonceAndBalanceProviderMock() + accountsProvider := mempool.NewAccountNonceAndBalanceProviderMock() err = txCache.OnProposedBlock( []byte("hash1"), @@ -661,7 +663,7 @@ func TestBenchmarkTxCache_addManyTransactionsWithSameNonce(t *testing.T) { TxCacheBoundsConfig: createMockTxBoundsConfig(), } - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() sw := core.NewStopWatch() @@ -746,7 +748,7 @@ func TestBenchmarkTxCache_addManyTransactionsInDifferentScenarios(t *testing.T) TxCacheBoundsConfig: createMockTxBoundsConfig(), } - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() sw := core.NewStopWatch() t.Run("numTransactions = 5_000 with decreasing nonce (worst case)", func(t *testing.T) { @@ -825,7 +827,7 @@ func TestBenchmarkTxCache_addManyTransactionsInDifferentScenarios(t *testing.T) func Test_ResetTracker(t *testing.T) { t.Parallel() - accountsProvider := &txcachemocks.AccountNonceAndBalanceProviderMock{ + accountsProvider := &mempool.AccountNonceAndBalanceProviderMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 11, big.NewInt(6 * 100000 * oneBillion), true, nil }, @@ -843,7 +845,7 @@ func Test_ResetTracker(t *testing.T) { TxCacheBoundsConfig: createMockTxBoundsConfig(), } - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() cache, err := NewTxCache(config, host, 0) require.Nil(t, err) @@ -891,7 +893,7 @@ func Test_ResetTracker(t *testing.T) { } func newUnconstrainedCacheToTest(boundsConfig config.TxCacheBoundsConfig) *TxCache { - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() cache, err := NewTxCache(ConfigSourceMe{ Name: "test", @@ -912,7 +914,7 @@ func newUnconstrainedCacheToTest(boundsConfig config.TxCacheBoundsConfig) *TxCac } func newCacheToTest(numBytesPerSenderThreshold uint32, countPerSenderThreshold uint32) *TxCache { - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() cache, err := NewTxCache(ConfigSourceMe{ Name: "test", diff --git a/txcache/virtualSelectionSession_test.go b/txcache/virtualSelectionSession_test.go index 7c50ad67e77..5da2109525a 100644 --- a/txcache/virtualSelectionSession_test.go +++ b/txcache/virtualSelectionSession_test.go @@ -9,14 +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/transaction" - "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" + "github.com/stretchr/testify/require" ) func Test_newVirtualSelectionSession(t *testing.T) { t.Parallel() - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() virtualSession := newVirtualSelectionSession(session, make(map[string]*virtualAccountRecord)) require.NotNil(t, virtualSession) } @@ -27,7 +29,7 @@ func Test_getVirtualRecord(t *testing.T) { t.Run("should return virtual record", func(t *testing.T) { t.Parallel() - sessionMock := txcachemocks.SelectionSessionMock{} + sessionMock := mempool.SelectionSessionMock{} virtualSession := newVirtualSelectionSession(&sessionMock, make(map[string]*virtualAccountRecord)) expectedRecord := virtualAccountRecord{ @@ -52,7 +54,7 @@ func Test_getVirtualRecord(t *testing.T) { t.Run("should return account from real session", func(t *testing.T) { t.Parallel() - sessionMock := txcachemocks.SelectionSessionMock{ + sessionMock := mempool.SelectionSessionMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 2, big.NewInt(2), true, nil }, @@ -80,7 +82,7 @@ func Test_getVirtualRecord(t *testing.T) { t.Run("should create empty record when account does not exist", func(t *testing.T) { t.Parallel() - sessionMock := txcachemocks.SelectionSessionMock{ + sessionMock := mempool.SelectionSessionMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, big.NewInt(0), false, nil }, @@ -98,7 +100,7 @@ func Test_getVirtualRecord(t *testing.T) { t.Parallel() expErr := errors.New("error") - sessionMock := txcachemocks.SelectionSessionMock{ + sessionMock := mempool.SelectionSessionMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, nil, false, expErr }, @@ -117,7 +119,7 @@ func Test_getNonce(t *testing.T) { t.Run("should return nonce from real session", func(t *testing.T) { t.Parallel() - sessionMock := txcachemocks.SelectionSessionMock{ + sessionMock := mempool.SelectionSessionMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 2, big.NewInt(2), true, nil }, @@ -136,7 +138,7 @@ func Test_getNonce(t *testing.T) { t.Run("should return nonce from account record", func(t *testing.T) { t.Parallel() - sessionMock := txcachemocks.SelectionSessionMock{ + sessionMock := mempool.SelectionSessionMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 2, big.NewInt(2), true, nil }, @@ -170,7 +172,7 @@ func Test_getNonce(t *testing.T) { t.Run("should err", func(t *testing.T) { t.Parallel() - sessionMock := txcachemocks.SelectionSessionMock{} + sessionMock := mempool.SelectionSessionMock{} virtualSession := newVirtualSelectionSession(&sessionMock, make(map[string]*virtualAccountRecord)) aliceRecord, err := newVirtualAccountRecord(core.OptionalUint64{Value: 0, HasValue: false}, big.NewInt(1)) @@ -183,7 +185,7 @@ func Test_getNonce(t *testing.T) { t.Run("should return errNonceNotSet", func(t *testing.T) { t.Parallel() - sessionMock := txcachemocks.SelectionSessionMock{ + sessionMock := mempool.SelectionSessionMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 2, big.NewInt(2), true, nil }, @@ -214,10 +216,10 @@ func Test_getNonce(t *testing.T) { } func Test_accumulateConsumedBalance(t *testing.T) { - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() t.Run("when sender is fee payer", func(t *testing.T) { - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() virtualSession := newVirtualSelectionSession(session, make(map[string]*virtualAccountRecord)) a := createTx([]byte("a-7"), "a", 7) @@ -246,7 +248,7 @@ func Test_accumulateConsumedBalance(t *testing.T) { }) t.Run("when relayer is fee payer", func(t *testing.T) { - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() virtualSession := newVirtualSelectionSession(session, make(map[string]*virtualAccountRecord)) a := createTx([]byte("a-7"), "a", 7).withRelayer([]byte("b")).withGasLimit(100_000) @@ -288,7 +290,7 @@ func Test_detectWillBalanceBeExceeded(t *testing.T) { t.Run("should exceed balance", func(t *testing.T) { t.Parallel() - sessionMock := txcachemocks.SelectionSessionMock{} + sessionMock := mempool.SelectionSessionMock{} virtualSession := newVirtualSelectionSession(&sessionMock, make(map[string]*virtualAccountRecord)) aliceRecord := virtualAccountRecord{ @@ -321,7 +323,7 @@ func Test_detectWillBalanceBeExceeded(t *testing.T) { t.Run("should not exceed balance", func(t *testing.T) { t.Parallel() - sessionMock := txcachemocks.SelectionSessionMock{} + sessionMock := mempool.SelectionSessionMock{} virtualSession := newVirtualSelectionSession(&sessionMock, make(map[string]*virtualAccountRecord)) aliceRecord := virtualAccountRecord{ @@ -358,7 +360,7 @@ func Test_isIncorrectlyGuarded(t *testing.T) { t.Run("should return not correctly guarded", func(t *testing.T) { t.Parallel() - sessionMock := txcachemocks.SelectionSessionMock{ + sessionMock := mempool.SelectionSessionMock{ IsIncorrectlyGuardedCalled: func(tx data.TransactionHandler) bool { return true }, @@ -375,7 +377,7 @@ func TestBenchmarkVirtualSelectionSession_getRecord(t *testing.T) { sw := core.NewStopWatch() t.Run("numAccounts = 300, numTransactionsPerAccount = 100", func(t *testing.T) { - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() virtualSession := newVirtualSelectionSession(session, make(map[string]*virtualAccountRecord)) numAccounts := 300 @@ -403,7 +405,7 @@ func TestBenchmarkVirtualSelectionSession_getRecord(t *testing.T) { }) t.Run("numAccounts = 10_000, numTransactionsPerAccount = 3", func(t *testing.T) { - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() sessionWrapper := newVirtualSelectionSession(session, make(map[string]*virtualAccountRecord)) numAccounts := 10_000 @@ -431,7 +433,7 @@ func TestBenchmarkVirtualSelectionSession_getRecord(t *testing.T) { }) t.Run("numAccounts = 30_000, numTransactionsPerAccount = 1", func(t *testing.T) { - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() sessionWrapper := newVirtualSelectionSession(session, make(map[string]*virtualAccountRecord)) numAccounts := 30_000 @@ -481,7 +483,7 @@ func Test_setChangeGuardianIfNeeded(t *testing.T) { b := createTx([]byte("tx-2"), "alice", 43) c := createTx([]byte("tx-3"), "alice", 44).withData([]byte("SetGuardian@newGuardian")).withGasLimit(100000) - session := txcachemocks.NewSelectionSessionMock() + session := mempool.NewSelectionSessionMock() session.IsIncorrectlyGuardedCalled = func(tx data.TransactionHandler) bool { return tx.GetNonce() == b.Tx.GetNonce() // for coverage } diff --git a/txcache/virtualSessionComputer_test.go b/txcache/virtualSessionComputer_test.go index acdcb778b6a..f3f2377f8a8 100644 --- a/txcache/virtualSessionComputer_test.go +++ b/txcache/virtualSessionComputer_test.go @@ -8,7 +8,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/stretchr/testify/require" - "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" ) func Test_fromBreadcrumbToVirtualRecord(t *testing.T) { @@ -97,7 +97,7 @@ func Test_createVirtualSelectionSession(t *testing.T) { t.Parallel() t.Run("should create blocked record for carol because it has discontinuous nonce with session nonce", func(t *testing.T) { - sessionMock := txcachemocks.SelectionSessionMock{ + sessionMock := mempool.SelectionSessionMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 2, big.NewInt(2), true, nil }, @@ -211,7 +211,7 @@ func Test_createVirtualSelectionSession(t *testing.T) { t.Run("should return error from selection session", func(t *testing.T) { var expectedErr = errors.New("expected err") - sessionMock := txcachemocks.SelectionSessionMock{ + sessionMock := mempool.SelectionSessionMock{ GetAccountNonceAndBalanceCalled: func(address []byte) (uint64, *big.Int, bool, error) { return 0, big.NewInt(0), true, expectedErr }, diff --git a/txcache/wrappedTransaction_test.go b/txcache/wrappedTransaction_test.go index 12a8c517dd2..48ed857e45b 100644 --- a/txcache/wrappedTransaction_test.go +++ b/txcache/wrappedTransaction_test.go @@ -5,13 +5,14 @@ import ( "testing" "github.com/multiversx/mx-chain-core-go/data" - "github.com/multiversx/mx-chain-go/testscommon/txcachemocks" "github.com/stretchr/testify/require" + + "github.com/multiversx/mx-chain-go/testscommon/txcachemocks/mempool" ) func TestWrappedTransaction_precomputeFields(t *testing.T) { t.Run("only move balance gas limit", func(t *testing.T) { - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() tx := createTx([]byte("a"), "a", 1).withValue(oneQuintillionBig).withDataLength(1).withGasLimit(51500).withGasPrice(oneBillion) tx.precomputeFields(host) @@ -23,7 +24,7 @@ func TestWrappedTransaction_precomputeFields(t *testing.T) { }) t.Run("move balance gas limit and execution gas limit (a)", func(t *testing.T) { - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() tx := createTx([]byte("b"), "b", 1).withDataLength(1).withGasLimit(51501).withGasPrice(oneBillion) tx.precomputeFields(host) @@ -34,7 +35,7 @@ func TestWrappedTransaction_precomputeFields(t *testing.T) { }) t.Run("move balance gas limit and execution gas limit (b)", func(t *testing.T) { - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() tx := createTx([]byte("c"), "c", 1).withDataLength(1).withGasLimit(oneMilion).withGasPrice(oneBillion) tx.precomputeFields(host) @@ -47,7 +48,7 @@ func TestWrappedTransaction_precomputeFields(t *testing.T) { }) t.Run("with guardian", func(t *testing.T) { - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() tx := createTx([]byte("a"), "a", 1).withValue(oneQuintillionBig) tx.precomputeFields(host) @@ -59,7 +60,7 @@ func TestWrappedTransaction_precomputeFields(t *testing.T) { }) t.Run("with nil transferred value", func(t *testing.T) { - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() tx := createTx([]byte("a"), "a", 1) tx.precomputeFields(host) @@ -69,7 +70,7 @@ func TestWrappedTransaction_precomputeFields(t *testing.T) { }) t.Run("queries host", func(t *testing.T) { - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() host.ComputeTxFeeCalled = func(_ data.TransactionWithFeeHandler) *big.Int { return big.NewInt(42) } @@ -86,7 +87,7 @@ func TestWrappedTransaction_precomputeFields(t *testing.T) { } func TestWrappedTransaction_decideFeePayer(t *testing.T) { - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() t.Run("when sender is fee payer", func(t *testing.T) { tx := createTx([]byte("a"), "a", 1) @@ -106,7 +107,7 @@ func TestWrappedTransaction_decideFeePayer(t *testing.T) { } func TestWrappedTransaction_isTransactionMoreValuableForNetwork(t *testing.T) { - host := txcachemocks.NewMempoolHostMock() + host := mempool.NewMempoolHostMock() t.Run("decide by price per unit", func(t *testing.T) { a := createTx([]byte("a-1"), "a", 1).withDataLength(1).withGasLimit(51500).withGasPrice(oneBillion) diff --git a/update/factory/exportHandlerFactory.go b/update/factory/exportHandlerFactory.go index 44bb0904c86..a42e4fc31a2 100644 --- a/update/factory/exportHandlerFactory.go +++ b/update/factory/exportHandlerFactory.go @@ -38,79 +38,83 @@ var log = logger.GetOrCreate("update/factory") // ArgsExporter is the argument structure to create a new exporter type ArgsExporter struct { - CoreComponents process.CoreComponentsHolder - CryptoComponents process.CryptoComponentsHolder - StatusCoreComponents process.StatusCoreComponentsHolder - NetworkComponents mxFactory.NetworkComponentsHolder - HeaderValidator epochStart.HeaderValidator - DataPool dataRetriever.PoolsHolder - StorageService dataRetriever.StorageService - RequestHandler process.RequestHandler - ShardCoordinator sharding.Coordinator - ActiveAccountsDBs map[state.AccountsDbIdentifier]state.AccountsAdapter - ExistingResolvers dataRetriever.ResolversContainer - ExistingRequesters dataRetriever.RequestersContainer - ExportFolder string - ExportTriesStorageConfig config.StorageConfig - ExportStateStorageConfig config.StorageConfig - ExportStateKeysConfig config.StorageConfig - MaxTrieLevelInMemory uint - WhiteListHandler process.WhiteListHandler - WhiteListerVerifiedTxs process.WhiteListHandler - MainInterceptorsContainer process.InterceptorsContainer - FullArchiveInterceptorsContainer process.InterceptorsContainer - NodesCoordinator nodesCoordinator.NodesCoordinator - HeaderSigVerifier process.InterceptedHeaderSigVerifier - HeaderIntegrityVerifier process.HeaderIntegrityVerifier - ValidityAttester process.ValidityAttester - RoundHandler process.RoundHandler - MaxHardCapForMissingNodes int - NumConcurrentTrieSyncers int - TrieSyncerVersion int - CheckNodesOnDisk bool - NodeOperationMode common.NodeOperation - InterceptedDataVerifierFactory process.InterceptedDataVerifierFactory - Config config.Config + CoreComponents process.CoreComponentsHolder + CryptoComponents process.CryptoComponentsHolder + StatusCoreComponents process.StatusCoreComponentsHolder + NetworkComponents mxFactory.NetworkComponentsHolder + HeaderValidator epochStart.HeaderValidator + DataPool dataRetriever.PoolsHolder + StorageService dataRetriever.StorageService + RequestHandler process.RequestHandler + ShardCoordinator sharding.Coordinator + ActiveAccountsDBs map[state.AccountsDbIdentifier]state.AccountsAdapter + ExistingResolvers dataRetriever.ResolversContainer + ExistingRequesters dataRetriever.RequestersContainer + ExportFolder string + ExportTriesStorageConfig config.StorageConfig + ExportStateStorageConfig config.StorageConfig + ExportStateKeysConfig config.StorageConfig + MaxTrieLevelInMemory uint + WhiteListHandler process.WhiteListHandler + WhiteListerVerifiedTxs process.WhiteListHandler + MainInterceptorsContainer process.InterceptorsContainer + FullArchiveInterceptorsContainer process.InterceptorsContainer + NodesCoordinator nodesCoordinator.NodesCoordinator + HeaderSigVerifier process.InterceptedHeaderSigVerifier + HeaderIntegrityVerifier process.HeaderIntegrityVerifier + ValidityAttester process.ValidityAttester + RoundHandler process.RoundHandler + InterceptorDebugConfig config.InterceptorResolverDebugConfig + MaxHardCapForMissingNodes int + NumConcurrentTrieSyncers int + TrieSyncerVersion int + CheckNodesOnDisk bool + NodeOperationMode common.NodeOperation + InterceptedDataVerifierFactory process.InterceptedDataVerifierFactory + PeerAuthenticationTimeBetweenSendsInSec int64 + Config config.Config } type exportHandlerFactory struct { - coreComponents process.CoreComponentsHolder - cryptoComponents process.CryptoComponentsHolder - statusCoreComponents process.StatusCoreComponentsHolder - networkComponents mxFactory.NetworkComponentsHolder - headerValidator epochStart.HeaderValidator - dataPool dataRetriever.PoolsHolder - storageService dataRetriever.StorageService - requestHandler process.RequestHandler - shardCoordinator sharding.Coordinator - activeAccountsDBs map[state.AccountsDbIdentifier]state.AccountsAdapter - exportFolder string - exportTriesStorageConfig config.StorageConfig - exportStateStorageConfig config.StorageConfig - exportStateKeysConfig config.StorageConfig - maxTrieLevelInMemory uint - whiteListHandler process.WhiteListHandler - whiteListerVerifiedTxs process.WhiteListHandler - mainInterceptorsContainer process.InterceptorsContainer - fullArchiveInterceptorsContainer process.InterceptorsContainer - existingResolvers dataRetriever.ResolversContainer - existingRequesters dataRetriever.RequestersContainer - epochStartTrigger epochStart.TriggerHandler - accounts state.AccountsAdapter - nodesCoordinator nodesCoordinator.NodesCoordinator - headerSigVerifier process.InterceptedHeaderSigVerifier - headerIntegrityVerifier process.HeaderIntegrityVerifier - validityAttester process.ValidityAttester - resolverContainer dataRetriever.ResolversContainer - requestersContainer dataRetriever.RequestersContainer - roundHandler process.RoundHandler - maxHardCapForMissingNodes int - numConcurrentTrieSyncers int - trieSyncerVersion int - checkNodesOnDisk bool - nodeOperationMode common.NodeOperation - interceptedDataVerifierFactory process.InterceptedDataVerifierFactory - config config.Config + coreComponents process.CoreComponentsHolder + cryptoComponents process.CryptoComponentsHolder + statusCoreComponents process.StatusCoreComponentsHolder + networkComponents mxFactory.NetworkComponentsHolder + headerValidator epochStart.HeaderValidator + dataPool dataRetriever.PoolsHolder + storageService dataRetriever.StorageService + requestHandler process.RequestHandler + shardCoordinator sharding.Coordinator + activeAccountsDBs map[state.AccountsDbIdentifier]state.AccountsAdapter + exportFolder string + exportTriesStorageConfig config.StorageConfig + exportStateStorageConfig config.StorageConfig + exportStateKeysConfig config.StorageConfig + maxTrieLevelInMemory uint + whiteListHandler process.WhiteListHandler + whiteListerVerifiedTxs process.WhiteListHandler + mainInterceptorsContainer process.InterceptorsContainer + fullArchiveInterceptorsContainer process.InterceptorsContainer + existingResolvers dataRetriever.ResolversContainer + existingRequesters dataRetriever.RequestersContainer + epochStartTrigger epochStart.TriggerHandler + accounts state.AccountsAdapter + nodesCoordinator nodesCoordinator.NodesCoordinator + headerSigVerifier process.InterceptedHeaderSigVerifier + headerIntegrityVerifier process.HeaderIntegrityVerifier + validityAttester process.ValidityAttester + resolverContainer dataRetriever.ResolversContainer + requestersContainer dataRetriever.RequestersContainer + roundHandler process.RoundHandler + interceptorDebugConfig config.InterceptorResolverDebugConfig + maxHardCapForMissingNodes int + numConcurrentTrieSyncers int + trieSyncerVersion int + checkNodesOnDisk bool + nodeOperationMode common.NodeOperation + interceptedDataVerifierFactory process.InterceptedDataVerifierFactory + peerAuthenticationTimeBetweenSendsInSec int64 + config config.Config } // NewExportHandlerFactory creates an exporter factory @@ -236,40 +240,42 @@ func NewExportHandlerFactory(args ArgsExporter) (*exportHandlerFactory, error) { } e := &exportHandlerFactory{ - coreComponents: args.CoreComponents, - cryptoComponents: args.CryptoComponents, - networkComponents: args.NetworkComponents, - headerValidator: args.HeaderValidator, - dataPool: args.DataPool, - storageService: args.StorageService, - requestHandler: args.RequestHandler, - shardCoordinator: args.ShardCoordinator, - activeAccountsDBs: args.ActiveAccountsDBs, - exportFolder: args.ExportFolder, - exportTriesStorageConfig: args.ExportTriesStorageConfig, - exportStateStorageConfig: args.ExportStateStorageConfig, - exportStateKeysConfig: args.ExportStateKeysConfig, - mainInterceptorsContainer: args.MainInterceptorsContainer, - fullArchiveInterceptorsContainer: args.FullArchiveInterceptorsContainer, - whiteListHandler: args.WhiteListHandler, - whiteListerVerifiedTxs: args.WhiteListerVerifiedTxs, - existingResolvers: args.ExistingResolvers, - existingRequesters: args.ExistingRequesters, - accounts: args.ActiveAccountsDBs[state.UserAccountsState], - nodesCoordinator: args.NodesCoordinator, - headerSigVerifier: args.HeaderSigVerifier, - headerIntegrityVerifier: args.HeaderIntegrityVerifier, - validityAttester: args.ValidityAttester, - maxTrieLevelInMemory: args.MaxTrieLevelInMemory, - roundHandler: args.RoundHandler, - maxHardCapForMissingNodes: args.MaxHardCapForMissingNodes, - numConcurrentTrieSyncers: args.NumConcurrentTrieSyncers, - trieSyncerVersion: args.TrieSyncerVersion, - checkNodesOnDisk: args.CheckNodesOnDisk, - statusCoreComponents: args.StatusCoreComponents, - nodeOperationMode: args.NodeOperationMode, - interceptedDataVerifierFactory: args.InterceptedDataVerifierFactory, - config: args.Config, + coreComponents: args.CoreComponents, + cryptoComponents: args.CryptoComponents, + networkComponents: args.NetworkComponents, + headerValidator: args.HeaderValidator, + dataPool: args.DataPool, + storageService: args.StorageService, + requestHandler: args.RequestHandler, + shardCoordinator: args.ShardCoordinator, + activeAccountsDBs: args.ActiveAccountsDBs, + exportFolder: args.ExportFolder, + exportTriesStorageConfig: args.ExportTriesStorageConfig, + exportStateStorageConfig: args.ExportStateStorageConfig, + exportStateKeysConfig: args.ExportStateKeysConfig, + mainInterceptorsContainer: args.MainInterceptorsContainer, + fullArchiveInterceptorsContainer: args.FullArchiveInterceptorsContainer, + whiteListHandler: args.WhiteListHandler, + whiteListerVerifiedTxs: args.WhiteListerVerifiedTxs, + existingResolvers: args.ExistingResolvers, + existingRequesters: args.ExistingRequesters, + accounts: args.ActiveAccountsDBs[state.UserAccountsState], + nodesCoordinator: args.NodesCoordinator, + headerSigVerifier: args.HeaderSigVerifier, + headerIntegrityVerifier: args.HeaderIntegrityVerifier, + validityAttester: args.ValidityAttester, + maxTrieLevelInMemory: args.MaxTrieLevelInMemory, + roundHandler: args.RoundHandler, + interceptorDebugConfig: args.InterceptorDebugConfig, + maxHardCapForMissingNodes: args.MaxHardCapForMissingNodes, + numConcurrentTrieSyncers: args.NumConcurrentTrieSyncers, + trieSyncerVersion: args.TrieSyncerVersion, + checkNodesOnDisk: args.CheckNodesOnDisk, + statusCoreComponents: args.StatusCoreComponents, + nodeOperationMode: args.NodeOperationMode, + interceptedDataVerifierFactory: args.InterceptedDataVerifierFactory, + peerAuthenticationTimeBetweenSendsInSec: args.PeerAuthenticationTimeBetweenSendsInSec, + config: args.Config, } return e, nil @@ -570,31 +576,32 @@ func (e *exportHandlerFactory) prepareFolders(folder string) error { func (e *exportHandlerFactory) createInterceptors() error { argsInterceptors := ArgsNewFullSyncInterceptorsContainerFactory{ - CoreComponents: e.coreComponents, - CryptoComponents: e.cryptoComponents, - Accounts: e.accounts, - ShardCoordinator: e.shardCoordinator, - NodesCoordinator: e.nodesCoordinator, - MainMessenger: e.networkComponents.NetworkMessenger(), - FullArchiveMessenger: e.networkComponents.FullArchiveNetworkMessenger(), - Store: e.storageService, - DataPool: e.dataPool, - MaxTxNonceDeltaAllowed: math.MaxInt32, - TxFeeHandler: &disabled.FeeHandler{}, - BlockBlackList: cache.NewTimeCache(time.Second), - HeaderSigVerifier: e.headerSigVerifier, - HeaderIntegrityVerifier: e.headerIntegrityVerifier, - SizeCheckDelta: math.MaxUint32, - ValidityAttester: e.validityAttester, - EpochStartTrigger: e.epochStartTrigger, - WhiteListHandler: e.whiteListHandler, - WhiteListerVerifiedTxs: e.whiteListerVerifiedTxs, - MainInterceptorsContainer: e.mainInterceptorsContainer, - FullArchiveInterceptorsContainer: e.fullArchiveInterceptorsContainer, - AntifloodHandler: e.networkComponents.InputAntiFloodHandler(), - NodeOperationMode: e.nodeOperationMode, - InterceptedDataVerifierFactory: e.interceptedDataVerifierFactory, - Config: e.config, + CoreComponents: e.coreComponents, + CryptoComponents: e.cryptoComponents, + Accounts: e.accounts, + ShardCoordinator: e.shardCoordinator, + NodesCoordinator: e.nodesCoordinator, + MainMessenger: e.networkComponents.NetworkMessenger(), + FullArchiveMessenger: e.networkComponents.FullArchiveNetworkMessenger(), + Store: e.storageService, + DataPool: e.dataPool, + MaxTxNonceDeltaAllowed: math.MaxInt32, + TxFeeHandler: &disabled.FeeHandler{}, + BlockBlackList: cache.NewTimeCache(time.Second), + HeaderSigVerifier: e.headerSigVerifier, + HeaderIntegrityVerifier: e.headerIntegrityVerifier, + SizeCheckDelta: math.MaxUint32, + ValidityAttester: e.validityAttester, + EpochStartTrigger: e.epochStartTrigger, + WhiteListHandler: e.whiteListHandler, + WhiteListerVerifiedTxs: e.whiteListerVerifiedTxs, + MainInterceptorsContainer: e.mainInterceptorsContainer, + FullArchiveInterceptorsContainer: e.fullArchiveInterceptorsContainer, + AntifloodHandler: e.networkComponents.InputAntiFloodHandler(), + NodeOperationMode: e.nodeOperationMode, + InterceptedDataVerifierFactory: e.interceptedDataVerifierFactory, + PeerAuthenticationTimeBetweenSendsInSec: e.peerAuthenticationTimeBetweenSendsInSec, + Config: e.config, } fullSyncInterceptors, err := NewFullSyncInterceptorsContainerFactory(argsInterceptors) if err != nil { diff --git a/update/factory/fullSyncInterceptors.go b/update/factory/fullSyncInterceptors.go index be428c6f79d..f681c27d8eb 100644 --- a/update/factory/fullSyncInterceptors.go +++ b/update/factory/fullSyncInterceptors.go @@ -8,10 +8,9 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/core/throttler" "github.com/multiversx/mx-chain-core-go/marshal" - "github.com/multiversx/mx-chain-go/config" - "github.com/multiversx/mx-chain-go/storage/cache" "github.com/multiversx/mx-chain-go/common" + "github.com/multiversx/mx-chain-go/config" "github.com/multiversx/mx-chain-go/dataRetriever" "github.com/multiversx/mx-chain-go/process" "github.com/multiversx/mx-chain-go/process/dataValidators" @@ -23,6 +22,7 @@ import ( "github.com/multiversx/mx-chain-go/sharding" "github.com/multiversx/mx-chain-go/sharding/nodesCoordinator" "github.com/multiversx/mx-chain-go/state" + "github.com/multiversx/mx-chain-go/storage/cache" "github.com/multiversx/mx-chain-go/update" "github.com/multiversx/mx-chain-go/update/disabled" ) @@ -58,31 +58,32 @@ type fullSyncInterceptorsContainerFactory struct { // ArgsNewFullSyncInterceptorsContainerFactory holds the arguments needed for fullSyncInterceptorsContainerFactory type ArgsNewFullSyncInterceptorsContainerFactory struct { - CoreComponents process.CoreComponentsHolder - CryptoComponents process.CryptoComponentsHolder - Accounts state.AccountsAdapter - ShardCoordinator sharding.Coordinator - NodesCoordinator nodesCoordinator.NodesCoordinator - MainMessenger process.TopicHandler - FullArchiveMessenger process.TopicHandler - Store dataRetriever.StorageService - DataPool dataRetriever.PoolsHolder - MaxTxNonceDeltaAllowed int - TxFeeHandler process.FeeHandler - BlockBlackList process.TimeCacher - HeaderSigVerifier process.InterceptedHeaderSigVerifier - HeaderIntegrityVerifier process.HeaderIntegrityVerifier - SizeCheckDelta uint32 - ValidityAttester process.ValidityAttester - EpochStartTrigger process.EpochStartTriggerHandler - WhiteListHandler update.WhiteListHandler - WhiteListerVerifiedTxs update.WhiteListHandler - MainInterceptorsContainer process.InterceptorsContainer - FullArchiveInterceptorsContainer process.InterceptorsContainer - AntifloodHandler process.P2PAntifloodHandler - NodeOperationMode common.NodeOperation - InterceptedDataVerifierFactory process.InterceptedDataVerifierFactory - Config config.Config + CoreComponents process.CoreComponentsHolder + CryptoComponents process.CryptoComponentsHolder + Accounts state.AccountsAdapter + ShardCoordinator sharding.Coordinator + NodesCoordinator nodesCoordinator.NodesCoordinator + MainMessenger process.TopicHandler + FullArchiveMessenger process.TopicHandler + Store dataRetriever.StorageService + DataPool dataRetriever.PoolsHolder + MaxTxNonceDeltaAllowed int + TxFeeHandler process.FeeHandler + BlockBlackList process.TimeCacher + HeaderSigVerifier process.InterceptedHeaderSigVerifier + HeaderIntegrityVerifier process.HeaderIntegrityVerifier + SizeCheckDelta uint32 + ValidityAttester process.ValidityAttester + EpochStartTrigger process.EpochStartTriggerHandler + WhiteListHandler update.WhiteListHandler + WhiteListerVerifiedTxs update.WhiteListHandler + MainInterceptorsContainer process.InterceptorsContainer + FullArchiveInterceptorsContainer process.InterceptorsContainer + AntifloodHandler process.P2PAntifloodHandler + NodeOperationMode common.NodeOperation + InterceptedDataVerifierFactory process.InterceptedDataVerifierFactory + PeerAuthenticationTimeBetweenSendsInSec int64 + Config config.Config } // NewFullSyncInterceptorsContainerFactory is responsible for creating a new interceptors factory object @@ -145,17 +146,19 @@ func NewFullSyncInterceptorsContainerFactory( } argInterceptorFactory := &interceptorFactory.ArgInterceptedDataFactory{ - CoreComponents: args.CoreComponents, - CryptoComponents: args.CryptoComponents, - ShardCoordinator: args.ShardCoordinator, - NodesCoordinator: args.NodesCoordinator, - FeeHandler: args.TxFeeHandler, - HeaderSigVerifier: args.HeaderSigVerifier, - HeaderIntegrityVerifier: args.HeaderIntegrityVerifier, - ValidityAttester: args.ValidityAttester, - EpochStartTrigger: args.EpochStartTrigger, - WhiteListerVerifiedTxs: args.WhiteListerVerifiedTxs, - ArgsParser: smartContract.NewArgumentParser(), + CoreComponents: args.CoreComponents, + CryptoComponents: args.CryptoComponents, + ShardCoordinator: args.ShardCoordinator, + NodesCoordinator: args.NodesCoordinator, + FeeHandler: args.TxFeeHandler, + HeaderSigVerifier: args.HeaderSigVerifier, + HeaderIntegrityVerifier: args.HeaderIntegrityVerifier, + ValidityAttester: args.ValidityAttester, + EpochStartTrigger: args.EpochStartTrigger, + WhiteListerVerifiedTxs: args.WhiteListerVerifiedTxs, + ArgsParser: smartContract.NewArgumentParser(), + PeerAuthCacher: args.DataPool.PeerAuthentications(), + PeerAuthenticationTimeBetweenSendsInSec: args.PeerAuthenticationTimeBetweenSendsInSec, } icf := &fullSyncInterceptorsContainerFactory{ @@ -228,6 +231,11 @@ func (ficf *fullSyncInterceptorsContainerFactory) Create() (process.Interceptors return ficf.mainContainer, ficf.fullArchiveContainer, nil } +// AddShardTrieNodeInterceptors returns nil +func (ficf *fullSyncInterceptorsContainerFactory) AddShardTrieNodeInterceptors(_ process.InterceptorsContainer) error { + return nil +} + func checkBaseParams( coreComponents process.CoreComponentsHolder, cryptoComponents process.CryptoComponentsHolder,